From 62c1a72ba9fe8bcc54e4bc4c1442a3c81494a2ad Mon Sep 17 00:00:00 2001 From: Herbert Poul Date: Wed, 18 Mar 2020 22:30:20 +0100 Subject: [PATCH] support for AES-KDF in kdbx 4.x #4 --- .gitignore | 2 +- lib/src/crypto/key_encrypter_kdf.dart | 21 +++++++++++++++++++-- lib/src/internal/crypto_utils.dart | 2 +- test/aeskdf.kdbx | Bin 0 -> 1495 bytes test/kdbx4_test.dart | 6 ++++++ test_output_chacha20.kdbx | Bin 1604 -> 0 bytes 6 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 test/aeskdf.kdbx delete mode 100644 test_output_chacha20.kdbx diff --git a/.gitignore b/.gitignore index 76770df..84f5bfe 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,4 @@ doc/api/ /test.kdbx /test_v4.kdbx /test_v4x.kdbx - +/test_output*.kdbx diff --git a/lib/src/crypto/key_encrypter_kdf.dart b/lib/src/crypto/key_encrypter_kdf.dart index 8ded916..36ae5ee 100644 --- a/lib/src/crypto/key_encrypter_kdf.dart +++ b/lib/src/crypto/key_encrypter_kdf.dart @@ -1,11 +1,14 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:crypto/crypto.dart' as crypto; import 'package:kdbx/kdbx.dart'; import 'package:kdbx/src/crypto/argon2.dart'; import 'package:kdbx/src/internal/byte_utils.dart'; +import 'package:kdbx/src/internal/crypto_utils.dart'; import 'package:kdbx/src/kdbx_var_dictionary.dart'; import 'package:logging/logging.dart'; +import 'package:pointycastle/export.dart'; final _logger = Logger('key_encrypter_kdf'); @@ -88,9 +91,10 @@ class KeyEncrypterKdf { break; case KdfType.Aes: _logger.fine('Must be using aes'); - break; + return encryptAes(key, kdfParameters); } - throw UnsupportedError('unsupported encrypt stuff.'); + throw UnsupportedError( + 'unsupported KDF Type UUID ${ByteUtils.toHexList(uuid)}.'); } Uint8List encryptArgon2(Uint8List key, VarDictionary kdfParameters) { @@ -106,4 +110,17 @@ class KeyEncrypterKdf { KdfField.version.read(kdfParameters), ); } + + Uint8List encryptAes(Uint8List key, VarDictionary kdfParameters) { + final encryptionKey = KdfField.salt.read(kdfParameters); + final rounds = KdfField.rounds.read(kdfParameters); + assert(encryptionKey.length == 32); + final cipher = ECBBlockCipher(AESFastEngine()) + ..init(true, KeyParameter(encryptionKey)); + var transformedKey = key; + for (int i = 0; i < rounds; i++) { + transformedKey = AesHelper.processBlocks(cipher, transformedKey); + } + return crypto.sha256.convert(transformedKey).bytes as Uint8List; + } } diff --git a/lib/src/internal/crypto_utils.dart b/lib/src/internal/crypto_utils.dart index e73c91b..d4d1f1c 100644 --- a/lib/src/internal/crypto_utils.dart +++ b/lib/src/internal/crypto_utils.dart @@ -20,7 +20,7 @@ class AesHelper { final Pbkdf2Parameters params = Pbkdf2Parameters(salt, iterationCount, derivedKeyLength); final KeyDerivator keyDerivator = - PBKDF2KeyDerivator(HMac(SHA256Digest(), 16)); + PBKDF2KeyDerivator(HMac(SHA256Digest(), 64)); keyDerivator.init(params); return keyDerivator.process(password); diff --git a/test/aeskdf.kdbx b/test/aeskdf.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..0a7916866162953c4c55f7a006659fdbcd7de011 GIT binary patch literal 1495 zcmV;|1t|Ih*`k_f`%AR|00aO65C8xGF~RcYzi~rQzE}kzYW!ON0|Wp70096100bZa z001>-aDl8`dCZ9}m7q@S*O9NcDMB{aIor&(9PfZnX3qx@0000(jd|9HHYwY-BjgmO zB*Uo-T>t<800BY;0000aRaHqu5C8xG$=UOoVv0myzjO@#qJJul&NR&Mi!i*>l^w000C4 z000dN4GKpYf<8R#0C9|i=Y^R!TNI~5116tySw-27rdyeW;n7KIi)k^gMtqChaQ?(W z%8I_|;jPw}@sEsX?2#_`IPFwZO}^nuKrH9d!Ri+4($SJ^14XrtnJ!=%u*#M) z>g@tEA%3h8kVOa>8x*88(_#)1JUgnH9OG)3GEImj`vv%1wUoa~T&7o%v1>QH-M{}O zc2E0NAp)Tx{&jhRT1Ea19ehWIBZ)!}J5f)Fp8$Vrh_xJNX;AT#<seX)Ug(k))}7TpHb@F!bAFo`O4L8!I%+`VTGq!uf9W4=*$* z+$>xR{Yv~H>0_&%JKK)csAa;z91wbpXQW(k(YUwdVWRy|Y5{!i4sD4rSKj~;;SeC) z5sgexMBm{GH6}uyC3)OlCejmHMFk-RFv@AEKedjk(63Bku*!fEznVM=qhhQilBj1h zO^|>UjFqIiCp841uUMO1vbyp8woHJtuRih)kaWh557;-u9*l$l)$}WxC&_0|gBJPT z_G2WPe*SyO#CoWTGC@IN00`Ch$KTI<3>~Q9dvi%HQ3Q!6cA8hd1Z)B0&3qY(mW=ze zsQ|nARATtmp@4cn&TZhGx6huvWX=^9xMc`kiSdF8n&$NjI7Cku6eVOvMusv3w@D~l zLu;tLKPd{~#$0J}&^jRHdxRmJo<-4z%6}SIsiBoO3_EWEeBG9$U?Xn#dWkF{!gq_F zsI6c~@(aj`_M?rAEc}=A?bf~!5DrQWahkbPGG8w{Mj{h(syyO~al*EiNzLX(#Au6% zWcwUni?uvS6Wg?$WO^7(u(R;i%UP;X4Q7ycnp#sK#G0UhzgT;+3YTeX919_^^~e$c z^3wKd8v{^#qkeB(ft1i>;Hu+eq{rF@>r+K)DF*j$OF4D0Z&51S4B0{2c0Y1qH7oWZ}-f z(cS&mF~nRB9hO&NFYt4|ATvRQdJOVVZSWewILN{hin7-UCuCCtD?2iqqDiBv6ZCNu&Mwtiw#QDpA?_ zog{wrWKPN1<3|&&>5?%U`?PE%QA1azk<^%IiwddPpIf?#l~4w@9;XMKYy;cs@dCx^ z^tB+w1k8bL2OvEwWj_ce%>6195@2wp7RX%`J<-fa(QdD-9cnMC#$buoR;-|O;Fzn9 zlR=X7+#wLD+&xCgRuE(waP-CL&#okzS4%DOhl|KR?YR3dVVPOJiv-(k>9>0rQx(Jq zyOrt=8Tl$KHnR8SBvD8vv^c~r3K2eNG);jkgbFN?f<#$bum6hGOgAxGJ{nHZj^{K2f7Cgnj(TT#Nb}Ub%e+ z=XPNIXVtxHi}9XYTqxX14v_{Iv2-d#!bhLM9Hh!yFS@#(yWcUDE86|4Pd*L2_D4H| xK;C$NgtOnFKh9$gw;XnnYD|a`B{8}ygA)h~qa!>dr=^LKEMPCD8*l&s002HGu^|8e literal 0 HcmV?d00001 diff --git a/test/kdbx4_test.dart b/test/kdbx4_test.dart index e89db92..0c93850 100644 --- a/test/kdbx4_test.dart +++ b/test/kdbx4_test.dart @@ -94,6 +94,12 @@ void main() { kdbxFormat.read(data, Credentials(ProtectedValue.fromString('asdf'))); expect(file.body.rootGroup.entries, hasLength(1)); }); + test('Reading aes-kdf', () async { + final data = await File('test/aeskdf.kdbx').readAsBytes(); + final file = + kdbxFormat.read(data, Credentials(ProtectedValue.fromString('asdf'))); + expect(file.body.rootGroup.entries, hasLength(1)); + }); }); group('Writing', () { test('Create and save', () { diff --git a/test_output_chacha20.kdbx b/test_output_chacha20.kdbx deleted file mode 100644 index 49974011cec98481522ec222d41ee67f04049bc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1604 zcmV-K2D|wK*`k_f`%AR|00aO65C8xG)&q(wi*HP|r6e<&G26A80|Wp70096100bZa z005hS6TLg0E=a;OH}N(nf7Hc7c~-~>JrOqcNDEC!6>tX(0001SLVoFfNm=qe4uh== zivR!s00BY;0000aRaHqu5C8xG?_+J>j44D*k@u;j1LFz|1pxp607(b{000UA00000 z000F60000@2mk;800004000001OWg508j(~00062002S(0000}AOHXW5F$Ust@=p) z{5ElK-7X}yq(Kj9mQ>!dyq*5EA;1OWg509FJ5000vJ0000000000DR0z*J{$lw zx$vLNq!YKyx|U^rVsDzJC_^{KruG7eYw+1|0#$Ftfg7q@I)n}!B|}yDxJ)f42!cb@ zX^#E@NuF3&=h;a*2;gdTtqx2#_mLd6f9=|xsv!coCnNBn#{>WXydhrNK=hbMzmvMU1Z{J_Jixt&eMF7qd8b>4z5Z>j zb?%h9Uzo70-efd09~tWzA3(k|iV}Tdj8V|41U9b~lSnb51I(98zoKI36lI#Yl>t0| zlc_7~O5#(VZW}x^tNo~Yy(C-9nV+ug#C^~F zPn6+PjDo-GW{yrf`r=PBZkFC>tFZSd$L@NE0p|ms9q&|uyDTaaI%dGP?M3s1Sl7Xy z5-^IOLDXo~c#BmIe`N4jkxeR_8D|YRlTrg&Fjh0`-=Yr4j*w1exmbQ~8vs=ZoEm6P zyZLlHTCs_qAmcf_{SaSnQV0K4pu7@@6Cpr^dWDNCvM|WwvL}3>tY3;lLw^bS$R1GN zG=L!%lOSd=@}R5JWc6h2?D@hNd|`Dd=_}_&qeq2dH3s)UX3Uj^&Z5rduZPF? zPqG3S8qN5fj}AxW$|{r4?#1+ByMp=IpLg3ND!D*gZqIhNpuB$a!>l|MjF>tcwFX!h zqYh=B#PTJ|NkVwI)^4-eyCP09!)`0XnM7+(!@7Ws%rzMmZ7Cc-sTz_vZ*8IQi` zEh^_`LP%c^f>z^2&4{bbi*h$;HFbkS#hml^)C!S8fhXUj?-ELmJ3j9LT#)jYXOqrM zm2x(acX83l|PNy|T0KNV$DrB~2ebn3Ci39NzW3rNm2`(ApHBtv=H53YTkrI=E!VSo z^vyl2S~GuxT4ZW{`kbCwTY_YmlDgW{!gzK+|9!DdnY5HqC}z!~dZ{DL(kDK~@K6~uojTh_2cSp3whXP&?AVvfF-0W-9jyV373fyUd&qkqU51*T+@__q z20G2-9XUt&Zvm-`0p5+4B=kp4Yb0@3JNW+(WiXPP6#36}POw}*Xd~`~)34?w)#r{V zE~mR&M1BRV@yYUaH*2X@ICSNUgYBId42mQ`8lKbx_@6~jVnTscc^Y-{N4YFqXyU)_ zJ7iP^nZYl2*4j;37|Cr+npTAC!DV_)n#nDnHxRC6tbJ11@>ddLvwdih4XAN!45$H)mW!ePz