From 6511ce973e0fac65934bb71ccb782681ccfb1dff Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Thu, 22 Jul 2021 16:22:50 +0200 Subject: [PATCH] improve nnbd, add debugging to aes decryption --- CHANGELOG.md | 5 +++++ lib/src/kdbx_format.dart | 22 ++++++++++++---------- lib/src/kdbx_header.dart | 24 ++++++++++++------------ pubspec.yaml | 2 +- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0a3a92..9217049 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.0+1 + +- Small Null-safety improvement. +- add debugging to AES decryption. + ## 2.0.0 - Null-safety migration diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index 1fae647..29af7f7 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -265,14 +265,14 @@ class KdbxBody extends KdbxNode { KdbxFile kdbxFile, Uint8List? compressedBytes) async { final byteWriter = WriterHelper(); byteWriter.writeBytes( - kdbxFile.header.fields[HeaderFields.StreamStartBytes]!.bytes!); + kdbxFile.header.fields[HeaderFields.StreamStartBytes]!.bytes); HashedBlockReader.writeBlocks(ReaderHelper(compressedBytes), byteWriter); final bytes = byteWriter.output.toBytes(); final masterKey = await KdbxFormat._generateMasterKeyV3( kdbxFile.header, kdbxFile.credentials); final encrypted = KdbxFormat._encryptDataAes(masterKey, bytes, - kdbxFile.header.fields[HeaderFields.EncryptionIV]!.bytes!); + kdbxFile.header.fields[HeaderFields.EncryptionIV]!.bytes); return encrypted; } @@ -537,7 +537,7 @@ class KdbxFormat { throw UnsupportedError('Unsupported version ${header.version}'); } else if (file.header.version < KdbxVersion.V4) { final streamKey = - file.header.fields[HeaderFields.ProtectedStreamKey]!.bytes!; + file.header.fields[HeaderFields.ProtectedStreamKey]!.bytes; final gen = ProtectedSaltGenerator(streamKey); body.meta.headerHash.set(headerHash.buffer); @@ -703,7 +703,7 @@ class KdbxFormat { Uint8List transformContentV4ChaCha20( KdbxHeader header, Uint8List encrypted, Uint8List cipherKey) { - final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; + final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes; final chaCha = ChaCha7539Engine() ..init(true, ParametersWithIV(KeyParameter(cipherKey), encryptionIv)); return chaCha.process(encrypted); @@ -726,7 +726,7 @@ class KdbxFormat { Future<_KeysV4> _computeKeysV4( KdbxHeader header, Credentials credentials) async { - final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes!; + final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes; final kdfParameters = header.readKdfParameters; if (masterSeed.length != 32) { throw const FormatException('Master seed must be 32 bytes.'); @@ -823,14 +823,16 @@ class KdbxFormat { Uint8List _decryptContent( KdbxHeader header, Uint8List masterKey, Uint8List encryptedPayload) { - final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; + final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes; final decryptCipher = CBCBlockCipher(AESFastEngine()); decryptCipher.init( false, ParametersWithIV(KeyParameter(masterKey), encryptionIv)); + _logger.finer('decrypting ${encryptedPayload.length} with block size ' + '${decryptCipher.blockSize}'); final paddedDecrypted = AesHelper.processBlocks(decryptCipher, encryptedPayload); - final streamStart = header.fields[HeaderFields.StreamStartBytes]!.bytes!; + final streamStart = header.fields[HeaderFields.StreamStartBytes]!.bytes; if (paddedDecrypted.lengthInBytes < streamStart.lengthInBytes) { _logger.warning( @@ -852,7 +854,7 @@ class KdbxFormat { Uint8List _decryptContentV4( KdbxHeader header, Uint8List cipherKey, Uint8List encryptedPayload) { - final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; + final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes; final decryptCipher = CBCBlockCipher(AESFastEngine()); decryptCipher.init( @@ -867,7 +869,7 @@ class KdbxFormat { /// TODO combine this with [_decryptContentV4] (or [_encryptDataAes]?) Uint8List _encryptContentV4Aes( KdbxHeader header, Uint8List cipherKey, Uint8List bytes) { - final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; + final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes; final encryptCypher = CBCBlockCipher(AESFastEngine()); encryptCypher.init( true, ParametersWithIV(KeyParameter(cipherKey), encryptionIv)); @@ -879,7 +881,7 @@ class KdbxFormat { KdbxHeader header, Credentials credentials) async { final rounds = header.v3KdfTransformRounds; final seed = header.fields[HeaderFields.TransformSeed]!.bytes; - final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes!; + final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes; _logger.finer( 'Rounds: $rounds (${ByteUtils.toHexList(header.fields[HeaderFields.TransformRounds]!.bytes)})'); final transformedKey = await KeyEncrypterKdf.encryptAesAsync( diff --git a/lib/src/kdbx_header.dart b/lib/src/kdbx_header.dart index a1f44d4..ba71a5a 100644 --- a/lib/src/kdbx_header.dart +++ b/lib/src/kdbx_header.dart @@ -133,7 +133,7 @@ class HeaderField implements HeaderFieldBase { @override final HeaderFields field; - final Uint8List? bytes; + final Uint8List bytes; String get name => field.toString(); } @@ -332,10 +332,10 @@ class KdbxHeader { if (value == null) { return; } - _logger.finer('Writing header $field (${value.bytes!.lengthInBytes})'); + _logger.finer('Writing header $field (${value.bytes.lengthInBytes})'); writer.writeUint8(field.index); - _writeFieldSize(writer, value.bytes!.lengthInBytes); - writer.writeBytes(value.bytes!); + _writeFieldSize(writer, value.bytes.lengthInBytes); + writer.writeBytes(value.bytes); } void _writeFieldSize(WriterHelper writer, int size) { @@ -427,7 +427,7 @@ class KdbxHeader { ReaderHelper reader, KdbxVersion version, List fields, - T Function(TE field, Uint8List? bytes) createField) => + T Function(TE field, Uint8List bytes) createField) => Map.fromEntries(readField(reader, version, fields, createField) .map((field) => MapEntry(field.field, field))); @@ -435,12 +435,13 @@ class KdbxHeader { ReaderHelper reader, KdbxVersion version, List fields, - T Function(TE field, Uint8List? bytes) createField) sync* { + T Function(TE field, Uint8List bytes) createField) sync* { while (true) { final headerId = reader.readUint8(); final bodySize = version >= KdbxVersion.V4 ? reader.readUint32() : reader.readUint16(); - final bodyBytes = bodySize > 0 ? reader.readBytes(bodySize) : null; + final bodyBytes = + bodySize > 0 ? reader.readBytes(bodySize) : Uint8List(0); // _logger.finer( // 'Read header ${fields[headerId]}: ${ByteUtils.toHexList(bodyBytes)}'); if (headerId > 0) { @@ -476,22 +477,21 @@ class KdbxHeader { Cipher? get cipher { if (version < KdbxVersion.V4) { assert( - CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID]!.bytes!) == + CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID]!.bytes) == Cipher.aes); return Cipher.aes; } try { - return CryptoConsts.cipherFromBytes( - fields[HeaderFields.CipherID]!.bytes!); + return CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID]!.bytes); } catch (e, stackTrace) { _logger.warning( 'Unable to find cipher. ' - '${fields[HeaderFields.CipherID]?.bytes?.encodeBase64()}', + '${fields[HeaderFields.CipherID]?.bytes.encodeBase64()}', e, stackTrace); throw KdbxCorruptedFileException( 'Invalid cipher. ' - '${fields[HeaderFields.CipherID]?.bytes?.encodeBase64()}', + '${fields[HeaderFields.CipherID]?.bytes.encodeBase64()}', ); } } diff --git a/pubspec.yaml b/pubspec.yaml index 4ac9f73..18b8dfe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: kdbx description: KeepassX format implementation in pure dart. (kdbx 3.x and 4.x support). -version: 2.0.0 +version: 2.0.0+1 homepage: https://github.com/authpass/kdbx.dart environment: