Browse Source

separate headers and inner headers, removed old test.

remove-cryptography-dependency
Herbert Poul 5 years ago
parent
commit
c107b6e780
  1. 9
      lib/src/kdbx_format.dart
  2. 74
      lib/src/kdbx_header.dart
  3. 7
      test/kdbx_test.dart

9
lib/src/kdbx_format.dart

@ -380,12 +380,9 @@ class KdbxFormat {
if (header.compression == Compression.gzip) { if (header.compression == Compression.gzip) {
final content = GZipCodec().decode(decrypted) as Uint8List; final content = GZipCodec().decode(decrypted) as Uint8List;
final contentReader = ReaderHelper(content); final contentReader = ReaderHelper(content);
final fieldIterable = final headerFields = KdbxHeader.readInnerHeaderFields(contentReader, 4);
KdbxHeader.readField(contentReader, 4, InnerHeaderFields.values);
final headerFields = Map.fromEntries(
fieldIterable.map((field) => MapEntry(field.field, field)));
_logger.fine('inner header fields: $headerFields'); _logger.fine('inner header fields: $headerFields');
header.fields.addAll(headerFields); header.innerFields.addAll(headerFields);
final xml = utf8.decode(contentReader.readRemaining()); final xml = utf8.decode(contentReader.readRemaining());
_logger.fine('content: $xml'); _logger.fine('content: $xml');
return KdbxFile(credentials, header, _loadXml(header, xml)); return KdbxFile(credentials, header, _loadXml(header, xml));
@ -453,7 +450,7 @@ class KdbxFormat {
ProtectedSaltGenerator _createProtectedSaltGenerator(KdbxHeader header) { ProtectedSaltGenerator _createProtectedSaltGenerator(KdbxHeader header) {
final protectedValueEncryption = header.innerRandomStreamEncryption; final protectedValueEncryption = header.innerRandomStreamEncryption;
final streamKey = header.fields[HeaderFields.ProtectedStreamKey].bytes; final streamKey = header.protectedStreamKey;
if (protectedValueEncryption == ProtectedValueEncryption.salsa20) { if (protectedValueEncryption == ProtectedValueEncryption.salsa20) {
return ProtectedSaltGenerator(streamKey); return ProtectedSaltGenerator(streamKey);
} else if (protectedValueEncryption == ProtectedValueEncryption.chaCha20) { } else if (protectedValueEncryption == ProtectedValueEncryption.chaCha20) {

74
lib/src/kdbx_header.dart

@ -49,15 +49,30 @@ enum InnerHeaderFields {
Binary, Binary,
} }
class HeaderField { abstract class HeaderFieldBase<T> {
T get field;
}
class HeaderField implements HeaderFieldBase<HeaderFields> {
HeaderField(this.field, this.bytes); HeaderField(this.field, this.bytes);
@override
final HeaderFields field; final HeaderFields field;
final Uint8List bytes; final Uint8List bytes;
String get name => field.toString(); String get name => field.toString();
} }
class InnerHeaderField implements HeaderFieldBase<InnerHeaderFields> {
InnerHeaderField(this.field, this.bytes);
@override
final InnerHeaderFields field;
final Uint8List bytes;
String get name => field.toString();
}
class KdbxHeader { class KdbxHeader {
KdbxHeader({ KdbxHeader({
@required this.sig1, @required this.sig1,
@ -87,7 +102,6 @@ class KdbxHeader {
HeaderFields.CompressionFlags, HeaderFields.CompressionFlags,
HeaderFields.MasterSeed, HeaderFields.MasterSeed,
HeaderFields.EncryptionIV, HeaderFields.EncryptionIV,
HeaderFields.InnerRandomStreamID,
]; ];
if (majorVersion < 4) { if (majorVersion < 4) {
return baseHeaders + return baseHeaders +
@ -117,6 +131,10 @@ class KdbxHeader {
fields[field] = HeaderField(field, bytes); fields[field] = HeaderField(field, bytes);
} }
void _setInnerHeaderField(InnerHeaderFields field, Uint8List bytes) {
innerFields[field] = InnerHeaderField(field, bytes);
}
void generateSalts() { void generateSalts() {
// TODO make sure default algorithm is "secure" engouh. Or whether we should // TODO make sure default algorithm is "secure" engouh. Or whether we should
// use like [SecureRandom] from PointyCastle? // use like [SecureRandom] from PointyCastle?
@ -198,8 +216,11 @@ class KdbxHeader {
final versionMajor = reader.readUint16(); final versionMajor = reader.readUint16();
_logger.finer('Reading version: $versionMajor.$versionMinor'); _logger.finer('Reading version: $versionMajor.$versionMinor');
final headerFields = Map.fromEntries(readField(reader, versionMajor) final headerFields = readAllFields(
.map((field) => MapEntry(field.field, field))); reader,
versionMajor,
HeaderFields.values,
(HeaderFields field, value) => HeaderField(field, value));
return KdbxHeader( return KdbxHeader(
sig1: sig1, sig1: sig1,
@ -211,8 +232,27 @@ class KdbxHeader {
); );
} }
static Iterable<HeaderField> readField(ReaderHelper reader, int versionMajor, static Map<HeaderFields, HeaderField> readHeaderFields(
[List<dynamic> fields = HeaderFields.values]) sync* { ReaderHelper reader, int versionMajor) =>
readAllFields(reader, versionMajor, HeaderFields.values,
(HeaderFields field, value) => HeaderField(field, value));
static Map<InnerHeaderFields, InnerHeaderField> readInnerHeaderFields(
ReaderHelper reader, int versionMajor) =>
readAllFields(reader, versionMajor, InnerHeaderFields.values,
(InnerHeaderFields field, value) => InnerHeaderField(field, value));
static Map<TE, T> readAllFields<T extends HeaderFieldBase<TE>, TE>(
ReaderHelper reader,
int versionMajor,
List<TE> fields,
T createField(TE field, Uint8List bytes)) =>
Map<TE, T>.fromEntries(
readField(reader, versionMajor, fields, createField)
.map((field) => MapEntry(field.field, field)));
static Iterable<T> readField<T, TE>(ReaderHelper reader, int versionMajor,
List<TE> fields, T createField(TE field, Uint8List bytes)) sync* {
while (true) { while (true) {
final headerId = reader.readUint8(); final headerId = reader.readUint8();
final int bodySize = final int bodySize =
@ -221,16 +261,15 @@ class KdbxHeader {
_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 dynamic field = fields[headerId]; final TE field = fields[headerId];
if (field is HeaderFields) { yield createField(field, bodyBytes);
yield HeaderField(field, bodyBytes); /* else {
} else {
if (field == InnerHeaderFields.InnerRandomStreamID) { if (field == InnerHeaderFields.InnerRandomStreamID) {
yield HeaderField(HeaderFields.InnerRandomStreamID, bodyBytes); yield HeaderField(HeaderFields.InnerRandomStreamID, bodyBytes);
} else if (field == InnerHeaderFields.InnerRandomStreamKey) { } else if (field == InnerHeaderFields.InnerRandomStreamKey) {
yield HeaderField(HeaderFields.ProtectedStreamKey, bodyBytes); yield HeaderField(HeaderFields.ProtectedStreamKey, bodyBytes);
} }
} }*/
} else { } else {
break; break;
} }
@ -242,6 +281,7 @@ class KdbxHeader {
final int versionMinor; final int versionMinor;
final int versionMajor; final int versionMajor;
final Map<HeaderFields, HeaderField> fields; final Map<HeaderFields, HeaderField> fields;
final Map<InnerHeaderFields, InnerHeaderField> innerFields = {};
/// end position of the header, if we have been reading from a stream. /// end position of the header, if we have been reading from a stream.
final int endPos; final int endPos;
@ -259,8 +299,16 @@ class KdbxHeader {
} }
ProtectedValueEncryption get innerRandomStreamEncryption => ProtectedValueEncryption get innerRandomStreamEncryption =>
ProtectedValueEncryption.values[ReaderHelper.singleUint32( ProtectedValueEncryption
fields[HeaderFields.InnerRandomStreamID].bytes)]; .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( VarDictionary get readKdfParameters => VarDictionary.read(
ReaderHelper(fields[HeaderFields.KdfParameters].bytes)); ReaderHelper(fields[HeaderFields.KdfParameters].bytes));

7
test/kdbx_test.dart

@ -93,11 +93,4 @@ void main() {
File('test.kdbx').writeAsBytesSync(saved); 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')));
});
});
} }

Loading…
Cancel
Save