From c107b6e780801f618093eaae2902d381a7703172 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Sat, 22 Feb 2020 06:13:21 +0100 Subject: [PATCH] separate headers and inner headers, removed old test. --- lib/src/kdbx_format.dart | 9 ++--- lib/src/kdbx_header.dart | 74 +++++++++++++++++++++++++++++++++------- test/kdbx_test.dart | 7 ---- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index 90f8497..2d62e24 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -380,12 +380,9 @@ class KdbxFormat { if (header.compression == Compression.gzip) { final content = GZipCodec().decode(decrypted) as Uint8List; final contentReader = ReaderHelper(content); - final fieldIterable = - KdbxHeader.readField(contentReader, 4, InnerHeaderFields.values); - final headerFields = Map.fromEntries( - fieldIterable.map((field) => MapEntry(field.field, field))); + final headerFields = KdbxHeader.readInnerHeaderFields(contentReader, 4); _logger.fine('inner header fields: $headerFields'); - header.fields.addAll(headerFields); + header.innerFields.addAll(headerFields); final xml = utf8.decode(contentReader.readRemaining()); _logger.fine('content: $xml'); return KdbxFile(credentials, header, _loadXml(header, xml)); @@ -453,7 +450,7 @@ class KdbxFormat { ProtectedSaltGenerator _createProtectedSaltGenerator(KdbxHeader header) { final protectedValueEncryption = header.innerRandomStreamEncryption; - final streamKey = header.fields[HeaderFields.ProtectedStreamKey].bytes; + final streamKey = header.protectedStreamKey; if (protectedValueEncryption == ProtectedValueEncryption.salsa20) { return ProtectedSaltGenerator(streamKey); } else if (protectedValueEncryption == ProtectedValueEncryption.chaCha20) { diff --git a/lib/src/kdbx_header.dart b/lib/src/kdbx_header.dart index 6730eca..aa07b04 100644 --- a/lib/src/kdbx_header.dart +++ b/lib/src/kdbx_header.dart @@ -49,15 +49,30 @@ enum InnerHeaderFields { Binary, } -class HeaderField { +abstract class HeaderFieldBase { + T get field; +} + +class HeaderField implements HeaderFieldBase { HeaderField(this.field, this.bytes); + @override final HeaderFields field; final Uint8List bytes; String get name => field.toString(); } +class InnerHeaderField implements HeaderFieldBase { + InnerHeaderField(this.field, this.bytes); + + @override + final InnerHeaderFields field; + final Uint8List bytes; + + String get name => field.toString(); +} + class KdbxHeader { KdbxHeader({ @required this.sig1, @@ -87,7 +102,6 @@ class KdbxHeader { HeaderFields.CompressionFlags, HeaderFields.MasterSeed, HeaderFields.EncryptionIV, - HeaderFields.InnerRandomStreamID, ]; if (majorVersion < 4) { return baseHeaders + @@ -117,6 +131,10 @@ class KdbxHeader { fields[field] = HeaderField(field, bytes); } + void _setInnerHeaderField(InnerHeaderFields field, Uint8List bytes) { + innerFields[field] = InnerHeaderField(field, bytes); + } + void generateSalts() { // TODO make sure default algorithm is "secure" engouh. Or whether we should // use like [SecureRandom] from PointyCastle? @@ -198,8 +216,11 @@ class KdbxHeader { final versionMajor = reader.readUint16(); _logger.finer('Reading version: $versionMajor.$versionMinor'); - final headerFields = Map.fromEntries(readField(reader, versionMajor) - .map((field) => MapEntry(field.field, field))); + final headerFields = readAllFields( + reader, + versionMajor, + HeaderFields.values, + (HeaderFields field, value) => HeaderField(field, value)); return KdbxHeader( sig1: sig1, @@ -211,8 +232,27 @@ class KdbxHeader { ); } - static Iterable readField(ReaderHelper reader, int versionMajor, - [List fields = HeaderFields.values]) sync* { + static Map readHeaderFields( + ReaderHelper reader, int versionMajor) => + readAllFields(reader, versionMajor, HeaderFields.values, + (HeaderFields field, value) => HeaderField(field, value)); + + static Map readInnerHeaderFields( + ReaderHelper reader, int versionMajor) => + readAllFields(reader, versionMajor, InnerHeaderFields.values, + (InnerHeaderFields field, value) => InnerHeaderField(field, value)); + + static Map readAllFields, TE>( + ReaderHelper reader, + int versionMajor, + List fields, + T createField(TE field, Uint8List bytes)) => + Map.fromEntries( + readField(reader, versionMajor, fields, createField) + .map((field) => MapEntry(field.field, field))); + + static Iterable readField(ReaderHelper reader, int versionMajor, + List fields, T createField(TE field, Uint8List bytes)) sync* { while (true) { final headerId = reader.readUint8(); final int bodySize = @@ -221,16 +261,15 @@ class KdbxHeader { _logger.finer( 'Read header ${fields[headerId]}: ${ByteUtils.toHexList(bodyBytes)}'); if (headerId > 0) { - final dynamic field = fields[headerId]; - if (field is HeaderFields) { - yield HeaderField(field, bodyBytes); - } else { + final TE field = fields[headerId]; + yield createField(field, bodyBytes); + /* else { if (field == InnerHeaderFields.InnerRandomStreamID) { yield HeaderField(HeaderFields.InnerRandomStreamID, bodyBytes); } else if (field == InnerHeaderFields.InnerRandomStreamKey) { yield HeaderField(HeaderFields.ProtectedStreamKey, bodyBytes); } - } + }*/ } else { break; } @@ -242,6 +281,7 @@ class KdbxHeader { final int versionMinor; final int versionMajor; final Map fields; + final Map innerFields = {}; /// end position of the header, if we have been reading from a stream. final int endPos; @@ -259,8 +299,16 @@ class KdbxHeader { } ProtectedValueEncryption get innerRandomStreamEncryption => - ProtectedValueEncryption.values[ReaderHelper.singleUint32( - fields[HeaderFields.InnerRandomStreamID].bytes)]; + ProtectedValueEncryption + .values[ReaderHelper.singleUint32(_innerRandomStreamEncryptionBytes)]; + + Uint8List get _innerRandomStreamEncryptionBytes => versionMajor >= 4 + ? innerFields[InnerHeaderFields.InnerRandomStreamID].bytes + : fields[HeaderFields.InnerRandomStreamID].bytes; + + Uint8List get protectedStreamKey => versionMajor >= 4 + ? innerFields[InnerHeaderFields.InnerRandomStreamKey].bytes + : fields[HeaderFields.ProtectedStreamKey].bytes; VarDictionary get readKdfParameters => VarDictionary.read( ReaderHelper(fields[HeaderFields.KdfParameters].bytes)); diff --git a/test/kdbx_test.dart b/test/kdbx_test.dart index ce08173..bb7fd12 100644 --- a/test/kdbx_test.dart +++ b/test/kdbx_test.dart @@ -93,11 +93,4 @@ void main() { File('test.kdbx').writeAsBytesSync(saved); }); }); - - group('kdbx 4.x', () { - test('Fails with exception', () async { - final data = await File('test/keepassxcpasswords.kdbx').readAsBytes(); - kdbxForamt.read(data, Credentials(ProtectedValue.fromString('asdf'))); - }); - }); }