From 3ae6e398ce80f4f0fa7d3f521b5c094238b95ad5 Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Sat, 27 Mar 2021 11:02:20 +0000 Subject: [PATCH] Support for V2 keyfile https://forum.authpass.app/t/issuues-with-keyfile/84/3 --- CHANGELOG.md | 1 + lib/src/kdbx_format.dart | 10 +++++++++- pubspec.yaml | 2 +- test/kdbx_test.dart | 22 ++++++++++++++++------ test/keyfile/keyfilev2.kdbx | Bin 0 -> 1870 bytes test/keyfile/keyfilev2.keyx | 12 ++++++++++++ 6 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 test/keyfile/keyfilev2.kdbx create mode 100644 test/keyfile/keyfilev2.keyx 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 0000000000000000000000000000000000000000..44f945d172fbb30a1f5b41bf37a0ad59f15d61e6 GIT binary patch literal 1870 zcmV-U2eJ4A*`k_f`%AR}00RI55CAd3^5(yBLr}h01tDtuTK@wC0096100bZa=(%7Q zrJH>6PB3!L6=^aQPOpE-1UIu(&goq}CRlXt1t0*}Vp~r+S~GTYmrZXV&T*vP*O)cW zUYW^Ta>))M_vqsW2moN}00000000LN0IH^|{12QO<4Y-*KC^8(zz84!5bwwxqiwWK z;rB53=r>wDOnW z!rm>{LizO_G^Q9 z7na}F(!X@WO{m${SP{X|iFTlND9X1B*46S>H3H522aCZ-Lhs6{LSrm{@(@PfmMle9 z<=y1Y;?`GZ!M;;sFK|PgK&Z-YfS=yB7v3J+6Fpp?_mW9XX>JG?iZlUz#z>aQq zB~_fXHDCla7I&75MWJShl|(S^!Fibyp=S4Ut*unS76{I~57?U}G`APQ4Lq26H4e=p zQW707FNwhcH_C`!5&n?nUw!FSyE%_Ee8qq4(_||M&Z1)8xA8GmLYR$XM+ltV6F|sjV`)Z`JpjECUCnc4R|=_F_4d8nn^%0@C;Rtq($ru59}e~LnEEW4JLSIojN^n znEzqCvlbq7n#|I55AAs%TO$&rme7QwczAz-Wsi;&c_I*Q*>YUqHGcu%^KJpW+O152 z*A`SwN?~doazPw>;YTolH4IB1zZz{y6g=E%khLye3TxMY*%^oQPiq;quRd;v3_fwY zE<#;9woQ4|@8o0`+mwS6X$2^{Ia=q|$H_nS=k@M-63o!|MEZk6f9!#c%h=i3wWtw- z^jWF0#ZPni{f2HJq{|DEm3241AF8Q*JP6>XjAZYL15r+0QCf`y9QbO~IZzkh-qqO_;Hsd{YI2yS`ytAsTgAL-DJK#Ygr8*17F8b+Py)G!(v zK1&wt>{DZHiE61n<32KSEH;Bz5pPl;OG{ZpF$?MB3r7?5B z6N|NO!S7O@7a`=zwe1hA>}x@?YFPJvddth1xwhadVxh^xl(``#_C%%&0gB&FK&W5t zeVc(D*OqoO<=-yP-Jx$6w&H$iTN|}RRR%?Fp|{()(_fHw2jA8*C_NX>@NVeiN-{+6L@#(J_RBX|_kLOwkhsSq2)k0n(TLmKY9 zjJ_Tze@nB;`(-Uhg>+~1k+C$7c6n%7@>#jK=RfYNI#U3lyZn`>ko=zY8^10%C#KBR z-RpLJb%X3VL3i!UI>aR@ur_0MEX{rnfaDdq{#g$yMt&PCuN4RF{dOj!Tl~7%A%~9a zvY@4M+;nMIk1}1=w&VEfw+o8IoEEuq#B}=r24j+Xe#qUxE1BG+w9&znRH5@~bnr{v IIOER)Ad8)le*gdg literal 0 HcmV?d00001 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