fix: fast pbkdf2 with OpenSSL
This commit is contained in:
parent
4f32168017
commit
a25d1932ee
|
|
@ -50,6 +50,10 @@ coverage_without_olm:
|
|||
- apt update
|
||||
- apt install -y dart
|
||||
- ln -s /usr/lib/dart/bin/pub /usr/bin/
|
||||
# Need to check if famedlysdk works without OpenSSL.
|
||||
# Cannot uninstall OpenSSL, because it may be used internally.
|
||||
# Break famedlysdk's FFI to OpenSSL instead:
|
||||
- sed -i 's/libcrypto/libnocrypto/g' lib/src/utils/crypto/ffi.dart
|
||||
- pub get
|
||||
- pub run test
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Matrix SDK for the famedly talk app written in dart.
|
|||
## Native libraries
|
||||
|
||||
For E2EE, libolm must be provided (see https://pub.dev/packages/olm#using-dart-olm).
|
||||
Additionally, OpenSSL (libcrypto) must be provided on native platforms for E2EE.
|
||||
|
||||
## API
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,10 @@ import 'dart:async';
|
|||
import 'package:base58check/base58.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:pointycastle/digests/sha512.dart';
|
||||
import 'package:pointycastle/key_derivators/api.dart';
|
||||
import 'package:pointycastle/key_derivators/pbkdf2.dart';
|
||||
import 'package:pointycastle/macs/hmac.dart';
|
||||
|
||||
import '../famedlysdk.dart';
|
||||
import '../src/database/database.dart';
|
||||
import '../src/utils/crypto/crypto.dart';
|
||||
import '../src/utils/run_in_background.dart';
|
||||
import '../src/utils/run_in_root.dart';
|
||||
import 'encryption.dart';
|
||||
|
|
@ -154,16 +151,11 @@ class SSSS {
|
|||
.trim();
|
||||
}
|
||||
|
||||
static Uint8List keyFromPassphrase(String passphrase, PassphraseInfo info) {
|
||||
static Future<Uint8List> keyFromPassphrase(String passphrase, PassphraseInfo info) async {
|
||||
if (info.algorithm != AlgorithmTypes.pbkdf2) {
|
||||
throw Exception('Unknown algorithm');
|
||||
}
|
||||
final out = Uint8List(info.bits != null ? (info.bits / 8).ceil() : 32);
|
||||
final generator = PBKDF2KeyDerivator(HMac(SHA512Digest(), 128));
|
||||
generator.init(
|
||||
Pbkdf2Parameters(utf8.encode(info.salt), info.iterations, out.length));
|
||||
generator.deriveKey(utf8.encode(passphrase), 0, out, 0);
|
||||
return out;
|
||||
return await pbkdf2(utf8.encode(passphrase), utf8.encode(info.salt), info.iterations, info.bits ?? 256);
|
||||
}
|
||||
|
||||
void setValidator(String type, FutureOr<bool> Function(String) validator) {
|
||||
|
|
@ -717,6 +709,6 @@ class _KeyFromPassphraseArgs {
|
|||
_KeyFromPassphraseArgs({this.passphrase, this.info});
|
||||
}
|
||||
|
||||
Uint8List _keyFromPassphrase(_KeyFromPassphraseArgs args) {
|
||||
return SSSS.keyFromPassphrase(args.passphrase, args.info);
|
||||
Future<Uint8List> _keyFromPassphrase(_KeyFromPassphraseArgs args) async {
|
||||
return await SSSS.keyFromPassphrase(args.passphrase, args.info);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export 'native.dart' if (dart.library.js) 'js.dart';
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
final libcrypto = Platform.isIOS
|
||||
? DynamicLibrary.process()
|
||||
: DynamicLibrary.open(Platform.isAndroid
|
||||
? 'libcrypto.so'
|
||||
: Platform.isWindows
|
||||
? 'libcrypto.dll'
|
||||
: Platform.isMacOS ? 'libcrypto.1.1.dylib' : 'libcrypto.so.1.1');
|
||||
|
||||
final PKCS5_PBKDF2_HMAC = libcrypto.lookupFunction<
|
||||
IntPtr Function(Pointer<Uint8> pass, IntPtr passlen, Pointer<Uint8> salt, IntPtr saltlen, IntPtr iter, Pointer<NativeType> digest, IntPtr keylen, Pointer<Uint8> out),
|
||||
int Function(Pointer<Uint8> pass, int passlen, Pointer<Uint8> salt, int saltlen, int iter, Pointer<NativeType> digest, int keylen, Pointer<Uint8> out)
|
||||
>('PKCS5_PBKDF2_HMAC');
|
||||
|
||||
final EVP_sha512 = libcrypto.lookupFunction<
|
||||
Pointer<NativeType> Function(),
|
||||
Pointer<NativeType> Function()
|
||||
>('EVP_sha512');
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) 2020 Famedly GmbH
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'subtle.dart';
|
||||
|
||||
Future<Uint8List> pbkdf2(Uint8List passphrase, Uint8List salt, int iterations, int bits) async {
|
||||
final raw = await importKey('raw', passphrase, 'PBKDF2', false, ['deriveBits']);
|
||||
final res = await deriveBits(Pbkdf2Params(name: 'PBKDF2', hash: 'SHA-512', salt: salt, iterations: iterations), raw, bits);
|
||||
return Uint8List.view(res);
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import 'dart:typed_data';
|
||||
import 'dart:ffi';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'ffi.dart';
|
||||
|
||||
Uint8List pbkdf2(Uint8List passphrase, Uint8List salt, int iterations, int bits) {
|
||||
final outLen = bits ~/ 8;
|
||||
final mem = malloc.call<Uint8>(passphrase.length + salt.length + outLen);
|
||||
final saltMem = mem.elementAt(passphrase.length);
|
||||
final outMem = saltMem.elementAt(salt.length);
|
||||
try {
|
||||
mem.asTypedList(passphrase.length).setAll(0, passphrase);
|
||||
saltMem.asTypedList(salt.length).setAll(0, salt);
|
||||
PKCS5_PBKDF2_HMAC(mem, passphrase.length, saltMem, salt.length, iterations, EVP_sha512(), outLen, outMem);
|
||||
return Uint8List.fromList(outMem.asTypedList(outLen));
|
||||
} finally {
|
||||
malloc.free(mem);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2020 Famedly GmbH
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
@JS()
|
||||
library subtle;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:js_util';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@JS()
|
||||
class CryptoKey {}
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class Pbkdf2Params {
|
||||
external factory Pbkdf2Params({String name, String hash, Uint8List salt, int iterations});
|
||||
String name;
|
||||
String hash;
|
||||
Uint8List salt;
|
||||
int iterations;
|
||||
}
|
||||
|
||||
@JS('crypto.subtle.importKey')
|
||||
external dynamic _importKey(String format, dynamic keyData, dynamic algorithm,
|
||||
bool extractable, List<String> keyUsages);
|
||||
|
||||
Future<CryptoKey> importKey(String format, dynamic keyData, dynamic algorithm,
|
||||
bool extractable, List<String> keyUsages) {
|
||||
return promiseToFuture(
|
||||
_importKey(format, keyData, algorithm, extractable, keyUsages));
|
||||
}
|
||||
|
||||
@JS('crypto.subtle.exportKey')
|
||||
external dynamic _exportKey(String algorithm, CryptoKey key);
|
||||
|
||||
Future<dynamic> exportKey(String algorithm, CryptoKey key) {
|
||||
return promiseToFuture(_exportKey(algorithm, key));
|
||||
}
|
||||
|
||||
@JS('crypto.subtle.deriveKey')
|
||||
external dynamic _deriveKey(dynamic algorithm, CryptoKey baseKey, dynamic derivedKeyAlgorithm, bool extractable, List<String> keyUsages);
|
||||
|
||||
Future<ByteBuffer> deriveKey(dynamic algorithm, CryptoKey baseKey, dynamic derivedKeyAlgorithm, bool extractable, List<String> keyUsages) {
|
||||
return promiseToFuture(_deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages));
|
||||
}
|
||||
|
||||
@JS('crypto.subtle.deriveBits')
|
||||
external dynamic _deriveBits(dynamic algorithm, CryptoKey baseKey, int length);
|
||||
|
||||
Future<ByteBuffer> deriveBits(dynamic algorithm, CryptoKey baseKey, int length) {
|
||||
return promiseToFuture(_deriveBits(algorithm, baseKey, length));
|
||||
}
|
||||
Loading…
Reference in New Issue