refactor: use OpenSSL for AES
This commit is contained in:
parent
7faf05fe90
commit
761138a56d
|
|
@ -23,7 +23,6 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:base58check/base58.dart';
|
import 'package:base58check/base58.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:encrypt/encrypt.dart';
|
|
||||||
|
|
||||||
import '../famedlysdk.dart';
|
import '../famedlysdk.dart';
|
||||||
import '../src/database/database.dart';
|
import '../src/database/database.dart';
|
||||||
|
|
@ -78,13 +77,13 @@ class SSSS {
|
||||||
return _DerivedKeys(aesKey: aesKey.bytes, hmacKey: hmacKey.bytes);
|
return _DerivedKeys(aesKey: aesKey.bytes, hmacKey: hmacKey.bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static _Encrypted encryptAes(String data, Uint8List key, String name,
|
static Future<_Encrypted> encryptAes(String data, Uint8List key, String name,
|
||||||
[String ivStr]) {
|
[String ivStr]) async {
|
||||||
Uint8List iv;
|
Uint8List iv;
|
||||||
if (ivStr != null) {
|
if (ivStr != null) {
|
||||||
iv = base64.decode(ivStr);
|
iv = base64.decode(ivStr);
|
||||||
} else {
|
} else {
|
||||||
iv = Uint8List.fromList(SecureRandom(16).bytes);
|
iv = Uint8List.fromList(uc.secureRandomBytes(16));
|
||||||
}
|
}
|
||||||
// we need to clear bit 63 of the IV
|
// we need to clear bit 63 of the IV
|
||||||
iv[8] &= 0x7f;
|
iv[8] &= 0x7f;
|
||||||
|
|
@ -92,9 +91,7 @@ class SSSS {
|
||||||
final keys = deriveKeys(key, name);
|
final keys = deriveKeys(key, name);
|
||||||
|
|
||||||
final plain = Uint8List.fromList(utf8.encode(data));
|
final plain = Uint8List.fromList(utf8.encode(data));
|
||||||
final ciphertext = AES(Key(keys.aesKey), mode: AESMode.ctr, padding: null)
|
final ciphertext = await uc.aesCtr.encrypt(plain, keys.aesKey, iv);
|
||||||
.encrypt(plain, iv: IV(iv))
|
|
||||||
.bytes;
|
|
||||||
|
|
||||||
final hmac = Hmac(sha256, keys.hmacKey).convert(ciphertext);
|
final hmac = Hmac(sha256, keys.hmacKey).convert(ciphertext);
|
||||||
|
|
||||||
|
|
@ -104,7 +101,7 @@ class SSSS {
|
||||||
mac: base64.encode(hmac.bytes));
|
mac: base64.encode(hmac.bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
static String decryptAes(_Encrypted data, Uint8List key, String name) {
|
static Future<String> decryptAes(_Encrypted data, Uint8List key, String name) async {
|
||||||
final keys = deriveKeys(key, name);
|
final keys = deriveKeys(key, name);
|
||||||
final cipher = base64.decode(data.ciphertext);
|
final cipher = base64.decode(data.ciphertext);
|
||||||
final hmac = base64
|
final hmac = base64
|
||||||
|
|
@ -113,8 +110,7 @@ class SSSS {
|
||||||
if (hmac != data.mac.replaceAll(RegExp(r'=+$'), '')) {
|
if (hmac != data.mac.replaceAll(RegExp(r'=+$'), '')) {
|
||||||
throw Exception('Bad MAC');
|
throw Exception('Bad MAC');
|
||||||
}
|
}
|
||||||
final decipher = AES(Key(keys.aesKey), mode: AESMode.ctr, padding: null)
|
final decipher = await uc.aesCtr.encrypt(cipher, keys.aesKey, base64.decode(data.iv));
|
||||||
.decrypt(Encrypted(cipher), iv: IV(base64.decode(data.iv)));
|
|
||||||
return String.fromCharCodes(decipher);
|
return String.fromCharCodes(decipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,9 +192,9 @@ class SSSS {
|
||||||
// we need to derive the key off of the passphrase
|
// we need to derive the key off of the passphrase
|
||||||
content.passphrase = PassphraseInfo();
|
content.passphrase = PassphraseInfo();
|
||||||
content.passphrase.algorithm = AlgorithmTypes.pbkdf2;
|
content.passphrase.algorithm = AlgorithmTypes.pbkdf2;
|
||||||
content.passphrase.salt =
|
content.passphrase.salt = base64
|
||||||
base64.encode(SecureRandom(pbkdf2SaltLength).bytes); // generate salt
|
.encode(uc.secureRandomBytes(pbkdf2SaltLength)); // generate salt
|
||||||
content.passphrase.iterations = pbkdf2DefaultIterations;
|
content.passphrase.iterations = pbkdf2DefaultIterations;;
|
||||||
content.passphrase.bits = ssssKeyLength * 8;
|
content.passphrase.bits = ssssKeyLength * 8;
|
||||||
privateKey = await runInBackground(
|
privateKey = await runInBackground(
|
||||||
_keyFromPassphrase,
|
_keyFromPassphrase,
|
||||||
|
|
@ -210,10 +206,10 @@ class SSSS {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// we need to just generate a new key from scratch
|
// we need to just generate a new key from scratch
|
||||||
privateKey = Uint8List.fromList(SecureRandom(ssssKeyLength).bytes);
|
privateKey = Uint8List.fromList(uc.secureRandomBytes(ssssKeyLength));
|
||||||
}
|
}
|
||||||
// now that we have the private key, let's create the iv and mac
|
// now that we have the private key, let's create the iv and mac
|
||||||
final encrypted = encryptAes(zeroStr, privateKey, '');
|
final encrypted = await encryptAes(zeroStr, privateKey, '');
|
||||||
content.iv = encrypted.iv;
|
content.iv = encrypted.iv;
|
||||||
content.mac = encrypted.mac;
|
content.mac = encrypted.mac;
|
||||||
content.algorithm = AlgorithmTypes.secretStorageV1AesHmcSha2;
|
content.algorithm = AlgorithmTypes.secretStorageV1AesHmcSha2;
|
||||||
|
|
@ -223,7 +219,7 @@ class SSSS {
|
||||||
// make sure we generate a unique key id
|
// make sure we generate a unique key id
|
||||||
final keyId = () sync* {
|
final keyId = () sync* {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
yield base64.encode(SecureRandom(keyidByteLength).bytes);
|
yield base64.encode(uc.secureRandomBytes(keyidByteLength));
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
.firstWhere((keyId) => getKey(keyId) == null);
|
.firstWhere((keyId) => getKey(keyId) == null);
|
||||||
|
|
@ -238,14 +234,14 @@ class SSSS {
|
||||||
await waitForAccountData;
|
await waitForAccountData;
|
||||||
|
|
||||||
final key = open(keyId);
|
final key = open(keyId);
|
||||||
key.setPrivateKey(privateKey);
|
await key.setPrivateKey(privateKey);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkKey(Uint8List key, SecretStorageKeyContent info) {
|
Future<bool> checkKey(Uint8List key, SecretStorageKeyContent info) async {
|
||||||
if (info.algorithm == AlgorithmTypes.secretStorageV1AesHmcSha2) {
|
if (info.algorithm == AlgorithmTypes.secretStorageV1AesHmcSha2) {
|
||||||
if ((info.mac is String) && (info.iv is String)) {
|
if ((info.mac is String) && (info.iv is String)) {
|
||||||
final encrypted = encryptAes(zeroStr, key, '', info.iv);
|
final encrypted = await encryptAes(zeroStr, key, '', info.iv);
|
||||||
return info.mac.replaceAll(RegExp(r'=+$'), '') ==
|
return info.mac.replaceAll(RegExp(r'=+$'), '') ==
|
||||||
encrypted.mac.replaceAll(RegExp(r'=+$'), '');
|
encrypted.mac.replaceAll(RegExp(r'=+$'), '');
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -303,7 +299,7 @@ class SSSS {
|
||||||
final enc = secretInfo.content['encrypted'][keyId];
|
final enc = secretInfo.content['encrypted'][keyId];
|
||||||
final encryptInfo = _Encrypted(
|
final encryptInfo = _Encrypted(
|
||||||
iv: enc['iv'], ciphertext: enc['ciphertext'], mac: enc['mac']);
|
iv: enc['iv'], ciphertext: enc['ciphertext'], mac: enc['mac']);
|
||||||
final decrypted = decryptAes(encryptInfo, key, type);
|
final decrypted = await decryptAes(encryptInfo, key, type);
|
||||||
if (cacheTypes.contains(type) && client.database != null) {
|
if (cacheTypes.contains(type) && client.database != null) {
|
||||||
// cache the thing
|
// cache the thing
|
||||||
await client.database
|
await client.database
|
||||||
|
|
@ -319,7 +315,7 @@ class SSSS {
|
||||||
{bool add = false}) async {
|
{bool add = false}) async {
|
||||||
final triggerCacheCallback =
|
final triggerCacheCallback =
|
||||||
_cacheCallbacks.containsKey(type) && await getCached(type) == null;
|
_cacheCallbacks.containsKey(type) && await getCached(type) == null;
|
||||||
final encrypted = encryptAes(secret, key, type);
|
final encrypted = await encryptAes(secret, key, type);
|
||||||
Map<String, dynamic> content;
|
Map<String, dynamic> content;
|
||||||
if (add && client.accountData[type] != null) {
|
if (add && client.accountData[type] != null) {
|
||||||
content = client.accountData[type].content.copy();
|
content = client.accountData[type].content.copy();
|
||||||
|
|
@ -649,7 +645,7 @@ class OpenSSSS {
|
||||||
throw Exception('Nothing specified');
|
throw Exception('Nothing specified');
|
||||||
}
|
}
|
||||||
// verify the validity of the key
|
// verify the validity of the key
|
||||||
if (!ssss.checkKey(privateKey, keyData)) {
|
if (!await ssss.checkKey(privateKey, keyData)) {
|
||||||
privateKey = null;
|
privateKey = null;
|
||||||
throw Exception('Inalid key');
|
throw Exception('Inalid key');
|
||||||
}
|
}
|
||||||
|
|
@ -658,8 +654,8 @@ class OpenSSSS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPrivateKey(Uint8List key) {
|
Future<void> setPrivateKey(Uint8List key) async {
|
||||||
if (!ssss.checkKey(key, keyData)) {
|
if (!await ssss.checkKey(key, keyData)) {
|
||||||
throw Exception('Invalid key');
|
throw Exception('Invalid key');
|
||||||
}
|
}
|
||||||
privateKey = key;
|
privateKey = key;
|
||||||
|
|
|
||||||
|
|
@ -1 +1,11 @@
|
||||||
export 'native.dart' if (dart.library.js) 'js.dart';
|
export 'native.dart' if (dart.library.js) 'js.dart';
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
Uint8List secureRandomBytes(int len) {
|
||||||
|
final rng = Random.secure();
|
||||||
|
final list = Uint8List(len);
|
||||||
|
list.setAll(0, Iterable.generate(list.length, (i) => rng.nextInt(256)));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,3 +28,38 @@ final EVP_sha512 = libcrypto.lookupFunction<
|
||||||
Pointer<NativeType> Function(),
|
Pointer<NativeType> Function(),
|
||||||
Pointer<NativeType> Function()
|
Pointer<NativeType> Function()
|
||||||
>('EVP_sha512');
|
>('EVP_sha512');
|
||||||
|
|
||||||
|
final EVP_aes_128_ctr = libcrypto.lookupFunction<
|
||||||
|
Pointer<NativeType> Function(),
|
||||||
|
Pointer<NativeType> Function()
|
||||||
|
>('EVP_aes_128_ctr');
|
||||||
|
|
||||||
|
final EVP_aes_256_ctr = libcrypto.lookupFunction<
|
||||||
|
Pointer<NativeType> Function(),
|
||||||
|
Pointer<NativeType> Function()
|
||||||
|
>('EVP_aes_256_ctr');
|
||||||
|
|
||||||
|
final EVP_CIPHER_CTX_new = libcrypto.lookupFunction<
|
||||||
|
Pointer<NativeType> Function(),
|
||||||
|
Pointer<NativeType> Function()
|
||||||
|
>('EVP_CIPHER_CTX_new');
|
||||||
|
|
||||||
|
final EVP_EncryptInit_ex = libcrypto.lookupFunction<
|
||||||
|
Pointer<NativeType> Function(Pointer<NativeType> ctx, Pointer<NativeType> alg, Pointer<NativeType> some, Pointer<Uint8> key, Pointer<Uint8> iv),
|
||||||
|
Pointer<NativeType> Function(Pointer<NativeType> ctx, Pointer<NativeType> alg, Pointer<NativeType> some, Pointer<Uint8> key, Pointer<Uint8> iv)
|
||||||
|
>('EVP_EncryptInit_ex');
|
||||||
|
|
||||||
|
final EVP_EncryptUpdate = libcrypto.lookupFunction<
|
||||||
|
Pointer<NativeType> Function(Pointer<NativeType> ctx, Pointer<Uint8> output, Pointer<IntPtr> outputLen, Pointer<Uint8> input, IntPtr inputLen),
|
||||||
|
Pointer<NativeType> Function(Pointer<NativeType> ctx, Pointer<Uint8> output, Pointer<IntPtr> outputLen, Pointer<Uint8> input, int inputLen)
|
||||||
|
>('EVP_EncryptUpdate');
|
||||||
|
|
||||||
|
final EVP_EncryptFinal_ex = libcrypto.lookupFunction<
|
||||||
|
Pointer<NativeType> Function(Pointer<NativeType> ctx, Pointer<Uint8> data, Pointer<IntPtr> len),
|
||||||
|
Pointer<NativeType> Function(Pointer<NativeType> ctx, Pointer<Uint8> data, Pointer<IntPtr> len)
|
||||||
|
>('EVP_EncryptFinal_ex');
|
||||||
|
|
||||||
|
final EVP_CIPHER_CTX_free = libcrypto.lookupFunction<
|
||||||
|
Pointer<NativeType> Function(Pointer<NativeType> ctx),
|
||||||
|
Pointer<NativeType> Function(Pointer<NativeType> ctx)
|
||||||
|
>('EVP_CIPHER_CTX_free');
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'subtle.dart';
|
import 'subtle.dart';
|
||||||
|
import 'subtle.dart' as subtle;
|
||||||
|
|
||||||
abstract class Hash {
|
abstract class Hash {
|
||||||
Hash._(this.name);
|
Hash._(this.name);
|
||||||
|
|
@ -26,6 +27,26 @@ class _Sha512 extends Hash {
|
||||||
_Sha512() : super._('SHA-512');
|
_Sha512() : super._('SHA-512');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class Cipher {
|
||||||
|
Cipher._(this.name);
|
||||||
|
String name;
|
||||||
|
Object params(Uint8List iv);
|
||||||
|
Future<Uint8List> encrypt(Uint8List input, Uint8List key, Uint8List iv) async {
|
||||||
|
final subtleKey = await importKey('raw', key, name, false, ['encrypt']);
|
||||||
|
return (await subtle.encrypt(params(iv), subtleKey, input))
|
||||||
|
.asUint8List();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Cipher aesCtr = _AesCtr();
|
||||||
|
|
||||||
|
class _AesCtr extends Cipher {
|
||||||
|
_AesCtr() : super._('AES-CTR');
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object params(Uint8List iv) => AesCtrParams(name: name, counter: iv, length: 64);
|
||||||
|
}
|
||||||
|
|
||||||
Future<Uint8List> pbkdf2(Uint8List passphrase, Uint8List salt, Hash hash, int iterations, int bits) async {
|
Future<Uint8List> pbkdf2(Uint8List passphrase, Uint8List salt, Hash hash, int iterations, int bits) async {
|
||||||
final raw = await importKey('raw', passphrase, 'PBKDF2', false, ['deriveBits']);
|
final raw = await importKey('raw', passphrase, 'PBKDF2', false, ['deriveBits']);
|
||||||
final res = await deriveBits(Pbkdf2Params(name: 'PBKDF2', hash: hash.name, salt: salt, iterations: iterations), raw, bits);
|
final res = await deriveBits(Pbkdf2Params(name: 'PBKDF2', hash: hash.name, salt: salt, iterations: iterations), raw, bits);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,47 @@ class _Sha512 extends Hash {
|
||||||
_Sha512() : super._(EVP_sha512());
|
_Sha512() : super._(EVP_sha512());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class Cipher {
|
||||||
|
Cipher._();
|
||||||
|
Pointer<NativeType> getAlg(int keysize);
|
||||||
|
Uint8List encrypt(Uint8List input, Uint8List key, Uint8List iv) {
|
||||||
|
final alg = getAlg(key.length * 8);
|
||||||
|
final mem = malloc.call<Uint8>(sizeOf<IntPtr>() + key.length + iv.length + input.length);
|
||||||
|
final lenMem = mem.cast<IntPtr>();
|
||||||
|
final keyMem = mem.elementAt(sizeOf<IntPtr>());
|
||||||
|
final ivMem = keyMem.elementAt(key.length);
|
||||||
|
final dataMem = ivMem.elementAt(iv.length);
|
||||||
|
try {
|
||||||
|
keyMem.asTypedList(key.length).setAll(0, key);
|
||||||
|
ivMem.asTypedList(iv.length).setAll(0, iv);
|
||||||
|
dataMem.asTypedList(input.length).setAll(0, input);
|
||||||
|
final ctx = EVP_CIPHER_CTX_new();
|
||||||
|
EVP_EncryptInit_ex(ctx, alg, nullptr, keyMem, ivMem);
|
||||||
|
EVP_EncryptUpdate(ctx, dataMem, lenMem, dataMem, input.length);
|
||||||
|
EVP_EncryptFinal_ex(ctx, dataMem.elementAt(lenMem.value), lenMem);
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
return Uint8List.fromList(dataMem.asTypedList(input.length));
|
||||||
|
} finally {
|
||||||
|
malloc.free(mem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Cipher aesCtr = _AesCtr();
|
||||||
|
|
||||||
|
class _AesCtr extends Cipher {
|
||||||
|
_AesCtr() : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Pointer<NativeType> getAlg(int keysize) {
|
||||||
|
switch (keysize) {
|
||||||
|
case 128: return EVP_aes_128_ctr();
|
||||||
|
case 256: return EVP_aes_256_ctr();
|
||||||
|
default: throw ArgumentError('invalid key size');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Uint8List pbkdf2(Uint8List passphrase, Uint8List salt, Hash hash, int iterations, int bits) {
|
Uint8List pbkdf2(Uint8List passphrase, Uint8List salt, Hash hash, int iterations, int bits) {
|
||||||
final outLen = bits ~/ 8;
|
final outLen = bits ~/ 8;
|
||||||
final mem = malloc.call<Uint8>(passphrase.length + salt.length + outLen);
|
final mem = malloc.call<Uint8>(passphrase.length + salt.length + outLen);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,29 @@ class Pbkdf2Params {
|
||||||
int iterations;
|
int iterations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JS()
|
||||||
|
@anonymous
|
||||||
|
class AesCtrParams {
|
||||||
|
external factory AesCtrParams({String name, Uint8List counter, int length});
|
||||||
|
String name;
|
||||||
|
Uint8List counter;
|
||||||
|
int length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JS('crypto.subtle.encrypt')
|
||||||
|
external dynamic _encrypt(dynamic algorithm, CryptoKey key, Uint8List data);
|
||||||
|
|
||||||
|
Future<ByteBuffer> encrypt(dynamic algorithm, CryptoKey key, Uint8List data) {
|
||||||
|
return promiseToFuture(_encrypt(algorithm, key, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JS('crypto.subtle.decrypt')
|
||||||
|
external dynamic _decrypt(dynamic algorithm, CryptoKey key, Uint8List data);
|
||||||
|
|
||||||
|
Future<ByteBuffer> decrypt(dynamic algorithm, CryptoKey key, Uint8List data) {
|
||||||
|
return promiseToFuture(_decrypt(algorithm, key, data));
|
||||||
|
}
|
||||||
|
|
||||||
@JS('crypto.subtle.importKey')
|
@JS('crypto.subtle.importKey')
|
||||||
external dynamic _importKey(String format, dynamic keyData, dynamic algorithm,
|
external dynamic _importKey(String format, dynamic keyData, dynamic algorithm,
|
||||||
bool extractable, List<String> keyUsages);
|
bool extractable, List<String> keyUsages);
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ dependencies:
|
||||||
html_unescape: ^1.0.2
|
html_unescape: ^1.0.2
|
||||||
moor: ^4.0.0
|
moor: ^4.0.0
|
||||||
random_string: ^2.1.0
|
random_string: ^2.1.0
|
||||||
encrypt: ^5.0.0-beta.1
|
|
||||||
crypto: ^3.0.0
|
crypto: ^3.0.0
|
||||||
base58check: ^2.0.0
|
base58check: ^2.0.0
|
||||||
olm: ^2.0.0
|
olm: ^2.0.0
|
||||||
|
|
@ -26,7 +25,6 @@ dependencies:
|
||||||
isolate: ^2.0.3
|
isolate: ^2.0.3
|
||||||
logger: ^1.0.0
|
logger: ^1.0.0
|
||||||
matrix_api_lite: ^0.2.4
|
matrix_api_lite: ^0.2.4
|
||||||
pointycastle: ^3.0.0-nullsafety.2
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
test: ^1.15.7
|
test: ^1.15.7
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,24 @@
|
||||||
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
import 'package:famedlysdk/encryption.dart';
|
import 'package:famedlysdk/encryption.dart';
|
||||||
import 'package:logger/logger.dart';
|
import 'package:logger/logger.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import 'package:encrypt/encrypt.dart';
|
|
||||||
import 'package:olm/olm.dart' as olm;
|
import 'package:olm/olm.dart' as olm;
|
||||||
|
|
||||||
import '../fake_client.dart';
|
import '../fake_client.dart';
|
||||||
import '../fake_matrix_api.dart';
|
import '../fake_matrix_api.dart';
|
||||||
|
|
||||||
|
Uint8List secureRandomBytes(int len) {
|
||||||
|
final rng = Random.secure();
|
||||||
|
final list = Uint8List(len);
|
||||||
|
list.setAll(0, Iterable.generate(list.length, (i) => rng.nextInt(256)));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
class MockSSSS extends SSSS {
|
class MockSSSS extends SSSS {
|
||||||
MockSSSS(Encryption encryption) : super(encryption);
|
MockSSSS(Encryption encryption) : super(encryption);
|
||||||
|
|
||||||
|
|
@ -69,12 +76,12 @@ void main() {
|
||||||
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3');
|
'0FajDWYaM6wQ4O60OZnLvwZfsBNu4Bu3');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('encrypt / decrypt', () {
|
test('encrypt / decrypt', () async {
|
||||||
if (!olmEnabled) return;
|
if (!olmEnabled) return;
|
||||||
final key = Uint8List.fromList(SecureRandom(32).bytes);
|
final key = Uint8List.fromList(secureRandomBytes(32));
|
||||||
|
|
||||||
final enc = SSSS.encryptAes('secret foxies', key, 'name');
|
final enc = await SSSS.encryptAes('secret foxies', key, 'name');
|
||||||
final dec = SSSS.decryptAes(enc, key, 'name');
|
final dec = await SSSS.decryptAes(enc, key, 'name');
|
||||||
expect(dec, 'secret foxies');
|
expect(dec, 'secret foxies');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -117,7 +124,7 @@ void main() {
|
||||||
|
|
||||||
test('encode / decode recovery key', () async {
|
test('encode / decode recovery key', () async {
|
||||||
if (!olmEnabled) return;
|
if (!olmEnabled) return;
|
||||||
final key = Uint8List.fromList(SecureRandom(32).bytes);
|
final key = Uint8List.fromList(secureRandomBytes(32));
|
||||||
final encoded = SSSS.encodeRecoveryKey(key);
|
final encoded = SSSS.encodeRecoveryKey(key);
|
||||||
final decoded = SSSS.decodeRecoveryKey(encoded);
|
final decoded = SSSS.decodeRecoveryKey(encoded);
|
||||||
expect(key, decoded);
|
expect(key, decoded);
|
||||||
|
|
@ -472,13 +479,13 @@ void main() {
|
||||||
expect(client.encryption.ssss.isKeyValid(newKey.keyId), true);
|
expect(client.encryption.ssss.isKeyValid(newKey.keyId), true);
|
||||||
var testKey = client.encryption.ssss.open(newKey.keyId);
|
var testKey = client.encryption.ssss.open(newKey.keyId);
|
||||||
await testKey.unlock(passphrase: 'test');
|
await testKey.unlock(passphrase: 'test');
|
||||||
testKey.setPrivateKey(newKey.privateKey);
|
await testKey.setPrivateKey(newKey.privateKey);
|
||||||
|
|
||||||
// without passphrase
|
// without passphrase
|
||||||
newKey = await client.encryption.ssss.createKey();
|
newKey = await client.encryption.ssss.createKey();
|
||||||
expect(client.encryption.ssss.isKeyValid(newKey.keyId), true);
|
expect(client.encryption.ssss.isKeyValid(newKey.keyId), true);
|
||||||
testKey = client.encryption.ssss.open(newKey.keyId);
|
testKey = client.encryption.ssss.open(newKey.keyId);
|
||||||
testKey.setPrivateKey(newKey.privateKey);
|
await testKey.setPrivateKey(newKey.privateKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('dispose client', () async {
|
test('dispose client', () async {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue