You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
10 KiB
Dart
276 lines
10 KiB
Dart
/*
|
|
* Famedly Matrix SDK
|
|
* Copyright (C) 2020 Famedly GmbH
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
import 'package:matrix/matrix.dart';
|
|
import 'package:matrix/encryption.dart';
|
|
|
|
import 'package:test/test.dart';
|
|
import 'package:olm/olm.dart' as olm;
|
|
|
|
import '../fake_client.dart';
|
|
|
|
void main() {
|
|
group('Bootstrap', () {
|
|
Logs().level = Level.error;
|
|
var olmEnabled = true;
|
|
|
|
late Client client;
|
|
late Map<String, dynamic> oldSecret;
|
|
late String origKeyId;
|
|
|
|
test('setupClient', () async {
|
|
client = await getClient();
|
|
await client.abortSync();
|
|
});
|
|
|
|
test('setup', () async {
|
|
try {
|
|
await olm.init();
|
|
olm.get_library_version();
|
|
} catch (e) {
|
|
olmEnabled = false;
|
|
Logs().w('[LibOlm] Failed to load LibOlm', e);
|
|
}
|
|
Logs().i('[LibOlm] Enabled: $olmEnabled');
|
|
if (!olmEnabled) return;
|
|
|
|
Bootstrap? bootstrap;
|
|
bootstrap = client.encryption!.bootstrap(
|
|
onUpdate: () async {
|
|
while (bootstrap == null) {
|
|
await Future.delayed(Duration(milliseconds: 5));
|
|
}
|
|
if (bootstrap.state == BootstrapState.askWipeSsss) {
|
|
bootstrap.wipeSsss(true);
|
|
} else if (bootstrap.state == BootstrapState.askNewSsss) {
|
|
await bootstrap.newSsss('foxies');
|
|
} else if (bootstrap.state == BootstrapState.askWipeCrossSigning) {
|
|
bootstrap.wipeCrossSigning(true);
|
|
} else if (bootstrap.state == BootstrapState.askSetupCrossSigning) {
|
|
await bootstrap.askSetupCrossSigning(
|
|
setupMasterKey: true,
|
|
setupSelfSigningKey: true,
|
|
setupUserSigningKey: true,
|
|
);
|
|
} else if (bootstrap.state == BootstrapState.askWipeOnlineKeyBackup) {
|
|
bootstrap.wipeOnlineKeyBackup(true);
|
|
} else if (bootstrap.state ==
|
|
BootstrapState.askSetupOnlineKeyBackup) {
|
|
await bootstrap.askSetupOnlineKeyBackup(true);
|
|
}
|
|
},
|
|
);
|
|
while (bootstrap.state != BootstrapState.done) {
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
}
|
|
final defaultKey = client.encryption!.ssss.open();
|
|
await defaultKey.unlock(passphrase: 'foxies');
|
|
|
|
// test all the x-signing keys match up
|
|
for (final keyType in {'master', 'user_signing', 'self_signing'}) {
|
|
final privateKey = base64
|
|
.decode(await defaultKey.getStored('m.cross_signing.$keyType'));
|
|
final keyObj = olm.PkSigning();
|
|
try {
|
|
final pubKey = keyObj.init_with_seed(privateKey);
|
|
expect(
|
|
pubKey,
|
|
client.userDeviceKeys[client.userID]
|
|
?.getCrossSigningKey(keyType)
|
|
?.publicKey);
|
|
} finally {
|
|
keyObj.free();
|
|
}
|
|
}
|
|
|
|
await defaultKey.store('foxes', 'floof');
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
oldSecret =
|
|
json.decode(json.encode(client.accountData['foxes']!.content));
|
|
origKeyId = defaultKey.keyId;
|
|
}, timeout: Timeout(Duration(minutes: 2)));
|
|
|
|
test('change recovery passphrase', () async {
|
|
if (!olmEnabled) return;
|
|
Bootstrap? bootstrap;
|
|
bootstrap = client.encryption!.bootstrap(
|
|
onUpdate: () async {
|
|
while (bootstrap == null) {
|
|
await Future.delayed(Duration(milliseconds: 5));
|
|
}
|
|
if (bootstrap.state == BootstrapState.askWipeSsss) {
|
|
bootstrap.wipeSsss(false);
|
|
} else if (bootstrap.state == BootstrapState.askUseExistingSsss) {
|
|
bootstrap.useExistingSsss(false);
|
|
} else if (bootstrap.state == BootstrapState.askUnlockSsss) {
|
|
await bootstrap.oldSsssKeys![client.encryption!.ssss.defaultKeyId]!
|
|
.unlock(passphrase: 'foxies');
|
|
bootstrap.unlockedSsss();
|
|
} else if (bootstrap.state == BootstrapState.askNewSsss) {
|
|
await bootstrap.newSsss('newfoxies');
|
|
} else if (bootstrap.state == BootstrapState.askWipeCrossSigning) {
|
|
bootstrap.wipeCrossSigning(false);
|
|
} else if (bootstrap.state == BootstrapState.askWipeOnlineKeyBackup) {
|
|
bootstrap.wipeOnlineKeyBackup(false);
|
|
}
|
|
},
|
|
);
|
|
while (bootstrap.state != BootstrapState.done) {
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
}
|
|
final defaultKey = client.encryption!.ssss.open();
|
|
await defaultKey.unlock(passphrase: 'newfoxies');
|
|
|
|
// test all the x-signing keys match up
|
|
for (final keyType in {'master', 'user_signing', 'self_signing'}) {
|
|
final privateKey = base64
|
|
.decode(await defaultKey.getStored('m.cross_signing.$keyType'));
|
|
final keyObj = olm.PkSigning();
|
|
try {
|
|
final pubKey = keyObj.init_with_seed(privateKey);
|
|
expect(
|
|
pubKey,
|
|
client.userDeviceKeys[client.userID]
|
|
?.getCrossSigningKey(keyType)
|
|
?.publicKey);
|
|
} finally {
|
|
keyObj.free();
|
|
}
|
|
}
|
|
|
|
expect(await defaultKey.getStored('foxes'), 'floof');
|
|
}, timeout: Timeout(Duration(minutes: 2)));
|
|
|
|
test('change passphrase with multiple keys', () async {
|
|
if (!olmEnabled) return;
|
|
await client.setAccountData(client.userID!, 'foxes', oldSecret);
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
|
|
Bootstrap? bootstrap;
|
|
bootstrap = client.encryption!.bootstrap(
|
|
onUpdate: () async {
|
|
while (bootstrap == null) {
|
|
await Future.delayed(Duration(milliseconds: 5));
|
|
}
|
|
if (bootstrap.state == BootstrapState.askWipeSsss) {
|
|
bootstrap.wipeSsss(false);
|
|
} else if (bootstrap.state == BootstrapState.askUseExistingSsss) {
|
|
bootstrap.useExistingSsss(false);
|
|
} else if (bootstrap.state == BootstrapState.askUnlockSsss) {
|
|
await bootstrap.oldSsssKeys![client.encryption!.ssss.defaultKeyId]!
|
|
.unlock(passphrase: 'newfoxies');
|
|
await bootstrap.oldSsssKeys![origKeyId]!
|
|
.unlock(passphrase: 'foxies');
|
|
bootstrap.unlockedSsss();
|
|
} else if (bootstrap.state == BootstrapState.askNewSsss) {
|
|
await bootstrap.newSsss('supernewfoxies');
|
|
} else if (bootstrap.state == BootstrapState.askWipeCrossSigning) {
|
|
bootstrap.wipeCrossSigning(false);
|
|
} else if (bootstrap.state == BootstrapState.askWipeOnlineKeyBackup) {
|
|
bootstrap.wipeOnlineKeyBackup(false);
|
|
}
|
|
},
|
|
);
|
|
while (bootstrap.state != BootstrapState.done) {
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
}
|
|
final defaultKey = client.encryption!.ssss.open();
|
|
await defaultKey.unlock(passphrase: 'supernewfoxies');
|
|
|
|
// test all the x-signing keys match up
|
|
for (final keyType in {'master', 'user_signing', 'self_signing'}) {
|
|
final privateKey = base64
|
|
.decode(await defaultKey.getStored('m.cross_signing.$keyType'));
|
|
final keyObj = olm.PkSigning();
|
|
try {
|
|
final pubKey = keyObj.init_with_seed(privateKey);
|
|
expect(
|
|
pubKey,
|
|
client.userDeviceKeys[client.userID]
|
|
?.getCrossSigningKey(keyType)
|
|
?.publicKey);
|
|
} finally {
|
|
keyObj.free();
|
|
}
|
|
}
|
|
|
|
expect(await defaultKey.getStored('foxes'), 'floof');
|
|
}, timeout: Timeout(Duration(minutes: 2)));
|
|
|
|
test('setup new ssss', () async {
|
|
if (!olmEnabled) return;
|
|
client.accountData.clear();
|
|
Bootstrap? bootstrap;
|
|
bootstrap = client.encryption!.bootstrap(
|
|
onUpdate: () async {
|
|
while (bootstrap == null) {
|
|
await Future.delayed(Duration(milliseconds: 5));
|
|
}
|
|
if (bootstrap.state == BootstrapState.askNewSsss) {
|
|
await bootstrap.newSsss('thenewestfoxies');
|
|
} else if (bootstrap.state == BootstrapState.askSetupCrossSigning) {
|
|
await bootstrap.askSetupCrossSigning();
|
|
} else if (bootstrap.state ==
|
|
BootstrapState.askSetupOnlineKeyBackup) {
|
|
await bootstrap.askSetupOnlineKeyBackup(false);
|
|
}
|
|
},
|
|
);
|
|
while (bootstrap.state != BootstrapState.done) {
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
}
|
|
final defaultKey = client.encryption!.ssss.open();
|
|
await defaultKey.unlock(passphrase: 'thenewestfoxies');
|
|
}, timeout: Timeout(Duration(minutes: 2)));
|
|
|
|
test('bad ssss', () async {
|
|
if (!olmEnabled) return;
|
|
client.accountData.clear();
|
|
await client.setAccountData(client.userID!, 'foxes', oldSecret);
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
var askedBadSsss = false;
|
|
Bootstrap? bootstrap;
|
|
bootstrap = client.encryption!.bootstrap(
|
|
onUpdate: () async {
|
|
while (bootstrap == null) {
|
|
await Future.delayed(Duration(milliseconds: 5));
|
|
}
|
|
if (bootstrap.state == BootstrapState.askWipeSsss) {
|
|
bootstrap.wipeSsss(false);
|
|
} else if (bootstrap.state == BootstrapState.askBadSsss) {
|
|
askedBadSsss = true;
|
|
bootstrap.ignoreBadSecrets(false);
|
|
}
|
|
},
|
|
);
|
|
while (bootstrap.state != BootstrapState.error) {
|
|
await Future.delayed(Duration(milliseconds: 50));
|
|
}
|
|
expect(askedBadSsss, true);
|
|
});
|
|
|
|
test('dispose client', () async {
|
|
if (!olmEnabled) return;
|
|
await client.dispose(closeDatabase: true);
|
|
});
|
|
});
|
|
}
|