From 0e2a56a729e8ca1afecb747ddfa5e4eecdcb2917 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Mon, 24 Feb 2020 20:31:14 +0100 Subject: [PATCH] fixed validating of hmac blocks. --- lib/src/crypto/key_encrypter_kdf.dart | 3 -- lib/src/crypto/protected_salt_generator.dart | 24 ++------- lib/src/kdbx_format.dart | 57 +++++++++++--------- lib/src/kdbx_header.dart | 4 +- 4 files changed, 38 insertions(+), 50 deletions(-) diff --git a/lib/src/crypto/key_encrypter_kdf.dart b/lib/src/crypto/key_encrypter_kdf.dart index e294bb2..5d62053 100644 --- a/lib/src/crypto/key_encrypter_kdf.dart +++ b/lib/src/crypto/key_encrypter_kdf.dart @@ -93,9 +93,6 @@ class KeyEncrypterKdf { } Uint8List encryptArgon2(Uint8List key, VarDictionary kdfParameters) { - _logger.fine('argon2():'); - _logger.fine('key: ${ByteUtils.toHexList(key)}'); - KdfField.debugAll(kdfParameters); return argon2.argon2( key, KdfField.salt.read(kdfParameters), diff --git a/lib/src/crypto/protected_salt_generator.dart b/lib/src/crypto/protected_salt_generator.dart index e788d2b..a30bf69 100644 --- a/lib/src/crypto/protected_salt_generator.dart +++ b/lib/src/crypto/protected_salt_generator.dart @@ -43,22 +43,18 @@ class ProtectedSaltGenerator { } class ChachaProtectedSaltGenerator implements ProtectedSaltGenerator { - ChachaProtectedSaltGenerator._(this._secretKey, this._nonce, this._state); + ChachaProtectedSaltGenerator._(this._state); factory ChachaProtectedSaltGenerator.create(Uint8List key) { final hash = sha512.convert(key); final secretKey = hash.bytes.sublist(0, 32); final nonce = hash.bytes.sublist(32, 32 + 12); - return ChachaProtectedSaltGenerator._( + return ChachaProtectedSaltGenerator._(cryptography.chacha20.newState( cryptography.SecretKey(secretKey), - cryptography.SecretKey(nonce), - cryptography.chacha20.newState(cryptography.SecretKey(secretKey), - nonce: cryptography.SecretKey(nonce))); + nonce: cryptography.SecretKey(nonce))); } - final cryptography.SecretKey _secretKey; - final cryptography.SecretKey _nonce; final cryptography.KeyStreamCipherState _state; @override @@ -72,19 +68,7 @@ class ChachaProtectedSaltGenerator implements ProtectedSaltGenerator { return null; } final result = _state.convert(bytes); -// try { - _logger.fine('decoding protected value.'); - final ret = utf8.decode(result); - _logger.fine('Successfully decoded stuff.'); - return ret; -// } on FormatException catch (e, stackTrace) { -// final ret = utf8.decode(result, allowMalformed: true); -// _logger.severe( -// 'Error while decoding utf8. ignoring malformed. result: {$ret}', -// e, -// stackTrace); -// return ret; -// } + return utf8.decode(result); } @override diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index 58293ca..3f1a9e0 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -277,7 +277,7 @@ class KdbxBody extends KdbxNode { _logger.fine('We need AES'); final result = kdbxFile.kdbxFormat ._encryptContentV4Aes(header, cipherKey, compressedBytes); - _logger.fine('Result: ${ByteUtils.toHexList(result)}'); +// _logger.fine('Result: ${ByteUtils.toHexList(result)}'); return result; } else if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.chaCha20].uuid) { _logger.fine('We need chacha20'); @@ -399,22 +399,21 @@ class KdbxFormat { 'Does not match ${ByteUtils.toHexList(hash)} vs ${ByteUtils.toHexList(actualHash)}'); throw KdbxCorruptedFileException('Header hash does not match.'); } - _logger - .finest('KdfParameters: ${header.readKdfParameters.toDebugString()}'); +// _logger +// .finest('KdfParameters: ${header.readKdfParameters.toDebugString()}'); _logger.finest('Header hash matches.'); final keys = _computeKeysV4(header, credentials); final headerHmac = _getHeaderHmac(reader.byteData.sublist(0, header.endPos), keys.hmacKey); final expectedHmac = reader.readBytes(headerHmac.bytes.length); - _logger.fine('Expected: ${ByteUtils.toHexList(expectedHmac)}'); - _logger.fine('Actual : ${ByteUtils.toHexList(headerHmac.bytes)}'); +// _logger.fine('Expected: ${ByteUtils.toHexList(expectedHmac)}'); +// _logger.fine('Actual : ${ByteUtils.toHexList(headerHmac.bytes)}'); if (!ByteUtils.eq(headerHmac.bytes, expectedHmac)) { throw KdbxInvalidKeyException(); } // final hmacTransformer = crypto.Hmac(crypto.sha256, hmacKey.bytes); // final blockreader.readBytes(32); - final bodyContent = hmacBlockTransformer(reader); - _logger.fine('body decrypt: ${ByteUtils.toHexList(bodyContent)}'); + final bodyContent = hmacBlockTransformer(keys.hmacKey, reader); final decrypted = decrypt(header, bodyContent, keys.cipherKey); _logger.finer('compression: ${header.compression}'); if (header.compression == Compression.gzip) { @@ -429,17 +428,37 @@ class KdbxFormat { return null; } - Uint8List hmacBlockTransformer(ReaderHelper reader) { - Uint8List blockHash; - int blockLength; + Uint8List hmacBlockTransformer(Uint8List hmacKey, ReaderHelper reader) { final ret = []; + int blockIndex = 0; while (true) { - blockHash = reader.readBytes(32); - blockLength = reader.readUint32(); + final blockKeySrc = WriterHelper() + ..writeUint64(blockIndex) + ..writeBytes(hmacKey); + final blockKey = crypto.sha512.convert(blockKeySrc.output.toBytes()); + + final blockHash = reader.readBytes(32); + final blockLength = reader.readUint32(); + final blockBytes = reader.readBytes(blockLength); + final tmp = WriterHelper(); + tmp.writeUint64(blockIndex); + tmp.writeInt32(blockLength); + tmp.writeBytes(blockBytes); +// _logger.fine('blockHash: ${ByteUtils.toHexList(tmp.output.toBytes())}'); +// _logger.fine('blockKey: ${ByteUtils.toHexList(blockKey.bytes)}'); + final hmac = crypto.Hmac(crypto.sha256, blockKey.bytes); + final calculatedHash = hmac.convert(tmp.output.toBytes()); +// _logger +// .fine('CalculatedHash: ${ByteUtils.toHexList(calculatedHash.bytes)}'); + if (!ByteUtils.eq(blockHash, calculatedHash.bytes)) { + throw KdbxCorruptedFileException('Invalid hash block.'); + } + if (blockLength < 1) { return Uint8List.fromList(ret); } - ret.addAll(reader.readBytes(blockLength)); + blockIndex++; + ret.addAll(blockBytes); } } @@ -449,7 +468,6 @@ class KdbxFormat { if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.aes].uuid) { _logger.fine('We need AES'); final result = _decryptContentV4(header, cipherKey, encrypted); - _logger.fine('Result: ${ByteUtils.toHexList(result)}'); return result; } else if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.chaCha20].uuid) { _logger.fine('We need chacha20'); @@ -470,8 +488,6 @@ class KdbxFormat { final hmacKey = crypto.sha512.convert(writer.output.toBytes()).bytes; final src = headerBytes; final hmacKeyStuff = crypto.Hmac(crypto.sha256, hmacKey); - _logger.fine('keySha: ${ByteUtils.toHexList(hmacKey)}'); - _logger.fine('src: ${ByteUtils.toHexList(src)}'); return hmacKeyStuff.convert(src); } @@ -483,22 +499,17 @@ class KdbxFormat { } final credentialHash = credentials.getHash(); - _logger.fine('MasterSeed: ${ByteUtils.toHexList(masterSeed)}'); - _logger.fine('credentialHash: ${ByteUtils.toHexList(credentialHash)}'); final key = KeyEncrypterKdf(argon2).encrypt(credentialHash, kdfParameters); - _logger.fine('keyv4: ${ByteUtils.toHexList(key)}'); // final keyWithSeed = Uint8List(65); // keyWithSeed.replaceRange(0, masterSeed.length, masterSeed); // keyWithSeed.replaceRange( // masterSeed.length, masterSeed.length + key.length, key); // keyWithSeed[64] = 1; - _logger.fine('masterSeed: ${ByteUtils.toHexList(masterSeed)}'); final keyWithSeed = masterSeed + key + Uint8List.fromList([1]); assert(keyWithSeed.length == 65); final cipher = crypto.sha256.convert(keyWithSeed.sublist(0, 64)); final hmacKey = crypto.sha512.convert(keyWithSeed); - _logger.fine('hmacKey: ${ByteUtils.toHexList(hmacKey.bytes)}'); return _KeysV4(hmacKey.bytes as Uint8List, cipher.bytes as Uint8List); } @@ -553,10 +564,6 @@ class KdbxFormat { throw KdbxInvalidKeyException(); } - _logger.finest('streamStart: ${ByteUtils.toHexList(streamStart)}'); - _logger.finest( - 'actual : ${ByteUtils.toHexList(paddedDecrypted.sublist(0, streamStart.lengthInBytes))}'); - if (!ByteUtils.eq( streamStart, paddedDecrypted.sublist(0, streamStart.lengthInBytes))) { throw KdbxInvalidKeyException(); diff --git a/lib/src/kdbx_header.dart b/lib/src/kdbx_header.dart index 88c8033..e181b34 100644 --- a/lib/src/kdbx_header.dart +++ b/lib/src/kdbx_header.dart @@ -352,8 +352,8 @@ class KdbxHeader { final int bodySize = versionMajor >= 4 ? reader.readUint32() : reader.readUint16(); final bodyBytes = bodySize > 0 ? reader.readBytes(bodySize) : null; - _logger.finer( - 'Read header ${fields[headerId]}: ${ByteUtils.toHexList(bodyBytes)}'); +// _logger.finer( +// 'Read header ${fields[headerId]}: ${ByteUtils.toHexList(bodyBytes)}'); if (headerId > 0) { final TE field = fields[headerId]; yield createField(field, bodyBytes);