fix: fast pbkdf2 with OpenSSL
This commit is contained in:
parent
4f32168017
commit
a25d1932ee
|
|
@ -50,6 +50,10 @@ coverage_without_olm:
|
||||||
- apt update
|
- apt update
|
||||||
- apt install -y dart
|
- apt install -y dart
|
||||||
- ln -s /usr/lib/dart/bin/pub /usr/bin/
|
- 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 get
|
||||||
- pub run test
|
- pub run test
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ Matrix SDK for the famedly talk app written in dart.
|
||||||
## Native libraries
|
## Native libraries
|
||||||
|
|
||||||
For E2EE, libolm must be provided (see https://pub.dev/packages/olm#using-dart-olm).
|
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
|
## API
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,10 @@ 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 '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 '../famedlysdk.dart';
|
||||||
import '../src/database/database.dart';
|
import '../src/database/database.dart';
|
||||||
|
import '../src/utils/crypto/crypto.dart';
|
||||||
import '../src/utils/run_in_background.dart';
|
import '../src/utils/run_in_background.dart';
|
||||||
import '../src/utils/run_in_root.dart';
|
import '../src/utils/run_in_root.dart';
|
||||||
import 'encryption.dart';
|
import 'encryption.dart';
|
||||||
|
|
@ -154,16 +151,11 @@ class SSSS {
|
||||||
.trim();
|
.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Uint8List keyFromPassphrase(String passphrase, PassphraseInfo info) {
|
static Future<Uint8List> keyFromPassphrase(String passphrase, PassphraseInfo info) async {
|
||||||
if (info.algorithm != AlgorithmTypes.pbkdf2) {
|
if (info.algorithm != AlgorithmTypes.pbkdf2) {
|
||||||
throw Exception('Unknown algorithm');
|
throw Exception('Unknown algorithm');
|
||||||
}
|
}
|
||||||
final out = Uint8List(info.bits != null ? (info.bits / 8).ceil() : 32);
|
return await pbkdf2(utf8.encode(passphrase), utf8.encode(info.salt), info.iterations, info.bits ?? 256);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setValidator(String type, FutureOr<bool> Function(String) validator) {
|
void setValidator(String type, FutureOr<bool> Function(String) validator) {
|
||||||
|
|
@ -717,6 +709,6 @@ class _KeyFromPassphraseArgs {
|
||||||
_KeyFromPassphraseArgs({this.passphrase, this.info});
|
_KeyFromPassphraseArgs({this.passphrase, this.info});
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List _keyFromPassphrase(_KeyFromPassphraseArgs args) {
|
Future<Uint8List> _keyFromPassphrase(_KeyFromPassphraseArgs args) async {
|
||||||
return SSSS.keyFromPassphrase(args.passphrase, args.info);
|
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