Merge branch 'soru/auto-selfsign' into 'main'

feat: Auto-selfsign and auto-cache when opening ssss keys

See merge request famedly/famedlysdk!650
This commit is contained in:
Krille Fear 2021-02-13 13:59:18 +00:00
commit 1f7e517dbf
3 changed files with 73 additions and 9 deletions

View File

@ -23,6 +23,7 @@ import 'package:olm/olm.dart' as olm;
import '../famedlysdk.dart'; import '../famedlysdk.dart';
import 'encryption.dart'; import 'encryption.dart';
import 'ssss.dart';
class CrossSigning { class CrossSigning {
final Encryption encryption; final Encryption encryption;
@ -71,13 +72,21 @@ class CrossSigning {
} }
Future<void> selfSign( Future<void> selfSign(
{String passphrase, String recoveryKey, String keyOrPassphrase}) async { {String passphrase,
final handle = encryption.ssss.open(EventTypes.CrossSigningMasterKey); String recoveryKey,
await handle.unlock( String keyOrPassphrase,
OpenSSSS openSsss}) async {
var handle = openSsss;
if (handle == null) {
handle = encryption.ssss.open(EventTypes.CrossSigningMasterKey);
await handle.unlock(
passphrase: passphrase, passphrase: passphrase,
recoveryKey: recoveryKey, recoveryKey: recoveryKey,
keyOrPassphrase: keyOrPassphrase); keyOrPassphrase: keyOrPassphrase,
await handle.maybeCacheAll(); postUnlock: false,
);
await handle.maybeCacheAll();
}
final masterPrivateKey = final masterPrivateKey =
base64.decode(await handle.getStored(EventTypes.CrossSigningMasterKey)); base64.decode(await handle.getStored(EventTypes.CrossSigningMasterKey));
final keyObj = olm.PkSigning(); final keyObj = olm.PkSigning();

View File

@ -29,6 +29,7 @@ import 'package:password_hash/password_hash.dart';
import '../famedlysdk.dart'; import '../famedlysdk.dart';
import '../src/database/database.dart'; import '../src/database/database.dart';
import '../src/utils/run_in_background.dart'; import '../src/utils/run_in_background.dart';
import '../src/utils/run_in_root.dart';
import 'encryption.dart'; import 'encryption.dart';
const CACHE_TYPES = <String>{ const CACHE_TYPES = <String>{
@ -617,20 +618,32 @@ class OpenSSSS {
Uint8List privateKey; Uint8List privateKey;
bool get isUnlocked => privateKey != null; bool get isUnlocked => privateKey != null;
bool get hasPassphrase => keyData.passphrase != null;
String get recoveryKey => String get recoveryKey =>
isUnlocked ? SSSS.encodeRecoveryKey(privateKey) : null; isUnlocked ? SSSS.encodeRecoveryKey(privateKey) : null;
Future<void> unlock( Future<void> unlock(
{String passphrase, String recoveryKey, String keyOrPassphrase}) async { {String passphrase,
String recoveryKey,
String keyOrPassphrase,
bool postUnlock = true}) async {
if (keyOrPassphrase != null) { if (keyOrPassphrase != null) {
try { try {
await unlock(recoveryKey: keyOrPassphrase); await unlock(recoveryKey: keyOrPassphrase, postUnlock: postUnlock);
} catch (_) { } catch (_) {
await unlock(passphrase: keyOrPassphrase); if (hasPassphrase) {
await unlock(passphrase: keyOrPassphrase, postUnlock: postUnlock);
} else {
rethrow;
}
} }
return; return;
} else if (passphrase != null) { } else if (passphrase != null) {
if (!hasPassphrase) {
throw Exception(
'Tried to unlock with passphrase while key does not have a passphrase');
}
privateKey = await runInBackground( privateKey = await runInBackground(
_keyFromPassphrase, _keyFromPassphrase,
_KeyFromPassphraseArgs( _KeyFromPassphraseArgs(
@ -647,6 +660,9 @@ class OpenSSSS {
privateKey = null; privateKey = null;
throw Exception('Inalid key'); throw Exception('Inalid key');
} }
if (postUnlock) {
await runInRoot(() => _postUnlock());
}
} }
void setPrivateKey(Uint8List key) { void setPrivateKey(Uint8List key) {
@ -671,6 +687,19 @@ class OpenSSSS {
Future<void> maybeCacheAll() async { Future<void> maybeCacheAll() async {
await ssss.maybeCacheAll(keyId, privateKey); await ssss.maybeCacheAll(keyId, privateKey);
} }
Future<void> _postUnlock() async {
// first try to cache all secrets that aren't cached yet
await maybeCacheAll();
// now try to self-sign
if (ssss.encryption.crossSigning.enabled && ssss.client.isUnknownSession) {
try {
await ssss.encryption.crossSigning.selfSign(openSsss: this);
} catch (e, s) {
Logs().e('[SSSS] Failed to self-sign', e, s);
}
}
}
} }
class _KeyFromPassphraseArgs { class _KeyFromPassphraseArgs {

View File

@ -124,9 +124,10 @@ void main() {
}); });
test('cache', () async { test('cache', () async {
await client.encryption.ssss.clearCache();
final handle = final handle =
client.encryption.ssss.open(EventTypes.CrossSigningSelfSigning); client.encryption.ssss.open(EventTypes.CrossSigningSelfSigning);
await handle.unlock(recoveryKey: SSSS_KEY); await handle.unlock(recoveryKey: SSSS_KEY, postUnlock: false);
expect( expect(
(await client.encryption.ssss (await client.encryption.ssss
.getCached(EventTypes.CrossSigningSelfSigning)) != .getCached(EventTypes.CrossSigningSelfSigning)) !=
@ -155,6 +156,30 @@ void main() {
true); true);
}); });
test('postUnlock', () async {
await client.encryption.ssss.clearCache();
client.userDeviceKeys[client.userID].masterKey.setDirectVerified(false);
final handle =
client.encryption.ssss.open(EventTypes.CrossSigningSelfSigning);
await handle.unlock(recoveryKey: SSSS_KEY);
expect(
(await client.encryption.ssss
.getCached(EventTypes.CrossSigningSelfSigning)) !=
null,
true);
expect(
(await client.encryption.ssss
.getCached(EventTypes.CrossSigningUserSigning)) !=
null,
true);
expect(
(await client.encryption.ssss.getCached(EventTypes.MegolmBackup)) !=
null,
true);
expect(
client.userDeviceKeys[client.userID].masterKey.directVerified, true);
});
test('make share requests', () async { test('make share requests', () async {
final key = final key =
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE'];
@ -245,6 +270,7 @@ void main() {
final key = final key =
client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE']; client.userDeviceKeys[client.userID].deviceKeys['OTHERDEVICE'];
key.setDirectVerified(false); key.setDirectVerified(false);
client.userDeviceKeys[client.userID].masterKey.setDirectVerified(false);
event = ToDeviceEvent( event = ToDeviceEvent(
sender: client.userID, sender: client.userID,
type: 'm.secret.request', type: 'm.secret.request',