diff --git a/.idea/dictionaries/herbert.xml b/.idea/dictionaries/herbert.xml index c942ae1..eeea0d7 100644 --- a/.idea/dictionaries/herbert.xml +++ b/.idea/dictionaries/herbert.xml @@ -7,6 +7,7 @@ hmac kdbx keepass + uint \ No newline at end of file diff --git a/bin/kdbx.dart b/bin/kdbx.dart index 45a3277..8df3366 100644 --- a/bin/kdbx.dart +++ b/bin/kdbx.dart @@ -94,7 +94,7 @@ abstract class KdbxFileCommand extends Command { final keyFileData = keyFile == null ? null : await File(keyFile).readAsBytes(); - Argon2FfiFlutter.resolveLibraryForceDynamic = true; + Argon2.resolveLibraryForceDynamic = true; final file = await KdbxFormat(Argon2FfiFlutter()).read( bytes, Credentials.composite(ProtectedValue.fromString(password), keyFileData), diff --git a/lib/src/internal/byte_utils.dart b/lib/src/internal/byte_utils.dart index 5ae1966..2517872 100644 --- a/lib/src/internal/byte_utils.dart +++ b/lib/src/internal/byte_utils.dart @@ -3,6 +3,14 @@ import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; +import 'package:kdbx/kdbx.dart'; + +/// A bitmask that limits an integer to 32 bits. +const mask32 = 0xFFFFFFFF; + +/// The number of bytes in a 32-bit word. +const bytesPerWord = 4; + class ByteUtils { static final _random = Random.secure(); @@ -27,8 +35,13 @@ class ByteUtils { list?.map((val) => toHex(val))?.join(' ') ?? '(null)'; } +const dartWebWorkaround = true; + class ReaderHelper { - ReaderHelper(this.byteData) : lengthInBytes = byteData.lengthInBytes; + factory ReaderHelper(Uint8List byteData) => KdbxFormat.dartWebWorkaround + ? ReaderHelper(byteData) + : ReaderHelper._(byteData); + ReaderHelper._(this.byteData) : lengthInBytes = byteData.lengthInBytes; final Uint8List byteData; int pos = 0; @@ -72,10 +85,24 @@ class ReaderHelper { int readUint8() => _nextByteBuffer(1).getUint8(0); int readUint16() => _nextByteBuffer(2).getUint16(0, Endian.little); int readUint32() => _nextByteBuffer(4).getUint32(0, Endian.little); - int readUint64() => _nextByteBuffer(8).getUint64(0, Endian.little); + int readUint64() { + if (!dartWebWorkaround) { + return _nextByteBuffer(8).getUint64(0, Endian.little); + } else { + final lo = readUint32(); + final hi = readUint32(); + return hi << 32 + lo; + } + } int readInt32() => _nextByteBuffer(4).getInt32(0, Endian.little); - int readInt64() => _nextByteBuffer(8).getInt64(0, Endian.little); + int readInt64() { + if (!dartWebWorkaround) { + return _nextByteBuffer(8).getInt64(0, Endian.little); + } else { + return readUint64(); + } + } Uint8List readBytes(int size) => _nextBytes(size); @@ -90,10 +117,17 @@ class ReaderHelper { static int singleUint64(Uint8List bytes) => ReaderHelper(bytes).readUint64(); } +class ReaderHelperDartWeb extends ReaderHelper { + ReaderHelperDartWeb(Uint8List byteData) : super._(byteData); +} + typedef LengthWriter = void Function(int length); class WriterHelper { - WriterHelper([BytesBuilder output]) : output = output ?? BytesBuilder(); + factory WriterHelper([BytesBuilder output]) => KdbxFormat.dartWebWorkaround + ? WriterHelperDartWeb(output) + : WriterHelper._(output); + WriterHelper._([BytesBuilder output]) : output = output ?? BytesBuilder(); final BytesBuilder output; @@ -148,3 +182,30 @@ class WriterHelper { return bytes.length; } } + +class WriterHelperDartWeb extends WriterHelper { + WriterHelperDartWeb([BytesBuilder output]) : super._(output); + + @override + void writeUint64(int value, [LengthWriter lengthWriter]) { + lengthWriter?.call(8); + + final _endian = Endian.little; + final highBits = value >> 32; + final lowBits = value & mask32; + final byteData = ByteData(8); + if (_endian == Endian.big) { + byteData.setUint32(0, highBits, _endian); + byteData.setUint32(0 + bytesPerWord, lowBits, _endian); + } else { + byteData.setUint32(0, lowBits, _endian); + byteData.setUint32(0 + bytesPerWord, highBits, _endian); + } + _write(byteData); + } + + @override + void writeInt64(int value, [LengthWriter lengthWriter]) { + writeUint64(value, lengthWriter); + } +} diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index efa72b3..acac4e2 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -324,6 +324,7 @@ class KdbxFormat { KdbxFormat([this.argon2]); final Argon2 argon2; + static bool dartWebWorkaround = false; KdbxFile create( Credentials credentials, diff --git a/pubspec.yaml b/pubspec.yaml index 453fcf8..f3c6da2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,7 +29,7 @@ dependencies: args: '>1.5.0 <2.0.0' prompts: '>=1.3.0 <2.0.0' logging_appenders: '>=0.1.0 <1.0.0' - argon2_ffi_base: '>=0.1.0 <1.0.0' + argon2_ffi_base: '>=0.1.4+2 <1.0.0' dev_dependencies: pedantic: '>=1.7.0 <2.0.0' diff --git a/test/internal/test_utils.dart b/test/internal/test_utils.dart index b097e75..08ccb1d 100644 --- a/test/internal/test_utils.dart +++ b/test/internal/test_utils.dart @@ -13,7 +13,7 @@ final _logger = Logger('test_utils'); class TestUtil { static KdbxFormat kdbxFormat() { - Argon2FfiFlutter.resolveLibraryForceDynamic = true; + Argon2.resolveLibraryForceDynamic = true; return KdbxFormat(Argon2FfiFlutter(resolveLibrary: (path) { final cwd = Directory('.').absolute.uri; final p = cwd.resolve(path);