diff --git a/.github/workflows/app.yml b/.github/workflows/app.yml index 4322566d..eb3ff6dc 100644 --- a/.github/workflows/app.yml +++ b/.github/workflows/app.yml @@ -18,6 +18,11 @@ jobs: - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ env.dart_version }} + - uses: famedly/backend-build-workflows/.github/actions/rust-prepare@main + with: + gitlab_user: ${{ secrets.GITLAB_USER }} + gitlab_pass: ${{ secrets.GITLAB_PASS }} + gitlab_ssh: ${{ secrets.CI_SSH_PRIVATE_KEY}} - name: Run tests run: | export HOMESERVER_IMPLEMENTATION=${{matrix.homeserver}} @@ -27,6 +32,7 @@ jobs: source scripts/integration-create-environment-variables.sh scripts/integration-prepare-homeserver.sh scripts/prepare.sh + scripts/prepare_vodozemac.sh scripts/test_driver.sh coverage_without_olm: @@ -54,7 +60,7 @@ jobs: coverage: #runs-on: arm-ubuntu-latest-16core runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - run: cat .github/workflows/versions.env >> $GITHUB_ENV @@ -62,9 +68,15 @@ jobs: with: sdk: ${{ env.dart_version }} #architecture: "arm64" + - uses: famedly/backend-build-workflows/.github/actions/rust-prepare@main + with: + gitlab_user: ${{ secrets.GITLAB_USER }} + gitlab_pass: ${{ secrets.GITLAB_PASS }} + gitlab_ssh: ${{ secrets.CI_SSH_PRIVATE_KEY}} - name: Run tests run: | sudo apt-get update && sudo apt-get install --no-install-recommends --no-install-suggests -y lcov libsqlite3-0 libsqlite3-dev libolm3 libssl3 + ./scripts/prepare_vodozemac.sh ./scripts/test.sh - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} diff --git a/.gitignore b/.gitignore index 68373a03..5419541b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ coverage/ coverage_badge.svg coverage.xml TEST-report.* +rust # IntelliJ related *.iml diff --git a/lib/encryption/cross_signing.dart b/lib/encryption/cross_signing.dart index 70c89a6d..f725fff2 100644 --- a/lib/encryption/cross_signing.dart +++ b/lib/encryption/cross_signing.dart @@ -16,9 +16,10 @@ * along with this program. If not, see . */ +import 'dart:convert'; import 'dart:typed_data'; -import 'package:olm/olm.dart' as olm; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption/encryption.dart'; import 'package:matrix/encryption/ssss.dart'; @@ -31,26 +32,22 @@ class CrossSigning { CrossSigning(this.encryption) { encryption.ssss.setValidator(EventTypes.CrossSigningSelfSigning, (String secret) async { - final keyObj = olm.PkSigning(); try { - return keyObj.init_with_seed(base64decodeUnpadded(secret)) == + final keyObj = vod.PkSigning.fromSecretKey(secret); + return keyObj.publicKey.toBase64() == client.userDeviceKeys[client.userID]!.selfSigningKey!.ed25519Key; } catch (_) { return false; - } finally { - keyObj.free(); } }); encryption.ssss.setValidator(EventTypes.CrossSigningUserSigning, (String secret) async { - final keyObj = olm.PkSigning(); try { - return keyObj.init_with_seed(base64decodeUnpadded(secret)) == + final keyObj = vod.PkSigning.fromSecretKey(secret); + return keyObj.publicKey.toBase64() == client.userDeviceKeys[client.userID]!.userSigningKey!.ed25519Key; } catch (_) { return false; - } finally { - keyObj.free(); } }); } @@ -92,14 +89,13 @@ class CrossSigning { final masterPrivateKey = base64decodeUnpadded( await handle.getStored(EventTypes.CrossSigningMasterKey), ); - final keyObj = olm.PkSigning(); String? masterPubkey; try { - masterPubkey = keyObj.init_with_seed(masterPrivateKey); + masterPubkey = vod.PkSigning.fromSecretKey(base64Encode(masterPrivateKey)) + .publicKey + .toBase64(); } catch (e) { masterPubkey = null; - } finally { - keyObj.free(); } final userDeviceKeys = client.userDeviceKeys[client.userID]?.deviceKeys[client.deviceID]; @@ -210,12 +206,7 @@ class CrossSigning { } String _sign(String canonicalJson, Uint8List key) { - final keyObj = olm.PkSigning(); - try { - keyObj.init_with_seed(key); - return keyObj.sign(canonicalJson); - } finally { - keyObj.free(); - } + final keyObj = vod.PkSigning.fromSecretKey(base64Encode(key)); + return keyObj.sign(canonicalJson).toBase64(); } } diff --git a/lib/encryption/key_manager.dart b/lib/encryption/key_manager.dart index 77fe1679..ee7ef210 100644 --- a/lib/encryption/key_manager.dart +++ b/lib/encryption/key_manager.dart @@ -21,6 +21,7 @@ import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:olm/olm.dart' as olm; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption/encryption.dart'; import 'package:matrix/encryption/utils/base64_unpadded.dart'; @@ -45,19 +46,18 @@ class KeyManager { KeyManager(this.encryption) { encryption.ssss.setValidator(megolmKey, (String secret) async { - final keyObj = olm.PkDecryption(); try { + final keyObj = vod.PkDecryption.fromSecretKey( + vod.Curve25519PublicKey.fromBase64(secret), + ); final info = await getRoomKeysBackupInfo(false); if (info.algorithm != BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2) { return false; } - return keyObj.init_with_private_key(base64decodeUnpadded(secret)) == - info.authData['public_key']; + return keyObj.publicKey == info.authData['public_key']; } catch (_) { return false; - } finally { - keyObj.free(); } }); encryption.ssss.setCacheCallback(megolmKey, (String secret) { @@ -669,54 +669,57 @@ class KeyManager { } final privateKey = base64decodeUnpadded((await encryption.ssss.getCached(megolmKey))!); - final decryption = olm.PkDecryption(); final info = await getRoomKeysBackupInfo(); String backupPubKey; - try { - backupPubKey = decryption.init_with_private_key(privateKey); - if (info.algorithm != BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2 || - info.authData['public_key'] != backupPubKey) { - return; - } - for (final roomEntry in keys.rooms.entries) { - final roomId = roomEntry.key; - for (final sessionEntry in roomEntry.value.sessions.entries) { - final sessionId = sessionEntry.key; - final session = sessionEntry.value; - final sessionData = session.sessionData; - Map? decrypted; - try { - decrypted = json.decode( - decryption.decrypt( - sessionData['ephemeral'] as String, - sessionData['mac'] as String, - sessionData['ciphertext'] as String, + final decryption = vod.PkDecryption.fromSecretKey( + vod.Curve25519PublicKey.fromBytes(privateKey), + ); + backupPubKey = decryption.publicKey; + + if (info.algorithm != BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2 || + info.authData['public_key'] != backupPubKey) { + return; + } + for (final roomEntry in keys.rooms.entries) { + final roomId = roomEntry.key; + for (final sessionEntry in roomEntry.value.sessions.entries) { + final sessionId = sessionEntry.key; + final session = sessionEntry.value; + final sessionData = session.sessionData; + Map? decrypted; + try { + decrypted = json.decode( + decryption.decrypt( + vod.PkMessage( + base64decodeUnpadded(sessionData['ciphertext'] as String), + base64decodeUnpadded(sessionData['mac'] as String), + vod.Curve25519PublicKey.fromBase64( + sessionData['ephemeral'] as String, + ), ), - ); - } catch (e, s) { - Logs().e('[LibOlm] Error decrypting room key', e, s); - } - final senderKey = decrypted?.tryGet('sender_key'); - if (decrypted != null && senderKey != null) { - decrypted['session_id'] = sessionId; - decrypted['room_id'] = roomId; - await setInboundGroupSession( - roomId, - sessionId, - senderKey, - decrypted, - forwarded: true, - senderClaimedKeys: - decrypted.tryGetMap('sender_claimed_keys') ?? - {}, - uploaded: true, - ); - } + ), + ); + } catch (e, s) { + Logs().e('[LibOlm] Error decrypting room key', e, s); + } + final senderKey = decrypted?.tryGet('sender_key'); + if (decrypted != null && senderKey != null) { + decrypted['session_id'] = sessionId; + decrypted['room_id'] = roomId; + await setInboundGroupSession( + roomId, + sessionId, + senderKey, + decrypted, + forwarded: true, + senderClaimedKeys: + decrypted.tryGetMap('sender_claimed_keys') ?? + {}, + uploaded: true, + ); } } - } finally { - decryption.free(); } } @@ -875,57 +878,56 @@ class KeyManager { final privateKey = base64decodeUnpadded((await encryption.ssss.getCached(megolmKey))!); // decryption is needed to calculate the public key and thus see if the claimed information is in fact valid - final decryption = olm.PkDecryption(); + final info = await getRoomKeysBackupInfo(false); String backupPubKey; - try { - backupPubKey = decryption.init_with_private_key(privateKey); - if (info.algorithm != - BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2 || - info.authData['public_key'] != backupPubKey) { - decryption.free(); - return; - } - final args = GenerateUploadKeysArgs( - pubkey: backupPubKey, - dbSessions: [], - userId: userID, + final decryption = vod.PkDecryption.fromSecretKey( + vod.Curve25519PublicKey.fromBytes(privateKey), + ); + backupPubKey = decryption.publicKey; + + if (info.algorithm != + BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2 || + info.authData['public_key'] != backupPubKey) { + return; + } + final args = GenerateUploadKeysArgs( + pubkey: backupPubKey, + dbSessions: [], + userId: userID, + ); + // we need to calculate verified beforehand, as else we pass a closure to an isolate + // with 500 keys they do, however, noticably block the UI, which is why we give brief async suspentions in here + // so that the event loop can progress + var i = 0; + for (final dbSession in dbSessions) { + final device = + client.getUserDeviceKeysByCurve25519Key(dbSession.senderKey); + args.dbSessions.add( + DbInboundGroupSessionBundle( + dbSession: dbSession, + verified: device?.verified ?? false, + ), ); - // we need to calculate verified beforehand, as else we pass a closure to an isolate - // with 500 keys they do, however, noticably block the UI, which is why we give brief async suspentions in here - // so that the event loop can progress - var i = 0; - for (final dbSession in dbSessions) { - final device = - client.getUserDeviceKeysByCurve25519Key(dbSession.senderKey); - args.dbSessions.add( - DbInboundGroupSessionBundle( - dbSession: dbSession, - verified: device?.verified ?? false, - ), - ); - i++; - if (i > 10) { - await Future.delayed(Duration(milliseconds: 1)); - i = 0; - } + i++; + if (i > 10) { + await Future.delayed(Duration(milliseconds: 1)); + i = 0; } - final roomKeys = - await client.nativeImplementations.generateUploadKeys(args); - Logs().i('[Key Manager] Uploading ${dbSessions.length} room keys...'); - // upload the payload... - await client.putRoomKeys(info.version, roomKeys); - // and now finally mark all the keys as uploaded - // no need to optimze this, as we only run it so seldomly and almost never with many keys at once - for (final dbSession in dbSessions) { - await database.markInboundGroupSessionAsUploaded( - dbSession.roomId, - dbSession.sessionId, - ); - } - } finally { - decryption.free(); + } + final roomKeys = + await client.nativeImplementations.generateUploadKeys(args); + Logs().i('[Key Manager] Uploading ${dbSessions.length} room keys...'); + // upload the payload... + await client.putRoomKeys(info.version, roomKeys); + // and now finally mark all the keys as uploaded + // no need to optimze this, as we only run it so seldomly and almost never with many keys at once + for (final dbSession in dbSessions) { + await database.markInboundGroupSessionAsUploaded( + dbSession.roomId, + dbSession.sessionId, + ); } } catch (e, s) { Logs().e('[Key Manager] Error uploading room keys', e, s); @@ -1247,9 +1249,10 @@ class RoomKeyRequest extends ToDeviceEvent { /// you would likely want to use [NativeImplementations] and /// [Client.nativeImplementations] instead RoomKeys generateUploadKeysImplementation(GenerateUploadKeysArgs args) { - final enc = olm.PkEncryption(); try { - enc.set_recipient_key(args.pubkey); + final enc = vod.PkEncryption.fromPublicKey( + vod.Curve25519PublicKey.fromBase64(args.pubkey), + ); // first we generate the payload to upload all the session keys in this chunk final roomKeys = RoomKeys(rooms: {}); for (final dbSession in args.dbSessions) { @@ -1279,17 +1282,15 @@ RoomKeys generateUploadKeysImplementation(GenerateUploadKeysArgs args) { forwardedCount: sess.forwardingCurve25519KeyChain.length, isVerified: dbSession.verified, //device?.verified ?? false, sessionData: { - 'ephemeral': encrypted.ephemeral, - 'ciphertext': encrypted.ciphertext, - 'mac': encrypted.mac, + 'ephemeral': encrypted.ephemeralKey.toBase64(), + 'ciphertext': base64Encode(encrypted.ciphertext), + 'mac': base64Encode(encrypted.mac), }, ); } - enc.free(); return roomKeys; } catch (e, s) { Logs().e('[Key Manager] Error generating payload', e, s); - enc.free(); rethrow; } } diff --git a/lib/encryption/utils/bootstrap.dart b/lib/encryption/utils/bootstrap.dart index 1746f28e..1645115f 100644 --- a/lib/encryption/utils/bootstrap.dart +++ b/lib/encryption/utils/bootstrap.dart @@ -20,12 +20,11 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:canonical_json/canonical_json.dart'; -import 'package:olm/olm.dart' as olm; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption/encryption.dart'; import 'package:matrix/encryption/key_manager.dart'; import 'package:matrix/encryption/ssss.dart'; -import 'package:matrix/encryption/utils/base64_unpadded.dart'; import 'package:matrix/matrix.dart'; enum BootstrapState { @@ -371,106 +370,82 @@ class Bootstrap { } final userID = client.userID!; try { - Uint8List masterSigningKey; + String masterSigningKey; final secretsToStore = {}; MatrixCrossSigningKey? masterKey; MatrixCrossSigningKey? selfSigningKey; MatrixCrossSigningKey? userSigningKey; String? masterPub; if (setupMasterKey) { - final master = olm.PkSigning(); - try { - masterSigningKey = master.generate_seed(); - masterPub = master.init_with_seed(masterSigningKey); - final json = { - 'user_id': userID, - 'usage': ['master'], - 'keys': { - 'ed25519:$masterPub': masterPub, - }, - }; - masterKey = MatrixCrossSigningKey.fromJson(json); - secretsToStore[EventTypes.CrossSigningMasterKey] = - base64.encode(masterSigningKey); - } finally { - master.free(); - } + final master = vod.PkSigning(); + masterSigningKey = master.secretKey; + masterPub = master.publicKey.toBase64(); + final json = { + 'user_id': userID, + 'usage': ['master'], + 'keys': { + 'ed25519:$masterPub': masterPub, + }, + }; + masterKey = MatrixCrossSigningKey.fromJson(json); + secretsToStore[EventTypes.CrossSigningMasterKey] = masterSigningKey; } else { Logs().v('Get stored key...'); - masterSigningKey = base64decodeUnpadded( - await newSsssKey?.getStored(EventTypes.CrossSigningMasterKey) ?? '', - ); + masterSigningKey = + await newSsssKey?.getStored(EventTypes.CrossSigningMasterKey) ?? ''; if (masterSigningKey.isEmpty) { // no master signing key :( throw BootstrapBadStateException('No master key'); } - final master = olm.PkSigning(); - try { - masterPub = master.init_with_seed(masterSigningKey); - } finally { - master.free(); - } + final master = vod.PkSigning.fromSecretKey(masterSigningKey); + masterPub = master.publicKey.toBase64(); } String? sign(Map object) { - final keyObj = olm.PkSigning(); - try { - keyObj.init_with_seed(masterSigningKey); - return keyObj - .sign(String.fromCharCodes(canonicalJson.encode(object))); - } finally { - keyObj.free(); - } + final keyObj = vod.PkSigning.fromSecretKey(masterSigningKey); + return keyObj + .sign(String.fromCharCodes(canonicalJson.encode(object))) + .toBase64(); } if (setupSelfSigningKey) { - final selfSigning = olm.PkSigning(); - try { - final selfSigningPriv = selfSigning.generate_seed(); - final selfSigningPub = selfSigning.init_with_seed(selfSigningPriv); - final json = { - 'user_id': userID, - 'usage': ['self_signing'], - 'keys': { - 'ed25519:$selfSigningPub': selfSigningPub, - }, - }; - final signature = sign(json); - json['signatures'] = { - userID: { - 'ed25519:$masterPub': signature, - }, - }; - selfSigningKey = MatrixCrossSigningKey.fromJson(json); - secretsToStore[EventTypes.CrossSigningSelfSigning] = - base64.encode(selfSigningPriv); - } finally { - selfSigning.free(); - } + final selfSigning = vod.PkSigning(); + final selfSigningPriv = selfSigning.secretKey; + final selfSigningPub = selfSigning.publicKey.toBase64(); + final json = { + 'user_id': userID, + 'usage': ['self_signing'], + 'keys': { + 'ed25519:$selfSigningPub': selfSigningPub, + }, + }; + final signature = sign(json); + json['signatures'] = { + userID: { + 'ed25519:$masterPub': signature, + }, + }; + selfSigningKey = MatrixCrossSigningKey.fromJson(json); + secretsToStore[EventTypes.CrossSigningSelfSigning] = selfSigningPriv; } if (setupUserSigningKey) { - final userSigning = olm.PkSigning(); - try { - final userSigningPriv = userSigning.generate_seed(); - final userSigningPub = userSigning.init_with_seed(userSigningPriv); - final json = { - 'user_id': userID, - 'usage': ['user_signing'], - 'keys': { - 'ed25519:$userSigningPub': userSigningPub, - }, - }; - final signature = sign(json); - json['signatures'] = { - userID: { - 'ed25519:$masterPub': signature, - }, - }; - userSigningKey = MatrixCrossSigningKey.fromJson(json); - secretsToStore[EventTypes.CrossSigningUserSigning] = - base64.encode(userSigningPriv); - } finally { - userSigning.free(); - } + final userSigning = vod.PkSigning(); + final userSigningPriv = userSigning.secretKey; + final userSigningPub = userSigning.publicKey.toBase64(); + final json = { + 'user_id': userID, + 'usage': ['user_signing'], + 'keys': { + 'ed25519:$userSigningPub': userSigningPub, + }, + }; + final signature = sign(json); + json['signatures'] = { + userID: { + 'ed25519:$masterPub': signature, + }, + }; + userSigningKey = MatrixCrossSigningKey.fromJson(json); + secretsToStore[EventTypes.CrossSigningUserSigning] = userSigningPriv; } // upload the keys! state = BootstrapState.loading; @@ -561,15 +536,13 @@ class Bootstrap { return; } try { - final keyObj = olm.PkDecryption(); + final keyObj = vod.PkDecryption(); String pubKey; Uint8List privKey; - try { - pubKey = keyObj.generate_key(); - privKey = keyObj.get_private_key(); - } finally { - keyObj.free(); - } + + pubKey = keyObj.publicKey; + privKey = keyObj.privateKey; + Logs().v('Create the new backup version...'); await client.postRoomKeysVersion( BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2, diff --git a/lib/encryption/utils/json_signature_check_extension.dart b/lib/encryption/utils/json_signature_check_extension.dart index 7c1f4f6c..e691d415 100644 --- a/lib/encryption/utils/json_signature_check_extension.dart +++ b/lib/encryption/utils/json_signature_check_extension.dart @@ -17,7 +17,7 @@ */ import 'package:canonical_json/canonical_json.dart'; -import 'package:olm/olm.dart' as olm; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; @@ -37,15 +37,15 @@ extension JsonSignatureCheckExtension on Map { final canonical = canonicalJson.encode(this); final message = String.fromCharCodes(canonical); var isValid = false; - final olmutil = olm.Utility(); try { - olmutil.ed25519_verify(key, message, signature); + vod.Ed25519PublicKey.fromBase64(key).verify( + message: message, + signature: vod.Ed25519Signature.fromBase64(signature), + ); isValid = true; } catch (e, s) { isValid = false; Logs().w('[LibOlm] Signature check failed', e, s); - } finally { - olmutil.free(); } return isValid; } diff --git a/lib/encryption/utils/key_verification.dart b/lib/encryption/utils/key_verification.dart index 007c00b2..467b0cde 100644 --- a/lib/encryption/utils/key_verification.dart +++ b/lib/encryption/utils/key_verification.dart @@ -21,8 +21,9 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:canonical_json/canonical_json.dart'; -import 'package:olm/olm.dart' as olm; +import 'package:crypto/crypto.dart' as crypto; import 'package:typed_data/typed_data.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption/encryption.dart'; import 'package:matrix/encryption/utils/base64_unpadded.dart'; @@ -1258,12 +1259,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { String? commitment; late String theirPublicKey; Map? macPayload; - olm.SAS? sas; - - @override - void dispose() { - sas?.free(); - } + vod.Sas? sas; + vod.EstablishedSas? establishedSas; List get knownAuthentificationTypes { final types = []; @@ -1322,7 +1319,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { await _sendKey(); } else { // we already sent our key, time to verify the commitment being valid - if (!_validateCommitment()) { + if (await _validateCommitment() == false) { await request.cancel('m.mismatched_commitment'); return; } @@ -1415,8 +1412,8 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { } Future _sendAccept() async { - final sas = this.sas = olm.SAS(); - commitment = _makeCommitment(sas.get_pubkey(), startCanonicalJson); + final sas = this.sas = vod.Sas(); + commitment = await _makeCommitment(sas.publicKey, startCanonicalJson); await request.send(EventTypes.KeyVerificationAccept, { 'method': type, 'key_agreement_protocol': keyAgreementProtocol, @@ -1451,31 +1448,31 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { } authenticationTypes = possibleAuthenticationTypes; commitment = payload['commitment']; - sas = olm.SAS(); + sas = vod.Sas(); return true; } Future _sendKey() async { await request.send('m.key.verification.key', { - 'key': sas!.get_pubkey(), + 'key': sas!.publicKey, }); } void _handleKey(Map payload) { theirPublicKey = payload['key']; - sas!.set_their_key(payload['key']); + establishedSas = sas!.establishSasSecret(payload['key']); } - bool _validateCommitment() { - final checkCommitment = _makeCommitment(theirPublicKey, startCanonicalJson); + Future _validateCommitment() async { + final checkCommitment = + await _makeCommitment(theirPublicKey, startCanonicalJson); return commitment == checkCommitment; } Uint8List makeSas(int bytes) { var sasInfo = ''; if (keyAgreementProtocol == 'curve25519-hkdf-sha256') { - final ourInfo = - '${client.userID}|${client.deviceID}|${sas!.get_pubkey()}|'; + final ourInfo = '${client.userID}|${client.deviceID}|${sas!.publicKey}|'; final theirInfo = '${request.userId}|${request.deviceId}|$theirPublicKey|'; sasInfo = @@ -1488,7 +1485,7 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { } else { throw Exception('Unknown key agreement protocol'); } - return sas!.generate_bytes(sasInfo, bytes); + return establishedSas!.generateBytes(sasInfo, bytes); } Future _sendMac() async { @@ -1554,21 +1551,20 @@ class _KeyVerificationMethodSas extends _KeyVerificationMethod { }); } - String _makeCommitment(String pubKey, String canonicalJson) { + Future _makeCommitment(String pubKey, String canonicalJson) async { if (hash == 'sha256') { - final olmutil = olm.Utility(); - final ret = olmutil.sha256(pubKey + canonicalJson); - olmutil.free(); - return ret; + final bytes = utf8.encode(pubKey + canonicalJson); + final digest = crypto.sha256.convert(bytes); + return base64.encode(digest.bytes); } throw Exception('Unknown hash method'); } String _calculateMac(String input, String info) { if (messageAuthenticationCode == 'hkdf-hmac-sha256.v2') { - return sas!.calculate_mac_fixed_base64(input, info); + return establishedSas!.calculateMac(input, info); } else if (messageAuthenticationCode == 'hkdf-hmac-sha256') { - return sas!.calculate_mac(input, info); + return establishedSas!.calculateMacDeprecated(input, info); } else { throw Exception('Unknown message authentification code'); } diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index a2e81cc5..073736ae 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -20,7 +20,7 @@ import 'dart:convert'; import 'package:canonical_json/canonical_json.dart'; import 'package:collection/collection.dart' show IterableExtension; -import 'package:olm/olm.dart' as olm; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; @@ -240,24 +240,17 @@ abstract class SignableKey extends MatrixSignableKey { String signature, { bool isSignatureWithoutLibolmValid = false, }) { - olm.Utility olmutil; - try { - olmutil = olm.Utility(); - } catch (e) { - // if no libolm is present we land in this catch block, and return the default - // set if no libolm is there. Some signatures should be assumed-valid while others - // should be assumed-invalid - return isSignatureWithoutLibolmValid; - } var valid = false; try { - olmutil.ed25519_verify(pubKey, signingContent, signature); + vod.Ed25519PublicKey.fromBase64(pubKey).verify( + message: signingContent, + signature: vod.Ed25519Signature.fromBase64(signature), + ); valid = true; - } catch (_) { + } catch (e) { + Logs().d('Invalid Ed25519 signature', e); // bad signature valid = false; - } finally { - olmutil.free(); } return valid; } diff --git a/pubspec.yaml b/pubspec.yaml index 8f282c70..a1333227 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,11 @@ dependencies: sqflite_common: ^2.4.5 sqlite3: ^2.1.0 typed_data: ^1.3.2 + vodozemac: + git: + url: https://github.com/famedly/dart-vodozemac.git + path: dart + ref: main webrtc_interface: ^1.2.0 dev_dependencies: diff --git a/scripts/prepare_vodozemac.sh b/scripts/prepare_vodozemac.sh new file mode 100755 index 00000000..d1b2b725 --- /dev/null +++ b/scripts/prepare_vodozemac.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +git clone https://github.com/famedly/dart-vodozemac.git +mv ./dart-vodozemac/rust ./ +rm -rf dart-vodozemac +cd ./rust +cargo build +cd .. \ No newline at end of file diff --git a/test/client_test.dart b/test/client_test.dart index f88a182d..6662ea1e 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -26,6 +26,7 @@ import 'package:collection/collection.dart'; import 'package:olm/olm.dart' as olm; import 'package:path/path.dart' show join; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import 'package:matrix/src/utils/client_init_exception.dart'; @@ -67,11 +68,15 @@ void main() { group('client mem', tags: 'olm', () { late Client matrix; - Logs().level = Level.error; + Future? vodInit; /// Check if all Elements get created - setUp(() async { + vodInit ??= vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); + await vodInit; matrix = await getClient(); }); diff --git a/test/device_keys_list_test.dart b/test/device_keys_list_test.dart index e9e15ffe..b1b57f69 100644 --- a/test/device_keys_list_test.dart +++ b/test/device_keys_list_test.dart @@ -19,18 +19,26 @@ import 'dart:convert'; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import './fake_client.dart'; -void main() { +void main() async { /// All Tests related to device keys group('Device keys', tags: 'olm', () { Logs().level = Level.error; late Client client; + Future? vodInit; + test('setupClient', () async { + vodInit ??= vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); + await vodInit; client = await getClient(); await client.abortSync(); }); diff --git a/test/encryption/bootstrap_test.dart b/test/encryption/bootstrap_test.dart index 6b4d1dbc..363e49bb 100644 --- a/test/encryption/bootstrap_test.dart +++ b/test/encryption/bootstrap_test.dart @@ -21,6 +21,7 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; @@ -35,6 +36,10 @@ void main() { late String origKeyId; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); client = await getClient(); @@ -75,20 +80,16 @@ void main() { // 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(); - } + final privateKey = + await defaultKey.getStored('m.cross_signing.$keyType'); + final keyObj = vod.PkSigning.fromSecretKey(privateKey); + final pubKey = keyObj.publicKey.toBase64(); + expect( + pubKey, + client.userDeviceKeys[client.userID] + ?.getCrossSigningKey(keyType) + ?.publicKey, + ); } await defaultKey.store('foxes', 'floof'); @@ -133,20 +134,16 @@ void main() { // 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(); - } + final privateKey = + await defaultKey.getStored('m.cross_signing.$keyType'); + final keyObj = vod.PkSigning.fromSecretKey(privateKey); + final pubKey = keyObj.publicKey.toBase64(); + expect( + pubKey, + client.userDeviceKeys[client.userID] + ?.getCrossSigningKey(keyType) + ?.publicKey, + ); } expect(await defaultKey.getStored('foxes'), 'floof'); @@ -192,20 +189,16 @@ void main() { // 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(); - } + final privateKey = + await defaultKey.getStored('m.cross_signing.$keyType'); + final keyObj = vod.PkSigning.fromSecretKey(privateKey); + final pubKey = keyObj.publicKey.toBase64(); + expect( + pubKey, + client.userDeviceKeys[client.userID] + ?.getCrossSigningKey(keyType) + ?.publicKey, + ); } expect(await defaultKey.getStored('foxes'), 'floof'); diff --git a/test/encryption/cross_signing_test.dart b/test/encryption/cross_signing_test.dart index a901c0a7..d1c4ec04 100644 --- a/test/encryption/cross_signing_test.dart +++ b/test/encryption/cross_signing_test.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import '../fake_client.dart'; @@ -31,6 +32,10 @@ void main() { late Client client; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); client = await getClient(); diff --git a/test/encryption/encrypt_decrypt_room_message_test.dart b/test/encryption/encrypt_decrypt_room_message_test.dart index afa44c4e..18226297 100644 --- a/test/encryption/encrypt_decrypt_room_message_test.dart +++ b/test/encryption/encrypt_decrypt_room_message_test.dart @@ -18,6 +18,7 @@ import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import '../fake_client.dart'; @@ -33,6 +34,10 @@ void main() { final now = DateTime.now(); setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); client = await getClient(); diff --git a/test/encryption/encrypt_decrypt_to_device_test.dart b/test/encryption/encrypt_decrypt_to_device_test.dart index 2476617e..649b87c7 100644 --- a/test/encryption/encrypt_decrypt_to_device_test.dart +++ b/test/encryption/encrypt_decrypt_to_device_test.dart @@ -18,6 +18,7 @@ import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import '../fake_client.dart'; @@ -41,6 +42,10 @@ void main() async { late Map payload; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); client = await getClient(); diff --git a/test/encryption/key_manager_test.dart b/test/encryption/key_manager_test.dart index 300d97d3..f2b4c809 100644 --- a/test/encryption/key_manager_test.dart +++ b/test/encryption/key_manager_test.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import '../fake_client.dart'; @@ -30,6 +31,10 @@ void main() { late Client client; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); client = await getClient(); diff --git a/test/encryption/key_request_test.dart b/test/encryption/key_request_test.dart index fb0af33a..b74f4a62 100644 --- a/test/encryption/key_request_test.dart +++ b/test/encryption/key_request_test.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import '../fake_client.dart'; @@ -42,6 +43,10 @@ void main() { Logs().level = Level.error; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); }); diff --git a/test/encryption/key_verification_test.dart b/test/encryption/key_verification_test.dart index 06fafcd9..35b08ea4 100644 --- a/test/encryption/key_verification_test.dart +++ b/test/encryption/key_verification_test.dart @@ -22,6 +22,7 @@ import 'dart:typed_data'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; @@ -58,6 +59,10 @@ void main() async { late Client client2; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); }); diff --git a/test/encryption/olm_manager_test.dart b/test/encryption/olm_manager_test.dart index d6a7e06a..e3a8a9fa 100644 --- a/test/encryption/olm_manager_test.dart +++ b/test/encryption/olm_manager_test.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption/utils/json_signature_check_extension.dart'; import 'package:matrix/matrix.dart'; @@ -32,6 +33,10 @@ void main() { late Client client; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); client = await getClient(); diff --git a/test/encryption/online_key_backup_test.dart b/test/encryption/online_key_backup_test.dart index 0774f436..0930ad3b 100644 --- a/test/encryption/online_key_backup_test.dart +++ b/test/encryption/online_key_backup_test.dart @@ -20,6 +20,7 @@ import 'dart:convert'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import '../fake_client.dart'; @@ -35,6 +36,10 @@ void main() { final senderKey = 'JBG7ZaPn54OBC7TuIEiylW3BZ+7WcGQhFBPB9pogbAg'; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); client = await getClient(); diff --git a/test/encryption/qr_verification_self_test.dart b/test/encryption/qr_verification_self_test.dart index 43ff4752..bc7e8a3a 100644 --- a/test/encryption/qr_verification_self_test.dart +++ b/test/encryption/qr_verification_self_test.dart @@ -20,6 +20,7 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; @@ -69,7 +70,14 @@ void main() async { late Client client1; late Client client2; + Future? vodInit; + setUp(() async { + vodInit ??= vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); + await vodInit; client1 = await getClient(); client2 = await getOtherClient(); diff --git a/test/encryption/ssss_test.dart b/test/encryption/ssss_test.dart index c9670caa..4d5136ec 100644 --- a/test/encryption/ssss_test.dart +++ b/test/encryption/ssss_test.dart @@ -22,6 +22,7 @@ import 'dart:typed_data'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/encryption.dart'; import 'package:matrix/matrix.dart'; @@ -54,6 +55,10 @@ void main() { late Client client; setUpAll(() async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.get_library_version(); client = await getClient(); diff --git a/test/room_test.dart b/test/room_test.dart index d51d698c..e11305b9 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -22,6 +22,7 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import 'fake_client.dart'; @@ -671,12 +672,20 @@ void main() { expect(fetchedParticipants.length, newParticipants.length); }); - test('calcEncryptionHealthState', () async { - expect( - await room.calcEncryptionHealthState(), - EncryptionHealthState.unverifiedDevices, - ); - }); + test( + 'calcEncryptionHealthState', + () async { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); + expect( + await room.calcEncryptionHealthState(), + EncryptionHealthState.allVerified, + ); + }, + tags: 'olm', + ); test('getEventByID', () async { final event = await room.getEventById('1234'); diff --git a/test_driver/matrixsdk_test.dart b/test_driver/matrixsdk_test.dart index 2945a39f..4a216aca 100644 --- a/test_driver/matrixsdk_test.dart +++ b/test_driver/matrixsdk_test.dart @@ -20,6 +20,7 @@ import 'dart:io'; import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; +import 'package:vodozemac/vodozemac.dart' as vod; import 'package:matrix/matrix.dart'; import '../test/fake_database.dart'; @@ -39,6 +40,10 @@ void main() => group( Client? testClientA, testClientB; try { + await vod.init( + wasmPath: './pkg/', + libraryPath: './rust/target/debug/', + ); await olm.init(); olm.Account(); Logs().i('[LibOlm] Enabled');