Browse Source

fixed validating of hmac blocks.

remove-cryptography-dependency
Herbert Poul 5 years ago
parent
commit
0e2a56a729
  1. 3
      lib/src/crypto/key_encrypter_kdf.dart
  2. 22
      lib/src/crypto/protected_salt_generator.dart
  3. 57
      lib/src/kdbx_format.dart
  4. 4
      lib/src/kdbx_header.dart

3
lib/src/crypto/key_encrypter_kdf.dart

@ -93,9 +93,6 @@ class KeyEncrypterKdf {
} }
Uint8List encryptArgon2(Uint8List key, VarDictionary kdfParameters) { Uint8List encryptArgon2(Uint8List key, VarDictionary kdfParameters) {
_logger.fine('argon2():');
_logger.fine('key: ${ByteUtils.toHexList(key)}');
KdfField.debugAll(kdfParameters);
return argon2.argon2( return argon2.argon2(
key, key,
KdfField.salt.read(kdfParameters), KdfField.salt.read(kdfParameters),

22
lib/src/crypto/protected_salt_generator.dart

@ -43,22 +43,18 @@ class ProtectedSaltGenerator {
} }
class ChachaProtectedSaltGenerator implements ProtectedSaltGenerator { class ChachaProtectedSaltGenerator implements ProtectedSaltGenerator {
ChachaProtectedSaltGenerator._(this._secretKey, this._nonce, this._state); ChachaProtectedSaltGenerator._(this._state);
factory ChachaProtectedSaltGenerator.create(Uint8List key) { factory ChachaProtectedSaltGenerator.create(Uint8List key) {
final hash = sha512.convert(key); final hash = sha512.convert(key);
final secretKey = hash.bytes.sublist(0, 32); final secretKey = hash.bytes.sublist(0, 32);
final nonce = hash.bytes.sublist(32, 32 + 12); final nonce = hash.bytes.sublist(32, 32 + 12);
return ChachaProtectedSaltGenerator._( return ChachaProtectedSaltGenerator._(cryptography.chacha20.newState(
cryptography.SecretKey(secretKey), 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; final cryptography.KeyStreamCipherState _state;
@override @override
@ -72,19 +68,7 @@ class ChachaProtectedSaltGenerator implements ProtectedSaltGenerator {
return null; return null;
} }
final result = _state.convert(bytes); final result = _state.convert(bytes);
// try { return utf8.decode(result);
_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;
// }
} }
@override @override

57
lib/src/kdbx_format.dart

@ -277,7 +277,7 @@ class KdbxBody extends KdbxNode {
_logger.fine('We need AES'); _logger.fine('We need AES');
final result = kdbxFile.kdbxFormat final result = kdbxFile.kdbxFormat
._encryptContentV4Aes(header, cipherKey, compressedBytes); ._encryptContentV4Aes(header, cipherKey, compressedBytes);
_logger.fine('Result: ${ByteUtils.toHexList(result)}'); // _logger.fine('Result: ${ByteUtils.toHexList(result)}');
return result; return result;
} else if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.chaCha20].uuid) { } else if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.chaCha20].uuid) {
_logger.fine('We need chacha20'); _logger.fine('We need chacha20');
@ -399,22 +399,21 @@ class KdbxFormat {
'Does not match ${ByteUtils.toHexList(hash)} vs ${ByteUtils.toHexList(actualHash)}'); 'Does not match ${ByteUtils.toHexList(hash)} vs ${ByteUtils.toHexList(actualHash)}');
throw KdbxCorruptedFileException('Header hash does not match.'); throw KdbxCorruptedFileException('Header hash does not match.');
} }
_logger // _logger
.finest('KdfParameters: ${header.readKdfParameters.toDebugString()}'); // .finest('KdfParameters: ${header.readKdfParameters.toDebugString()}');
_logger.finest('Header hash matches.'); _logger.finest('Header hash matches.');
final keys = _computeKeysV4(header, credentials); final keys = _computeKeysV4(header, credentials);
final headerHmac = final headerHmac =
_getHeaderHmac(reader.byteData.sublist(0, header.endPos), keys.hmacKey); _getHeaderHmac(reader.byteData.sublist(0, header.endPos), keys.hmacKey);
final expectedHmac = reader.readBytes(headerHmac.bytes.length); final expectedHmac = reader.readBytes(headerHmac.bytes.length);
_logger.fine('Expected: ${ByteUtils.toHexList(expectedHmac)}'); // _logger.fine('Expected: ${ByteUtils.toHexList(expectedHmac)}');
_logger.fine('Actual : ${ByteUtils.toHexList(headerHmac.bytes)}'); // _logger.fine('Actual : ${ByteUtils.toHexList(headerHmac.bytes)}');
if (!ByteUtils.eq(headerHmac.bytes, expectedHmac)) { if (!ByteUtils.eq(headerHmac.bytes, expectedHmac)) {
throw KdbxInvalidKeyException(); throw KdbxInvalidKeyException();
} }
// final hmacTransformer = crypto.Hmac(crypto.sha256, hmacKey.bytes); // final hmacTransformer = crypto.Hmac(crypto.sha256, hmacKey.bytes);
// final blockreader.readBytes(32); // final blockreader.readBytes(32);
final bodyContent = hmacBlockTransformer(reader); final bodyContent = hmacBlockTransformer(keys.hmacKey, reader);
_logger.fine('body decrypt: ${ByteUtils.toHexList(bodyContent)}');
final decrypted = decrypt(header, bodyContent, keys.cipherKey); final decrypted = decrypt(header, bodyContent, keys.cipherKey);
_logger.finer('compression: ${header.compression}'); _logger.finer('compression: ${header.compression}');
if (header.compression == Compression.gzip) { if (header.compression == Compression.gzip) {
@ -429,17 +428,37 @@ class KdbxFormat {
return null; return null;
} }
Uint8List hmacBlockTransformer(ReaderHelper reader) { Uint8List hmacBlockTransformer(Uint8List hmacKey, ReaderHelper reader) {
Uint8List blockHash;
int blockLength;
final ret = <int>[]; final ret = <int>[];
int blockIndex = 0;
while (true) { while (true) {
blockHash = reader.readBytes(32); final blockKeySrc = WriterHelper()
blockLength = reader.readUint32(); ..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) { if (blockLength < 1) {
return Uint8List.fromList(ret); 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) { if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.aes].uuid) {
_logger.fine('We need AES'); _logger.fine('We need AES');
final result = _decryptContentV4(header, cipherKey, encrypted); final result = _decryptContentV4(header, cipherKey, encrypted);
_logger.fine('Result: ${ByteUtils.toHexList(result)}');
return result; return result;
} else if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.chaCha20].uuid) { } else if (cipherId == CryptoConsts.CIPHER_IDS[Cipher.chaCha20].uuid) {
_logger.fine('We need chacha20'); _logger.fine('We need chacha20');
@ -470,8 +488,6 @@ class KdbxFormat {
final hmacKey = crypto.sha512.convert(writer.output.toBytes()).bytes; final hmacKey = crypto.sha512.convert(writer.output.toBytes()).bytes;
final src = headerBytes; final src = headerBytes;
final hmacKeyStuff = crypto.Hmac(crypto.sha256, hmacKey); final hmacKeyStuff = crypto.Hmac(crypto.sha256, hmacKey);
_logger.fine('keySha: ${ByteUtils.toHexList(hmacKey)}');
_logger.fine('src: ${ByteUtils.toHexList(src)}');
return hmacKeyStuff.convert(src); return hmacKeyStuff.convert(src);
} }
@ -483,22 +499,17 @@ class KdbxFormat {
} }
final credentialHash = credentials.getHash(); final credentialHash = credentials.getHash();
_logger.fine('MasterSeed: ${ByteUtils.toHexList(masterSeed)}');
_logger.fine('credentialHash: ${ByteUtils.toHexList(credentialHash)}');
final key = KeyEncrypterKdf(argon2).encrypt(credentialHash, kdfParameters); final key = KeyEncrypterKdf(argon2).encrypt(credentialHash, kdfParameters);
_logger.fine('keyv4: ${ByteUtils.toHexList(key)}');
// final keyWithSeed = Uint8List(65); // final keyWithSeed = Uint8List(65);
// keyWithSeed.replaceRange(0, masterSeed.length, masterSeed); // keyWithSeed.replaceRange(0, masterSeed.length, masterSeed);
// keyWithSeed.replaceRange( // keyWithSeed.replaceRange(
// masterSeed.length, masterSeed.length + key.length, key); // masterSeed.length, masterSeed.length + key.length, key);
// keyWithSeed[64] = 1; // keyWithSeed[64] = 1;
_logger.fine('masterSeed: ${ByteUtils.toHexList(masterSeed)}');
final keyWithSeed = masterSeed + key + Uint8List.fromList([1]); final keyWithSeed = masterSeed + key + Uint8List.fromList([1]);
assert(keyWithSeed.length == 65); assert(keyWithSeed.length == 65);
final cipher = crypto.sha256.convert(keyWithSeed.sublist(0, 64)); final cipher = crypto.sha256.convert(keyWithSeed.sublist(0, 64));
final hmacKey = crypto.sha512.convert(keyWithSeed); final hmacKey = crypto.sha512.convert(keyWithSeed);
_logger.fine('hmacKey: ${ByteUtils.toHexList(hmacKey.bytes)}');
return _KeysV4(hmacKey.bytes as Uint8List, cipher.bytes as Uint8List); return _KeysV4(hmacKey.bytes as Uint8List, cipher.bytes as Uint8List);
} }
@ -553,10 +564,6 @@ class KdbxFormat {
throw KdbxInvalidKeyException(); throw KdbxInvalidKeyException();
} }
_logger.finest('streamStart: ${ByteUtils.toHexList(streamStart)}');
_logger.finest(
'actual : ${ByteUtils.toHexList(paddedDecrypted.sublist(0, streamStart.lengthInBytes))}');
if (!ByteUtils.eq( if (!ByteUtils.eq(
streamStart, paddedDecrypted.sublist(0, streamStart.lengthInBytes))) { streamStart, paddedDecrypted.sublist(0, streamStart.lengthInBytes))) {
throw KdbxInvalidKeyException(); throw KdbxInvalidKeyException();

4
lib/src/kdbx_header.dart

@ -352,8 +352,8 @@ class KdbxHeader {
final int bodySize = final int bodySize =
versionMajor >= 4 ? reader.readUint32() : reader.readUint16(); versionMajor >= 4 ? reader.readUint32() : reader.readUint16();
final bodyBytes = bodySize > 0 ? reader.readBytes(bodySize) : null; final bodyBytes = bodySize > 0 ? reader.readBytes(bodySize) : null;
_logger.finer( // _logger.finer(
'Read header ${fields[headerId]}: ${ByteUtils.toHexList(bodyBytes)}'); // 'Read header ${fields[headerId]}: ${ByteUtils.toHexList(bodyBytes)}');
if (headerId > 0) { if (headerId > 0) {
final TE field = fields[headerId]; final TE field = fields[headerId];
yield createField(field, bodyBytes); yield createField(field, bodyBytes);

Loading…
Cancel
Save