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
- Null-safety migration

22
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(

24
lib/src/kdbx_header.dart

@ -133,7 +133,7 @@ class HeaderField implements HeaderFieldBase<HeaderFields> {
@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<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((field) => MapEntry(field.field, field)));
@ -435,12 +435,13 @@ class KdbxHeader {
ReaderHelper reader,
KdbxVersion version,
List<TE> 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()}',
);
}
}

2
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:

Loading…
Cancel
Save