diff --git a/CHANGELOG.md b/CHANGELOG.md index c689641..f7bccb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Implemented support for custom icons. - Implemented file merging/synchronization. - Fixed threading problem on save: only allow one save at a time for each file. +- Support for V2 keyfile https://forum.authpass.app/t/issuues-with-keyfile/84/3 ## 0.4.1 diff --git a/lib/src/kdbx_format.dart b/lib/src/kdbx_format.dart index 7b37a51..a4ec630 100644 --- a/lib/src/kdbx_format.dart +++ b/lib/src/kdbx_format.dart @@ -162,9 +162,17 @@ class KeyFileCredentials implements CredentialsPart { convert.hex.decode(keyFileAsString) as Uint8List)); } final xmlContent = xml.XmlDocument.parse(keyFileAsString); + final metaVersion = + xmlContent.findAllElements('Version').singleOrNull?.text; final key = xmlContent.findAllElements('Key').single; final dataString = key.findElements('Data').single; - final dataBytes = base64.decode(dataString.text); + final encoded = dataString.text.replaceAll(RegExp(r'\s'), ''); + Uint8List dataBytes; + if (metaVersion != null && metaVersion.startsWith('2.')) { + dataBytes = convert.hex.decode(encoded) as Uint8List; + } else { + dataBytes = base64.decode(encoded); + } _logger.finer('Decoded base64 of keyfile.'); return KeyFileCredentials._(ProtectedValue.fromBinary(dataBytes)); } catch (e, stackTrace) { diff --git a/pubspec.yaml b/pubspec.yaml index a1a210b..08df007 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: kdbx description: KeepassX format implementation in pure dart. (kdbx 3.x and 4.x support). -version: 0.4.2 +version: 1.0.0 homepage: https://github.com/authpass/kdbx.dart environment: diff --git a/test/kdbx_test.dart b/test/kdbx_test.dart index 8b40c75..ff40104 100644 --- a/test/kdbx_test.dart +++ b/test/kdbx_test.dart @@ -39,6 +39,15 @@ void main() { }); group('Composite key', () { + Future readFile( + String kdbxFile, String password, String keyFile) async { + final keyFileBytes = await File(keyFile).readAsBytes(); + final cred = Credentials.composite( + ProtectedValue.fromString(password), keyFileBytes); + final data = await File(kdbxFile).readAsBytes(); + return await kdbxFormat.read(data, cred); + } + test('Read with PW and keyfile', () async { final keyFileBytes = await File('test/password-and-keyfile.key').readAsBytes(); @@ -49,14 +58,15 @@ void main() { expect(file.body.rootGroup.entries, hasLength(2)); }); test('Read with PW and hex keyfile', () async { - final keyFileBytes = - await File('test/keyfile/hexkey_no_newline').readAsBytes(); - final cred = Credentials.composite( - ProtectedValue.fromString('testing99'), keyFileBytes); - final data = await File('test/keyfile/newdatabase2.kdbx').readAsBytes(); - final file = await kdbxFormat.read(data, cred); + final file = await readFile('test/keyfile/newdatabase2.kdbx', 'testing99', + 'test/keyfile/hexkey_no_newline'); expect(file.body.rootGroup.entries, hasLength(3)); }); + test('Keyfile v2 with PW and keyfile', () async { + final file = await readFile( + 'test/keyfile/keyfilev2.kdbx', 'qwe', 'test/keyfile/keyfilev2.keyx'); + expect(file.body.rootGroup.entries, hasLength(2)); + }); }); group('Creating', () { diff --git a/test/keyfile/keyfilev2.kdbx b/test/keyfile/keyfilev2.kdbx new file mode 100644 index 0000000..44f945d Binary files /dev/null and b/test/keyfile/keyfilev2.kdbx differ diff --git a/test/keyfile/keyfilev2.keyx b/test/keyfile/keyfilev2.keyx new file mode 100644 index 0000000..602fc88 --- /dev/null +++ b/test/keyfile/keyfilev2.keyx @@ -0,0 +1,12 @@ + + + + 2.0 + + + + EDAA196E 138FE3F9 026CF910 C441CEAE + 94B248D2 72600B6A 95A94793 EAC2DAD3 + + + \ No newline at end of file