fix: fast pbkdf2 with OpenSSL

This commit is contained in:
Lukas Lihotzki 2021-03-23 18:08:40 +01:00
parent 4f32168017
commit a25d1932ee
8 changed files with 117 additions and 13 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -0,0 +1 @@
export 'native.dart' if (dart.library.js) 'js.dart';

View File

@ -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');

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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));
}