Browse Source

improve nnbd, add debugging to aes decryption

pull/5/head
Herbert Poul 3 years ago
parent
commit
6511ce973e
  1. 5
      CHANGELOG.md
  2. 22
      lib/src/kdbx_format.dart
  3. 24
      lib/src/kdbx_header.dart
  4. 2
      pubspec.yaml

5
CHANGELOG.md

@ -1,3 +1,8 @@
## 2.0.0+1
- Small Null-safety improvement.
- add debugging to AES decryption.
## 2.0.0 ## 2.0.0
- Null-safety migration - Null-safety migration

22
lib/src/kdbx_format.dart

@ -265,14 +265,14 @@ class KdbxBody extends KdbxNode {
KdbxFile kdbxFile, Uint8List? compressedBytes) async { KdbxFile kdbxFile, Uint8List? compressedBytes) async {
final byteWriter = WriterHelper(); final byteWriter = WriterHelper();
byteWriter.writeBytes( byteWriter.writeBytes(
kdbxFile.header.fields[HeaderFields.StreamStartBytes]!.bytes!); kdbxFile.header.fields[HeaderFields.StreamStartBytes]!.bytes);
HashedBlockReader.writeBlocks(ReaderHelper(compressedBytes), byteWriter); HashedBlockReader.writeBlocks(ReaderHelper(compressedBytes), byteWriter);
final bytes = byteWriter.output.toBytes(); final bytes = byteWriter.output.toBytes();
final masterKey = await KdbxFormat._generateMasterKeyV3( final masterKey = await KdbxFormat._generateMasterKeyV3(
kdbxFile.header, kdbxFile.credentials); kdbxFile.header, kdbxFile.credentials);
final encrypted = KdbxFormat._encryptDataAes(masterKey, bytes, final encrypted = KdbxFormat._encryptDataAes(masterKey, bytes,
kdbxFile.header.fields[HeaderFields.EncryptionIV]!.bytes!); kdbxFile.header.fields[HeaderFields.EncryptionIV]!.bytes);
return encrypted; return encrypted;
} }
@ -537,7 +537,7 @@ class KdbxFormat {
throw UnsupportedError('Unsupported version ${header.version}'); throw UnsupportedError('Unsupported version ${header.version}');
} else if (file.header.version < KdbxVersion.V4) { } else if (file.header.version < KdbxVersion.V4) {
final streamKey = final streamKey =
file.header.fields[HeaderFields.ProtectedStreamKey]!.bytes!; file.header.fields[HeaderFields.ProtectedStreamKey]!.bytes;
final gen = ProtectedSaltGenerator(streamKey); final gen = ProtectedSaltGenerator(streamKey);
body.meta.headerHash.set(headerHash.buffer); body.meta.headerHash.set(headerHash.buffer);
@ -703,7 +703,7 @@ class KdbxFormat {
Uint8List transformContentV4ChaCha20( Uint8List transformContentV4ChaCha20(
KdbxHeader header, Uint8List encrypted, Uint8List cipherKey) { KdbxHeader header, Uint8List encrypted, Uint8List cipherKey) {
final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes;
final chaCha = ChaCha7539Engine() final chaCha = ChaCha7539Engine()
..init(true, ParametersWithIV(KeyParameter(cipherKey), encryptionIv)); ..init(true, ParametersWithIV(KeyParameter(cipherKey), encryptionIv));
return chaCha.process(encrypted); return chaCha.process(encrypted);
@ -726,7 +726,7 @@ class KdbxFormat {
Future<_KeysV4> _computeKeysV4( Future<_KeysV4> _computeKeysV4(
KdbxHeader header, Credentials credentials) async { KdbxHeader header, Credentials credentials) async {
final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes!; final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes;
final kdfParameters = header.readKdfParameters; final kdfParameters = header.readKdfParameters;
if (masterSeed.length != 32) { if (masterSeed.length != 32) {
throw const FormatException('Master seed must be 32 bytes.'); throw const FormatException('Master seed must be 32 bytes.');
@ -823,14 +823,16 @@ class KdbxFormat {
Uint8List _decryptContent( Uint8List _decryptContent(
KdbxHeader header, Uint8List masterKey, Uint8List encryptedPayload) { KdbxHeader header, Uint8List masterKey, Uint8List encryptedPayload) {
final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes;
final decryptCipher = CBCBlockCipher(AESFastEngine()); final decryptCipher = CBCBlockCipher(AESFastEngine());
decryptCipher.init( decryptCipher.init(
false, ParametersWithIV(KeyParameter(masterKey), encryptionIv)); false, ParametersWithIV(KeyParameter(masterKey), encryptionIv));
_logger.finer('decrypting ${encryptedPayload.length} with block size '
'${decryptCipher.blockSize}');
final paddedDecrypted = final paddedDecrypted =
AesHelper.processBlocks(decryptCipher, encryptedPayload); AesHelper.processBlocks(decryptCipher, encryptedPayload);
final streamStart = header.fields[HeaderFields.StreamStartBytes]!.bytes!; final streamStart = header.fields[HeaderFields.StreamStartBytes]!.bytes;
if (paddedDecrypted.lengthInBytes < streamStart.lengthInBytes) { if (paddedDecrypted.lengthInBytes < streamStart.lengthInBytes) {
_logger.warning( _logger.warning(
@ -852,7 +854,7 @@ class KdbxFormat {
Uint8List _decryptContentV4( Uint8List _decryptContentV4(
KdbxHeader header, Uint8List cipherKey, Uint8List encryptedPayload) { KdbxHeader header, Uint8List cipherKey, Uint8List encryptedPayload) {
final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes;
final decryptCipher = CBCBlockCipher(AESFastEngine()); final decryptCipher = CBCBlockCipher(AESFastEngine());
decryptCipher.init( decryptCipher.init(
@ -867,7 +869,7 @@ class KdbxFormat {
/// TODO combine this with [_decryptContentV4] (or [_encryptDataAes]?) /// TODO combine this with [_decryptContentV4] (or [_encryptDataAes]?)
Uint8List _encryptContentV4Aes( Uint8List _encryptContentV4Aes(
KdbxHeader header, Uint8List cipherKey, Uint8List bytes) { KdbxHeader header, Uint8List cipherKey, Uint8List bytes) {
final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes!; final encryptionIv = header.fields[HeaderFields.EncryptionIV]!.bytes;
final encryptCypher = CBCBlockCipher(AESFastEngine()); final encryptCypher = CBCBlockCipher(AESFastEngine());
encryptCypher.init( encryptCypher.init(
true, ParametersWithIV(KeyParameter(cipherKey), encryptionIv)); true, ParametersWithIV(KeyParameter(cipherKey), encryptionIv));
@ -879,7 +881,7 @@ class KdbxFormat {
KdbxHeader header, Credentials credentials) async { KdbxHeader header, Credentials credentials) async {
final rounds = header.v3KdfTransformRounds; final rounds = header.v3KdfTransformRounds;
final seed = header.fields[HeaderFields.TransformSeed]!.bytes; final seed = header.fields[HeaderFields.TransformSeed]!.bytes;
final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes!; final masterSeed = header.fields[HeaderFields.MasterSeed]!.bytes;
_logger.finer( _logger.finer(
'Rounds: $rounds (${ByteUtils.toHexList(header.fields[HeaderFields.TransformRounds]!.bytes)})'); 'Rounds: $rounds (${ByteUtils.toHexList(header.fields[HeaderFields.TransformRounds]!.bytes)})');
final transformedKey = await KeyEncrypterKdf.encryptAesAsync( final transformedKey = await KeyEncrypterKdf.encryptAesAsync(

24
lib/src/kdbx_header.dart

@ -133,7 +133,7 @@ class HeaderField implements HeaderFieldBase<HeaderFields> {
@override @override
final HeaderFields field; final HeaderFields field;
final Uint8List? bytes; final Uint8List bytes;
String get name => field.toString(); String get name => field.toString();
} }
@ -332,10 +332,10 @@ class KdbxHeader {
if (value == null) { if (value == null) {
return; return;
} }
_logger.finer('Writing header $field (${value.bytes!.lengthInBytes})'); _logger.finer('Writing header $field (${value.bytes.lengthInBytes})');
writer.writeUint8(field.index); writer.writeUint8(field.index);
_writeFieldSize(writer, value.bytes!.lengthInBytes); _writeFieldSize(writer, value.bytes.lengthInBytes);
writer.writeBytes(value.bytes!); writer.writeBytes(value.bytes);
} }
void _writeFieldSize(WriterHelper writer, int size) { void _writeFieldSize(WriterHelper writer, int size) {
@ -427,7 +427,7 @@ class KdbxHeader {
ReaderHelper reader, ReaderHelper reader,
KdbxVersion version, KdbxVersion version,
List<TE> fields, List<TE> fields,
T Function(TE field, Uint8List? bytes) createField) => T Function(TE field, Uint8List bytes) createField) =>
Map<TE, T>.fromEntries(readField(reader, version, fields, createField) Map<TE, T>.fromEntries(readField(reader, version, fields, createField)
.map((field) => MapEntry(field.field, field))); .map((field) => MapEntry(field.field, field)));
@ -435,12 +435,13 @@ class KdbxHeader {
ReaderHelper reader, ReaderHelper reader,
KdbxVersion version, KdbxVersion version,
List<TE> fields, List<TE> fields,
T Function(TE field, Uint8List? bytes) createField) sync* { T Function(TE field, Uint8List bytes) createField) sync* {
while (true) { while (true) {
final headerId = reader.readUint8(); final headerId = reader.readUint8();
final bodySize = final bodySize =
version >= KdbxVersion.V4 ? reader.readUint32() : reader.readUint16(); 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( // _logger.finer(
// 'Read header ${fields[headerId]}: ${ByteUtils.toHexList(bodyBytes)}'); // 'Read header ${fields[headerId]}: ${ByteUtils.toHexList(bodyBytes)}');
if (headerId > 0) { if (headerId > 0) {
@ -476,22 +477,21 @@ class KdbxHeader {
Cipher? get cipher { Cipher? get cipher {
if (version < KdbxVersion.V4) { if (version < KdbxVersion.V4) {
assert( assert(
CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID]!.bytes!) == CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID]!.bytes) ==
Cipher.aes); Cipher.aes);
return Cipher.aes; return Cipher.aes;
} }
try { try {
return CryptoConsts.cipherFromBytes( return CryptoConsts.cipherFromBytes(fields[HeaderFields.CipherID]!.bytes);
fields[HeaderFields.CipherID]!.bytes!);
} catch (e, stackTrace) { } catch (e, stackTrace) {
_logger.warning( _logger.warning(
'Unable to find cipher. ' 'Unable to find cipher. '
'${fields[HeaderFields.CipherID]?.bytes?.encodeBase64()}', '${fields[HeaderFields.CipherID]?.bytes.encodeBase64()}',
e, e,
stackTrace); stackTrace);
throw KdbxCorruptedFileException( throw KdbxCorruptedFileException(
'Invalid cipher. ' 'Invalid cipher. '
'${fields[HeaderFields.CipherID]?.bytes?.encodeBase64()}', '${fields[HeaderFields.CipherID]?.bytes.encodeBase64()}',
); );
} }
} }

2
pubspec.yaml

@ -1,6 +1,6 @@
name: kdbx name: kdbx
description: KeepassX format implementation in pure dart. (kdbx 3.x and 4.x support). 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 homepage: https://github.com/authpass/kdbx.dart
environment: environment:

Loading…
Cancel
Save