diff --git a/lib/argon2_ffi_base.dart b/lib/argon2_ffi_base.dart index 710348d..96b3d43 100644 --- a/lib/argon2_ffi_base.dart +++ b/lib/argon2_ffi_base.dart @@ -1,175 +1,5 @@ library argon2_ffi_base; -import 'dart:convert'; -import 'dart:ffi'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:ffi/ffi.dart'; - -// ignore_for_file: non_constant_identifier_names, - -import 'package:logging/logging.dart'; - -final _logger = Logger('argon2_ffi_base'); - -typedef Argon2HashNative = Pointer Function( - Pointer key, - Uint32 keyLen, - Pointer salt, - Uint32 saltlen, - Uint32 m_cost, // memory cost - Uint32 t_cost, // time cost (number iterations) - Uint32 parallelism, - IntPtr hashlen, - Uint8 type, - Uint32 version, -); - -typedef Argon2Hash = Pointer Function( - Pointer key, - int keyLen, - Pointer salt, - int saltlen, - int m_cost, // memory cost - int t_cost, // time cost (number iterations) - int parallelism, - int hashlen, - int type, - int version, -); - -typedef ResolveLibrary = String Function(String baseName); - -class Argon2FfiFlutter extends Argon2Base { - Argon2FfiFlutter({this.resolveLibrary}) { - final argon2lib = _loadLib(); - _nativeAdd = argon2lib - .lookup>('native_add') - .asFunction(); - argon2hash = argon2lib - .lookup>('hp_argon2_hash') - .asFunction(); - } - - static ResolveLibrary defaultResolveLibrary = (name) => name; - - /// forces loading of dynamic library on MacOS instead of assuming - /// argon2 was statically linked. (ie. flutter usage, vs dart usage) - static bool resolveLibraryForceDynamic = false; - - final ResolveLibrary resolveLibrary; - - int Function(int x, int y) _nativeAdd; - @override - Argon2Hash argon2hash; - - int addIt(int x, int y) => _nativeAdd(x, y); - - DynamicLibrary _loadLib() { - final resolveLibrary = this.resolveLibrary ?? defaultResolveLibrary; - - if (!resolveLibraryForceDynamic && (Platform.isIOS || Platform.isMacOS)) { - return DynamicLibrary.executable(); - } - final libraryNames = [ - [Platform.isAndroid, 'libargon2_ffi.so'], - [Platform.isLinux, './libargon2_ffi_plugin.so'], - [Platform.isWindows, 'argon2_ffi_plugin.dll'], - [Platform.isMacOS, 'libargon2_ffi.dylib'], - [Platform.isIOS, null], // only supports static linking. - ]; - final libraryName = libraryNames.firstWhere((element) => element[0] == true, - orElse: () => throw StateError( - 'Unsupported Operating System ${Platform.operatingSystem}'))[1] - as String; - final path = resolveLibrary(libraryName); - try { - return DynamicLibrary.open(libraryName); - } on ArgumentError catch (e, stackTrace) { - _logger.severe( - 'Error while loading dynamic library from $path ($libraryName)', - e, - stackTrace); - rethrow; - } - } -} - -abstract class Argon2 { - Uint8List argon2(Argon2Arguments args); - - Future argon2Async(Argon2Arguments args); -} - -class Argon2Arguments { - Argon2Arguments(this.key, this.salt, this.memory, this.iterations, - this.length, this.parallelism, this.type, this.version); - - final Uint8List key; - final Uint8List salt; - final int memory; - final int iterations; - final int length; - final int parallelism; - final int type; - final int version; -} - -abstract class Argon2Base extends Argon2 { -// @protected - Argon2Hash get argon2hash; - - @override - Uint8List argon2(Argon2Arguments args) { - final keyPtr = Uint8ArrayUtils.toPointer(args.key); - final saltPtr = Uint8ArrayUtils.toPointer(args.salt); -// final saltArray = allocate(count: args.salt.length); -// final saltList = saltArray.asTypedList(args.length); -// saltList.setAll(0, args.salt); -// const memoryCost = 1 << 16; - -// _logger.fine('saltArray: ${ByteUtils.toHexList(saltArray.view)}'); - - final result = argon2hash( - keyPtr, - args.key.length, - saltPtr, - args.salt.length, - args.memory, - args.iterations, - args.parallelism, - args.length, - args.type, - args.version, - ); - - free(keyPtr); - free(saltPtr); -// free(saltArray); - final resultString = Utf8.fromUtf8(result); - return base64.decode(resultString); - } - - @override - Future argon2Async(Argon2Arguments args) async { - return argon2(args); - } -} - -// from https://github.com/hanabi1224/flutter_native_extensions/blob/master/src/compression/dart_native_compression/lib/utils/uint8_list_utils.dart -class Uint8ArrayUtils { - static Uint8List fromPointer(Pointer ptr, int length) { - final view = ptr.asTypedList(length); - final builder = BytesBuilder(copy: false); - builder.add(view); - return builder.takeBytes(); - } - - static Pointer toPointer(Uint8List bytes) { - final ptr = allocate(count: bytes.length); - final byteList = ptr.asTypedList(bytes.length); - byteList.setAll(0, bytes); - return ptr.cast(); - } -} +export 'package:argon2_ffi_base/src/argon2_ffi_base.dart'; +export 'package:argon2_ffi_base/src/argon2_ffi_noop.dart' + if (dart.library.io) 'package:argon2_ffi_base/src/argon2_ffi_impl.dart'; diff --git a/lib/src/argon2_ffi_base.dart b/lib/src/argon2_ffi_base.dart new file mode 100644 index 0000000..c78783f --- /dev/null +++ b/lib/src/argon2_ffi_base.dart @@ -0,0 +1,21 @@ +import 'dart:typed_data'; + +abstract class Argon2 { + Uint8List argon2(Argon2Arguments args); + + Future argon2Async(Argon2Arguments args); +} + +class Argon2Arguments { + Argon2Arguments(this.key, this.salt, this.memory, this.iterations, + this.length, this.parallelism, this.type, this.version); + + final Uint8List key; + final Uint8List salt; + final int memory; + final int iterations; + final int length; + final int parallelism; + final int type; + final int version; +} diff --git a/lib/src/argon2_ffi_impl.dart b/lib/src/argon2_ffi_impl.dart new file mode 100644 index 0000000..fe77600 --- /dev/null +++ b/lib/src/argon2_ffi_impl.dart @@ -0,0 +1,137 @@ +import 'dart:convert'; +import 'dart:ffi'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:argon2_ffi_base/src/argon2_ffi_base.dart'; +import 'package:argon2_ffi_base/src/utils.dart'; +import 'package:ffi/ffi.dart'; +import 'package:logging/logging.dart'; + +final _logger = Logger('argon2_ffi_base'); + +// ignore_for_file: non_constant_identifier_names, + +typedef Argon2HashNative = Pointer Function( + Pointer key, + Uint32 keyLen, + Pointer salt, + Uint32 saltlen, + Uint32 m_cost, // memory cost + Uint32 t_cost, // time cost (number iterations) + Uint32 parallelism, + IntPtr hashlen, + Uint8 type, + Uint32 version, +); + +typedef Argon2Hash = Pointer Function( + Pointer key, + int keyLen, + Pointer salt, + int saltlen, + int m_cost, // memory cost + int t_cost, // time cost (number iterations) + int parallelism, + int hashlen, + int type, + int version, +); + +typedef ResolveLibrary = String Function(String baseName); + +abstract class Argon2Base extends Argon2 { +// @protected + Argon2Hash get argon2hash; + + @override + Uint8List argon2(Argon2Arguments args) { + final keyPtr = Uint8ArrayUtils.toPointer(args.key); + final saltPtr = Uint8ArrayUtils.toPointer(args.salt); +// final saltArray = allocate(count: args.salt.length); +// final saltList = saltArray.asTypedList(args.length); +// saltList.setAll(0, args.salt); +// const memoryCost = 1 << 16; + +// _logger.fine('saltArray: ${ByteUtils.toHexList(saltArray.view)}'); + + final result = argon2hash( + keyPtr, + args.key.length, + saltPtr, + args.salt.length, + args.memory, + args.iterations, + args.parallelism, + args.length, + args.type, + args.version, + ); + + free(keyPtr); + free(saltPtr); +// free(saltArray); + final resultString = Utf8.fromUtf8(result); + return base64.decode(resultString); + } + + @override + Future argon2Async(Argon2Arguments args) async { + return argon2(args); + } +} + +class Argon2FfiFlutter extends Argon2Base { + Argon2FfiFlutter({this.resolveLibrary}) { + final argon2lib = _loadLib(); + _nativeAdd = argon2lib + .lookup>('native_add') + .asFunction(); + argon2hash = argon2lib + .lookup>('hp_argon2_hash') + .asFunction(); + } + + static ResolveLibrary defaultResolveLibrary = (name) => name; + + /// forces loading of dynamic library on MacOS instead of assuming + /// argon2 was statically linked. (ie. flutter usage, vs dart usage) + static bool resolveLibraryForceDynamic = false; + + final ResolveLibrary resolveLibrary; + + int Function(int x, int y) _nativeAdd; + @override + Argon2Hash argon2hash; + + int addIt(int x, int y) => _nativeAdd(x, y); + + DynamicLibrary _loadLib() { + final resolveLibrary = this.resolveLibrary ?? defaultResolveLibrary; + + if (!resolveLibraryForceDynamic && (Platform.isIOS || Platform.isMacOS)) { + return DynamicLibrary.executable(); + } + final libraryNames = [ + [Platform.isAndroid, 'libargon2_ffi.so'], + [Platform.isLinux, './libargon2_ffi_plugin.so'], + [Platform.isWindows, 'argon2_ffi_plugin.dll'], + [Platform.isMacOS, 'libargon2_ffi.dylib'], + [Platform.isIOS, null], // only supports static linking. + ]; + final libraryName = libraryNames.firstWhere((element) => element[0] == true, + orElse: () => throw StateError( + 'Unsupported Operating System ${Platform.operatingSystem}'))[1] + as String; + final path = resolveLibrary(libraryName); + try { + return DynamicLibrary.open(libraryName); + } on ArgumentError catch (e, stackTrace) { + _logger.severe( + 'Error while loading dynamic library from $path ($libraryName)', + e, + stackTrace); + rethrow; + } + } +} diff --git a/lib/src/argon2_ffi_noop.dart b/lib/src/argon2_ffi_noop.dart new file mode 100644 index 0000000..556c268 --- /dev/null +++ b/lib/src/argon2_ffi_noop.dart @@ -0,0 +1,15 @@ +import 'dart:typed_data'; + +import 'package:argon2_ffi_base/src/argon2_ffi_base.dart'; + +class Argon2FfiFlutter extends Argon2 { + @override + Uint8List argon2(Argon2Arguments args) { + throw UnimplementedError(); + } + + @override + Future argon2Async(Argon2Arguments args) { + throw UnimplementedError(); + } +} diff --git a/lib/src/utils.dart b/lib/src/utils.dart new file mode 100644 index 0000000..9f23908 --- /dev/null +++ b/lib/src/utils.dart @@ -0,0 +1,23 @@ +// from https://github.com/hanabi1224/flutter_native_extensions/blob/master/src/compression/dart_native_compression/lib/utils/uint8_list_utils.dart +import 'dart:ffi'; +import 'dart:io'; + +import 'dart:typed_data'; + +import 'package:ffi/ffi.dart'; + +class Uint8ArrayUtils { + static Uint8List fromPointer(Pointer ptr, int length) { + final view = ptr.asTypedList(length); + final builder = BytesBuilder(copy: false); + builder.add(view); + return builder.takeBytes(); + } + + static Pointer toPointer(Uint8List bytes) { + final ptr = allocate(count: bytes.length); + final byteList = ptr.asTypedList(bytes.length); + byteList.setAll(0, bytes); + return ptr.cast(); + } +}