summaryrefslogtreecommitdiff
authorMatthew Shyu <matthew.shyu@amlogic.com>2017-08-11 04:42:20 (GMT)
committer Matthew Shyu <matthew.shyu@amlogic.com>2017-08-16 04:15:45 (GMT)
commit3f90827728a04024e1e01e8cbe9da15ac50b72d0 (patch)
tree63b2da38895bce2d5225debd3a3221dec98c2604
parenta48c2fbe733c193ba688aa23f4b16d96a93843bb (diff)
downloadkeymaster-3f90827728a04024e1e01e8cbe9da15ac50b72d0.zip
keymaster-3f90827728a04024e1e01e8cbe9da15ac50b72d0.tar.gz
keymaster-3f90827728a04024e1e01e8cbe9da15ac50b72d0.tar.bz2
Initial version of Keymaster 2 HAL and fix keymaster tests and vts tests
Change-Id: I124a6b9a36cfd9e680f3ca301295c85f72f7e209
Diffstat
-rw-r--r--8efb1e1c-37e5-4326-a5d68c33726c7d57.ta6857
-rw-r--r--Android.mk105
-rw-r--r--aml_keymaster_device.cpp997
-rw-r--r--aml_keymaster_device.h167
-rw-r--r--aml_keymaster_ipc.cpp116
-rw-r--r--aml_keymaster_ipc.h33
-rw-r--r--keymaster_ipc.h75
-rw-r--r--module.cpp61
-rw-r--r--unit_test/android_keymaster_messages_test.cpp732
-rw-r--r--unit_test/android_keymaster_test.cpp3976
-rw-r--r--unit_test/android_keymaster_test_utils.cpp902
-rw-r--r--unit_test/android_keymaster_test_utils.h470
-rw-r--r--unit_test/android_keymaster_utils.h306
-rw-r--r--unit_test/attestation_record.cpp690
-rw-r--r--unit_test/attestation_record.h62
-rw-r--r--unit_test/attestation_record_test.cpp145
-rw-r--r--unit_test/authorization_set_test.cpp745
-rw-r--r--unit_test/ecies_kem_test.cpp73
-rw-r--r--unit_test/gtest_main.cpp34
-rw-r--r--unit_test/hkdf_test.cpp78
-rw-r--r--unit_test/hmac_test.cpp84
-rw-r--r--unit_test/kdf1_test.cpp60
-rw-r--r--unit_test/kdf2_test.cpp86
-rw-r--r--unit_test/kdf_test.cpp46
-rw-r--r--unit_test/key_blob_test.cpp362
-rw-r--r--unit_test/keymaster0_engine.h103
-rw-r--r--unit_test/keymaster1_engine.h123
-rw-r--r--unit_test/keymaster_configuration_test.cpp68
-rw-r--r--unit_test/keymaster_enforcement_test.cpp872
-rw-r--r--unit_test/keymaster_tags.cpp173
-rw-r--r--unit_test/nist_curve_key_exchange_test.cpp219
-rw-r--r--unit_test/openssl_utils.h100
-rw-r--r--unit_test/sw_rsa_attest_root.key.pem15
33 files changed, 18935 insertions, 0 deletions
diff --git a/8efb1e1c-37e5-4326-a5d68c33726c7d57.ta b/8efb1e1c-37e5-4326-a5d68c33726c7d57.ta
new file mode 100644
index 0000000..d5cdc39
--- a/dev/null
+++ b/8efb1e1c-37e5-4326-a5d68c33726c7d57.ta
@@ -0,0 +1,6857 @@
+HSTO
+
+!>4gpzG?O."ELF
+
+
+ve tv@`DAFaDAFataEv!etA
+tv@`DAFaDAFataEvAet!
+tEvet!
+PV!FPaNXAB\!HPdJTdAFFfaH@LT!NJF^!BZFaDABb!Fa@DAB!@H\B!@TNRfZFaB!@DAF`D@@RVFaZ\HB!LJTB\NADA
+RB~a
+|!
+vNLNaJAFaDAH\A@ zHrA@\AH t
+xxAp! pFPNXNaJAFaDAH
+^TPZ\a RXaPV=@FJ@B(LLHNHhB!DDANFaHԁJޡ
+z@ B,
+~@DA@B!B(Jԡ
+tEvetavA@`DAFaDAFatEv!e t
+tEvaetvA@`DAFaDAFatE vet!
+tEvaet v@`DAFaDAFatEvAet!
+p
+  
+   
+
+  
+ 
+
+  
+ Bit-sliced AES for NEON, CRYPTOGAMS by <appro@openssl.org>
+ve tv@`DAFaDAFataEv!etA
+tv@`DAFaDAFataEvAet!
+tEvet!
+\Pa T
+P V^aZXaZ^ARaZ!NXaBTAJV!DXdLPdAFFfaH@FP!NJNR!J\FaDABb!Fa@DAB!@DVB!HPBZn\FaB!@DAF`D@@RVFaZ\HB!LLPJVNADA ZJ~a | vNLNaJAFaDAH
+ppaNXBTNaJAFaDAHT@zAHr@THtANLNADARB~a|!vxxp!pAP\! P^
+TVAZaZ V=_@ B,
+~@DA@B!B(J֡
+tEvetAva@`DAFaDAFa tEv!et
+tEvAetva@`DAFaDAFa tEvet!
+tEvAe tv@`DAFaDAFatEvaet!
+p
+|  J  j 2
+*݀ *lFUF
+
+
+
+P
+Q
+~ Q
+
+**` *r! *#` Jt *` j v ` xa j* ` z `|A JP
+-*
+>рP
+Ң 
+ ЁB!ZrH@*DA Z'
+th@J ЁFaZ' *
+'
+z$ | F~hFP``B
+ Rp
+B! ZrH@* ЁDAZ'
+
+z$ | FhF``B
+
+z FhF_``B
+
+p
+N
+`
+0
+
+RFF -/
+
+#Fb Ш   &
+hF
+RFF {-/
+
+#F#
+" 
+ ЁB!ZrH@*DA Z'
+th@J ЁFaZ' *
+'
+z$ | F~hF``B
+ Rp
+B! ZrH@* ЁDAZ'
+
+z$ | FhF
+
+z FhF``B
+
+r
+P
+`
+2
+
+FRFF -/
+
+#F*Fb ;В Zp( B!'
+hF
+RFF -/
+
+FF
+hF
+RF m-/
+
+#F#
+ 
+ 
+ N \
+ N 
+ NKN \(@ NlN|?m
+ #
+n B hl88 P`P"RbR!
+  
+zl | B~l @+IB!+BDA Fa &(h
+N  
+N \ 
+N  
+NK N \{NlN|?2
+ A
+B
+«@
+z  | B~  x M
+HB b
+zl | ~l @ BB! DA FaN N B8Н
+  $
+/ 6$=&i''NͲuu t,,X.4-6nnZZ[RRM;;vaַγ}{))R>q//^SShѹ
+
+II
+ l$$H\\]ŸnӽשּׁCbbĨ917Ӌyy2CȋY77nmmڌdձNN੩IllVV%ϯeeʎzz鮮Gպoxxo%%Jr..\$8WǴsQƗ#|ݡtt!>KKܽa ppB>>|ĵqffHHaa_55jWWйiX':'8볘+3"iip٩3-"< I·UUx((PzߥY  ڿe1BBhhAA)w--Z˰{TTֻm:,c|w{ko0g+׫vʂ}YGԢr&6?4q1#'u ,nZR;ֳ)/S
+I$\Ӭby7mNlVezx%.tKp>fHa5WiَU(ߌ BhA-T
+
+'@G(P)`)
+]
+
+vOeGP(p Ee EEVKE %
+vOO eGP(p Ee EEVKE %
+vOO eGP(p Ee EEVKE %
+|B|
+!\hT[:.6$g
+ WҖO aiKwZ
+*C"< Nj򹨶-ȩWLuݙ`&r\;fD4~[v)C#hc1cB@" Ƅ}$J=2m)K/0R wl+pH"dGČ?,}V3"NI8ʌ6 Ԙρ(z&ڤ?:, xP_jbF~Tؐ^9.Â]|i-o%;ȧ}nc{; x&Yn쨚Oen~ϼ!ٛ6oJ |)11#?*0f57NtʂАا3JAP/MvMCTMўjL,QeF^]5st.A ZgRے3VGmaךz 7Y<'5a<GzYҜ?sUy7sS[_o=߆Dx>h,4$8_@r %⼋I<(A q9޳ ؐVda{p2t\lHBWR j068@|9/4CDT{2#=L BN.f($v[Im%rdhԤ\]elpHP^FWث
+XE,?k:AOgst"57unGq)ʼnobV>Ky xZݨ31Y'_`QJ -zɜ;M*<Sa+~w&icU! }-_jFF
+j
+ap x
+bx  AGB(p F
+c x@
+
+0
+N
+<
+`jIYF,
+B
+GL`,
+"*
+
+"* #
+
+"
+
+"J 0C*"
+"0SCC*"
+"CPAJ/
+"J 0C*"@CC
+"0SBCC*"@CC
+"BCPAJ/
+ cPErPEBggg' p`_
+
+B'p@`P `p0
+f
+ 
+n"*c
+`(@**g,.^
+`(*,@.^
+g`Vbb##bCC*g``##`CC^ 0 R^`1bb##bCC^!F
+g`PR
+AD*g T$ V!& JAAf*0
+
+g`Vb##bCC*g`##`CCb##bCCh##hCC
+R
+!B&  *A
+& 
+$cPE
+g`V""##"CC*g 󀃰 #󂣰 J \, CC j \!,"󈃰"󊣰 j`p"C#\Al(󈃰(󊣰^A(##^a*󈃰*󊣰aF9*##!:,󈃰,󊣰#8Ͽ,##% R...#XA
+g JZa` jaF*g jA* .
+
+g`V""##*g  ##""## J ((##j **##^A,,##^a..#
+j
+O
+ DN
+O
+SkO
+ DN
+*}mJ#BjÊ㪭¥ "B,\b.^ǂ bd!fAha: jJ
+Ȭ¬"} mBbÂ㢭­"B,\b.^ǂ bd!fAha: jJ<
+oFF=
+
+?F
+"B< bǂ> J׬&"B}bmJâѬ=ȭ
+"Bb>悰ǢpJ&"Bb}‚mJǬѬ=
+"B
+Bb}¢m٬JǬ="
+Bb Ƣr( tHJBf*b}جm٬J"=B
+bł@墭tH@"vhJbfJլ}جmJ"B=b
+Ţ`­vh`"Bx麗Jfjլ}m"JBb=Ă
+x煮"Bbz﨨Jf튬}"mBJbmĢ!­"z﫨Bb<*J 
+
+"f
+Bjb6¢¬j"BbƢ­G*Bf*bj6᢬¬"jBbł墭­"GJbfJj6¬"BjbŢ­"BGjfjj׬6"BbjĂ䢭­"BbGfj׬6"BbĢmǭ! 
+"BbG TAGVaGBG &ffBf B B=
+ B BF
+Ȱf=
+fBf>
+=Ȱ?
+p=qr(=s8!tH=(uXAvh=Hwxax=hyz={&¸=ч?
+
+w 
+g
+OD
+NWDQ
+f
+ND
+NVDQ
+e
+MD
+NUDQ
+d
+LD
+NTDQ
+c
+KD
+NSDF љQ
+g
+OD
+NWD<4
+ 
+OyfyN
+ND
+VD<4
+ 
+OyeyN
+MD
+UD<4
+ 
+OydyN
+LD
+TD<4
+ 
+OycyN
+KD
+SD
+ 
+OygyN
+ODWD<4
+ 
+OyfyN
+NDVD<4
+ 
+OyeyN
+MDUD<4
+ 
+OydyN
+LDTD<4
+ 
+OycyN
+KDSDF o<4
+ 
+OygyN
+ ODWD<4
+ 
+OyfyN
+ NDVD<4
+ 
+OyeyN
+ MDUD<4
+ 
+OydyN
+ LDTD<4
+ 
+OycyN
+ KDSDF [<
+  
+dlHL*LJ
+OD flgn
+ O_D@%
+NDHԁ Hf
+ L@ O^D$
+MDh xe
+ Oz]D#
+zLD @d@
+ O\DD('
+KD `c
+ O[DB!&
+ODHց Hg
+ LO_D%
+NDh( x$f
+ Oz^D$
+zMD B! eB!
+ O]DFH#
+LD b$d
+ O\DDA'
+KDH (Hc
+ LO[D&
+ODhH ,xDg
+ Oz_D%
+zND DA0fDA
+ O^D`h$
+MD d4e
+ O]DFa#
+LDH 8Hd
+ LO\D'
+KDhh <xdc
+ Oz[D&
+zOD Fa
+ O_D%
+ND
+ Of^D$
+HЁMD x
+ Ox]D#
+LD d
+ O\D'
+KD c
+ O[D
+ODr! gT!O_D`l
+NDHҁ x fOL^D
+x%MD eO]D
+LD  dO\DB
+KD$tA cVAO[Dbl
+ODHԁ( x@gOL_D
+xEND, fO^D
+MD0 eO]DD
+LD4va dpaO\Ddl
+KDHց8 x`cOL[D
+xeOD< gO_D
+ND
+MD@ e@O]Dfl
+LDH x
+xKD  cO[D
+OD gO_D`
+NDB! fB!O^D`쨇
+MDH x eOL]D
+x%LD dO\D
+KD  cO[DOD
+$DAg DAWD  bO_DHND
+x@(fL VDxE  O^DMD
+,e UD  O]DLD
+0d TD  O\DKD
+4Fac FaSD  dO[DHOD
+x`8gL WDxe  O_DND
+<f VD  O^DMD
+
+
+x
+ g WD  O_DND
+f VD  O^DMD
+r!e T!UD  `lO]DHҁLD
+x dL TDx%  O\DKD
+c SD  O[DOD
+ g WD  O_DBND
+$tAf VAVD  blO^DHԁMD
+x@(eL UDxE  O]DLD
+,d TD  O\DKD
+0c SD  O[DD
+OD4va gpaO_Ddl
+NDHց8 x`fOL^D
+xeMD< eO]D
+LD
+KDL@ @9!  ! BcO[D
+
+ND  fO^D
+MD eO]D 
+LD`l L
+dO\D
+KD cO[D
+OD gO_D
+ND  fO^D@
+MDbl($ L*eO]D
+LD( dO\D
+KD, cO[D
+OD0 gO_D`
+NDdlH4 LJfO^D
+MD8 eO]D
+LD< dO\D
+KD cO[D
+(
+(pG
+'8!.m,M 8STs
+e
+jv.,r迢KfpK£Ql$օ5pjl7LwH'4 9JNOʜ[o.htocxxȄnjlPxq
+ D
+ DQ+ 4P _D wD@^;D 
+D  DQ+;P  VD
+
+vD@^ˑD D DQ+ :P MD  uD@^;DD DQ+
+9P  DD
+ tD@^DgD DQ+ 8P D {D@^;D D DQ+7P  D zfD@^D
+eD DQ+ 6P DyD@^;D  D  DQ+5P  D xdD@^˓D
+ D
+ DQ+ 4P _D wD@^;D 
+D  DQ+;P  VD
+
+vD@^ˑD
+ D DQ+ :P MD  uD@^;D D DQ+
+9P  DD
+ tD@^D gD DQ+ 8P D {D@^;D  D DQ+7P  D zfD@^D
+eD DQ+ 6P DyD@^;D  D  D5P  D OdDOqL@L
+D^˓D
+ D
+ D 4P _D ODOqC@C
+DwD@
+D^;D 
+D  D
+
+ODOqL@L
+D^ˑD D D :P MD  ODOqC@C
+D^;DD D
+9P  DD
+ ODOqL@L
+D^DgD D 8P D ODOqC@C
+D^;D D D7P  D OfDOqL@L
+D^D
+eD D 6P DODOqC@C
+D^;D  D  D 5P  D OdDOqL@L
+D^˓D
+ D
+ D
+ 4P _D ODOqC@C
+D^;D 
+D  D ;P  VD
+
+ODOqL@L
+,DvbD@
+D^ˑD
+ D D :P MD  ODOqC@C
+D^;D D D
+
+9P  DD
+ ODOqL@L
+D^D gD D 8P D ODOqC@C
+D^;D  D D 7P  D OfDOqL@L
+D^D
+eD D
+D^;D  D  D+5P  D  dDhZhDhDifDDiDiDHDDaE~v
+! *! J! jn
+n*nJnj
+dHA*fhAJAj@
+xF$dDLp@
+ b  p 4PDpE p`_D B!Dpe wDB!C  
+;P bDI  VD
+
+ DIvD )L   :PDI MD n
+DuID!)C `
+9PD  DDA
+
+ DDt@$DLp@ "b( p 8PDpE p`D B!Dpe{D B!C 7P"b(DI  D DIzfD")(
+L  6PDI Dn
+D yID #)8C ` 5PD  DA
+ FD
+xB$dDLp@
+$bH  p 4PDpE  p`_D B!Dpe wDB!C  
+;P$bHD
+I  VD
+
+ DIvD$)HL   :PDI  MD n
+DuID%)XC `
+9PD  DDA
+
+ @DtD$DLp@ &bh p 8PDpE  p`D B!Dpe{D B!C 7P&bhDI  D DIzfD&)h
+L  6PDI Dn
+D yID ')xC ` 5PD
+
+! *! J! jiFD
+xdDn
+L
+
+;PD  VD
+
+ DvDL   :PD MD DuDC 
+9PD  DD
+ A
+DtDn
+L   8PD` D D{D C 7PD  D DzfD
+L  6PD DD yD C 5PD  D A
+D
+xdDn
+L
+@  4PD`  _D D wDC 
+;PD
+  VD
+
+ DvDL   :PD  MD DuDC 
+9PD  DD
+ A
+DtDn
+L ` 8PD`  D D{D C 7PD  D DzfD
+L  6PD DD yD C 5PD  D A
+hdDhhDieDDiDiDBKDB[DBkDB{
+GoQcpn
+g))/F
+'&&\8!.*Zm,M߳ 8ScTs
+ew<
+jvG.;5,rdL迢0BKfpK0TQlReU$* qW5ѻ2pjҸSAQl7LwH'Hᵼ4cZų 9ˊAJNscwOʜ[o.h]t`/CocxrxȄ9dnj(c#齂lPyƲ+SrxqƜa&>'!Ǹ}xnO}orgȢ}c
+ ?G5 q}#w($@{2
+<L gCB>˾L*~e)Y:o_XGJDla
+ KhQK$O9O:I8J<IJ9:X)W*YZ (D
+, 0D 4 
+$ 
+
+
+ D
+  D HNOyOz
+yzEiFj  D
+ E  
+ F
+E  F F?c.OYPOZTst#$
+c dJcOIOJL9K:\y[z
+xi| D
+Ġ D  D
+O9O:I8J<IJ9:X)W*YZ (D
+, 0D 4 
+$ 
+
+
+ D
+  D HNOyOz
+yzEiFj  D
+ E  
+ F
+E  F F!.
+
+ D  F
+
+ D 
+ $(, bH
+$ (D , 048< 0F
+
+4 8D < } ~
+ISJ}兩{視|FHQ{טּPEQq~!ͷuXca IRJ}露{視|EHA{טּWDAp~!tHc` IQJ}{視|DH1{טּVC1wx~!s8cg IPJ}{視|CH!{טּUB!vh~!r(cfIWJ}{視|BH{טּTAuX~!qceIVJ}{視|AH{טּS@tH~O  ހޠpޅBޥNN0nJN0lN0ncdIUJ}笠{視|@Hq{טּRGqs8~wxccITJ}隸{視|GHa{טּQFar(~ЀРvhЅDХNN2n(LN2l(N2n(cbISJ}兩{視|FHQ{טּPEQq~uXcaIRJ}露{視|EHA{טּWDAp~ҀҠtH҅FҥNN4nHNN4lHN4nHc`IQJ}{視|DH1{טּVC1wx~s8cgIPJ}{視|CH!{טּUB!vh~ԀԠr(ԅHԥNN6nh@N6lhN6nhcfIWJ}{視|BH{טּTAuX~qceIVJ}{視|AH{טּS@tH~ր֠pօJ֥NN8nBN8lN8ncdIUJ}笠{視|@Hq{טּRGqs8~wxccITJ}隸{視|GHa{טּQFar(~؀ؠvh؅LإNN:nDN:lN:ncbISJ}兩{視|FHQ{טּPEQq~uXcaIRJ}露{視|EHA{טּWDAp~ڀڠtHڅNڥNN<nFN<lN<nc`IQJ}{視|DH1{טּVC1wx~s8cgIPJ}{視|CH!{טּUB!vh~܀ܠr(܅@ܥNN>nHN>lN>ncfIWJ}{視|BH{טּTAuX~qceIVJ}{視|AH{טּS@tH~Lppr(tHvh  s~xpGSHA512 block transform for ARMv4/NEON, CRYPTOGAMS by <appro@openssl.org>
+
+ 0S͂aQna
+be`
+b\A
+@HPa!@!BN.D(TA@r!t!&NA#1 t@H&ADt
+
+"n/J
+V!BaFa
+0 Bx¨th:@zhvvh8Hth<B|jH4h:Bth>@~hvh<jh6@vpp jH4h>z(rк2l8ń0
+ P
+-
+ P
+0 `-
+"-*"G"-
+-*G!` 2& Q@$!
+$t'%G&!Fg'RA@ D!B @EAD PFaF `Gt
+B&-&-D -D NH*N*-JH"-
+"-JFH"-
+F"G".`@P/
+"-*$-J%!-j$-%RaTš$V%X%YgPihP .
+$-*$G$@-j$
+B './L HHHHHH
+-/JB)./􃬢򂨣򁨦򀨧*./
+B􈌢򂈪򇈣򁈫򆈦򀈬  -/Jb򥈧򤈭򤨨 -/)./bH'./b&./b&./#./",((((((̢ȣȧȦȨ 
+-/*bHH򆨢򥨣((Ȣȣ%./*b,/jb􇨣Ȣ!./j"ȣ -/*b*./Jb&./b(fpfzȮH>؅F~(rrH0j:H<؅f|jH4H>ftpp(rN<HttN(2 `@fr jH4@0
+"-*$B -*"-J$D("PG"g$FH$-
+-*G `@P`-
+"-*$BH B -*"-j$F"F("g"$J&Jh&-J-
+--*g
+$-*$G$N-j$
+B './L HHHHHH
+-/JB)./􃬢򂨣򁨦򀨧*./
+B􈌢򂈪򇈣򁈫򆈦򀈬  -/Jb򥈧򤈭򤨨 -/)./bH'./b&./b&./#./",((((((̢ȣȧȦȨ 
+-/*bHH򆨢򥨣((Ȣȣ%./*b,/jb􇨣Ȣ!./j"ȣ -/*b*./Jb&./b(fpfzȮH>؅F~(rrH0j:H<؅f|jH4H>ftpp(rN<HttN(2.`@fr jH4@0
+"-*$-J%!-j$-%RaTš$V%X%YgPihP .
+"-*$B -*"-J$D("^G"g$FH$-
+-*G0 @-
+$-*$G$N-j$
+B './L HHHHHH
+-/JB)./􃬢򂨣򁨦򀨧*./
+B􈌢򂈪򇈣򁈫򆈦򀈬  -/Jb򥈧򤈭򤨨 -/)./bH'./b&./b&./#./",((((((̢ȣȧȦȨ 
+-/*bHH򆨢򥨣((Ȣȣ%./*b,/jb􇨣Ȣ!./j"ȣ -/*b*./Jb&./b(fpfzȮH>؅F~(rrH0j:H<؅f|jH4H>ftpp(rN<HttN(2 @fr jH4@0
+!-*!G!-
+-*G
+P
+P.N
+
+&-*&G&-
+-*G
+n~S
+P)fJDbdLbb&`)( 
+ȫ0Ȭ򨈠򅈡 򣈭0򆨤򅨥򄨪и򁨫0΀򣨬ȠȡȮ
+ȯ0ȭ򇈤򆈥򅈪 򄈫0򁈬򀨠 `G⇨f􄨭"
+pg&h`h@kh о
+ RaphpF0`aFFh<N6p`R (rsLh6p؁XwXuy)H44`8s`F`R &:a
+P)fJDbdLbb&`)( 
+ȫ0Ȭ򨈠򅈡 򣈭0򆨤򅨥򄨪и򁨫0΀򣨬ȠȡȮ
+ȯ0ȭ򇈤򆈥򅈪 򄈫0򁈬򀨠 E⇨b􄨭"
+pg"h h@kh о
+ RaphpF0`aFFh<N6p`R (rsLh6p؁XwXuy)H44 8s B`R &:a
+ @-
+"-*"G"-
+-*G
+"-*"G"-
+-*G
+K˼ "B 0 -Bü @<CD P MDż `\EF p mFǼ |GH Hɼ IJ  JA-#39FFYSlsy੆
+* 0@P`p
+`-*`G`
+ /*0/J`/jp//@/jAP/A/A/A@
+! *!p /J"` /j"
+" *! ' `86XDD(`|G0 /J"|' /*"Qg /
+"
+b8 A@
+NHx 0C
+0
+@ *@G@
+
+
+ol@j@O4wS'p~@S'p~@'7BS'pr~@S"pvS&p@6S&`z@r@&6DS&`tS$@r@b@ooT@@o@7oe@{Db@wO2S'p~@S'p~@'7ES'puS%P~@n@uS%Pn@@5S%Pn@%5BS%PrS" n@ oV@Jou@r@O2wS'p|@S'p|@'7ES'pu|@S%ptS$p@4S$@}@e@$4BS$@rS" e@nU@nl@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ nV@Jnt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@mU@ml@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ mV@Jmt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@lU@ll@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ lV@Jlt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@kU@kl@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ kV@Jkt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@jU@jl@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ jV@Jjt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@iU@il@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ iV@Jit@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@hU@hl@j@O4wS'p~@S'p~@'7BS'pr~@S"pvS&p@6S&`z@r@&6DS&`tS$@r@b@ hIhT@Q@O1OvS&`n@S%p%5Dt~@S%pS$@~@f@tS$@f@@4S$@f@$4AS$@qS!0f@^@OVU4D"T@"33Z@AK@A2Q@`C`
+ol@j@O4wS'p~@S'p~@'7BS'pr~@S"pvS&p@6S&`z@r@&6DS&`tS$@r@b@ooT@@o6oe@{Db@wO2S'p~@S'p~@'7ES'puS%P~@n@uS%Pn@@5S%Pn@%5BS%PrS" n@ oV@Jou@r@O2wS'p|@S'p|@'7ES'pu|@S%ptS$p@4S$@}@e@$4BS$@rS" e@nU@nl@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ nV@Jnt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@mU@ml@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ mV@Jmt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@lU@ll@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ lV@Jlt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@kU@kl@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ kV@Jkt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@jU@jl@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ jV@Jjt@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@iU@il@j@O2wS'p~@S'p~@'7DS'ptS$@~@f@tS$@f@@4S$@f@$4BS$@rS" f@ iV@Jit@r@O2wS'p}@S'p}@'7DS'ptS$@}@e@tS$@e@@4S$@e@$4BS$@rS" e@hU@hl@j@O4wS'p~@S'p~@'7BS'pr~@S"pvS&p@6S&`z@r@&6DS&`tS$@r@b@ hIhT@Q@O1Ov`S&`u@S&`u@&6DS&`tu@S$`uS%`@5S%Pt@l@%5AS%PqS!l@OC`
+
+
+`ipG
+`CpGYlpGO qDh F< F@Y_OqDh F< F@YS8OqhF(4p<xBн8@YA8{O -O 
+wO
+
+
+Cv/
+
+E#"C+CE k`#B`*``
+
+P
+ 
+OG|OwO
+w'
+E&C/CEt@}@d
+
+JzD JzD JzD " " pG
+JzD JzD JzD " " pG
+JzD JzD JzD @" p" pG
+JzD JzD JzD
+JzD8! JzD<!JzD@!@"D!\"H!pG
+JzDX! JzD\!JzD`!@"d!\"h!pG
+K"{D2K{D2K{D2K{D2
+K"{D2K{D2K{D3K{D3
+KzD{D|%"%%
+ZCҲ8PF J@  "  B  c"0 FZFF6QF"F<Ѹk<FS۲1F";
+i @E
+Os
+
+FFR{<xFC F
+(FM
+hh
+`
+;QlXB p:
+_#
+I{DxDcyDRYRk|l<
+hhBPBPApG
+Kh
+E
+6AF0F"FW?I
+D`E h.Fh
+
+2EF@"0FFD;  F1FJF
+F3FR =0FC F
+#h:!S"=)@C" F !FF Fp@ p8FFHHh1 F!F*F8@} 8
+h
+h
+h`b
+Њh,`h<`13
+ gF`  hdFhENhE,F Wh
+ E,
+ E (3O:O
+ hMh4
+ (6O:O
+ Thh4
+ O:O
+ hi (3
+ E (3O:O
+Lh [4
+ O
+(6Wh
+ (6KihO:O
+Li v4
+ O
+(3i
+ ENhWiO:O
+ hi (3
+ E (3O:O
+Lh [4
+ O
+(6h
+ EKihO:O
+ iWh (6
+ (6ihO:O
+i v4
+ O
+(3Wi
+ EhiO:O
+ Nhi (3
+ E (3O:O
+h [4
+ O
+(6h
+ (6ihO:O
+i v4
+ O
+(3i
+ E (3O:O
+Li [4
+ (6O:O
+ ii4
+ (3O
+ gF`  hdFhENhE,F Wh
+ E,
+ E (3O:O
+ hMh4
+ (6O:O
+ hh4
+ (3O
+O  RBF RF  F
+FAF3F#
+/JF F
+BF IFSF F
+
+
+
+*ʹƹ
+/+
+
+
+OCZ[F O|
+
+zFQF*F FYF"F[DF #
++F6;YF
++F' 
+0F
+0F
+ZF, QF
+
+ (6#
+ (6#
+ (7#
+ (7#i
+ (7#
+ (7#i
+ (7#
+ (7#
++)F2FSF
+9F
+Fah*C C
+
+kK
+ 
+
+  
+I  @FIF'
+ FQFq
+
+3c`B!F8F$
+*KhBE$8F:8FqF
+H#
+Yp>,
+G p _P
+ù
+ڍ#
+
+C% ch`
+
+
+  h0FS@`
+
+:
+ {
+
+Z 
+(F)Fw
+ F!Fb(F)F]ȱ7+F%FF#F,FFkh
+b
+HF )F:F#F
+Os
+kh+hh
+:^Chch";#hhC##hhO3jh
+*hh(FB)F"F;FvB(#F ,FFo8F)0Fi
+#
+
+(8F9FP8F9FBFj
+0FQF
+
+( F!FP F!FBFD
+(FQF
+ F!F:F
+ܸY ܸԿ''''
+b
+<F
+F.
+AF 98
+
+FF
+DB
+
+%2FF#F(F
+(F:F3F
+^h<@DñHF9Fk
+ H F(FOH*
+
+E F
+
+FL O O
+FEOl 2
+
+
+O
+z'''
+K#
+(F
+
+ F0 F@RuF F hSh#
+j+F:FG
+ 8
+j
+
+
+@#
+Z
+F FF# `
+#h FIF2FiCFG
+
+
+
+bp pp#q
+bq qq|-GF
+
+C
+ ey B
+)qjp pp
+
+jq
+ q
+q5
+6
+9q{p pp
+{q q q
+ p!qcp
+pcq qqyUF^Fay?Bh"yG'
+ CyGgAxpx1xB
+CxBa2yqyL,FL y6Ll
+jq: ?qq5
+    * ,;*
+,;\;2,;2 ,;2
+,;lO#
+cq; ?qqF(0
+#qap pp
+bq qq-OFhFFPhBӮh
+
+ CbyC
++qip pp
+iq qq5
+5 
+3qqp pp
+qq qq
+"p#qap pp
+bq qqy
+
+kq; ?qq5
+    * ,;*
+,;\;2,;2 ,;2
+,;lO#
+cq; ?qq0h
+Kb"
+H
+I{DxDnsyDK K hG F 
+FhFC`+h#`kh˱+hif`8FP``H#
+K"
+H I{DxD3yDKOKa#h FhG >
+0gIFRF"B1F
+
+ 
+HFjn#F
+
+hB @#
+H!F*FxD
+@ 
+иX#
+$
+9F
+
+ FV FRO
+8FL&
+KA"{D/f +h`F FO,F F>
+KB"{D/
+
+
+
+3h0FQF:$+FG
+|F1$
+$F F
+9F
+,h9F @F!F
++FG
+
+ FaF2F
++FG
++FG
+G
+QFG
+J
+8F -
+(@0F)F#F G
+ ,
+hBhB@9@#
+RFG
+"
+YB@FOs
+
+a F
+QF
+QF^%
+% F8 F}0F
+
+WF
+
+&h
+
+
+
+: !#h F
+1#h ,
+ F1FRF+FG(#h 
+F3FGб#h F
+F3FG(PF,
+
+O
+
+
+(
+
+OOOOE 8O( 8h"C)3nhD  MF0i#
+БBбBв#
+E
+BѮB Os
+ S' P'0h
+F9
+KE  + 
+d$ ^ 0S*
+F
+B8
+BM5U&
+V@2p$1(TD?0`G(6pOtT5H3ZD9?=Fh&@G '^@:4`Or2CD>C7pO LL
+,<Fg@n@8`OssL;F l@f@dD>`Oue6F &
+
+?`@1Jf
+
+
+
+TD
+OtT
+
+
+RDOr2DD  SD
+K
+
+SDOssDB  UD
+K
+
+C UDtOeD TD
+K
+
+E TDXOTD RD
+K
+
+RD8OBDD  SD
+K
+
+SDOssDB  UD
+K
+
+ UDOeDC  TD
+K
+
+E TDԢOTD RD
+K
+
+RDOBDD  SD
+K
+
+SDOssDB  UD
+K
+
+$UDpOeDC  TD
+K
+
+E TDPOTD RD
+K
+
+ RD0OBDD  SD
+K
+
+SDOssDB  UD
+K
+
+(UDOeDC  TD
+K
+
+E TD̡OTD RD
+K
+
+
+
+LD`TDOtTDJDBDDOrB D CD0KD$OssD  EDMDOU D DDLD OtTD BD bDOrBDcDCD Oss (D eDMDOUD dD LDOtTDg@bD OOrBWDOCDOss_D}OEDOUgD<ODDOtTf@h`FhrBhB`h`` >? 
+Q  R S0ciT0
+U  V "W0Y
+jp ppch+q
+jq qqh+r
+jr rrh+s
+js ssp
+|4?G ':C|BgJ}RD&@^@?}"G '>C}FgN@o@^D~~sc?G '>C~FgNfD@g@u5U?G '!>CFgN $/@4W@ `"p4?G '%>C#pFgN
+FD'@_@&p$`"(-?G ')>C'pFg~N ND@o@*p(`sc,?G ,@L+g@F lpNVDu,`.p5U?G'>C/pFg$FO> /@4W@12p0`405?9G('@H3_@FhVN^D=6`4p"6F &  7C7` GfJOGD8pKD:scO II
+)
+
+
+I;
+G i>O}>pUD<5U?G '  G
+
+?p @1JgȠDTDĠ\D 4D RD ZD "D  SD [D cD  UD x]D0UD  TD `\DDD  %xjVp $ν|*ƇGF0FؘiD\"kqCy!Ib%@@QZ^&ǶRD ̤ZD22D  SD [DcD UD   ]DUDTD   \DDD  RD lZD,22D  SD P[DcD UD  @ ]DUD  TD \D(DD  RD ZD22D SD   [DcD  UD أ]D$UD  TD \DDD
+ZD
+  ZD+22k[D
+(DK:z[D
+ ]]DK0
+uU[D  K\D4D
+ KD KZD r"[D  DDK:z[D
+ ]]DK$
+uU[D  K\D4D
+ cD J[D s# DDJ:z<ZD
+ U]DJ
+uUZD  \DJ 4DZD
+  DK{+ DD}J :zBD
+R xK
+rRU@vKd4D
+e@DDqK{+ke@SD[$lM cUDc
+
+RDhMRMDb  ,LDcMtDMDd  DD^M+ EDkCDYM cUDc BDdDR SMb DDeDtDD(d LM D+EDIMk  cD} cEMc0 MDR,@Mb _@utDD<Md<OV@DUD+9Mkf@eD cc R< b ^@.NtDdU@^Dvh[`Ehheh&`F``48?4q"am 8D꾤K`Kp~('09|eVD")*C#9Y[e ]O~o,CN~S*5:ӆ]/SD!7Ç ZEogL*mpF F?+ 8K"8H9I{DxDcyDGG!vZ8*
+Q  R S0ciT0
+U  V "W01
+jp ppch+q
+jq qqh+r
+jr rrh+s
+js ssp
+cBqH|"qIqKxDyD{D-٥ BٵBPFF;CF 3F2FBFXFG
+ XFBFG
+  $(FBFG
+Y)YQ@Q*Q4,;
+L
+h'
+
+
+0"C
+
+
+
+0#AJ
+\Ec
+ J@
+
+
+O
+E>ZF
+
+1F$
+
+
+g@ FBaAgO6n@O@
+g #
+IO0
+#@{O6
+#P@Y@ #
+P@Y@#P@Y@# P@Y@
+
+ #
+
+ *.##
+ 02##g 4## <gg
+ 6## 8## 
+:# K
+>{D;`K{D3`!
+L2F
+l, @C
+
+ 8hv F1F
+FK@
+ @
+O
+`Ehh F  G` F
+OODD FG "FD`
+
+/ &F
+ MFWE@F4G" | 
+
+ђF
+3A@?CEh
+
+ @
+3KEh
+
+HSxh1l!*@
+#b@k@#n @F1FJF.
+F0F@,4"F"p@-GFFFFlDhC@C
+jB
+@C
+n!FRF
+
+ "QPOc
++!F;F:!F;Fi}"
+1]4x=J@+BF F*
+
+"1"x
+|D
+p
+q
+
+ PF!F" -GF
+w
+َ#
+#
+F
+
+
+
+E@}
+HFH
+PFIF:F#FF(HFIF;Fo
+(@k
+#
+#
+" F)FF=
+A@yEXo - F4C0B(FO
+
+
+ F
+
+@v!F
+
+
+E&(FH
+)-CFFFO#
+ B
+T#
+Kq"{DY2FA >`X
+)-OFFFب#
+ B
+٭#
+
+ XF!RF x
+.
+#
+
+f@ C+at@#v>#
+K"{DHFy*Fw `
+JC60FHF@Os
+'
+) '
+h` h` i `
+Ci`pG i `
+i` j`
+XB6C0B Os
+2F "F =`
+FB@C#
+hZOs
+&+(F
+!<I"yD
+O
+
+h:w#
+#
+hBOs
+#
+@
+#
+8F
+j '
+ @[FFFFF F
+#
+Os
+XFQF*F#F
+?j '
+ @-GFFFF lhB @
+
+N FFFP@#
+ "+F FX@#
+ "
+#
+FFF Ohs
+H!F2FxD
+I{DxD#yD@@\
+ciV [0X  Y 
+Z "B
+pchq*q jq
+qhr*r jr
+rhs*s js
+s#it*t jt
+tpfl
+I
+K< @ K#aKcaKaKa#f
+K8K#aKcaKaKa #f
+#jb g0d  e 
+f ">f8F1F@"Pn+ +Y U6Qp.O!pp Y U6Qp .O!pp
+T&06p*p jp
+p5n0F
+#
+#
+!F"0F4jHF1F"++ب#3D
+QFODHF
+
+
+0  
+   l0l 
+0 1F 
+   "000+@+1<0T?bhqp(p hp
+p*q jq
+q5B p<@T?bhqp(p hp
+p*q jq
+q5B
+#
+
+F8Os
+2F_
+
+2F
+2F
+2F
+2Fp
+2F
+2Fr
+
+2FP
+
+(FF{`(F>`Fu
+2
+2x
+2E
+
+HLxDhFE F0
+2
+
+2l
+2
+2P
+ 2
+
+2(
+2Z
+2 -
+2 
+2E=
+2E
+2Ey
+2E
+
+
+H
+3yD== Fp
+M
+K}D FX3@;<
+M3
+K}D FX3@;<
+Mq
+K}D FX3@;$
+K}D FX3#`8
+
+JF{DX3@,;u
+@FCE!F(FC%AFF8FC$
+K FYDF(FC(FC
+@FCE!F(FCAFF8FC
+K FYDF(FC(FC
+@FCaE!F(FCAFF8FC
+K FYDGF(FC(FC
+@FCE!F(FCAFF8FC
+K FYDF(FC(FC
+@FCE!F(FC AFF8FC
+K FYDF(FC(FC
+JF{DX3@4;
+JF{DX3@ ;
+JF{DX3@$;q
+JF{DX3@$;Y
+K JF{DX3`@kD
+K JF{DX3`@kD
+K JF{DX3`hD
+
+H
+
+
+
+
+E7O
+
+HF AF:F#FCu-GFFFFC0F F+h(F
+FF&K}D FX`3#`pfl
+MFFy K}D F
+F HxDy(F C1FF F
+I
+)F
+"h
+o( `BwF0`
+K{D ` `
+KB X;
+L K"|D!{D F
+(Fk B;h6k BuAF<2F
+KFhh{D3
+KFhh{D3
+0F7(F
+A8FBhF4(B)RF
+B hkGF@FBF@FBF FPFAFB8YF#`F
+BhkGF@FBRF@FBSF FPFAFBb$
+B.
+PFAhkG
++h x"sZxbsx3s+`
+0F)F!
+)pFF h;P5
+KF{Ds`hA
+KF{Ds`hA
+KF{D s`hA|
+Hr"IKxDyD{D9X9jEd
+I{DxD3yD909Bc
+"F@F1FE+h ,`
+
+i`Kaaa bKbpGKhiH{DXhh"Exr@
+M
+,hKBP;B򞀂KB
+;B;B
+аO<O
+аO5 0F9F?40F9F?E-2hx+"غB  +r3h33`0F9F ?~0F9F?xh
+B܋BBD `
+H)FxD F!%?"a?(F|
+ H
+ ;Dh
+(Fp0
+
+5?EE
+0F?[0F?
+FO
+1F:Fo
+B
+F<H!FxDPFL? G
+KBЃ1
+O6+M
+!FRF
+BFKED%JBVF
+
+ F?E FO O
+KEh{D3`-(F?.(F?h-(F?(F? F8
+1F?;0F?B9F?0F?#F
+# 40LN9FBFB
+гB JBAF F08F?yEHF9F3F
+tJB_tJB
+#AF
+#=o7? 3V#(F
+#0F6F0'HoxD2M
+#FлHoxD3o 0o6-?
+0F>HAFo xD
+H"IKxDyD{D6f6x F?!  F? F?#
+o+h(F1F[jGF
+KF{D83E0;(F=
+@F FB
+F F   F@WF @lF FB
+sh+t3h
+@ @@@o+qr F.AF
+@g
+@f
+IFF@FF
+ F?(F=#?1F?!9FF"F F)>F0F?%0F?
+GF
+L
+N M|D K~D}D0FX*FK0*FXp@˿
+8F;h1F
+_+B#`o4
+#`PFO
++@<FBF>F:0+ + +uF.TF*F&0+ + +aFPFF0+ + +M F\FF
+FF >7Fk8` + (FAF"F;F[
+ FZ `
+FF
+FF
+0F+`
+
+L
+N M|D K~D}D0FX*F-K0*FXp@%
+@ @@ {O3pw
+  .FL9F(F>%>nF
+;>U
+tFP
+GF
+; >o(>> #o F >P >OF
+MFF K}DT
+L
+N M|D K~D}D0FX*F9K0*FXp@1
+DFO
+o
+FF Fo f (FB. o8f
+K
+
+;=
+FP
+GF
+;o(==>Ro F
+MFF K}D@
+O0 `pmFksCҠmFkFH1FxD'o O0+`p p p
+"F)FFȹ9hF0F:F=I"F
+L0F:
+*F 9cF=JF8F9
+*F6 9F<F8F9*
+ܰ_@_аOѠhH"IKxDyD{D22
+
+
+#b 
+ < 
+
+F<"F;?o(&KOr%H&I{DxD33yD1`1rrF;8;7F
+ bF<c<b 
+!cK@HI{DxDC3yD1]1oF<<o(a F;
+
+@ x@t@ pO3pl
+
+$$$$$
++hB Fx
+ 8hrF5
+F
+ (`"`g"u"
+OJ
+@
+
+(FFFF8s^FP#
+K"%F{D 9F
+F
+05FPK#
+#C`F Fh+ ܀h5i 5Y`
++
+ "FH#
+#c`0F
+Fh!hFH#
+Od ~D ) \Z,+,-,
+1a``D`
+O< OajD
+ D1 )M
+K"%F{D 9F
+K"%F{D 9F
+
+p$h<8SSB T:\ 
+#FD  ;\ 
+ 
+Fh!hFP@
+Xp p h3 `pG hp pZp
+ p h3 `pG-NF FFFA A
+A /NE;="VE"x=cx4C#^E cx="xCcxCx4C# F)F:
+G
+ z#
+A$A$A$
+
+ F
+E9F(F2F
+<h3`
+#
+ ى#
+ܻ'ٜ#
+ F!B?
+
+
+E
+O
+ _
+
+
+
+ B#
+ bJUF
+:k
+!
+! s0 (
+
+
+h2<
+@'
+0F!Fhx+` g"
+hhhpGhhpGi4^pGK{D`pGb
+N~D0hH"F; 0h;F(Fp1
+|D#h'#%HxD: `#K{Dh<#
+-ܑ)\Z*+*-*
+D\0> *wRx0 /q&3B0j\!pBf W!pB`ܸO/ 
+O< OajD\0> /&اx0 v\#p0>B W#pB(+
+ 32+ FSXBXA
+E F
+Fo
+KA"{D`@F %`Fh1F*F;h
+h|FHF#
+hFF
+8F@@VC
+F F
+O5
+
+37
+
+i
+
+
+F %@F@@#
++bѹ
+F ' 8F8@3
+
+* @3
+
+B
+hO
+0FYF*F$a
+FF@Os
+6@D@
+ 
+  7
+
+hB۝60s F@Os
+!F0F*!F
+
+87 @
+F /"
+
+F &,F@@&#
++FFP@1#
+
+&0
+c@FF@9#
+0F)F8
+KF."FKFF
+?'O
+
+
+x
+y
+
+
+!F
+O3
+
+8
+ZF
+H <
+H H<(F 8E(F8OKF "8F O
+{D9#h X,X 
+9;X<
+
+   (F8wE  `*8F1RHF1OAF (F8}
+
+FO
+F
+
+@i@Ϳ
++ 
+9F(F
+(F!F7#
+ K{DT3f4
+
+
+
+8n*hch+`&ch3`
+hB131Bai@
+FA3
+:/ɲ]!C%=0
+CY2&(>"!2%
+bp "2` 
+"pcp  0`#p
+2`pF!
+#+T
+H"IKxDyD{D+T+f0`p
+F FFF
+E
+F2F08
+#h&`_FRF08(F0"!DO
+"+
+
+O
++ ڲ*[ K@40"h2*q"`@FZFZ#50#`D+4VE
+
+8F @*DDUE5
+[K @#<
+F,0h"R
+
+j FG F4F0B
+#
+CapG@i
+CbaFF
+
+!F
+F
+NB4F F~D(F1F(4
+:FB
+J8d#i0F F-AF$F F
+*ТBF8F)F
+)+)).) :r)/
+): ):s)-Ђ)
+8
+K`
+F
+ {D\[\pCppG
+" 00
++ 
+|# 0
+#"!0
+",C
+)' )CD)܉)Ќ);)Г)6 i h
+٘FC.H@.I.KxDyD{DFFh%iB*H@)I*KxDyD{Dh`iB(B5F%9F*F77h"iB`HOrIKxDyD{Dv/D@F8F
+8FS0FP
+#
+ FV"FF
+x
+OO
+ 7 %F )
+I2
+t#
+ H" I KxDyD{D** s`
+
+ gDz pz`p4 :;9
+!701ɲ
+!7?Fи
+ F1.+x-+
+p
+e#
+ӽ#
+6"h#`
+H
+OHt"OIPKxDyD{D)Y)k
+0FU& .F
+;B#ss
+HP"
+I KxDyD{D)m){h.y h.v
+kh2{B`l`+h"Zs
+O&O&O&9
+I KxDyD{D3)F)X
+JH*"JIJKxDyD{D))fxx-E%&x5CxEefyy-E%&y5CyEefzz-E%&zz5CEdXМx]x$D$x,CxDd]yy$D$y,CyDd]zz$D$z,CzDd]{{$D${,C{Dd]| |$D$|,C|Dd]}
+}$D$},C}Dd]~ ~$D$~,C~Dd ]$D$,CDc 
+KHP"KIKKxDyD{D(6(H
+AF"
+
+#Et
+FF9F"(FF50F "5g0 
+J#
+<r " Fyx
+-BbO#*F!F(
+-BbO#*F!F= "QF2F o? F
+#
+#
+5pG-OFFFFhF"
+3
+
+0F"
+0
+k+ @7#
+ZF"%IFBF
+
+J8FQF+F
+ @#
+ 
+OAs
+ A
+T+ O6s
+v#
+#
+
+0
+
+O
++ SD[DOS:ZDJ
+
+ JD(
+
+
+O"BHѼ ?''1DD?1ED?1CD?1BDDDCC`C`-GO h~'Dh $ OTO
+H$)?
+
+6O
+J2
+# OU 6I5 "6Ѽ !?''1?1?1?1EBCC`C`-O FFF
+xqx3x?G! C
+ix+xB")yCxBbyW@C# Cy5CcIFX@'p;
+ qcp; p;p
+cq qq4
+
+
+3
+,$ 
+ qcp; p;p
+cq qq;
+7p0qsp; ?p
+psq
++xiyO KK+*yK xKkyC#IFCy5Cc
+Fbq: ?qq4F
+
+
+dS@
+ 
+<,<,< ,<
+,<<O#
+sq; ?qq-O FFFFhF Oz(O2
+ H" I KxDyD{D'Q'cD4`p(Fp
+Z#
+VPF l`
+AF:F(F
+AF:F(F
+AF:F(F
+AF:F(F
+(0$($F`06!"F46)F9 
+ڣ#
+k_
+a
+ HDZF4H h0F RF4@ ( F
+
+0
+
+
+u
+ Ht" I KxDyD{D'b'tD4`p(Fp t
+>#
+gPF@- &HS"&I'KxDyD{D3''-
+AF:F(F
+AF:F(F
+AF:F(F
+AF:F(F
+AF:F(F
+AF:F(F
+AF:F(F
+AF:F(F
+AF:F(F
+AF:F(F
+F
+ F<P;0(<
+0
+0F>BEH@]EIEKxDyD{D
+BF
+F F/
+B
+ D$
+
+F FP]
+F
+F7BTH"TIUKxDyD{D
+
+
+8D F
+P
+phpy qÈKqCh
+qChqz rCKrh
+rhr{ sÉKsh
+shs| tCKti
+titpG
+pX3 +p@y pÈKpCh
+pChpx qCKqh
+qP;q1BpGξܼ-OF FF1B.F\F4FO{
+1F(F|=6k@`
+EFH"FIGKxDyD{DB
+DH"DIEKxDyD{D&&@)AH"AIAKxDyD{DrE&
+B<(, 0FO|AF
+B(3
+  I 2B F@
+3. >fC
+\, "J 3 ~BF
+CJT5BFj
+F F
+F{D
+JzDx2Cx20
+ 
+ rhQU5"x0ԚB"xBzH|xD BbxB44rhQU5"x0՚B{x; 
+#
+ @I
+phAU5Q]
+M4xDFXOs
+9@F!F2F@#,F
+&'FO
+@  !F F2K
+* ;
+ Y\x3@ F!3x4ёBB Zx9 
+3JFxzD2  
+
+z?
+{T`h*th*p F*m
+K"%F{D
+(F!F2FF F(F|
+ F2G%F,F 1
+
+ 
+gc#rOR`E
+g g
+E
+#E
+j-O
+iiiiOii h)|I
+aO)*O)JO)i$|Dab!~AI c"|BO#~CO9OXHOlOl8Ol\LG O
+O, GsGsss.Ft.-tEDtuEuDuLuLIvAAvvvBwBwww
+
+
+
+ 
+
+@+#
+
+ 
+, #'#####$##%#&##(#'###)##*##
+##
+## (-'#$%&-.(')*+, .g/#g-(g'g$gg%gg&gE
+#EEtOT`E
+J 
+OakI 
+# e"2@;@aaO3aO|B
+
+  #
+c *b#c jb1hF-OHhh i
+%hC
+X
+
+ 
+
+  Xgg
+g!gg
+g
+g!ggX ggg
+g ggg!gg
+g g
+O|BK O3@@
+#i@O~@CO1#@# @c E#rOR`K
+B EE#ErOR`C
+
+BE@O~F O|B@# e
+
+  #
+c  #c $ #0FhF Fz
+iFv
+Fr
+!FF$
+hFiFiFc
+F
+ZFV<
+F $
+JFF<
+F$:F6<F $F*F&<
+F1$
+F<
+Fc$
+F<F1$FF<
+F$
+F
+F<(F
+jF)0
+
+X
+\0 h0ppl`dpp`h`t0p`lp(d`h0
+#C
+# #gC ##C##C##C##C#I #G
+gK #A
+#O|J O;g
+@ 
+ a
+
+# I 
+ 
+#gO~@@
+ iO9 #4&c #`g
+ g gBO~H #
+ gg # a
+ gO~J O;#
+ c  "q(!F F!*Fh!*FFm(F("Fh#!Fc3-CFF((F9F2Fx F(F9F2FPI8F!FBFN(F)F(HHFPxAPhF
+F' F9F*F,(F9F*F8FiFJFHFiFJF
+k FFOPjMJ z
+(
+k FF OPkOajK4
+k FF"OPkMK
+k OiCLH (#
+
+,g#"@+@ahA$# i
+iFzD#2FhFiF
+2FF&
+s
+F Fj F!F
+jF F!F!F\(X(FT(!FF(FFF(F(=(F9>(F &(-(F)>(F&2(2F>(2F &(F (F >(F1&((F>(Fc&2(2F>(2F1&(F(F>(F4NrF~D(F F"Fb F!F]jF F!FX(!F(
+FOjF2(@2xAF " jF2('2iAF " F!FH+!FYx0 F!Fx
+x''
+jF58F@6(
+P7jF8FiF
+8F9FJF- F?%(
+P
+q cP
+:iFYPFP-piF5RFhF1F
+hPiFC c
+0P
+piF5- F
+(
+ #B K Bb 
+EhDt 
+ g BAb 
+
+" Br 
+EIO0
+ g
+
+DAt 
+ #t
+ 
+g
+[ FAf
+
+ E"
+ $
+$E'
+*
+ (E,
+,Br 
+E1
+ Br 
+E7
+ 9
+ <@Ap # 3
+G #g*3G.g g w
+K 
+g
+$#C g$#&# wNC&#sgY(#C*# # 3G#g.g3A #( 3GcB*g
+g
+2DA g$ wA$I3E]&#C&#  E$E#C# (# C
+#sB
+2*g6DgJG(g g wA3Y$#C#  g6G"E$# # C
+#cB
+2E$DEE (E$<H 
+E
+2$DEE E$H 
+#C# #C
+# 
+
+
+2 g6D G g g6H 
+A
+YPA!#c#OPRA
+EORTC
+#c
+#OPRA
+E$ E
+EmB
+
+aE$EE  E$HE$E
+EUB
+Ui
+E$E 
+EE $EEM#EE #E"E 3E#"EE3A #  C
+ #sB
+2$DE E
+E $E
+EcM3Eg]6G Eg #
+
+ EQ$E E E$AH 
+EY$EE #E
+E#E
+#
+
+gG # 3 
+#
+ 
+DK
+ #J
+K OPRE
+Ecg ##ORPC
+EMDE%6gI
+A!K
+#A
+#EEc
+#A
+C#K 4Fa=F"G
+EG D
+U  
+K
+
+E$HEE
+I£Q#C#r {B"]
+k B"_ OBVG'A"F+F
+EcF#B KFB"_OBVG'OTREi B"k
+OCWAVKG'B FFE
+k B"_OBVG'EF F cF#b kFB"E_OBVAG'KB OC[B"i K+OBZEF F
+c OeYOTX #"#OIWH(G'OHVC FFFi g"ORXOcYFH(OIQOHPG A!{r
+k B"]TE%0F9F@NI
+g%N
+ wC#KB 2
+g
+hDG g
+g
+ w
+&FK /F
+@  <#aDC#kb OC[B"K+OBZI OiS
+
+g B"gYgPA!FFGr
+k {B"YPA!
+K 2F;F
+gcTF#OkSOZR]FYB"A!OBPOVRG$eB"{EOBXEOCYAKI)B FFB"
+k OCYOBXI)EF F c F
+#kb F_B"
+G'OBVOZRK i "gB"OkSTOCUG{E%r 
+k ]B"E%OBTA2F;Fgc #KB ]B"E%OBTOVRG
+q CB3C{q q CCCq#[O2{
+2rsr{ C3Cr3 r3 ;s3 C3CLSsOsO3,CLCCsO tO#StOC47FC3PCtOZOZ:t00Su3
+u3 NFCCOYHC@uv
+Sv CA3Cv v CCMFCw#[O2OY#bww1-O_} FF !P-FF@1#@1_1?C@_1X ""F)F`3`(`6-- "u  "o"F)FLipp
+
+ Brgp
+BArgp
+`Brw 
+cOPOHAhL
+ E2#MCD
+ EI4E52E4#C2##2EE2E@
+ #4EE4E#4EE4E  #4EEL@
+ #4EE4E#4EE4E  #4EE4E#4EE4EM<<CDt7
+" #6EE6E#6EE6E  #6EE6E#6EE6E#6EE<EH
+! #6EE#6E"#6EE6E  #6EE6E#6EE6E#6EE6E#6EE6EN>>[ m Cd9
+* #8EE!8E #8EE8E #" #8EE8E#8EE8E#8EE8E#8EE8E#8EEO>EFFCt89
+$ #8EE+8E*#8EE!8E #8EE8E#" #8EE8E#
+8E # DE 8EGBEb .
+
+# D#DC
+ (
+# D#DC
+ 3YD#A)E(
+# D#DA D#A.E
+  3YD#A)E (
+# D#DA D#A.E  3YD#A)E(
+# D##DA" D#A.E  3YD#A)E(
+# D!#DA  D#A.E  3YD#A)
+E(
+EE
+E&
+E
+
+E)(
+#&A'
+# D%#DA$ D#A.
+E  3YD#(A)
+# D'#DA& D#A."E ) 3
+Y# D(#DA D#A E.
+ D 3Y##DA:E g 3 D:#C
+K e0# *EORPDC
+C#K e6#6(E8EC
+k 8#g.ORPC
+k #g0C
+k
+#C
+E@FIFe#"EC
+g
+k I
+#MLgC
+a#
+.
+3G#g
+3A HI0g
+3G#.g
+gGKgN#
+6##gNCO##
+
+cD
+K # 3
+K
+K #  SD.
+K ı # 3
+K #,cDI (#
+3I (#
+3I (
+3I (
+(CDI (,
+#  
+ 3(I # cDI 
+#
+3
+K # 3I 
+
+K
+J
+Ei #C
+O@RK C#a
+#ORPC
+
+e #"EC
+OPRA
+i #ORXC
+
+(,
+
+I
+
+ 4 
+I
+<
+I
+I   T
+K H&E
+=DK Oܢ E UI
+E
+
+ E 
+EM
+K
+ DEE  EE E E4
+H 
+E
+pt
+2K EF
+K D#
+C 2#FE
+7KK U2O
+EK 
+U
+
+DK 
+
+OPRA
+#i C
+k
+A
+E%K aOPRA
+A
+YPA!#c#OPRI
+I 
+HK
+k EB"OCYI)EOBXkb FF
+k B"OCYI)OBXA"F+FEc #KB OCYB"I)OBXEF F c #kb OCYB"I)OBXA"F+FEc  #KB OCYB"I)OBXEF Fc #kb OCYB"I)OBXA"F+F
+Ec #KB OCYB"I)OBXEF Fc 
+#kb OCYB"I)OBXA"F+FEc #KB OCYB"I)OBXEF Fc #kb YB"A!OBPI "FOXW+FcG'#OiSz@FIFcC#aIEUE ` 
+H 
+
+G'OBVK OkSOZRi B"YPA!RFI [Fc#OXROiSFFB"OF]OBTE%OPRA6gB"K gOCUI TOiSE%OXRgFYB"F
+k EA!OBPGBFKFFcF#{r OC[B"K+OBZEb k
+i
+B"OCY F
+OBXI)OZRK )FEB"OkS
+OCWEkG'b OCY
+k B"gI)OBXA"F+Fc #B KB"]OBTGE%{r
+$0  CF3-C%0 &0 CC-C'0#[O2s
+( )0s C3-C*0; +0; ,0; C3-CG-0 .0
+CBC-C/0d00
+10 -C3`pC5p20OZOZ:304`FO#60OCC CC@7080
+90  CA3C:0 ;0 CCC<0_iO8OX#OXH= >0? _}-AF?0x=FFF
+p  pp 8 8"pp c`* F$>W
+c]Z
+Fp)Fzz)FF(FF*p<ODDDghbe `*x `iF "M
+
+hF F
+h`pG h `
+i` Bh`-GFFFFF
+!'H8F!! 8F!豍QF(h"3FHF!
+ѣi0F
+Os
+
+K{DhG9F*F*
+
+KA"{D1
+!
+Ci`pG Dh `
+h` h`
+*
+8O
+
+,4"\2ҲTR
+
+
+
+
+P8FMJF
+
+тBS
+
+
+
+
+iS F1F
+ K
+
+
+
+
+
+
+
+
+
+
+
+F
+x3
+"
+"FF "FyF
+,|ii"F
+OG"FADki "FYxiiO"FADo`ki"FYf(h 6.@
+"8#
+ "
+P
+
+"P#
+"
+)FF@ h %` 0`(F
+KF{D
+ڕ#
+FFFh#
+
+тBS
+
+GF8@ʿ
+h ?Ӕ
+XFOp Fp!FOr(J F)FzD
+L
+.H".I/KxDyD{DCN1eRKaX!(HxD````
+F F Q F
+K(F{Dhb`
+ F8A
+D`-O Q W; F
+F*W%
+  F F
+
+!FK
+
+FP#
+#
+FOq F F 
+Ft! F F8 
+F!Կ F Fh JhDhBh3k#GO0o
+>
+"^
+G F
+F F(x4
+г +рh] 븀h]|h]Os
+#
+#
+i- Os
+i@- Os
+i- Os
+i - Os
+j
+^kFOs
+Ѐ+Os
+K}"{D i @
+KF{D+h(F!hiGF h%` |I
+"V
+"yD1
+"
+"yD1M
+0F
+#
+hS#
+~#
+3F\#` 
+"d
+"yD1
+"
+"yD1P
+1#
+K"{D F
+@ (`1F1Fp@ p
+"
+"yD19
+"G
+"yD1
+KA"{D F
+KA"{Dp F &9F@"@`%i(F0F`
+Os
+HF
+Os
+F
+BF;BABd\A B
++Os
+!
+@w#
+"
+ "yD1v
+"
+ "yD1*
+08F
+h#h#+-GFO
+0ZFH#
+0FH#
+`h`F9F0F"
+
+
+ўH_"IKxDyD{Dl~( @"@9$F O
+
+~d^$J
+7p`
+ S 
+Ay<qi QIDE71qc
+
+Ҫ#
+F H
+E O
+  T'HFAF:F$JT
+"F+F#D 2C
+
+ 4
+ 
+[
+K
+K
+K
+F(F
+H7"IKxDyD{Dp
+F F%02F
+
+
+H"IKxDyD{D
+chS%
+F
+F
+FF
+
+A+ 0pGFF
+4DE
+JzD#hhB
+#FpGO0pG pG
+#'FpGF
+#ûFpGFPhLN~D0FsphH)FF 0FkhHxDe K(F I@m2{D
+hGI0FyD FXFpF (F|F(0F:FFq !}
+LF
+I|D`X
+ I
+LF
+I|D`X
+ I
+LF
+I|D`X
+ I
+LF
+I|D`X
+ I
+LF
+I|D`X
+ I
+LF
+I|D`X
+ I
+i!=FPl#
+
+J#h
+(
+(
+(
+JF
+B=nYF0F >F#
+cb #b
+, (
+$
+chh cj3(F!F!!F(F!< X4
+&
+VpFP@U
+!0
+)8F))L|DL|DL|DL|DI(FObyDT!F(FObN I(FObyD8@E
+#
++sI "yD"F@@
+,
+NDI(FyD"@I(FyD!F
+FA)7:a)W: @
+O
+IF
+(F
+! (
+
+!!@Oa4 F9F Fg(FA mi
+HF!F"
+# "Cp
+
+*&
+0F
+ F8O's
+O
+(F]F8@#
+Sp
+YF F "
+!
+D
+RD
+#Cp
+`rh
+`jh
+```^0F[(FX 8Fh0Fe(Fb
+
+`
+
+O3
+F
+F
+O3
+F
+O3
+O3
+F
+1FBF;F
+"
+ "yD
+
+ F2 FhBѿ
+ݶ
+I "yD 1
+
+Hm"IKxDyD{D(F!F F H
+(F 3(FBъ
+>
+FIyD
+FIyD
+0"J
+"B
+0"1
+  "
+ F
+F8@
+g@F
+Д#
+ ӕ#
+ PF)F ls\
+0
+
+Eё 
+hPF
+  PFFZF
+
+ZF
+RFgSDgf
+f>O
+ (
+PFYF :
+
+
+EHF!F :F$H"$I%KxDyD{Dc!:\pD T
+;BKEo
+ 4 @j#
+#
+
+@w
+"
+F
+`
+
+!
+
+R]
+
+0"f
+
+
+"=
+ 0"(@@
+"FP@
+"
+(
+"
+FPOs
+
+ FA=F(B F#
+
+!
+ 2
+
+
+mF( B #
+KD"{D8 F
+k
+HFtFP@
+ HF FHF9FF
+ FB
+A 
+ i*|J
+h)|I
+
+
+
+
+
+FpG pGF@hh8 F
+I`C` HyDxDF ` F,F  
+p
+Kh"{D F
+FFFh#
+ FFX#
+FFrFh#
+`F
+"G
+ p
+
+O
+Cx4B"!xbx4B" + F
+wh8FF3FYF FJFGO8NI F"yDG
+(F!Fh3hi
+I F"yDGf
+"F.
+ O
+Y
+IFFSЃh0FBBFG
+#F
+
+(FF1F&0F G D\D
+FKh+IyD0Y
+V#
+FPe#
+}#
+F#
+"$#xB
+P+U+
+d=ihFX@#
+@#
+(FF8@3
+`D
+rI *FyD F
+
+mJmI{DzDyD`
+ 
+`0ѫ`,a
+@
+"(F#xB
+ F7F Fkt(а(Ѡh)F0@hh)F0@0h)F0@P#
+`
+RH"RISKxDyD{D  FF8#
+I F$yDIyD
+FhDIF;F2F FyD8FH@I2F FyD+h Fh
+K"%F{Do 1F
+ O
+#
+"yD3
+K"%F{D= 1F
+I(FyDB  F
++ +PP<2O*0FiFF
+O CD~3 +~ؓy /TzOCy DzsO
+
+A
+zDzzO A Dy({/,%9,#Q{/) 9)$/OD DuБ{.)&B/ ټ9
+d!
+ '
+
++6
+O
+ O
+ O
+FFhըI "yD
+"yD
+#
+]iI FZFyD@
+j
+FF
+
+h
++h?
+Fh
+=<,E <$=$
+h[hhF F
+hhh#h++ `hQh@(`hQh@)ͰFF)0220F*FԱ##`F )F0F9F@#h53#`0FBO0MFh+@h+@h F@l8F YFp
+6HF hB0(F
+
+@h`Gh8F+ F9FFp(h08F K FLFXk6(hB0 F
+FF
+X
+  
+C!C
+  !
+ 
+3#
+K"{D6
+hhhpGpFm  pchc#h۹)F`hyLB5
+5
+Ѫ#
+#a cas (N~D3hsHxDF0`8#
+0F!FCh
+`#ih f!Fff
+ j@0sFo
+`f!F&f
+-
+"
+6E
+=  --1F " 67 =2xZ*-*+*0",FpZp3F-prxZpбx.)6=x609 )<3FB3F
+hFYC
+
+;hi`&
+
+j@Ā*j
+E 0 @3kBPi h;դDCB:h+hhhZX
+2BU
+ ` `` h 
+ coF
+ hnعaF#l FGF
+
+
+
+F f#g cg#
+#
+YFPF8F0  PFEPFEHFO
+
+S P  O
+ QF(hF
+
+
+EѺ
+
+
+@s
+A F(FB@
+F
+F
+% f!Fi
+FFF FF
+.i2"
+
+
+!F(Fqh4;`DE8F-CFFFF
+
+h|`0F)F`x` -AFFFFh$]F
+FP (F!F:F3FA@3
+Ch``ch
+[h`p FFF8 F %O5-I*FF
+FIF{DXX!F@>G
+FIF{DXX!F@G
+FIF{DXX!F@JF
+FIF{DXX!F@<F
+FIF{DXX!F@HF
+FIF{DXX!F@NF
+FIF{DXX!F@
+~F
+FIF{DXX!F@^F
+FIF{DXX!F@>F
+FIF{DXX!F@F
+FIF{DXX!F@ʾE
+FIF{DXX!F@E
+FIF{DXX!F@rE
+FIF{DXX!F@:&E
+FIF{DXX!F@*E
+FIF{DXX!F@иD
+FIF{DXX!F@ND
+FIF{DXX!F@.D
+FIF{DXX!F@C
+FIF{DXX!F@vC
+FIF{DXX!F@VC
+FIF{DXX!F@C
+FIF{DXX!F@
+B
+FIF{DXX!F@~B
+FIF{DXX!F@6B
+FIF{DXX!F@hA
+FIF{DXX!F@ @
+FIF{DXX!F@ƾ@
+FIF{DXX!F@ؾb@
+FIF{DXX!F@
+FIF{DXX!F@?
+FIF{DXX!F@"b?
+FIF{DXX!F@
+FIF{DXX!F@ڽ>
+HEZ(Ќ(#iC
+O2F(hQF FhBF
+
+(hMEL8F
+
+(FA=F(B p#
+p
+)F F1FF F(F|
+)F~ F1FnF FD(F|q
+)F. F1F,F F(F| 
+
+FXi a%d
+
+
+
+
+khh`hh!F[nh`hHAFM
+F F
+Թ Ah
+%@ F
+(hO
+ D=xhAF
+
+ F
+ DY .EY,:(F9h
+9)F8FFF
+h*
+!F0Fv*FF8F4BF0FZB@P(F hF F+a *X
+ѩx*xkx
+IyDIyD= Fih 
+FFUOs
+QFF FFHOs
+
+HFEHF )FJF{F Fb6@
+AбiA (FG [Fx9F
+- I F"yDP4%'F48x
+1F`BF
+]8F
+DJ#F
++x@+3
+F
+5phB!I FyDSh㱛h FIJF
+DP#
+q๱#
+1ع@
+"
+PFOs
+hOs
+
+
+ Z8`
+
+~
+#I0FyD0
+hB,+* #
+2Fh+h
+ (F6
+hhhpGh
+hhhpGChpGsFN~D3hsHxDF0`8T#
+KA"{DV(F F
+M}D(h
+FFF-
+ HFYF"@ *  $K"
+5 !F8ln)F048lTB
+
+
+
+I F "yD Fih 8
+zj
+8F`%JzD
+ F*FSF
+hhhpGhhpGjFAj
+j
+ѣ#
+M
+I{Dh}DyD X(4L,K
+܂(܁(6(Є(1(܉(Ћ()PEXE$jCjCjCjCjCjC 
+jC@jCjCsb8F EoK8FY
+#
+K {DP(F F2
+
+TZp#i3#a F@hh h F@{-EFhFF0 
+0$~D
+7NO
+
+ $
+
+MchCsc`!F F0
+*-F F@e
+ FH@o
+
+F  F F,F Fp0FhFYF
+F F,F F00FFo
+
+
+(F
+
+
+
+O
+K"
+H!{DxD
+K"
+H!{DxD
+FpGK{D`pG
+K"
+H@{DxD
+KB K" H@{DxD
+K"
+HOq{DxD
+K"
+H\!{DxD
+
+K"
+H@_{DxD
+
+KB
+K"
+H@
+!{DxD
+K"
+H@S!{DxD
+K"
+HOq{DxD
+FFk˱gk1FjB4B(/F:F kak8c
+BDckk<[B D 
+1[BD1F
+KB
+lKB
+أSB
+=FFO" #:F
+k)F*F
+|
+
+>| K" H@{DxD
+-ĕ
+bib
+MKB(F
+i%K"HOq{DxD(
+ch
+FFxK"H@!{DxD(
+liC
+ KBM(F 
+BC #iCE> j)`j&i9F j##afFP`j1F`FKB&iC#a KB3B3BK"HO4q{DxD(
+|KBMM(F
+N*h#hBGK
+0F
+9ch+i(F"!jFbj/i0 acikajb00BjkckB0K
+jjDZ~'K"'H@!{DxDH
+jjk
+!kc~K"HO?q{DxD
+kkFK"H@1{DxD
+𽕆
+%kb9F2FF K@#1 H*F{DxD
+q
+5KB F0
+ch+(K
+i[#jK
+j F3&kbAF:F>FxKOXqH2F{DxD(
+
+00
+A{DxD
+KL F
+L F0
+sB;BsBѨkik 
+A
+kKB F
+>ch+K
+)iZ#jK
+j F_# Fb9F2F@i
+ch+.K
+i
+j+K
+k1F*FFK"H@A{DxD
+
+5KB F0
+Pb LK"H@)Q{DxD
+
+zD
+ kjk(F
+,J(FX#F
+H
+;BKB3BK"H@a{DxD
+{
+H
+;BKB3BK"HOa{DxD
+ 9`{`l{ala
+H
+;BK"H@Oq{DxD
+LH"@aq{DxD
+2H"Oa{DxD
+(H"@jq{DxD
+
+K"
+H@q{DxD
+K"
+H@q{DxD
+` HxDA`hF
+` JzDh;`3@]h
+o
+p
+H xD
+
+o
+lK(F
+K"
+H!{DxD
+
+ո Q
+F{D
+HxD
+
+ F
+t
+
+Y
+ F
+F
+)
+ F
+
+
+F¿pF FF!F
+_
+0F!F x
+H LxD K" HA!{D
+[h
+
+a
+l2
+d@VH@E2UIVKxDyD{D P,
+BIKxDyD{D`O
+B lH@kIlKxDyD{DB3z&.gHOrfIgKxDyD{DS,8S<AhB`H@`I`KxDyD{DC`D<D,1FD,MWK{Dh
+"*I*KxDyD{DN3A F)FF
+V5p
+تBؑBبB3B
+; 
+ܸ#fи*fи @X.jи0и-@_U
+3h6C{j
+ 
+
+`H`"
+
+AEq0
+"
+" F<F F "
+#x( ! AqF FT  XF
+;<
+E
+պ
+
+o0$
+K9
+8
+
+ 1YE{ #
+;:
+q1JzD
+ȿ$,($+U
++,
+Jh01
+F
+  B FAF h
+8A0F_z1F O  #F {O O
+
+!MF
+ !7OE
+
+!FO !FK !FG
+ch"
+FKFFF;F
+:/FF
+74
+
+50_(
+ (
+( (а HBHApG pGA
+@6KD FF
+{h[B{`O6 JzDx0F
+B
+   0\>HFQ2F1)
+1
+0
+i
+
+
+h
+F F
+(FF F(F!F(F!F0F!F0F7F FhF!F!FjF+F>FɄ
+?
+  5 @˄
+ @7
+Y@
+'J#<#D@:M@
+
+v1F F
+!rp1t!
+s/
+qbFbXFO^p'2
+$mAvAA(
+18#*   
+Fi&YF F 
+?
+_
+eF  @(
+y
+#B@K@~
+K@B@
+#
+
+v\E
+
+E `@i@
+t\
+04#
+x
+E|l
+
+F@O@
+#F@O@g{@r@
+gF@O@gp
+g
+gg x
+@
+p@y@
+g 
+
+ 
+ 
+ 
+ #`@i@
+
+JF8FEE#T@]@
+
+ *F48FB,%,,cArb
+#(gr@{@&g(#t@}@&En^
+>7FF*FF Fa F>0F F O
+
+* * :PBPApG*XBXApG
+))
+.+/+:+=+?+$%\  B
+hB+++@hIh@hKhpG@hIhܼO0pG
++
+
+ih
+G FF@-G FF iF
+xh
+h Fh
+!C4( (#hC#hC#`
+
+F@0#
+E4GFWF0 F
+F F1FRFj1F
+AF:F(F3F
+#jb#B  F7 
+ #Aqjb
+gai
+C
+
+
+
+B:R
+BhR# BF@Ͽ3B
+FF(#hXh#h]`( 88
+FF(#hhp#h`( 888FhiB
+FoF(#hiY#ha( 888Fh
+
+( ( (а XBXApG pGa+A8(
+ڰG
+Z6:h
+8X'O (F OI  
+8 X_F
+h `:`9F"F3F""?GE
+6:h
+ JZEZF
+ BEBF
+3FE !FXF
+
+
+Fx1Bxp*(xxp:
+JO O;K?
+
+&PF1O3S`d
+
+D ;
+O
+   GYh
+O
+
+
+
+
+
+
+
+
+: ?
+E ;
+O
+DEмB
+*Fch
+
+FHhFhR<R `R,FhS` i` HiGp
+R#F
+O 
+T!8
+B
+T!
+   Bت
+
+ gE5  T%
+F
+Y%:F5vPFshB
+Y'
+&v
+OD
+ *F6MFW
+@BF
+FFF!F9FBF#FD1F
+FFF!F9FBF#F1F
+FFF!F9FBF#F;1F
+919Y::j ;;<ѐ<==>>>?W?o@ӆ@AmABFB2 2$2$3$3$4$4$5$ 5$6$'6$57$C7$L8$Y8$l9$R 2(j2(" 3(}3(^ 4(4(
+90%90::0N:0b;0s;0<0<0=0ˁ=0>0>0?0?0&@02@0GA0WA0nB0{B0C0C0D0ӂD0E0E0F0-F0JG0cG0qH0H0I0I0J0σJ0K0K0 L0L0)M0?M0MN0cN0uO0O0P0P0Q0΄Q0R0R0S0'S0?T0XT0qU0U0V0V0W0҅W0X0X0Y0Y0,Z0;Z0[[0j[0\0\0]0ӆ]0^0
+I@$I@;J@YJ@qK@K@L@L@M@M@N@N@AO@jO@P@P@Q@R@R@ S@3S@HT@kT@yU@U@V@V@W@W@X@#X@R Y@Y@ Z@Z@ [@̠[@ \@\@+!]@B]@!^@^@!_@_@!`@ޡ`@""a@Za@q"b@b@"c@c@"d@d@#e@e@5#f@_f@#g@g@#h@h@%$i@Si@t$j@j@&k@?k@U&l@Шl@(m@'m@U)n@n@)o@֩o@)p@p@
+B
+B'% B B' B B& Bߦ B&B'B;B]'B'B'!Bx'&B(+Bb+B(,B!,B
+2t2tE 3t~ 2x2x 3x3x 4xÉ4x 5x5x
+6x/6x?
+7x`7xs
+8x8x
+9x9x
+:xԊ:x
+;x;x <x<x5 =xJ=x
+>xR>x2|
+j
+{*
+ p'd'_0 lb^xGf
+kG|
+ෑJ .<Eɋyǐ:%;$%`7
+
+#ae
+NQ
+S zLv
+c" .I'T^jUi~wKVA\o&1m&l@PXMBeg4ѤO
+qN8qc~uks)
+K=c #.r!Ź(ɜ`幡̀AƘu]12D:mH
+ܚ}ts1s'0]Aۅ0a(6p1Ĺ
+Kџ;hd Y`Щ#~qc88GZSwq4 +Ӑep*yLqZ{jriၑljV@9/_jwcc 7
+([ ow0%0nV4ERј4|qP`tT "GRb4Z''Jivj֨^R
+b|}R
+!
+V(W&`Pqy?ʳ%dDg%gMRw4:Qm<H5gRR-:v?pO\09NA_[l{uB"XG[rgk0ZzQd |m);o 40aEW
+хRЀ;U.?gdqvZ/o4Byd8ւvÊb*}wKcWz1e
+
++0C:ͳ4yJQ"; tgN)܀b4h!
+
+!/wP“[klMJȮTQwą6h3ێ^(ZD"2kr.:+^$l6'W `'_
+
+
+
+
+
+
+
+
+
+
+ 
+
+
+
+  
+
+6J&,o])(|1
+`~zC|_cM7-X Hzj)s*H=
+ A2VPD׿غ' 9C#U k2JV"42\!7c#L"CuZGdDՁ
+
+!
+1
+9
+=
+I
+W
+a
+c
+g
+o
+u
+{
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    # ) - ? G Q W ] e o {   % / 1 A [ _ a m s w      ! + - = ? O U i y !'/5;KWY]kqu}  %)1CGMOSY[gk!%+9=?Qisy{ '-9EGY_cio #)+17AGS_qsy} '-7CEIOW]gim{!/3;EMYkoqu %)+7=ACI_egk} %39=EOUimou #'3A]cw{57;CIMUgqw}13EIQ[y!#-/5?MQik{}#%/17;AGOUYeks '+-3=EKOUs !#59?AKS]ciqu{} %+/=IMOmq 9IKQgu{   ' ) - 3 G M Q _ c e i w } !!5!A!I!O!Y![!_!s!}!!!!!!!!!!!!!!!!!" """!"%"+"1"9"K"O"c"g"s"u"""""""""""""""# # #'#)#/#3#5#E#Q#S#Y#c#k################$ $$$)$=$A$C$M$_$g$k$y$}$$$$$$$$$$$$$$$$$$%%%%'%1%=%C%K%O%s%%%%%%%%%%%%%%%%& &&&'&)&5&;&?&K&S&Y&e&i&o&{&&&&&&&&&&&&&&&''5'7'M'S'U'_'k'm's'w''''''''''''''(( ((((!(1(=(?(I(Q([(](a(g(u((((((((((((()))!)#)?)G)])e)i)o)u))))))))))))))))***%*/*O*U*_*e*k*m*s***************+'+1+3+=+?+K+O+U+i+m+o+{++++++++++++++ ,,,#,/,5,9,A,W,Y,i,w,,,,,,,,,,,,,,,,---;-C-I-M-a-e-q-----------... ...%.-.3.7.9.?.W.[.o.y................/ / //'/)/A/E/K/M/Q/W/o/u/}///////////////0 0#0)070;0U0Y0[0g0q0y0}000000000000000001 11!1'1-191C1E1K1]1a1g1m1s11111111111111 2222)252Y2]2c2k2o2u2w2{22222222222222223%3+3/353A3G3[3_3g3k3s3y33333333333334444474E4U4W4c4i4m44444444444444 555-535;5A5Q5e5o5q5w5{5}555555555555555666#6165676;6M6O6S6Y6a6k6m6666666666667777?7E7I7O7]7a7u7777777777778 8!83858A8G8K8S8W8_8e8o8q8}8888888888888899#9%9)9/9=9A9M9[9k9y9}999999999999999999::::':+:1:K:Q:[:c:g:m:y::::::::::::;;;!;#;-;9;E;S;Y;_;q;{;;;;;;;;;;;;;;;;;;< <<<<)<5<C<O<S<[<e<k<q<<<<<<<<<<<<<= ====!=-=3=7=?=C=o=s=u=y={=============> >>>>#>)>/>3>A>W>c>e>w>>>>>>>>>>>>>>>> ? ?7?;?=?A?Y?_?e?g?y?}????????????@!@%@+@1@?@C@E@]@a@g@m@@@@@@@@@@@@@ A AAA!A3A5A;A?AYAeAkAwA{AAAAAAAAAAABBBB#B)B/BCBSBUB[BaBsB}BBBBBBBBBBBBBBCCC%C'C3C7C9COCWCiCCCCCCCCCCCCCCCCC D DD#D)D;D?DEDKDQDSDYDeDoDDDDDDDDDDDDDDEEE+E1EAEIESEUEaEwE}EEEEEEEE
+FE{3`SNB. h'=6xJ{C^t9ۃoâ:J3L;`oJu}Yd3md5_sha1_init
+
+
+
+
+
+
+
+
+
+
+
+
+U U*H *H *H *H *H *H *H *H *H ++ +++<*H ++*H +*H  *H  *H  *H  *H  *H  *H  *H  *H  *H  `HB`HB`HB+*H + + *H  *H  +`HB`HB`HB`HB`HB`HB`HB `HB `HBUUUUUUUUU U#+UUeUdU*UU+U+UU U *H}B
+*H}B *H8+*H8+$+$*H *H  U%++++++++7+7+7
++7
++7
+`HBUUU+e*H  *H  *H  *H  *H  *H  *H 
+*H 
+*H 
+*H 
+*H 
+*H 
+*H  *H  *H  *H  *H  *H  *H *H ++*H  *H *H *H 
++7*H  U)U.++0++0+0+ **H*H8*H8*H *H *H  *H  
+*H   *H   *H   *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H  *H +
++ + +
++
++++++++++ +
++ + + +++++++++++++++++++++++++ +
++ +++++++++++ + + + + +
++
++
++
++
++ + + + +0+0+0+0+0+0+0+0+0+0+0 +0
++0 ++ U++++++++++++:X &,d &,d UU7*H +
++ +
+UHU$U7U8*H=*H=*H=*H=*H=*H=*H=*H=*H=*H=*H=*H=+7`He`He`He`He`He`He`He`He`He)`He*`He+`He,U*H8*H8*H8 & &, &,d &,d &,d &,d &,d
+ &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d
+ &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d &,d% &,d& &,d' &,d( &,d) &,d* &,d+ &,d- &,d. &,d/ &,d0 &,d1 &,d2 &,d3 &,d4 &,d5 &,d6 &,d7 &,d8U-+++++U,UAg*g*
+g*
+g* g*
+*H g+7+7U U+++
+*H=
++
+g+ g+ U
+*H  `He`He(
+******b*c**
+*H *H>+$+$+$+$+$+$+$+$+$ +$
++$ +$ +$ +$*H  +H?
+ SiJJ)
+A @ @ 4!R
+"A  !@h@2l
+ !!IP&  DA
+B`! E
+ @ ) 0
+@B
+
+
+
+
+$
+d
+
+
+
+
+
+o/Dyeڜ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ haead_ssl3_tag_len
+YG=6D^:հ5mp?LPiBl)+18{k Ψ`%cfc}Ҵ#ZoAsu0RhEf;S<iliQ҄(f mrae!2Hz8u=Jo=?Wk?Jnr{[t꾜zm@I*upNtu>MfO<lwGQcT;0*u)L|m8U[#(6A&>ʜz+`Edo\ MKZ&irb>^SOsvt:MևllDr>sN=ZuY,GA馗&js[ee[0bY26RJ!^ kZ̰ҋ_;>qf(?' Z#a -D\NxzWenqDCPgHZF HzEi׊ 돲mvX St-SnQ<}ukjR0EQ6 F?>7Ϙ^ZSw `@\⥭*k~#[ (-@L5
+&by M]1~ӡAa]Ta|IDt/z/]>)M7n(l X2o6W]v1Cyב1b<Ґ,V7{@X"y:1
+}ztFr=ܽh q2i;xWnnE7J QOg<#hdjE2!Y|PVi{X;MFmE4͉nJ{@'+fY{935/.hS\Rw't#>.. %%99=ng^fc(Nt]wv^b<W+6.9>um:15y5& kZ$m1K FJ+C7Ys<vx˦ y_( [QBoP=!^%]7G0pN CG*b]3bV$fCh;i|կ>!1 j^|EՌW_/OR|X_Q!/[j4mXKs]ė*lFBWP5<mlΰɢwk 70,^_Z0Nbe+V>MϺb_r(W=W}q1옐T G\r$d |s;UX0/bfU!%dqKvYŪgÆ%NK?D.j]SP3٨-}*?
+Ҽ3bSwCf'C_U*Lzy Xzte d0\UVE;><jRr[9 #JL] #j==}?#GqlWC3{bF}i8'o8ŮfsL!:6VudǫZ&BRCm 1$$o!ۃi;kT/\wa ;>OZ)1?6ⷜҼ/=rhR%LJ`~BdrF[Yqexternal/openssl/src/crypto/dh/dh.c
+
+
+
+
+
+
+
+
+*H 
+*H  external/openssl/src/crypto/pkcs8/pkcs8_x509.c
+
+
+
+
+%8sNext Update:
+
+
+ Revocation Date:
+
+
+
+
+
+
+
+
+
+
+ Public key OCSP hash:
+
+
+%12s%s
+
+ Not After :
+%*s
+
+%*s
+
+
+
+
+
+
+
+
+%*s
+
+%*s
+
+
+
+
+
+
+
+
+%*sZone: %s, User:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+*H=DB
+California10U Mountain View10U
+ Google, Inc.10U Android0 160104124053Z 351230124053Z0v1 0 UUS10U
+California10U
+ Google, Inc.10U Android1)0'U Android Software Attestation Key00  *H 
+California10U Mountain View10U
+ Google, Inc.10U Android0 160104123108Z 351230123108Z0c1 0 UUS10U
+California10U Mountain View10U
+ Google, Inc.10U Android00  *H 
+*H=01 0 UUS10U
+California10U Mountain View10U
+ Google, Inc.10U Android1301U *Android Keystore Software Attestation Root0 160111004609Z 260108004609Z01 0 UUS10U
+California10U
+ Google, Inc.10U Android1;09U 2Android Keystore Software Attestation Intermediate0Y0*H=*H=B
+*H=H
+*H=01 0 UUS10U
+California10U Mountain View10U
+ Google, Inc.10U Android1301U *Android Keystore Software Attestation Root0 160111004350Z 360106004350Z01 0 UUS10U
+California10U Mountain View10U
+ Google, Inc.10U Android1301U *Android Keystore Software Attestation Root0Y0*H=*H=B
+*H=G
+
+
+
+
+N
+V
+V
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+i
+i
+
+
+
+n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+A 
+ *D
+
+
+
+
+
+
+
+
+
+
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..3c8074b
--- a/dev/null
+++ b/Android.mk
@@ -0,0 +1,105 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+KEYMASTER_TA_BINARY := 8efb1e1c-37e5-4326-a5d68c33726c7d57
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := keystore.amlogic
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := module.cpp \
+ aml_keymaster_ipc.cpp \
+ aml_keymaster_device.cpp \
+
+LOCAL_C_INCLUDES := \
+ system/security/keystore \
+ $(LOCAL_PATH)/include \
+ system/keymaster/ \
+ system/keymaster/include \
+ external/boringssl/include \
+ vendor/amlogic/tdk/ca_export_arm/include \
+
+LOCAL_CFLAGS = -fvisibility=hidden -Wall -Werror
+LOCAL_CFLAGS += -DANDROID_BUILD
+ifeq ($(USE_SOFT_KEYSTORE), false)
+LOCAL_CFLAGS += -DUSE_HW_KEYMASTER
+endif
+LOCAL_SHARED_LIBRARIES := libcrypto \
+ liblog \
+ libkeystore_binder \
+ libteec \
+ libkeymaster_messages \
+ libkeymaster1 \
+ libteec
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_REQUIRED_MODULES := $(KEYMASTER_TA_BINARY)
+include $(BUILD_SHARED_LIBRARY)
+
+#####################################################
+# TA Library
+#####################################################
+include $(CLEAR_VARS)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := $(KEYMASTER_TA_BINARY)
+LOCAL_MODULE_SUFFIX := .ta
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/teetz
+LOCAL_SRC_FILES := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
+include $(BUILD_PREBUILT)
+
+
+# Unit tests for libkeymaster
+include $(CLEAR_VARS)
+LOCAL_MODULE := amlkeymaster_tests
+LOCAL_SRC_FILES := \
+ unit_test/android_keymaster_test.cpp \
+ unit_test/android_keymaster_test_utils.cpp \
+ unit_test/attestation_record.cpp
+# unit_test/attestation_record_test.cpp \
+ unit_test/authorization_set_test.cpp \
+# unit_test/android_keymaster_messages_test.cpp \
+ unit_test/hkdf_test.cpp \
+ unit_test/hmac_test.cpp \
+ unit_test/kdf1_test.cpp \
+ unit_test/kdf2_test.cpp \
+ unit_test/kdf_test.cpp \
+ unit_test/key_blob_test.cpp \
+ unit_test/keymaster_enforcement_test.cpp
+
+LOCAL_C_INCLUDES := \
+ external/boringssl/include \
+ system/keymaster/include \
+ system/keymaster \
+ system/security/softkeymaster/include
+
+LOCAL_CFLAGS = -Wall -Werror -Wunused -DKEYMASTER_NAME_TAGS
+LOCAL_CLANG_CFLAGS += -Wno-error=unused-const-variable -Wno-error=unused-private-field
+# TODO(krasin): reenable coverage flags, when the new Clang toolchain is released.
+# Currently, if enabled, these flags will cause an internal error in Clang.
+LOCAL_CLANG_CFLAGS += -fno-sanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp
+LOCAL_MODULE_TAGS := tests
+LOCAL_SHARED_LIBRARIES := \
+ libsoftkeymasterdevice \
+ libkeymaster_messages \
+ libkeymaster1 \
+ libcrypto \
+ libsoftkeymaster \
+ libhardware
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_NATIVE_TEST)
+
diff --git a/aml_keymaster_device.cpp b/aml_keymaster_device.cpp
new file mode 100644
index 0000000..df8b9b9
--- a/dev/null
+++ b/aml_keymaster_device.cpp
@@ -0,0 +1,997 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AmlKeymaster"
+
+#include <assert.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/sha.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <fstream>
+#include <iostream>
+#include <inttypes.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include <hardware/keymaster2.h>
+#include <keymaster/authorization_set.h>
+#include <log/log.h>
+#include <utils/String8.h>
+
+#include "keymaster_ipc.h"
+#include "aml_keymaster_device.h"
+#include "aml_keymaster_ipc.h"
+
+const uint32_t RECV_BUF_SIZE = 66 * 1024;
+const uint32_t SEND_BUF_SIZE = (66 * 1024 - sizeof(struct keymaster_message) - 16 /* tipc header */);
+
+const size_t kMaximumAttestationChallengeLength = 128;
+const size_t kMaximumFinishInputLength = 64 * 1024;
+
+namespace keymaster {
+
+static keymaster_error_t translate_error(TEEC_Result err) {
+ switch (err) {
+ case TEEC_SUCCESS:
+ return KM_ERROR_OK;
+ case TEEC_ERROR_ACCESS_DENIED:
+ return KM_ERROR_SECURE_HW_ACCESS_DENIED;
+
+ case TEEC_ERROR_CANCEL:
+ return KM_ERROR_OPERATION_CANCELLED;
+
+ case TEEC_ERROR_NOT_IMPLEMENTED:
+ return KM_ERROR_UNIMPLEMENTED;
+
+ case TEEC_ERROR_OUT_OF_MEMORY:
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ case TEEC_ERROR_BUSY:
+ return KM_ERROR_SECURE_HW_BUSY;
+
+ case TEEC_ERROR_COMMUNICATION:
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+
+ case TEEC_ERROR_SHORT_BUFFER:
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+
+ default:
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+}
+
+AmlKeymasterDevice::AmlKeymasterDevice(const hw_module_t* module) {
+ static_assert(std::is_standard_layout<AmlKeymasterDevice>::value,
+ "AmlKeymasterDevice must be standard layout");
+ static_assert(offsetof(AmlKeymasterDevice, device_) == 0,
+ "device_ must be the first member of AmlKeymasterDevice");
+ static_assert(offsetof(AmlKeymasterDevice, device_.common) == 0,
+ "common must be the first member of keymaster2_device");
+
+ ALOGI("Creating device");
+ ALOGD("Device address: %p", this);
+
+ device_ = {};
+
+ device_.common.tag = HARDWARE_DEVICE_TAG;
+ device_.common.version = 1;
+ device_.common.module = const_cast<hw_module_t*>(module);
+ device_.common.close = close_device;
+
+ device_.flags = KEYMASTER_SUPPORTS_EC;
+
+ device_.configure = configure;
+ device_.add_rng_entropy = add_rng_entropy;
+ device_.generate_key = generate_key;
+ device_.get_key_characteristics = get_key_characteristics;
+ device_.import_key = import_key;
+ device_.export_key = export_key;
+ device_.attest_key = attest_key;
+ device_.upgrade_key = upgrade_key;
+ device_.delete_key = delete_key;
+ device_.delete_all_keys = nullptr;
+ device_.begin = begin;
+ device_.update = update;
+ device_.finish = finish;
+ device_.abort = abort;
+
+ KM_context.fd = 0;
+ KM_session.ctx = NULL;
+ KM_session.session_id = 0;
+
+ TEEC_Result rc = aml_keymaster_connect(&KM_context, &KM_session);
+ error_ = translate_error(rc);
+ if (rc != TEEC_SUCCESS) {
+ ALOGE("failed to connect to keymaster (0x%x)", rc);
+ return;
+ }
+
+ GetVersionRequest version_request;
+ GetVersionResponse version_response;
+ error_ = Send(KM_GET_VERSION, version_request, &version_response);
+ if (error_ == KM_ERROR_INVALID_ARGUMENT || error_ == KM_ERROR_UNIMPLEMENTED) {
+ ALOGE("\"Bad parameters\" error on GetVersion call. Version 0 is not supported.");
+ error_ = KM_ERROR_VERSION_MISMATCH;
+ return;
+ }
+ message_version_ = MessageVersion(version_response.major_ver, version_response.minor_ver,
+ version_response.subminor_ver);
+ if (message_version_ < 0) {
+ // Can't translate version? Keymaster implementation must be newer.
+ ALOGE("Keymaster version %d.%d.%d not supported.", version_response.major_ver,
+ version_response.minor_ver, version_response.subminor_ver);
+ error_ = KM_ERROR_VERSION_MISMATCH;
+ }
+}
+
+AmlKeymasterDevice::~AmlKeymasterDevice() {
+ aml_keymaster_disconnect(&KM_context, &KM_session);
+}
+
+namespace {
+
+// Allocates a new buffer with malloc and copies the contents of |buffer| to it. Caller takes
+// ownership of the returned buffer.
+uint8_t* DuplicateBuffer(const uint8_t* buffer, size_t size) {
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(malloc(size));
+ if (tmp) {
+ memcpy(tmp, buffer, size);
+ }
+ return tmp;
+}
+
+template <typename RequestType>
+void AddClientAndAppData(const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ RequestType* request) {
+ request->additional_params.Clear();
+ if (client_id) {
+ request->additional_params.push_back(TAG_APPLICATION_ID, *client_id);
+ }
+ if (app_data) {
+ request->additional_params.push_back(TAG_APPLICATION_DATA, *app_data);
+ }
+}
+
+} // unnamed namespace
+
+struct tag_table_entry {
+ const char *name;
+ keymaster_tag_t tag;
+};
+
+static struct tag_table_entry tag_table[] =
+{
+ {"KM_TAG_PURPOSE", KM_TAG_PURPOSE},
+ {"KM_TAG_ALGORITHM", KM_TAG_ALGORITHM},
+ {"KM_TAG_KEY_SIZE", KM_TAG_KEY_SIZE},
+ {"KM_TAG_BLOCK_MODE", KM_TAG_BLOCK_MODE},
+ {"KM_TAG_DIGEST", KM_TAG_DIGEST},
+ {"KM_TAG_PADDING", KM_TAG_PADDING},
+ {"KM_TAG_CALLER_NONCE", KM_TAG_CALLER_NONCE},
+ {"KM_TAG_MIN_MAC_LENGTH", KM_TAG_MIN_MAC_LENGTH},
+ {"KM_TAG_RSA_PUBLIC_EXPONENT", KM_TAG_RSA_PUBLIC_EXPONENT},
+ {"KM_TAG_BLOB_USAGE_REQUIREMENTS", KM_TAG_BLOB_USAGE_REQUIREMENTS},
+ {"KM_TAG_BOOTLOADER_ONLY", KM_TAG_BOOTLOADER_ONLY},
+ {"KM_TAG_ACTIVE_DATETIME", KM_TAG_ACTIVE_DATETIME},
+ {"KM_TAG_ORIGINATION_EXPIRE_DATETIME", KM_TAG_ORIGINATION_EXPIRE_DATETIME},
+ {"KM_TAG_USAGE_EXPIRE_DATETIME",KM_TAG_USAGE_EXPIRE_DATETIME},
+ {"KM_TAG_MIN_SECONDS_BETWEEN_OPS",KM_TAG_MIN_SECONDS_BETWEEN_OPS},
+ {"KM_TAG_MAX_USES_PER_BOOT",KM_TAG_MAX_USES_PER_BOOT},
+ {"KM_TAG_ALL_USERS", KM_TAG_ALL_USERS},
+ {"KM_TAG_USER_ID", KM_TAG_USER_ID},
+ {"KM_TAG_USER_SECURE_ID",KM_TAG_USER_SECURE_ID},
+ {"KM_TAG_NO_AUTH_REQUIRED",KM_TAG_NO_AUTH_REQUIRED},
+ {"KM_TAG_USER_AUTH_TYPE ", KM_TAG_USER_AUTH_TYPE},
+ {"KM_TAG_AUTH_TIMEOUT ",KM_TAG_AUTH_TIMEOUT },
+ {"KM_TAG_ALL_APPLICATIONS ", KM_TAG_ALL_APPLICATIONS },
+ {"KM_TAG_APPLICATION_ID", KM_TAG_APPLICATION_ID},
+ {"KM_TAG_APPLICATION_DATA ",KM_TAG_APPLICATION_DATA },
+ {"KM_TAG_CREATION_DATETIME ",KM_TAG_CREATION_DATETIME },
+ {"KM_TAG_ORIGIN ", KM_TAG_ORIGIN },
+ {"KM_TAG_ROLLBACK_RESISTANT ", KM_TAG_ROLLBACK_RESISTANT },
+ {"KM_TAG_ROOT_OF_TRUST", KM_TAG_ROOT_OF_TRUST},
+ {"KM_TAG_ASSOCIATED_DATA ",KM_TAG_ASSOCIATED_DATA},
+ {"KM_TAG_NONCE", KM_TAG_NONCE},
+ {"KM_TAG_AUTH_TOKEN",KM_TAG_AUTH_TOKEN},
+ {"KM_TAG_MAC_LENGTH", KM_TAG_MAC_LENGTH},
+};
+
+const size_t tag_table_size = sizeof(tag_table)/sizeof(struct tag_table_entry);
+
+void AmlKeymasterDevice::dump_tag_item_value(const char *name, const keymaster_key_param_t* item)
+{
+ keymaster_tag_type_t type = KM_INVALID;
+
+ if (item) {
+ type = keymaster_tag_get_type(item->tag);
+ switch (type) {
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ ALOGI("%s: %" PRIx64 "\n", name, item->long_integer);
+ //printf("%s: %" PRIx64 "\n", name, item->long_integer);
+ break;
+ case KM_DATE:
+ ALOGI("%s: %" PRIx64 "\n", name, item->date_time);
+ //printf("%s: %" PRIx64 "\n", name, item->date_time);
+ break;
+ case KM_BYTES:
+ case KM_BIGNUM:
+ ALOGI("%s: blob data: %p, len: 0x%zx\n", name, item->blob.data, item->blob.data_length);
+ //printf("%s: blob data: %p, len: 0x%zx\n", name, item->blob.data, item->blob.data_length);
+ break;
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ ALOGI("%s: 0x%x\n", name, item->enumerated);
+ //printf("%s: 0x%x\n", name, item->enumerated);
+ break;
+ case KM_BOOL:
+ ALOGI("%s: 0x%x\n", name, item->boolean);
+ //printf("%s: 0x%x\n", name, item->boolean);
+ break;
+ case KM_UINT:
+ case KM_UINT_REP:
+ ALOGI("%s: 0x%x\n", name, item->integer);
+ //printf("%s: 0x%x\n", name, item->integer);
+ break;
+ default:
+ ALOGI("%s: invalid type: %d\n", name, type);
+ //printf("%s: invalid type: %d\n", name, type);
+ break;
+ }
+ }
+}
+
+void AmlKeymasterDevice::dump_tags(const char *name, const keymaster_key_param_set_t *params)
+{
+ size_t i = 0, j =0;
+ keymaster_key_param_t* item = params->params;
+
+ ALOGI("==== start dump %s, length (%zu)\n", name, params->length);
+ //printf("==== start dump %s, length (%zu)\n", name, params->length);
+ for (i = 0; i < params->length; i++) {
+ for (j = 0; j < tag_table_size; j++) {
+ if (tag_table[j].tag == item[i].tag) {
+ dump_tag_item_value(tag_table[j].name, &item[i]);
+ break;
+ }
+ }
+ }
+ ALOGI("==== end dump %s\n", name);
+ //printf("==== end dump %s\n", name);
+}
+
+keymaster_error_t AmlKeymasterDevice::configure(const keymaster_key_param_set_t* params) {
+ ALOGD("Device received configure\n");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+
+ AuthorizationSet params_copy(*params);
+ ConfigureRequest request;
+ if (!params_copy.GetTagValue(TAG_OS_VERSION, &request.os_version) ||
+ !params_copy.GetTagValue(TAG_OS_PATCHLEVEL, &request.os_patchlevel)) {
+ ALOGD("Configuration parameters must contain OS version and patch level");
+ return KM_ERROR_INVALID_ARGUMENT;
+ }
+
+ ConfigureResponse response;
+ keymaster_error_t err = Send(KM_CONFIGURE, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::add_rng_entropy(const uint8_t* data, size_t data_length) {
+ ALOGD("Device received add_rng_entropy");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ AddEntropyRequest request;
+ request.random_data.Reinitialize(data, data_length);
+ AddEntropyResponse response;
+ return Send(KM_ADD_RNG_ENTROPY, request, &response);
+}
+
+keymaster_error_t AmlKeymasterDevice::simple_bin2ascii(uint8_t *data, size_t data_length, char *out) {
+ for (size_t i = 0; i < data_length; i++) {
+ if (((data[i] & 0xf0) >> 4) < 0xa)
+ out[i * 2] = ((data[i] & 0xf0) >> 4) + 48;
+ else
+ out[i * 2] = ((data[i] & 0xf0) >> 4) + 87;
+ if ((data[i] & 0xf) < 0xa)
+ out[i * 2 + 1] = (data[i] & 0xf) + 48;
+ else
+ out[i * 2 + 1] = (data[i] & 0xf) + 87;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::store_encrypted_key(keymaster_key_blob_t* key_blob) {
+ SHA256_CTX sha256_ctx;
+ UniquePtr<uint8_t[]> hash_buf(new (std::nothrow) uint8_t[SHA256_DIGEST_LENGTH + 1]);
+ UniquePtr<char[]> name_buf(new (std::nothrow) char [SHA256_DIGEST_LENGTH * 2 + 1]);
+ std::ofstream out;
+ char name[256];
+
+ // Hash key data to create filename.
+ Eraser sha256_ctx_eraser(sha256_ctx);
+ memset(name_buf.get(), 0, SHA256_DIGEST_LENGTH * 2 + 1);
+ SHA256_Init(&sha256_ctx);
+ SHA256_Update(&sha256_ctx, key_blob->key_material, key_blob->key_material_size);
+ SHA256_Final(hash_buf.get(), &sha256_ctx);
+
+ simple_bin2ascii(hash_buf.get(), SHA256_DIGEST_LENGTH, name_buf.get());
+ name_buf[SHA256_DIGEST_LENGTH * 2] = '\0';
+ sprintf(name, "/data/tee/%s", name_buf.get());
+ out.open(name, std::ofstream::out | std::ofstream::binary);
+ if (out.is_open()) {
+ out.write((const char *)key_blob->key_material, key_blob->key_material_size);
+ out.close();
+ } else {
+ ALOGE("error opening key files\n");
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::delete_encrypted_key(const keymaster_key_blob_t* key_blob) {
+ SHA256_CTX sha256_ctx;
+ UniquePtr<uint8_t[]> hash_buf(new (std::nothrow) uint8_t[SHA256_DIGEST_LENGTH + 1]);
+ UniquePtr<char[]> name_buf(new (std::nothrow) char [SHA256_DIGEST_LENGTH * 2 + 1]);
+ std::ofstream out;
+ char name[256];
+ int result = -1;
+
+ // Hash key data to get filename.
+ Eraser sha256_ctx_eraser(sha256_ctx);
+ memset(name_buf.get(), 0, SHA256_DIGEST_LENGTH * 2 + 1);
+ SHA256_Init(&sha256_ctx);
+ SHA256_Update(&sha256_ctx, key_blob->key_material, key_blob->key_material_size);
+ SHA256_Final(hash_buf.get(), &sha256_ctx);
+
+ simple_bin2ascii(hash_buf.get(), SHA256_DIGEST_LENGTH, name_buf.get());
+ name_buf[SHA256_DIGEST_LENGTH * 2] = '\0';
+ sprintf(name, "/data/tee/%s", name_buf.get());
+ out.open(name, std::ofstream::out | std::ofstream::binary);
+ result = unlink(name);
+
+ if (!result) {
+ return KM_ERROR_OK;
+ } else {
+ ALOGE("cannot locate %s\n", name);
+ return KM_ERROR_INVALID_OPERATION_HANDLE;
+ }
+}
+
+keymaster_error_t AmlKeymasterDevice::generate_key(
+ const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received generate_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!key_blob) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ GenerateKeyRequest request(message_version_);
+ request.key_description.Reinitialize(*params);
+ request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+ GenerateKeyResponse response(message_version_);
+ keymaster_error_t err = Send(KM_GENERATE_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ key_blob->key_material_size = response.key_blob.key_material_size;
+ key_blob->key_material =
+ DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+ if (!key_blob->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ if (characteristics) {
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::get_key_characteristics(
+ const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data, keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received get_key_characteristics");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_blob || !key_blob->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!characteristics) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ GetKeyCharacteristicsRequest request;
+ request.SetKeyMaterial(*key_blob);
+ AddClientAndAppData(client_id, app_data, &request);
+
+ GetKeyCharacteristicsResponse response;
+ keymaster_error_t err = Send(KM_GET_KEY_CHARACTERISTICS, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::import_key(
+ const keymaster_key_param_set_t* params, keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics) {
+ ALOGD("Device received import_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!params || !key_data) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!key_blob) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ ImportKeyRequest request(message_version_);
+ request.key_description.Reinitialize(*params);
+ request.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+
+ dump_tags("import", &request.key_description);
+ request.key_format = key_format;
+ request.SetKeyMaterial(key_data->data, key_data->data_length);
+
+ ImportKeyResponse response(message_version_);
+ keymaster_error_t err = Send(KM_IMPORT_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ key_blob->key_material_size = response.key_blob.key_material_size;
+ key_blob->key_material =
+ DuplicateBuffer(response.key_blob.key_material, response.key_blob.key_material_size);
+ if (!key_blob->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ dump_tags("hw", &response.enforced);
+ dump_tags("sw", &response.unenforced);
+ if (characteristics) {
+ response.enforced.CopyToParamSet(&characteristics->hw_enforced);
+ response.unenforced.CopyToParamSet(&characteristics->sw_enforced);
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::export_key(keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ ALOGD("Device received export_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_to_export || !key_to_export->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!export_data) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ export_data->data = nullptr;
+ export_data->data_length = 0;
+
+ ExportKeyRequest request(message_version_);
+ request.key_format = export_format;
+ request.SetKeyMaterial(*key_to_export);
+ AddClientAndAppData(client_id, app_data, &request);
+
+ ExportKeyResponse response(message_version_);
+ keymaster_error_t err = Send(KM_EXPORT_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ export_data->data_length = response.key_data_length;
+ export_data->data = DuplicateBuffer(response.key_data, response.key_data_length);
+ if (!export_data->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::attest_key(const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain) {
+ ALOGD("Device received attest_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_to_attest || !attest_params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!cert_chain) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ cert_chain->entry_count = 0;
+ cert_chain->entries = nullptr;
+
+ AttestKeyRequest request;
+ request.SetKeyMaterial(*key_to_attest);
+ request.attest_params.Reinitialize(*attest_params);
+
+ keymaster_blob_t attestation_challenge = {};
+ request.attest_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge);
+ if (attestation_challenge.data_length > kMaximumAttestationChallengeLength) {
+ ALOGE("%zu-byte attestation challenge; only %zu bytes allowed",
+ attestation_challenge.data_length, kMaximumAttestationChallengeLength);
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+
+ AttestKeyResponse response;
+ keymaster_error_t err = Send(KM_ATTEST_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ // Allocate and clear storage for cert_chain.
+ keymaster_cert_chain_t& rsp_chain = response.certificate_chain;
+ cert_chain->entries = reinterpret_cast<keymaster_blob_t*>(
+ malloc(rsp_chain.entry_count * sizeof(*cert_chain->entries)));
+ if (!cert_chain->entries) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ cert_chain->entry_count = rsp_chain.entry_count;
+ for (keymaster_blob_t& entry : array_range(cert_chain->entries, cert_chain->entry_count)) {
+ entry = {};
+ }
+
+ // Copy cert_chain contents
+ size_t i = 0;
+ for (keymaster_blob_t& entry : array_range(rsp_chain.entries, rsp_chain.entry_count)) {
+ cert_chain->entries[i].data = DuplicateBuffer(entry.data, entry.data_length);
+ if (!cert_chain->entries[i].data) {
+ keymaster_free_cert_chain(cert_chain);
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ cert_chain->entries[i].data_length = entry.data_length;
+ ++i;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key) {
+ ALOGD("Device received upgrade_key");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key_to_upgrade || !upgrade_params) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!upgraded_key) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ UpgradeKeyRequest request;
+ request.SetKeyMaterial(*key_to_upgrade);
+ request.upgrade_params.Reinitialize(*upgrade_params);
+
+ UpgradeKeyResponse response;
+ keymaster_error_t err = Send(KM_UPGRADE_KEY, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ upgraded_key->key_material_size = response.upgraded_key.key_material_size;
+ upgraded_key->key_material = DuplicateBuffer(response.upgraded_key.key_material,
+ response.upgraded_key.key_material_size);
+ if (!upgraded_key->key_material) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::delete_key(const keymaster_key_blob_t* key) {
+ (void)key;
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::begin(keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ ALOGD("Device received begin");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!key || !key->key_material) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!operation_handle) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+
+ BeginOperationRequest request;
+ request.purpose = purpose;
+ request.SetKeyMaterial(*key);
+ request.additional_params.Reinitialize(*in_params);
+
+ BeginOperationResponse response;
+ keymaster_error_t err = Send(KM_BEGIN_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ *operation_handle = response.op_handle;
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ ALOGD("Device received update");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (!input) {
+ return KM_ERROR_UNEXPECTED_NULL_POINTER;
+ }
+ if (!input_consumed) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ UpdateOperationRequest request;
+ request.op_handle = operation_handle;
+ if (in_params) {
+ request.additional_params.Reinitialize(*in_params);
+ }
+ if (input && input->data_length > 0) {
+ size_t max_input_size = SEND_BUF_SIZE - request.SerializedSize();
+ request.input.Reinitialize(input->data, std::min(input->data_length, max_input_size));
+ }
+
+ UpdateOperationResponse response;
+ keymaster_error_t err = Send(KM_UPDATE_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ *input_consumed = response.input_consumed;
+ if (output) {
+ output->data_length = response.output.available_read();
+ output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+ if (!output->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ } else if (response.output.available_read() > 0) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ ALOGD("Device received finish");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+ if (input && input->data_length > kMaximumFinishInputLength) {
+ ALOGE("%zu-byte input to finish; only %zu bytes allowed",
+ input->data_length, kMaximumFinishInputLength);
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+
+ if (out_params) {
+ *out_params = {};
+ }
+ if (output) {
+ *output = {};
+ }
+
+ FinishOperationRequest request;
+ request.op_handle = operation_handle;
+ if (signature && signature->data && signature->data_length > 0) {
+ request.signature.Reinitialize(signature->data, signature->data_length);
+ }
+ if (input && input->data && input->data_length) {
+ request.input.Reinitialize(input->data, input->data_length);
+ }
+ if (in_params) {
+ request.additional_params.Reinitialize(*in_params);
+ }
+
+ FinishOperationResponse response;
+ keymaster_error_t err = Send(KM_FINISH_OPERATION, request, &response);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+
+ if (response.output_params.size() > 0) {
+ if (out_params) {
+ response.output_params.CopyToParamSet(out_params);
+ } else {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+ }
+ if (output) {
+ output->data_length = response.output.available_read();
+ output->data = DuplicateBuffer(response.output.peek_read(), output->data_length);
+ if (!output->data) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ } else if (response.output.available_read() > 0) {
+ return KM_ERROR_OUTPUT_PARAMETER_NULL;
+ }
+
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t AmlKeymasterDevice::abort(keymaster_operation_handle_t operation_handle) {
+ ALOGD("Device received abort");
+
+ if (error_ != KM_ERROR_OK) {
+ return error_;
+ }
+
+ AbortOperationRequest request;
+ request.op_handle = operation_handle;
+ AbortOperationResponse response;
+ return Send(KM_ABORT_OPERATION, request, &response);
+}
+
+hw_device_t* AmlKeymasterDevice::hw_device() {
+ return &device_.common;
+}
+
+static inline AmlKeymasterDevice* convert_device(const keymaster2_device_t* dev) {
+ return reinterpret_cast<AmlKeymasterDevice*>(const_cast<keymaster2_device_t*>(dev));
+}
+
+/* static */
+int AmlKeymasterDevice::close_device(hw_device_t* dev) {
+ delete reinterpret_cast<AmlKeymasterDevice*>(dev);
+ return 0;
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::configure(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params) {
+ return convert_device(dev)->configure(params);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::add_rng_entropy(const keymaster2_device_t* dev,
+ const uint8_t* data, size_t data_length) {
+ return convert_device(dev)->add_rng_entropy(data, data_length);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::generate_key(
+ const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->generate_key(params, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::get_key_characteristics(
+ const keymaster2_device_t* dev, const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id, const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->get_key_characteristics(key_blob, client_id, app_data,
+ characteristics);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::import_key(
+ const keymaster2_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics) {
+ return convert_device(dev)->import_key(params, key_format, key_data, key_blob, characteristics);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::export_key(const keymaster2_device_t* dev,
+ keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ return convert_device(dev)->export_key(export_format, key_to_export, client_id, app_data,
+ export_data);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::attest_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain) {
+ return convert_device(dev)->attest_key(key_to_attest, attest_params, cert_chain);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::upgrade_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key) {
+ return convert_device(dev)->upgrade_key(key_to_upgrade, upgrade_params, upgraded_key);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::delete_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_blob) {
+ return convert_device(dev)->delete_key(key_blob);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::begin(const keymaster2_device_t* dev,
+ keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ return convert_device(dev)->begin(purpose, key, in_params, out_params, operation_handle);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::update(
+ const keymaster2_device_t* dev, keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params, const keymaster_blob_t* input,
+ size_t* input_consumed, keymaster_key_param_set_t* out_params, keymaster_blob_t* output) {
+ return convert_device(dev)->update(operation_handle, in_params, input, input_consumed,
+ out_params, output);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::finish(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ return convert_device(dev)->finish(operation_handle, in_params, input, signature, out_params,
+ output);
+}
+
+/* static */
+keymaster_error_t AmlKeymasterDevice::abort(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle) {
+ return convert_device(dev)->abort(operation_handle);
+}
+
+keymaster_error_t AmlKeymasterDevice::Send(uint32_t command, const Serializable& req,
+ KeymasterResponse* rsp) {
+ uint32_t req_size = req.SerializedSize();
+
+ if (req_size > SEND_BUF_SIZE) {
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ }
+ //uint8_t send_buf[SEND_BUF_SIZE];
+ UniquePtr<uint8_t[]> send_buf (new (std::nothrow) uint8_t[SEND_BUF_SIZE]);
+ if (!send_buf.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ Eraser send_buf_eraser(send_buf.get(), SEND_BUF_SIZE);
+ req.Serialize(send_buf.get(), send_buf.get() + req_size);
+
+ // Send it
+ //uint8_t recv_buf[RECV_BUF_SIZE];
+ UniquePtr<uint8_t[]> recv_buf (new (std::nothrow) uint8_t[RECV_BUF_SIZE]);
+ if (!recv_buf.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ Eraser recv_buf_eraser(recv_buf.get(), RECV_BUF_SIZE);
+ uint32_t rsp_size = RECV_BUF_SIZE;
+ ALOGD("Sending cmd: %u with %d byte request\n", command, (int)req.SerializedSize());
+ TEEC_Result rc = aml_keymaster_call(&KM_session, command, send_buf.get(), req_size, recv_buf.get(), &rsp_size);
+ if (rc != TEEC_SUCCESS) {
+ return translate_error(rc);
+ } else {
+ ALOGD("Received %d byte response\n", rsp_size);
+ }
+
+ const keymaster_message* msg = (keymaster_message*)recv_buf.get();
+ const uint8_t* p = msg->payload;
+ if (!rsp->Deserialize(&p, p + rsp_size)) {
+ ALOGE("Error deserializing response of size %d\n", (int)rsp_size);
+ return KM_ERROR_UNKNOWN_ERROR;
+ } else if (rsp->error != KM_ERROR_OK) {
+ ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error);
+ return rsp->error;
+ }
+ return rsp->error;
+}
+
+} // namespace keymaster
diff --git a/aml_keymaster_device.h b/aml_keymaster_device.h
new file mode 100644
index 0000000..fb2a052
--- a/dev/null
+++ b/aml_keymaster_device.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AML_KEYMASTER_AML_KEYMASTER_DEVICE_H_
+#define AML_KEYMASTER_AML_KEYMASTER_DEVICE_H_
+
+#include <hardware/keymaster2.h>
+#include <keymaster/android_keymaster_messages.h>
+
+extern "C" {
+#include <tee_client_api.h>
+}
+
+namespace keymaster {
+
+/**
+ * Aml Keymaster device.
+ *
+ * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t
+ * and keymaster_device. This means it must remain a standard layout class (no virtual functions and
+ * no data members which aren't standard layout), and device_ must be the first data member.
+ * Assertions in the constructor validate compliance with those constraints.
+ */
+class AmlKeymasterDevice {
+ public:
+ /*
+ * These are the only symbols that will be exported by libamlkeymaster. All functionality
+ * can be reached via the function pointers in device_.
+ */
+ __attribute__((visibility("default"))) explicit AmlKeymasterDevice(const hw_module_t* module);
+ __attribute__((visibility("default"))) hw_device_t* hw_device();
+
+ ~AmlKeymasterDevice();
+
+ keymaster_error_t session_error() { return error_; }
+
+ keymaster_error_t configure(const keymaster_key_param_set_t* params);
+ keymaster_error_t add_rng_entropy(const uint8_t* data, size_t data_length);
+ keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ keymaster_error_t get_key_characteristics(const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* character);
+ keymaster_error_t import_key(const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ keymaster_error_t export_key(keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data, keymaster_blob_t* export_data);
+ keymaster_error_t attest_key(const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain);
+ keymaster_error_t upgrade_key(const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key);
+ keymaster_error_t delete_key(const keymaster_key_blob_t* key);
+ keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle);
+ keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ keymaster_error_t abort(keymaster_operation_handle_t operation_handle);
+
+ keymaster_error_t store_encrypted_key(keymaster_key_blob_t* key_blob);
+ keymaster_error_t delete_encrypted_key(const keymaster_key_blob_t* key_blob);
+ keymaster_error_t simple_bin2ascii(uint8_t *data, size_t data_length, char *out);
+ private:
+ keymaster_error_t Send(uint32_t command, const Serializable& request,
+ KeymasterResponse* response);
+
+ /*
+ * These static methods are the functions referenced through the function pointers in
+ * keymaster_device. They're all trivial wrappers.
+ */
+ static int close_device(hw_device_t* dev);
+ static keymaster_error_t configure(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params);
+ static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data,
+ size_t data_length);
+ static keymaster_error_t generate_key(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_blob,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t* character);
+ static keymaster_error_t import_key(const keymaster2_device_t* dev,
+ const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format,
+ const keymaster_blob_t* key_data,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t* characteristics);
+ static keymaster_error_t export_key(const keymaster2_device_t* dev,
+ keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data);
+ static keymaster_error_t attest_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_attest,
+ const keymaster_key_param_set_t* attest_params,
+ keymaster_cert_chain_t* cert_chain);
+ static keymaster_error_t upgrade_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key_to_upgrade,
+ const keymaster_key_param_set_t* upgrade_params,
+ keymaster_key_blob_t* upgraded_key);
+ static keymaster_error_t delete_key(const keymaster2_device_t* dev,
+ const keymaster_key_blob_t* key);
+ static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev);
+ static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose,
+ const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle);
+ static keymaster_error_t update(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ static keymaster_error_t finish(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params, keymaster_blob_t* output);
+ static keymaster_error_t abort(const keymaster2_device_t* dev,
+ keymaster_operation_handle_t operation_handle);
+
+ void dump_tags(const char *name, const keymaster_key_param_set_t *params);
+ void dump_tag_item_value(const char *name, const keymaster_key_param_t* item);
+
+ keymaster2_device_t device_;
+ keymaster_error_t error_;
+ int32_t message_version_;
+
+ TEEC_Context KM_context;
+ TEEC_Session KM_session;
+};
+
+} // namespace keymaster
+
+#endif // AML_KEYMASTER_AML_KEYMASTER_DEVICE_H_
diff --git a/aml_keymaster_ipc.cpp b/aml_keymaster_ipc.cpp
new file mode 100644
index 0000000..9745a0d
--- a/dev/null
+++ b/aml_keymaster_ipc.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AmlKeymaster"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+
+#include "keymaster_ipc.h"
+#include "aml_keymaster_ipc.h"
+
+TEEC_Result aml_keymaster_connect(TEEC_Context *c, TEEC_Session *s) {
+ TEEC_Result result = TEEC_SUCCESS;
+ TEEC_UUID svc_id = TA_KEYMASTER_UUID;
+ TEEC_Operation operation;
+ uint32_t err_origin;
+
+ memset(&operation, 0, sizeof(operation));
+
+ /* Initialize Context */
+ result = TEEC_InitializeContext(NULL, c);
+
+ if (result != TEEC_SUCCESS) {
+ ALOGD("TEEC_InitializeContext failed with error = %x\n", result);
+ return result;
+ }
+ /* Open Session */
+ result = TEEC_OpenSession(c, s, &svc_id,
+ TEEC_LOGIN_PUBLIC,
+ NULL, NULL,
+ &err_origin);
+
+ if (result != TEEC_SUCCESS) {
+ ALOGD("TEEC_Opensession failed with code 0x%x origin 0x%x",result, err_origin);
+ TEEC_FinalizeContext(c);
+ return result;
+ }
+ /* Init TA */
+ operation.paramTypes = TEEC_PARAM_TYPES(
+ TEEC_NONE, TEEC_NONE,
+ TEEC_NONE, TEEC_NONE);
+
+ result = TEEC_InvokeCommand(s,
+ KM_TA_INIT,
+ &operation,
+ NULL);
+
+ ALOGE("create id: %d, ctx: %p, ctx: %p\n", s->session_id, s->ctx, c);
+ return result;
+}
+
+TEEC_Result aml_keymaster_call(TEEC_Session *s, uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+ uint32_t* out_size) {
+ TEEC_Result res = TEEC_SUCCESS;
+ TEEC_Operation op;
+ uint32_t ret_orig;
+
+ memset(&op, 0, sizeof(op));
+
+ op.params[0].tmpref.buffer = in;
+ op.params[0].tmpref.size = in_size;
+ op.params[1].tmpref.buffer = out;
+ op.params[1].tmpref.size = *out_size;
+ op.paramTypes = TEEC_PARAM_TYPES(
+ TEEC_MEMREF_TEMP_INPUT,
+ TEEC_MEMREF_TEMP_OUTPUT,
+ TEEC_VALUE_OUTPUT,
+ TEEC_NONE);
+
+ ALOGE("id: %d, ctx: %p, cmd: %d\n", s->session_id, s->ctx, cmd);
+ res = TEEC_InvokeCommand(s, cmd, &op, &ret_orig);
+ if (res != TEEC_SUCCESS) {
+ ALOGE("Invoke cmd: %u failed with res(%x), ret_orig(%x), return(%d)\n",
+ cmd, res, ret_orig, op.params[2].value.a);
+ } else {
+ *out_size = op.params[2].value.b;
+ }
+
+ return res;
+}
+
+TEEC_Result aml_keymaster_disconnect(TEEC_Context *c, TEEC_Session *s) {
+ TEEC_Operation operation;
+ TEEC_Result result = TEEC_SUCCESS;
+
+ operation.paramTypes = TEEC_PARAM_TYPES(
+ TEEC_NONE, TEEC_NONE,
+ TEEC_NONE, TEEC_NONE);
+
+ result = TEEC_InvokeCommand(s,
+ KM_TA_TERM,
+ &operation,
+ NULL);
+
+ TEEC_CloseSession(s);
+ TEEC_FinalizeContext(c);
+
+ return result;
+}
diff --git a/aml_keymaster_ipc.h b/aml_keymaster_ipc.h
new file mode 100644
index 0000000..fa4a2da
--- a/dev/null
+++ b/aml_keymaster_ipc.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AML_KEYMASTER_AML_KEYMASTER_IPC_H_
+#define AML_KEYMASTER_AML_KEYMASTER_IPC_H_
+
+extern "C" {
+#include <tee_client_api.h>
+}
+
+__BEGIN_DECLS
+
+TEEC_Result aml_keymaster_connect(TEEC_Context *c, TEEC_Session *s);
+TEEC_Result aml_keymaster_call(TEEC_Session *s, uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
+ uint32_t* out_size);
+TEEC_Result aml_keymaster_disconnect(TEEC_Context *c, TEEC_Session *s);
+
+__END_DECLS
+
+#endif // AML_KEYMASTER_AML_KEYMASTER_IPC_H_
diff --git a/keymaster_ipc.h b/keymaster_ipc.h
new file mode 100644
index 0000000..1bbf2c7
--- a/dev/null
+++ b/keymaster_ipc.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+// clang-format off
+
+#define KEYMASTER_PORT "com.android.trusty.keymaster"
+#define KEYMASTER_MAX_BUFFER_LENGTH 4096
+
+/* This UUID is generated with uuidgen
+ the ITU-T UUID generator at http://www.itu.int/ITU-T/asn1/uuid.html */
+#define TA_KEYMASTER_UUID {0x8efb1e1c, 0x37e5, 0x4326, \
+ { 0xa5, 0xd6, 0x8c, 0x33, 0x72, 0x6c, 0x7d, 0x57} }
+
+// Commands
+enum keymaster_command : uint32_t {
+ KEYMASTER_RESP_BIT = 1,
+ KEYMASTER_STOP_BIT = 2,
+ KEYMASTER_REQ_SHIFT = 2,
+
+ KM_GENERATE_KEY = (0 << KEYMASTER_REQ_SHIFT),
+ KM_BEGIN_OPERATION = (1 << KEYMASTER_REQ_SHIFT),
+ KM_UPDATE_OPERATION = (2 << KEYMASTER_REQ_SHIFT),
+ KM_FINISH_OPERATION = (3 << KEYMASTER_REQ_SHIFT),
+ KM_ABORT_OPERATION = (4 << KEYMASTER_REQ_SHIFT),
+ KM_IMPORT_KEY = (5 << KEYMASTER_REQ_SHIFT),
+ KM_EXPORT_KEY = (6 << KEYMASTER_REQ_SHIFT),
+ KM_GET_VERSION = (7 << KEYMASTER_REQ_SHIFT),
+ KM_ADD_RNG_ENTROPY = (8 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_ALGORITHMS = (9 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_BLOCK_MODES = (10 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_PADDING_MODES = (11 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_DIGESTS = (12 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),
+ KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),
+ KM_GET_KEY_CHARACTERISTICS = (15 << KEYMASTER_REQ_SHIFT),
+ KM_ATTEST_KEY = (16 << KEYMASTER_REQ_SHIFT),
+ KM_UPGRADE_KEY = (17 << KEYMASTER_REQ_SHIFT),
+ KM_CONFIGURE = (18 << KEYMASTER_REQ_SHIFT),
+
+ KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_ATTESTATION_KEY = (0x2000 << KEYMASTER_REQ_SHIFT),
+ KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT),
+
+ KM_TA_INIT = (0x10000 << KEYMASTER_REQ_SHIFT),
+ KM_TA_TERM = (0x10001 << KEYMASTER_REQ_SHIFT),
+};
+
+#ifdef __ANDROID__
+
+/**
+ * keymaster_message - Serial header for communicating with KM server
+ * @cmd: the command, one of keymaster_command.
+ * @payload: start of the serialized command specific payload
+ */
+struct keymaster_message {
+ uint32_t cmd;
+ uint8_t payload[0];
+};
+
+#endif
diff --git a/module.cpp b/module.cpp
new file mode 100644
index 0000000..b9ee918
--- a/dev/null
+++ b/module.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <string.h>
+
+#include <hardware/hardware.h>
+#include <hardware/keymaster0.h>
+
+#include "aml_keymaster_device.h"
+
+using keymaster::AmlKeymasterDevice;
+
+/*
+ * Generic device handling
+ */
+static int aml_keymaster_open(const hw_module_t* module, const char* name, hw_device_t** device) {
+ if (strcmp(name, KEYSTORE_KEYMASTER) != 0) {
+ return -EINVAL;
+ }
+
+ AmlKeymasterDevice* dev = new AmlKeymasterDevice(module);
+ if (dev == NULL) {
+ return -ENOMEM;
+ }
+ *device = dev->hw_device();
+ // Do not delete dev; it will get cleaned up when the caller calls device->close(), and must
+ // exist until then.
+ return 0;
+}
+
+static struct hw_module_methods_t keystore_module_methods = {
+ .open = aml_keymaster_open,
+};
+
+struct keystore_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+ .common =
+ {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = KEYMASTER_MODULE_API_VERSION_2_0,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = KEYSTORE_HARDWARE_MODULE_ID,
+ .name = "Amlogic Keymaster HAL",
+ .author = "Amlogic Inc.",
+ .methods = &keystore_module_methods,
+ .dso = 0,
+ .reserved = {},
+ },
+};
diff --git a/unit_test/android_keymaster_messages_test.cpp b/unit_test/android_keymaster_messages_test.cpp
new file mode 100644
index 0000000..f7e65bc
--- a/dev/null
+++ b/unit_test/android_keymaster_messages_test.cpp
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <UniquePtr.h>
+
+#include <gtest/gtest.h>
+
+#include <keymaster/android_keymaster.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+
+#include "android_keymaster_test_utils.h"
+
+namespace keymaster {
+namespace test {
+
+/**
+ * Serialize and deserialize a message.
+ */
+template <typename Message>
+Message* round_trip(int32_t ver, const Message& message, size_t expected_size) {
+ size_t size = message.SerializedSize();
+ EXPECT_EQ(expected_size, size);
+ if (size == 0)
+ return NULL;
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, message.Serialize(buf.get(), buf.get() + size));
+
+ Message* deserialized = new Message(ver);
+ const uint8_t* p = buf.get();
+ EXPECT_TRUE(deserialized->Deserialize(&p, p + size));
+ EXPECT_EQ((ptrdiff_t)size, p - buf.get());
+ return deserialized;
+}
+
+struct EmptyKeymasterResponse : public KeymasterResponse {
+ explicit EmptyKeymasterResponse(int32_t ver) : KeymasterResponse(ver) {}
+ size_t NonErrorSerializedSize() const { return 1; }
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t* /* end */) const {
+ *buf++ = 0;
+ return buf;
+ }
+ bool NonErrorDeserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ if (*buf_ptr >= end)
+ return false;
+ EXPECT_EQ(0, **buf_ptr);
+ (*buf_ptr)++;
+ return true;
+ }
+};
+
+TEST(RoundTrip, EmptyKeymasterResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ EmptyKeymasterResponse msg(ver);
+ msg.error = KM_ERROR_OK;
+
+ UniquePtr<EmptyKeymasterResponse> deserialized(round_trip(ver, msg, 5));
+ }
+}
+
+TEST(RoundTrip, EmptyKeymasterResponseError) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ EmptyKeymasterResponse msg(ver);
+ msg.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ UniquePtr<EmptyKeymasterResponse> deserialized(round_trip(ver, msg, 4));
+ }
+}
+
+TEST(RoundTrip, SupportedByAlgorithmRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ SupportedByAlgorithmRequest req(ver);
+ req.algorithm = KM_ALGORITHM_EC;
+
+ UniquePtr<SupportedByAlgorithmRequest> deserialized(round_trip(ver, req, 4));
+ EXPECT_EQ(KM_ALGORITHM_EC, deserialized->algorithm);
+ }
+}
+
+TEST(RoundTrip, SupportedByAlgorithmAndPurposeRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ SupportedByAlgorithmAndPurposeRequest req(ver);
+ req.algorithm = KM_ALGORITHM_EC;
+ req.purpose = KM_PURPOSE_DECRYPT;
+
+ UniquePtr<SupportedByAlgorithmAndPurposeRequest> deserialized(round_trip(ver, req, 8));
+ EXPECT_EQ(KM_ALGORITHM_EC, deserialized->algorithm);
+ EXPECT_EQ(KM_PURPOSE_DECRYPT, deserialized->purpose);
+ }
+}
+
+TEST(RoundTrip, SupportedResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ SupportedResponse<keymaster_digest_t> rsp(ver);
+ keymaster_digest_t digests[] = {KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1};
+ rsp.error = KM_ERROR_OK;
+ rsp.SetResults(digests);
+
+ UniquePtr<SupportedResponse<keymaster_digest_t>> deserialized(round_trip(ver, rsp, 20));
+ EXPECT_EQ(array_length(digests), deserialized->results_length);
+ EXPECT_EQ(0, memcmp(deserialized->results, digests, array_size(digests)));
+ }
+}
+
+static keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD),
+ Authorization(TAG_APPLICATION_ID, "app_id", 6),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+};
+uint8_t TEST_DATA[] = "a key blob";
+
+TEST(RoundTrip, GenerateKeyRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ GenerateKeyRequest req(ver);
+ req.key_description.Reinitialize(params, array_length(params));
+ UniquePtr<GenerateKeyRequest> deserialized(round_trip(ver, req, 78));
+ EXPECT_EQ(deserialized->key_description, req.key_description);
+ }
+}
+
+TEST(RoundTrip, GenerateKeyResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ GenerateKeyResponse rsp(ver);
+ rsp.error = KM_ERROR_OK;
+ rsp.key_blob.key_material = dup_array(TEST_DATA);
+ rsp.key_blob.key_material_size = array_length(TEST_DATA);
+ rsp.enforced.Reinitialize(params, array_length(params));
+
+ UniquePtr<GenerateKeyResponse> deserialized(round_trip(ver, rsp, 109));
+ EXPECT_EQ(KM_ERROR_OK, deserialized->error);
+ EXPECT_EQ(deserialized->enforced, rsp.enforced);
+ EXPECT_EQ(deserialized->unenforced, rsp.unenforced);
+ }
+}
+
+TEST(RoundTrip, GenerateKeyResponseTestError) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ GenerateKeyResponse rsp(ver);
+ rsp.error = KM_ERROR_UNSUPPORTED_ALGORITHM;
+ rsp.key_blob.key_material = dup_array(TEST_DATA);
+ rsp.key_blob.key_material_size = array_length(TEST_DATA);
+ rsp.enforced.Reinitialize(params, array_length(params));
+
+ UniquePtr<GenerateKeyResponse> deserialized(round_trip(ver, rsp, 4));
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_ALGORITHM, deserialized->error);
+ EXPECT_EQ(0U, deserialized->enforced.size());
+ EXPECT_EQ(0U, deserialized->unenforced.size());
+ EXPECT_EQ(0U, deserialized->key_blob.key_material_size);
+ }
+}
+
+TEST(RoundTrip, GetKeyCharacteristicsRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ GetKeyCharacteristicsRequest req(ver);
+ req.additional_params.Reinitialize(params, array_length(params));
+ req.SetKeyMaterial("foo", 3);
+
+ UniquePtr<GetKeyCharacteristicsRequest> deserialized(round_trip(ver, req, 85));
+ EXPECT_EQ(7U, deserialized->additional_params.size());
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp(deserialized->key_blob.key_material, "foo", 3));
+ }
+}
+
+TEST(RoundTrip, GetKeyCharacteristicsResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ GetKeyCharacteristicsResponse msg(ver);
+ msg.error = KM_ERROR_OK;
+ msg.enforced.Reinitialize(params, array_length(params));
+ msg.unenforced.Reinitialize(params, array_length(params));
+
+ UniquePtr<GetKeyCharacteristicsResponse> deserialized(round_trip(ver, msg, 160));
+ EXPECT_EQ(msg.enforced, deserialized->enforced);
+ EXPECT_EQ(msg.unenforced, deserialized->unenforced);
+ }
+}
+
+TEST(RoundTrip, BeginOperationRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ BeginOperationRequest msg(ver);
+ msg.purpose = KM_PURPOSE_SIGN;
+ msg.SetKeyMaterial("foo", 3);
+ msg.additional_params.Reinitialize(params, array_length(params));
+
+ UniquePtr<BeginOperationRequest> deserialized(round_trip(ver, msg, 89));
+ EXPECT_EQ(KM_PURPOSE_SIGN, deserialized->purpose);
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp(deserialized->key_blob.key_material, "foo", 3));
+ EXPECT_EQ(msg.additional_params, deserialized->additional_params);
+ }
+}
+
+TEST(RoundTrip, BeginOperationResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ BeginOperationResponse msg(ver);
+ msg.error = KM_ERROR_OK;
+ msg.op_handle = 0xDEADBEEF;
+ msg.output_params.push_back(Authorization(TAG_NONCE, "foo", 3));
+
+ UniquePtr<BeginOperationResponse> deserialized;
+ switch (ver) {
+ case 0:
+ deserialized.reset(round_trip(ver, msg, 12));
+ break;
+ case 1:
+ case 2:
+ case 3:
+ deserialized.reset(round_trip(ver, msg, 39));
+ break;
+ default:
+ FAIL();
+ }
+
+ EXPECT_EQ(KM_ERROR_OK, deserialized->error);
+ EXPECT_EQ(0xDEADBEEF, deserialized->op_handle);
+
+ switch (ver) {
+ case 0:
+ EXPECT_EQ(0U, deserialized->output_params.size());
+ break;
+ case 1:
+ case 2:
+ case 3:
+ EXPECT_EQ(msg.output_params, deserialized->output_params);
+ break;
+ default:
+ FAIL();
+ }
+ }
+}
+
+TEST(RoundTrip, BeginOperationResponseError) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ BeginOperationResponse msg(ver);
+ msg.error = KM_ERROR_INVALID_OPERATION_HANDLE;
+ msg.op_handle = 0xDEADBEEF;
+
+ UniquePtr<BeginOperationResponse> deserialized(round_trip(ver, msg, 4));
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, deserialized->error);
+ }
+}
+
+TEST(RoundTrip, UpdateOperationRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ UpdateOperationRequest msg(ver);
+ msg.op_handle = 0xDEADBEEF;
+ msg.input.Reinitialize("foo", 3);
+
+ UniquePtr<UpdateOperationRequest> deserialized;
+ switch (ver) {
+ case 0:
+ deserialized.reset(round_trip(ver, msg, 15));
+ break;
+ case 1:
+ case 2:
+ case 3:
+ deserialized.reset(round_trip(ver, msg, 27));
+ break;
+ default:
+ FAIL();
+ }
+ EXPECT_EQ(3U, deserialized->input.available_read());
+ EXPECT_EQ(0, memcmp(deserialized->input.peek_read(), "foo", 3));
+ }
+}
+
+TEST(RoundTrip, UpdateOperationResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ UpdateOperationResponse msg(ver);
+ msg.error = KM_ERROR_OK;
+ msg.output.Reinitialize("foo", 3);
+ msg.input_consumed = 99;
+ msg.output_params.push_back(TAG_APPLICATION_ID, "bar", 3);
+
+ UniquePtr<UpdateOperationResponse> deserialized;
+ switch (ver) {
+ case 0:
+ deserialized.reset(round_trip(ver, msg, 11));
+ break;
+ case 1:
+ deserialized.reset(round_trip(ver, msg, 15));
+ break;
+ case 2:
+ case 3:
+ deserialized.reset(round_trip(ver, msg, 42));
+ break;
+ default:
+ FAIL();
+ }
+ EXPECT_EQ(KM_ERROR_OK, deserialized->error);
+ EXPECT_EQ(3U, deserialized->output.available_read());
+ EXPECT_EQ(0, memcmp(deserialized->output.peek_read(), "foo", 3));
+
+ switch (ver) {
+ case 0:
+ EXPECT_EQ(0U, deserialized->input_consumed);
+ break;
+ case 1:
+ EXPECT_EQ(99U, deserialized->input_consumed);
+ break;
+ case 2:
+ case 3:
+ EXPECT_EQ(99U, deserialized->input_consumed);
+ EXPECT_EQ(1U, deserialized->output_params.size());
+ break;
+ default:
+ FAIL();
+ }
+ }
+}
+
+TEST(RoundTrip, FinishOperationRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ FinishOperationRequest msg(ver);
+ msg.op_handle = 0xDEADBEEF;
+ msg.signature.Reinitialize("bar", 3);
+ msg.input.Reinitialize("baz", 3);
+
+ UniquePtr<FinishOperationRequest> deserialized;
+ switch (ver) {
+ case 0:
+ deserialized.reset(round_trip(ver, msg, 15));
+ break;
+ case 1:
+ case 2:
+ deserialized.reset(round_trip(ver, msg, 27));
+ break;
+ case 3:
+ deserialized.reset(round_trip(ver, msg, 34));
+ break;
+ default:
+ FAIL();
+ }
+ EXPECT_EQ(0xDEADBEEF, deserialized->op_handle);
+ EXPECT_EQ(3U, deserialized->signature.available_read());
+ EXPECT_EQ(0, memcmp(deserialized->signature.peek_read(), "bar", 3));
+ }
+}
+
+TEST(Round_Trip, FinishOperationResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ FinishOperationResponse msg(ver);
+ msg.error = KM_ERROR_OK;
+ msg.output.Reinitialize("foo", 3);
+
+ UniquePtr<FinishOperationResponse> deserialized;
+ switch (ver) {
+ case 0:
+ case 1:
+ deserialized.reset(round_trip(ver, msg, 11));
+ break;
+ case 2:
+ case 3:
+ deserialized.reset(round_trip(ver, msg, 23));
+ break;
+ default:
+ FAIL();
+ }
+ EXPECT_EQ(msg.error, deserialized->error);
+ EXPECT_EQ(msg.output.available_read(), deserialized->output.available_read());
+ EXPECT_EQ(0, memcmp(msg.output.peek_read(), deserialized->output.peek_read(),
+ msg.output.available_read()));
+ }
+}
+
+TEST(RoundTrip, ImportKeyRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ ImportKeyRequest msg(ver);
+ msg.key_description.Reinitialize(params, array_length(params));
+ msg.key_format = KM_KEY_FORMAT_X509;
+ msg.SetKeyMaterial("foo", 3);
+
+ UniquePtr<ImportKeyRequest> deserialized(round_trip(ver, msg, 89));
+ EXPECT_EQ(msg.key_description, deserialized->key_description);
+ EXPECT_EQ(msg.key_format, deserialized->key_format);
+ EXPECT_EQ(msg.key_data_length, deserialized->key_data_length);
+ EXPECT_EQ(0, memcmp(msg.key_data, deserialized->key_data, msg.key_data_length));
+ }
+}
+
+TEST(RoundTrip, ImportKeyResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ ImportKeyResponse msg(ver);
+ msg.error = KM_ERROR_OK;
+ msg.SetKeyMaterial("foo", 3);
+ msg.enforced.Reinitialize(params, array_length(params));
+ msg.unenforced.Reinitialize(params, array_length(params));
+
+ UniquePtr<ImportKeyResponse> deserialized(round_trip(ver, msg, 167));
+ EXPECT_EQ(msg.error, deserialized->error);
+ EXPECT_EQ(msg.key_blob.key_material_size, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp(msg.key_blob.key_material, deserialized->key_blob.key_material,
+ msg.key_blob.key_material_size));
+ EXPECT_EQ(msg.enforced, deserialized->enforced);
+ EXPECT_EQ(msg.unenforced, deserialized->unenforced);
+ }
+}
+
+TEST(RoundTrip, ExportKeyRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ ExportKeyRequest msg(ver);
+ msg.additional_params.Reinitialize(params, array_length(params));
+ msg.key_format = KM_KEY_FORMAT_X509;
+ msg.SetKeyMaterial("foo", 3);
+
+ UniquePtr<ExportKeyRequest> deserialized(round_trip(ver, msg, 89));
+ EXPECT_EQ(msg.additional_params, deserialized->additional_params);
+ EXPECT_EQ(msg.key_format, deserialized->key_format);
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp("foo", deserialized->key_blob.key_material, 3));
+ }
+}
+
+TEST(RoundTrip, ExportKeyResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ ExportKeyResponse msg(ver);
+ msg.error = KM_ERROR_OK;
+ msg.SetKeyMaterial("foo", 3);
+
+ UniquePtr<ExportKeyResponse> deserialized(round_trip(ver, msg, 11));
+ EXPECT_EQ(3U, deserialized->key_data_length);
+ EXPECT_EQ(0, memcmp("foo", deserialized->key_data, 3));
+ }
+}
+
+TEST(RoundTrip, DeleteKeyRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ DeleteKeyRequest msg(ver);
+ msg.SetKeyMaterial("foo", 3);
+
+ UniquePtr<DeleteKeyRequest> deserialized(round_trip(ver, msg, 7));
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp("foo", deserialized->key_blob.key_material, 3));
+ }
+}
+
+TEST(RoundTrip, DeleteKeyResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ DeleteKeyResponse msg(ver);
+ UniquePtr<DeleteKeyResponse> deserialized(round_trip(ver, msg, 4));
+ }
+}
+
+TEST(RoundTrip, DeleteAllKeysRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ DeleteAllKeysRequest msg(ver);
+ UniquePtr<DeleteAllKeysRequest> deserialized(round_trip(ver, msg, 0));
+ }
+}
+
+TEST(RoundTrip, DeleteAllKeysResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ DeleteAllKeysResponse msg(ver);
+ UniquePtr<DeleteAllKeysResponse> deserialized(round_trip(ver, msg, 4));
+ }
+}
+
+TEST(RoundTrip, GetVersionRequest) {
+ GetVersionRequest msg;
+
+ size_t size = msg.SerializedSize();
+ ASSERT_EQ(0U, size);
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, msg.Serialize(buf.get(), buf.get() + size));
+
+ GetVersionRequest deserialized;
+ const uint8_t* p = buf.get();
+ EXPECT_TRUE(deserialized.Deserialize(&p, p + size));
+ EXPECT_EQ((ptrdiff_t)size, p - buf.get());
+}
+
+TEST(RoundTrip, GetVersionResponse) {
+ GetVersionResponse msg;
+ msg.error = KM_ERROR_OK;
+ msg.major_ver = 9;
+ msg.minor_ver = 98;
+ msg.subminor_ver = 38;
+
+ size_t size = msg.SerializedSize();
+ ASSERT_EQ(7U, size);
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, msg.Serialize(buf.get(), buf.get() + size));
+
+ GetVersionResponse deserialized;
+ const uint8_t* p = buf.get();
+ EXPECT_TRUE(deserialized.Deserialize(&p, p + size));
+ EXPECT_EQ((ptrdiff_t)size, p - buf.get());
+ EXPECT_EQ(9U, msg.major_ver);
+ EXPECT_EQ(98U, msg.minor_ver);
+ EXPECT_EQ(38U, msg.subminor_ver);
+}
+
+TEST(RoundTrip, ConfigureRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ ConfigureRequest req(ver);
+ req.os_version = 1;
+ req.os_patchlevel = 1;
+
+ UniquePtr<ConfigureRequest> deserialized(round_trip(ver, req, 8));
+ EXPECT_EQ(deserialized->os_version, req.os_version);
+ EXPECT_EQ(deserialized->os_patchlevel, req.os_patchlevel);
+ }
+}
+
+TEST(RoundTrip, ConfigureResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ ConfigureResponse rsp(ver);
+ UniquePtr<ConfigureResponse> deserialized(round_trip(ver, rsp, 4));
+ }
+}
+
+TEST(RoundTrip, AddEntropyRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ AddEntropyRequest msg(ver);
+ msg.random_data.Reinitialize("foo", 3);
+
+ UniquePtr<AddEntropyRequest> deserialized(round_trip(ver, msg, 7));
+ EXPECT_EQ(3U, deserialized->random_data.available_read());
+ EXPECT_EQ(0, memcmp("foo", deserialized->random_data.peek_read(), 3));
+ }
+}
+
+TEST(RoundTrip, AddEntropyResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ AddEntropyResponse msg(ver);
+ UniquePtr<AddEntropyResponse> deserialized(round_trip(ver, msg, 4));
+ }
+}
+
+TEST(RoundTrip, AbortOperationRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ AbortOperationRequest msg(ver);
+ UniquePtr<AbortOperationRequest> deserialized(round_trip(ver, msg, 8));
+ }
+}
+
+TEST(RoundTrip, AbortOperationResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ AbortOperationResponse msg(ver);
+ UniquePtr<AbortOperationResponse> deserialized(round_trip(ver, msg, 4));
+ }
+}
+
+TEST(RoundTrip, AttestKeyRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ AttestKeyRequest msg(ver);
+ msg.SetKeyMaterial("foo", 3);
+ msg.attest_params.Reinitialize(params, array_length(params));
+
+ UniquePtr<AttestKeyRequest> deserialized(round_trip(ver, msg, 85));
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp("foo", deserialized->key_blob.key_material, 3));
+ EXPECT_EQ(msg.attest_params, deserialized->attest_params);
+ }
+}
+
+TEST(RoundTrip, AttestKeyResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ AttestKeyResponse msg(ver);
+ msg.error = KM_ERROR_OK;
+ EXPECT_TRUE(msg.AllocateChain(3));
+ msg.certificate_chain.entries[0] = {dup_buffer("foo", 3), 3};
+ msg.certificate_chain.entries[1] = {dup_buffer("bar", 3), 3};
+ msg.certificate_chain.entries[2] = {dup_buffer("baz", 3), 3};
+
+ UniquePtr<AttestKeyResponse> deserialized(round_trip(ver, msg, 29));
+ keymaster_cert_chain_t* chain = &deserialized->certificate_chain;
+
+ EXPECT_NE(nullptr, chain->entries);
+ EXPECT_EQ(3U, chain->entry_count);
+ EXPECT_EQ(3U, chain->entries[0].data_length);
+ EXPECT_EQ(0, memcmp("foo", chain->entries[0].data, 3));
+ EXPECT_EQ(3U, chain->entries[1].data_length);
+ EXPECT_EQ(0, memcmp("bar", chain->entries[1].data, 3));
+ EXPECT_EQ(3U, chain->entries[2].data_length);
+ EXPECT_EQ(0, memcmp("baz", chain->entries[2].data, 3));
+ }
+}
+
+TEST(RoundTrip, UpgradeKeyRequest) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ UpgradeKeyRequest msg(ver);
+ msg.SetKeyMaterial("foo", 3);
+ msg.upgrade_params.Reinitialize(params, array_length(params));
+
+ UniquePtr<UpgradeKeyRequest> deserialized(round_trip(ver, msg, 85));
+ EXPECT_EQ(3U, deserialized->key_blob.key_material_size);
+ EXPECT_EQ(0, memcmp("foo", deserialized->key_blob.key_material, 3));
+ EXPECT_EQ(msg.upgrade_params, deserialized->upgrade_params);
+ }
+}
+
+TEST(RoundTrip, UpgradeKeyResponse) {
+ for (int ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ UpgradeKeyResponse req(ver);
+ req.error = KM_ERROR_OK;
+ req.upgraded_key.key_material = dup_array(TEST_DATA);
+ req.upgraded_key.key_material_size = array_length(TEST_DATA);
+
+ UniquePtr<UpgradeKeyResponse> deserialized(round_trip(ver, req, 19));
+ EXPECT_EQ(KM_ERROR_OK, deserialized->error);
+ EXPECT_EQ(req.upgraded_key.key_material_size, deserialized->upgraded_key.key_material_size);
+ EXPECT_EQ(0, memcmp(req.upgraded_key.key_material, deserialized->upgraded_key.key_material,
+ req.upgraded_key.key_material_size));
+ }
+}
+
+uint8_t msgbuf[] = {
+ 220, 88, 183, 255, 71, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 173, 0, 0, 0, 228, 174, 98, 187, 191, 135, 253, 200, 51, 230, 114, 247, 151, 109,
+ 237, 79, 87, 32, 94, 5, 204, 46, 154, 30, 91, 6, 103, 148, 254, 129, 65, 171, 228,
+ 167, 224, 163, 9, 15, 206, 90, 58, 11, 205, 55, 211, 33, 87, 178, 149, 91, 28, 236,
+ 218, 112, 231, 34, 82, 82, 134, 103, 137, 115, 27, 156, 102, 159, 220, 226, 89, 42, 25,
+ 37, 9, 84, 239, 76, 161, 198, 72, 167, 163, 39, 91, 148, 191, 17, 191, 87, 169, 179,
+ 136, 10, 194, 154, 4, 40, 107, 109, 61, 161, 20, 176, 247, 13, 214, 106, 229, 45, 17,
+ 5, 60, 189, 64, 39, 166, 208, 14, 57, 25, 140, 148, 25, 177, 246, 189, 43, 181, 88,
+ 204, 29, 126, 224, 100, 143, 93, 60, 57, 249, 55, 0, 87, 83, 227, 224, 166, 59, 214,
+ 81, 144, 129, 58, 6, 57, 46, 254, 232, 41, 220, 209, 230, 167, 138, 158, 94, 180, 125,
+ 247, 26, 162, 116, 238, 202, 187, 100, 65, 13, 180, 44, 245, 159, 83, 161, 176, 58, 72,
+ 236, 109, 105, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 11, 0, 0, 0, 98, 0, 0, 0, 1, 0, 0, 32, 2, 0, 0, 0, 1, 0,
+ 0, 32, 3, 0, 0, 0, 2, 0, 0, 16, 1, 0, 0, 0, 3, 0, 0, 48, 0,
+ 1, 0, 0, 200, 0, 0, 80, 3, 0, 0, 0, 0, 0, 0, 0, 244, 1, 0, 112,
+ 1, 246, 1, 0, 112, 1, 189, 2, 0, 96, 144, 178, 236, 250, 255, 255, 255, 255, 145,
+ 1, 0, 96, 144, 226, 33, 60, 222, 2, 0, 0, 189, 2, 0, 96, 0, 0, 0, 0,
+ 0, 0, 0, 0, 190, 2, 0, 16, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 11, 0,
+ 0, 0, 98, 0, 0, 0, 1, 0, 0, 32, 2, 0, 0, 0, 1, 0, 0, 32, 3,
+ 0, 0, 0, 2, 0, 0, 16, 1, 0, 0, 0, 3, 0, 0, 48, 0, 1, 0, 0,
+ 200, 0, 0, 80, 3, 0, 0, 0, 0, 0, 0, 0, 244, 1, 0, 112, 1, 246, 1,
+ 0, 112, 1, 189, 2, 0, 96, 144, 178, 236, 250, 255, 255, 255, 255, 145, 1, 0, 96,
+ 144, 226, 33, 60, 222, 2, 0, 0, 189, 2, 0, 96, 0, 0, 0, 0, 0, 0, 0,
+ 0, 190, 2, 0, 16, 1, 0, 0, 0,
+};
+
+/*
+ * These tests don't have any assertions or expectations. They just try to parse garbage, to see if
+ * the result will be a crash. This is especially informative when run under Valgrind memcheck.
+ */
+
+template <typename Message> void parse_garbage() {
+ for (int32_t ver = 0; ver <= MAX_MESSAGE_VERSION; ++ver) {
+ Message msg(ver);
+ const uint8_t* end = msgbuf + array_length(msgbuf);
+ for (size_t i = 0; i < array_length(msgbuf); ++i) {
+ const uint8_t* begin = msgbuf + i;
+ const uint8_t* p = begin;
+ msg.Deserialize(&p, end);
+ }
+ }
+
+ time_t now = time(NULL);
+ std::cout << "Seeding rand() with " << now << " for fuzz test." << std::endl;
+ srand(now);
+
+ // Fill large buffer with random bytes.
+ const int kBufSize = 10000;
+ UniquePtr<uint8_t[]> buf(new uint8_t[kBufSize]);
+ for (size_t i = 0; i < kBufSize; ++i)
+ buf[i] = static_cast<uint8_t>(rand());
+
+ for (uint32_t ver = 0; ver < MAX_MESSAGE_VERSION; ++ver) {
+ Message msg(ver);
+ const uint8_t* end = buf.get() + kBufSize;
+ for (size_t i = 0; i < kBufSize; ++i) {
+ const uint8_t* begin = buf.get() + i;
+ const uint8_t* p = begin;
+ msg.Deserialize(&p, end);
+ }
+ }
+}
+#if 0
+#define GARBAGE_TEST(Message) \
+ TEST(GarbageTest, Message) { parse_garbage<Message>(); }
+
+GARBAGE_TEST(AbortOperationRequest);
+GARBAGE_TEST(AbortOperationResponse);
+GARBAGE_TEST(AddEntropyRequest);
+GARBAGE_TEST(AddEntropyResponse);
+GARBAGE_TEST(BeginOperationRequest);
+GARBAGE_TEST(BeginOperationResponse);
+GARBAGE_TEST(DeleteAllKeysRequest);
+GARBAGE_TEST(DeleteAllKeysResponse);
+GARBAGE_TEST(DeleteKeyRequest);
+GARBAGE_TEST(DeleteKeyResponse);
+GARBAGE_TEST(ExportKeyRequest);
+GARBAGE_TEST(ExportKeyResponse);
+GARBAGE_TEST(FinishOperationRequest);
+GARBAGE_TEST(FinishOperationResponse);
+GARBAGE_TEST(GenerateKeyRequest);
+GARBAGE_TEST(GenerateKeyResponse);
+GARBAGE_TEST(GetKeyCharacteristicsRequest);
+GARBAGE_TEST(GetKeyCharacteristicsResponse);
+GARBAGE_TEST(ImportKeyRequest);
+GARBAGE_TEST(ImportKeyResponse);
+GARBAGE_TEST(SupportedByAlgorithmAndPurposeRequest)
+GARBAGE_TEST(SupportedByAlgorithmRequest)
+GARBAGE_TEST(UpdateOperationRequest);
+GARBAGE_TEST(UpdateOperationResponse);
+GARBAGE_TEST(AttestKeyRequest);
+GARBAGE_TEST(AttestKeyResponse);
+GARBAGE_TEST(UpgradeKeyRequest);
+GARBAGE_TEST(UpgradeKeyResponse);
+
+// The macro doesn't work on this one.
+TEST(GarbageTest, SupportedResponse) {
+ parse_garbage<SupportedResponse<keymaster_digest_t>>();
+}
+#endif
+} // namespace test
+
+} // namespace keymaster
diff --git a/unit_test/android_keymaster_test.cpp b/unit_test/android_keymaster_test.cpp
new file mode 100644
index 0000000..d11b5be
--- a/dev/null
+++ b/unit_test/android_keymaster_test.cpp
@@ -0,0 +1,3976 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <hardware/keymaster0.h>
+#include <keymaster/key_factory.h>
+#include <keymaster/soft_keymaster_context.h>
+#include <keymaster/soft_keymaster_device.h>
+#include <keymaster/softkeymaster.h>
+
+#include "android_keymaster_test_utils.h"
+#include "attestation_record.h"
+#include "keymaster0_engine.h"
+#include "openssl_utils.h"
+
+#define CHECK_FAIL 1
+#define SUPPORT_TEST 1
+#define RSA_TEST 1
+#define EC_TEST 1
+#define AES_TEST 1
+#define HMAC_TEST 1
+#define MAX_TEST 1
+#define ENTROPY_TEST 1
+#define ATTESTATIONTEST 1
+#define KEYUPGRADETEST 0
+using std::ifstream;
+using std::istreambuf_iterator;
+using std::ofstream;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+extern "C" {
+int __android_log_print(int prio, const char* tag, const char* fmt);
+int __android_log_print(int prio, const char* tag, const char* fmt) {
+ (void)prio, (void)tag, (void)fmt;
+ return 0;
+}
+} // extern "C"
+
+namespace keymaster {
+namespace test {
+
+const uint32_t kOsVersion = 060000;
+const uint32_t kOsPatchLevel = 201603;
+
+StdoutLogger logger;
+
+template <typename T> vector<T> make_vector(const T* array, size_t len) {
+ return vector<T>(array, array + len);
+}
+
+/**
+ * KeymasterEnforcement class for use in testing. It's permissive in the sense that it doesn't
+ * check cryptoperiods, but restrictive in the sense that the clock never advances (so rate-limited
+ * keys will only work once).
+ */
+class TestKeymasterEnforcement : public KeymasterEnforcement {
+ public:
+ TestKeymasterEnforcement() : KeymasterEnforcement(3, 3) {}
+
+ virtual bool activation_date_valid(uint64_t /* activation_date */) const { return true; }
+ virtual bool expiration_date_passed(uint64_t /* expiration_date */) const { return false; }
+ virtual bool auth_token_timed_out(const hw_auth_token_t& /* token */,
+ uint32_t /* timeout */) const {
+ return false;
+ }
+ virtual uint32_t get_current_time() const { return 0; }
+ virtual bool ValidateTokenSignature(const hw_auth_token_t& /* token */) const { return true; }
+};
+
+/**
+ * Variant of SoftKeymasterContext that provides a TestKeymasterEnforcement.
+ */
+class TestKeymasterContext : public SoftKeymasterContext {
+ public:
+ TestKeymasterContext() {}
+ explicit TestKeymasterContext(const string& root_of_trust) : SoftKeymasterContext(root_of_trust) {}
+
+ KeymasterEnforcement* enforcement_policy() override { return &test_policy_; }
+
+ private:
+ TestKeymasterEnforcement test_policy_;
+};
+
+/**
+ * Test instance creator that builds a pure software keymaster2 implementation.
+ */
+class SoftKeymasterTestInstanceCreator : public Keymaster2TestInstanceCreator {
+ public:
+ keymaster2_device_t* CreateDevice() const override {
+ std::cerr << "Creating software-only device" << std::endl;
+ context_ = new TestKeymasterContext;
+ SoftKeymasterDevice* device = new SoftKeymasterDevice(context_);
+ AuthorizationSet version_info(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, kOsVersion)
+ .Authorization(TAG_OS_PATCHLEVEL, kOsPatchLevel));
+ device->keymaster2_device()->configure(device->keymaster2_device(), &version_info);
+ return device->keymaster2_device();
+ }
+
+ bool algorithm_in_km0_hardware(keymaster_algorithm_t) const override { return false; }
+ int keymaster0_calls() const override { return 0; }
+ bool is_keymaster1_hw() const override { return false; }
+ KeymasterContext* keymaster_context() const override { return context_; }
+
+ private:
+ mutable TestKeymasterContext* context_;
+};
+
+/**
+ * Test instance creator that builds a keymaster2 implementations.
+ */
+class AmlKeymasterTestInstanceCreator : public Keymaster2TestInstanceCreator {
+ public:
+ keymaster2_device_t* CreateDevice() const override {
+ const hw_module_t* mod;
+ keymaster2_device_t* device = NULL;
+
+ int rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+ if (rc) {
+ std::cerr << "Could not find any keystore module!" << std::endl;
+ } else {
+ assert(mod->module_api_version >= KEYMASTER_MODULE_API_VERSION_2_0);
+ rc = keymaster2_open(mod, &device);
+ if (rc) {
+ std::cerr << "Error "<< rc << "opening keystore keymaster2 device" << std::endl;
+ return NULL;
+ }
+ }
+
+ std::cerr << "Creating Amlogic keymaster2 device" << std::endl;
+ // context_ = new TestKeymasterContext;
+ // SoftKeymasterDevice* device = new SoftKeymasterDevice(context_);
+ AuthorizationSet version_info(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, kOsVersion)
+ .Authorization(TAG_OS_PATCHLEVEL, kOsPatchLevel));
+ device->configure(device, &version_info);
+ _device = device;
+ return device;
+ }
+
+ bool algorithm_in_km0_hardware(keymaster_algorithm_t) const override { return false; }
+ int keymaster0_calls() const override { return 0; }
+ bool is_keymaster1_hw() const override { return false; }
+ KeymasterContext* keymaster_context() const override { return (KeymasterContext*) _device; }
+
+ private:
+ static keymaster2_device_t* _device;
+ //mutable TestKeymasterContext* context_;
+};
+
+keymaster2_device_t* AmlKeymasterTestInstanceCreator::_device;
+/**
+ * Test instance creator that builds keymaster1 instances which wrap a faked hardware keymaster0
+ * instance, with or without EC support.
+ */
+class Keymaster0AdapterTestInstanceCreator : public Keymaster2TestInstanceCreator {
+ public:
+ explicit Keymaster0AdapterTestInstanceCreator(bool support_ec) : support_ec_(support_ec) {}
+
+ keymaster2_device_t* CreateDevice() const {
+ std::cerr << "Creating keymaster0-backed device (with ec: " << std::boolalpha << support_ec_
+ << ")." << std::endl;
+ hw_device_t* softkeymaster_device;
+ EXPECT_EQ(0, openssl_open(&softkeymaster_module.common, KEYSTORE_KEYMASTER,
+ &softkeymaster_device));
+ // Make the software device pretend to be hardware
+ keymaster0_device_t* keymaster0_device =
+ reinterpret_cast<keymaster0_device_t*>(softkeymaster_device);
+ keymaster0_device->flags &= ~KEYMASTER_SOFTWARE_ONLY;
+
+ if (!support_ec_) {
+ // Make the software device pretend not to support EC
+ keymaster0_device->flags &= ~KEYMASTER_SUPPORTS_EC;
+ }
+
+ counting_keymaster0_device_ = new Keymaster0CountingWrapper(keymaster0_device);
+
+ context_ = new TestKeymasterContext;
+ SoftKeymasterDevice* keymaster = new SoftKeymasterDevice(context_);
+ keymaster->SetHardwareDevice(counting_keymaster0_device_);
+ AuthorizationSet version_info(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, kOsVersion)
+ .Authorization(TAG_OS_PATCHLEVEL, kOsPatchLevel));
+ keymaster->keymaster2_device()->configure(keymaster->keymaster2_device(), &version_info);
+ return keymaster->keymaster2_device();
+ }
+
+ bool algorithm_in_km0_hardware(keymaster_algorithm_t algorithm) const override {
+ switch (algorithm) {
+ case KM_ALGORITHM_RSA:
+ return true;
+ case KM_ALGORITHM_EC:
+ return support_ec_;
+ default:
+ return false;
+ }
+ }
+ int keymaster0_calls() const override { return counting_keymaster0_device_->count(); }
+ bool is_keymaster1_hw() const override { return false; }
+ KeymasterContext* keymaster_context() const override { return context_; }
+
+ private:
+ mutable TestKeymasterContext* context_;
+ mutable Keymaster0CountingWrapper* counting_keymaster0_device_;
+ bool support_ec_;
+};
+
+/**
+ * Test instance creator that builds a SoftKeymasterDevice which wraps a fake hardware keymaster1
+ * instance, with minimal digest support.
+ */
+class Sha256OnlyKeymaster2TestInstanceCreator : public Keymaster2TestInstanceCreator {
+ keymaster2_device_t* CreateDevice() const {
+ std::cerr << "Creating keymaster1-backed device that supports only SHA256";
+
+ // fake_device doesn't leak because device (below) takes ownership of it.
+ keymaster1_device_t* fake_device = make_device_sha256_only(
+ (new SoftKeymasterDevice(new TestKeymasterContext("PseudoHW")))->keymaster_device());
+
+ // device doesn't leak; it's cleaned up by device->keymaster_device()->common.close().
+ context_ = new TestKeymasterContext;
+ SoftKeymasterDevice* device = new SoftKeymasterDevice(context_);
+ device->SetHardwareDevice(fake_device);
+
+ AuthorizationSet version_info(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, kOsVersion)
+ .Authorization(TAG_OS_PATCHLEVEL, kOsPatchLevel));
+ device->keymaster2_device()->configure(device->keymaster2_device(), &version_info);
+ return device->keymaster2_device();
+ }
+
+ bool algorithm_in_km0_hardware(keymaster_algorithm_t) const override { return false; }
+ int keymaster0_calls() const override { return 0; }
+ int minimal_digest_set() const override { return true; }
+ bool is_keymaster1_hw() const override { return true; }
+ KeymasterContext* keymaster_context() const override { return context_; }
+
+ private:
+ mutable TestKeymasterContext* context_;
+};
+
+static auto test_params = testing::Values(
+#if 0
+ InstanceCreatorPtr(new SoftKeymasterTestInstanceCreator),
+ InstanceCreatorPtr(new Keymaster0AdapterTestInstanceCreator(true /* support_ec */)),
+ InstanceCreatorPtr(new Keymaster0AdapterTestInstanceCreator(false /* support_ec */)),
+ InstanceCreatorPtr(new Sha256OnlyKeymaster2TestInstanceCreator)
+#endif
+ InstanceCreatorPtr(new AmlKeymasterTestInstanceCreator)
+ );
+
+class NewKeyGeneration : public Keymaster2Test {
+ protected:
+ void CheckBaseParams() {
+ AuthorizationSet auths = sw_enforced();
+ auths.Union(hw_enforced());
+ EXPECT_GT(auths.SerializedSize(), 12U);
+
+ EXPECT_TRUE(contains(auths, TAG_PURPOSE, KM_PURPOSE_SIGN));
+ EXPECT_TRUE(contains(auths, TAG_PURPOSE, KM_PURPOSE_VERIFY));
+ EXPECT_TRUE(contains(auths, TAG_USER_ID, 7));
+ EXPECT_TRUE(contains(auths, TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD));
+ EXPECT_TRUE(contains(auths, TAG_AUTH_TIMEOUT, 300));
+
+ // Verify that App ID, App data and ROT are NOT included.
+ EXPECT_FALSE(contains(auths, TAG_ROOT_OF_TRUST));
+ EXPECT_FALSE(contains(auths, TAG_APPLICATION_ID));
+ EXPECT_FALSE(contains(auths, TAG_APPLICATION_DATA));
+
+ // Just for giggles, check that some unexpected tags/values are NOT present.
+ EXPECT_FALSE(contains(auths, TAG_PURPOSE, KM_PURPOSE_ENCRYPT));
+ EXPECT_FALSE(contains(auths, TAG_PURPOSE, KM_PURPOSE_DECRYPT));
+ EXPECT_FALSE(contains(auths, TAG_AUTH_TIMEOUT, 301));
+
+ // Now check that unspecified, defaulted tags are correct.
+ EXPECT_TRUE(contains(auths, KM_TAG_CREATION_DATETIME));
+ if (GetParam()->is_keymaster1_hw()) {
+ // If the underlying (faked) HW is KM1, it will not have version info.
+ EXPECT_FALSE(auths.Contains(TAG_OS_VERSION));
+ EXPECT_FALSE(auths.Contains(TAG_OS_PATCHLEVEL));
+ } else {
+ // In all othe cases; SoftKeymasterDevice keys, or keymaster0 keys wrapped by
+ // SoftKeymasterDevice, version information will be present and up to date.
+ EXPECT_TRUE(contains(auths, TAG_OS_VERSION, kOsVersion));
+ EXPECT_TRUE(contains(auths, TAG_OS_PATCHLEVEL, kOsPatchLevel));
+ }
+ }
+};
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, NewKeyGeneration, test_params);
+#if RSA_TEST
+TEST_P(NewKeyGeneration, Rsa) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ CheckBaseParams();
+
+ // Check specified tags are all present, and in the right set.
+ AuthorizationSet crypto_params;
+ AuthorizationSet non_crypto_params;
+#if 0
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA)) {
+ EXPECT_NE(0U, hw_enforced().size());
+ EXPECT_NE(0U, sw_enforced().size());
+ crypto_params.push_back(hw_enforced());
+ non_crypto_params.push_back(sw_enforced());
+ } else {
+ EXPECT_EQ(0U, hw_enforced().size());
+ EXPECT_NE(0U, sw_enforced().size());
+ crypto_params.push_back(sw_enforced());
+ }
+#else
+ EXPECT_NE(0U, hw_enforced().size());
+ EXPECT_NE(0U, sw_enforced().size());
+ crypto_params.push_back(hw_enforced());
+ non_crypto_params.push_back(sw_enforced());
+#endif
+
+ EXPECT_TRUE(contains(crypto_params, TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_FALSE(contains(non_crypto_params, TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_TRUE(contains(crypto_params, TAG_KEY_SIZE, 256));
+ EXPECT_FALSE(contains(non_crypto_params, TAG_KEY_SIZE, 256));
+ EXPECT_TRUE(contains(crypto_params, TAG_RSA_PUBLIC_EXPONENT, 3));
+ EXPECT_FALSE(contains(non_crypto_params, TAG_RSA_PUBLIC_EXPONENT, 3));
+
+ EXPECT_EQ(KM_ERROR_OK, DeleteKey());
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(NewKeyGeneration, RsaDefaultSize) {
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3)
+ .SigningKey()));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if EC_TEST
+TEST_P(NewKeyGeneration, Ecdsa) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE)));
+ CheckBaseParams();
+
+ // Check specified tags are all present, and in the right set.
+ AuthorizationSet crypto_params;
+ AuthorizationSet non_crypto_params;
+#if 0
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC)) {
+ EXPECT_NE(0U, hw_enforced().size());
+ EXPECT_NE(0U, sw_enforced().size());
+ crypto_params.push_back(hw_enforced());
+ non_crypto_params.push_back(sw_enforced());
+ } else {
+ EXPECT_EQ(0U, hw_enforced().size());
+ EXPECT_NE(0U, sw_enforced().size());
+ crypto_params.push_back(sw_enforced());
+ }
+#else
+ EXPECT_NE(0U, hw_enforced().size());
+ EXPECT_NE(0U, sw_enforced().size());
+ crypto_params.push_back(hw_enforced());
+ non_crypto_params.push_back(sw_enforced());
+#endif
+
+ EXPECT_TRUE(contains(crypto_params, TAG_ALGORITHM, KM_ALGORITHM_EC));
+ EXPECT_FALSE(contains(non_crypto_params, TAG_ALGORITHM, KM_ALGORITHM_EC));
+ EXPECT_TRUE(contains(crypto_params, TAG_KEY_SIZE, 224));
+ EXPECT_FALSE(contains(non_crypto_params, TAG_KEY_SIZE, 224));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(1, GetParam()->keymaster0_calls());
+}
+#endif
+#if EC_TEST
+TEST_P(NewKeyGeneration, EcdsaDefaultSize) {
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC)
+ .SigningKey()
+ .Digest(KM_DIGEST_NONE)));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, EcdsaInvalidSize) {
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_KEY_SIZE,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(190).Digest(KM_DIGEST_NONE)));
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+
+#if EC_TEST
+TEST_P(NewKeyGeneration, EcdsaMismatchKeySize) {
+ ASSERT_EQ(KM_ERROR_INVALID_ARGUMENT,
+ GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(224)
+ .Authorization(TAG_EC_CURVE, KM_EC_CURVE_P_256)
+ .Digest(KM_DIGEST_NONE)));
+}
+#endif
+
+#if EC_TEST
+TEST_P(NewKeyGeneration, EcdsaAllValidSizes) {
+ size_t valid_sizes[] = {224, 256, 384, 521};
+ for (size_t size : valid_sizes) {
+ EXPECT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(size).Digest(
+ KM_DIGEST_NONE)))
+ << "Failed to generate size: " << size;
+ }
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+#endif
+#if HMAC_TEST
+TEST_P(NewKeyGeneration, HmacSha256) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacMultipleDigests) {
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_DIGEST,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA1)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacDigestNone) {
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_DIGEST,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacSha256TooShortMacLength) {
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 48)));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacSha256NonIntegralOctetMacLength) {
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 130)));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(NewKeyGeneration, HmacSha256TooLongMacLength) {
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 384)));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+typedef Keymaster2Test GetKeyCharacteristics;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, GetKeyCharacteristics, test_params);
+
+#if RSA_TEST
+TEST_P(GetKeyCharacteristics, SimpleRsa) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ AuthorizationSet original(sw_enforced());
+
+ ASSERT_EQ(KM_ERROR_OK, GetCharacteristics());
+ EXPECT_EQ(original, sw_enforced());
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(1, GetParam()->keymaster0_calls());
+}
+#endif
+typedef Keymaster2Test SigningOperationsTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, SigningOperationsTest, test_params);
+#if RSA_TEST
+TEST_P(SigningOperationsTest, RsaSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(SigningOperationsTest, RsaPssSha256Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(768, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PSS)));
+ // Use large message, which won't work without digesting.
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PADDING_MODE, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, RsaPkcs1Sha256Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)));
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(SigningOperationsTest, RsaPkcs1NoDigestSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)));
+ string message(53, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_RSA_PKCS1_1_5_SIGN);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(SigningOperationsTest, RsaPkcs1NoDigestTooLarge) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)));
+ string message(54, 'a');
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ string signature;
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&signature));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+#endif
+#if CHECK_FAIL
+TEST_P(SigningOperationsTest, RsaPssSha256TooSmallKey) {
+ // Key must be at least 10 bytes larger than hash, to provide eight bytes of random salt, so
+ // verify that nine bytes larger than hash won't work.
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256 + 9 * 8, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PSS)));
+ string message(1024, 'a');
+ string signature;
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PSS);
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_DIGEST, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+}
+#endif
+#if CHECK_FAIL
+TEST_P(SigningOperationsTest, RsaNoPaddingHugeData) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)));
+ string message(64 * 1024, 'a');
+ string signature;
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, UpdateOperation(message, &result, &input_consumed));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+#endif
+#if CHECK_FAIL
+TEST_P(SigningOperationsTest, RsaAbort) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, AbortOperation());
+ // Another abort should fail
+ EXPECT_EQ(KM_ERROR_INVALID_OPERATION_HANDLE, AbortOperation());
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(SigningOperationsTest, RsaUnsupportedPadding) {
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_SHA_2_256 /* supported digest */)
+ .Padding(KM_PAD_PKCS7));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PADDING_MODE, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, RsaNoDigest) {
+ // PSS requires a digest.
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_RSA_PSS));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PSS);
+ ASSERT_EQ(KM_ERROR_INCOMPATIBLE_DIGEST, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, RsaNoPadding) {
+ // Padding must be specified
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaKey(256, 3).SigningKey().Digest(
+ KM_DIGEST_NONE)));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PADDING_MODE, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, RsaTooShortMessage) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ string message = "1234567890123456789012345678901";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, RsaSignWithEncryptionKey) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ ASSERT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, RsaSignTooLargeMessage) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ string message(256 / 8, static_cast<char>(0xff));
+ string signature;
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+ string result;
+ size_t input_consumed;
+ ASSERT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ ASSERT_EQ(message.size(), input_consumed);
+ string output;
+ ASSERT_EQ(KM_ERROR_INVALID_ARGUMENT, FinishOperation(&output));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+#endif
+#if EC_TEST
+TEST_P(SigningOperationsTest, EcdsaSuccess) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE)));
+ string message(224 / 8, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+#endif
+#if EC_TEST
+TEST_P(SigningOperationsTest, EcdsaSha256Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(
+ KM_DIGEST_SHA_2_256)));
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, EcdsaSha384Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(
+ KM_DIGEST_SHA_2_384)));
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_384);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, EcdsaNoPaddingHugeData) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE)));
+ string message(64 * 1024, 'a');
+ string signature;
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ ASSERT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, EcdsaAllSizesAndHashes) {
+ vector<int> key_sizes = {224, 256, 384, 521};
+ vector<keymaster_digest_t> digests = {
+ KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, KM_DIGEST_SHA_2_256,
+ KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512,
+ };
+
+ for (int key_size : key_sizes) {
+ for (keymaster_digest_t digest : digests) {
+ ASSERT_EQ(
+ KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(key_size).Digest(digest)));
+
+ string message(1024, 'a');
+ string signature;
+ if (digest == KM_DIGEST_NONE)
+ message.resize(key_size / 8);
+ SignMessage(message, &signature, digest);
+ }
+ }
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(digests.size() * key_sizes.size() * 3,
+ static_cast<size_t>(GetParam()->keymaster0_calls()));
+}
+#endif
+#if AES_TEST
+TEST_P(SigningOperationsTest, AesEcbSign) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().AesEncryptionKey(128).Authorization(
+ TAG_BLOCK_MODE, KM_MODE_ECB)));
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_SIGN));
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_VERIFY));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if HMAC_TEST
+TEST_P(SigningOperationsTest, HmacSha1Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate other digests for HMAC.
+ return;
+
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA1)
+ .Authorization(TAG_MIN_MAC_LENGTH, 160));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ MacMessage(message, &signature, 160);
+ ASSERT_EQ(20U, signature.size());
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if HMAC_TEST
+TEST_P(SigningOperationsTest, HmacSha224Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate other digests for HMAC.
+ return;
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_224)
+ .Authorization(TAG_MIN_MAC_LENGTH, 160)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ MacMessage(message, &signature, 224);
+ ASSERT_EQ(28U, signature.size());
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacSha256Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate other digests for HMAC.
+ return;
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ MacMessage(message, &signature, 256);
+ ASSERT_EQ(32U, signature.size());
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacSha384Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate other digests for HMAC.
+ return;
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_384)
+ .Authorization(TAG_MIN_MAC_LENGTH, 384)));
+
+ string message = "12345678901234567890123456789012";
+ string signature;
+ MacMessage(message, &signature, 384);
+ ASSERT_EQ(48U, signature.size());
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacSha512Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate other digests for HMAC.
+ return;
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_512)
+ .Authorization(TAG_MIN_MAC_LENGTH, 384)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ MacMessage(message, &signature, 512);
+ ASSERT_EQ(64U, signature.size());
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacLengthInKey) {
+ // TODO(swillden): unified API should generate an error on key generation.
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ MacMessage(message, &signature, 160);
+ ASSERT_EQ(20U, signature.size());
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase1) {
+ uint8_t key_data[] = {
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ };
+ string message = "Hi There";
+ uint8_t sha_224_expected[] = {
+ 0x89, 0x6f, 0xb1, 0x12, 0x8a, 0xbb, 0xdf, 0x19, 0x68, 0x32, 0x10, 0x7c, 0xd4, 0x9d,
+ 0xf3, 0x3f, 0x47, 0xb4, 0xb1, 0x16, 0x99, 0x12, 0xba, 0x4f, 0x53, 0x68, 0x4b, 0x22,
+ };
+ uint8_t sha_256_expected[] = {
+ 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf,
+ 0xce, 0xaf, 0x0b, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83,
+ 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7,
+ };
+ uint8_t sha_384_expected[] = {
+ 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62, 0x6b, 0x08, 0x25, 0xf4,
+ 0xab, 0x46, 0x90, 0x7f, 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6,
+ 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c, 0xfa, 0xea, 0x9e, 0xa9,
+ 0x07, 0x6e, 0xde, 0x7f, 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6,
+ };
+ uint8_t sha_512_expected[] = {
+ 0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d, 0x4f, 0xf0, 0xb4, 0x24, 0x1a,
+ 0x1d, 0x6c, 0xb0, 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78, 0x7a, 0xd0,
+ 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde, 0xda, 0xa8, 0x33, 0xb7, 0xd6, 0xb8, 0xa7,
+ 0x02, 0x03, 0x8b, 0x27, 0x4e, 0xae, 0xa3, 0xf4, 0xe4, 0xbe, 0x9d, 0x91, 0x4e,
+ 0xeb, 0x61, 0xf1, 0x70, 0x2e, 0x69, 0x6c, 0x20, 0x3a, 0x12, 0x68, 0x54,
+ };
+
+ string key = make_string(key_data);
+
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ if (!GetParam()->minimal_digest_set()) {
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected));
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase2) {
+ string key = "Jefe";
+ string message = "what do ya want for nothing?";
+ uint8_t sha_224_expected[] = {
+ 0xa3, 0x0e, 0x01, 0x09, 0x8b, 0xc6, 0xdb, 0xbf, 0x45, 0x69, 0x0f, 0x3a, 0x7e, 0x9e,
+ 0x6d, 0x0f, 0x8b, 0xbe, 0xa2, 0xa3, 0x9e, 0x61, 0x48, 0x00, 0x8f, 0xd0, 0x5e, 0x44,
+ };
+ uint8_t sha_256_expected[] = {
+ 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24,
+ 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27,
+ 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43,
+ };
+ uint8_t sha_384_expected[] = {
+ 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, 0x61, 0x7f, 0x78, 0xd2,
+ 0xb5, 0x8a, 0x6b, 0x1b, 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47,
+ 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e, 0x8e, 0x22, 0x40, 0xca,
+ 0x5e, 0x69, 0xe2, 0xc7, 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49,
+ };
+ uint8_t sha_512_expected[] = {
+ 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 0xe3, 0x95, 0xfb, 0xe7, 0x3b,
+ 0x56, 0xe0, 0xa3, 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 0x10, 0x27,
+ 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54, 0x97, 0x58, 0xbf, 0x75, 0xc0, 0x5a, 0x99,
+ 0x4a, 0x6d, 0x03, 0x4f, 0x65, 0xf8, 0xf0, 0xe6, 0xfd, 0xca, 0xea, 0xb1, 0xa3,
+ 0x4d, 0x4a, 0x6b, 0x4b, 0x63, 0x6e, 0x07, 0x0a, 0x38, 0xbc, 0xe7, 0x37,
+ };
+
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ if (!GetParam()->minimal_digest_set()) {
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected));
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase3) {
+ string key(20, 0xaa);
+ string message(50, 0xdd);
+ uint8_t sha_224_expected[] = {
+ 0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6, 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a,
+ 0xd2, 0x64, 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1, 0xec, 0x83, 0x33, 0xea,
+ };
+ uint8_t sha_256_expected[] = {
+ 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8,
+ 0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8,
+ 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 0x0a, 0xa2, 0xac, 0xe0,
+ 0x14, 0xc8, 0xa8, 0x6f, 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+ 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 0x2a, 0x5a, 0xb3, 0x9d,
+ 0xc1, 0x38, 0x14, 0xb9, 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27,
+ };
+ uint8_t sha_512_expected[] = {
+ 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75, 0x6c,
+ 0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 0x55, 0xf8,
+ 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22,
+ 0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 0xb9, 0x46, 0xa3, 0x37,
+ 0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb,
+ };
+
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ if (!GetParam()->minimal_digest_set()) {
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected));
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase4) {
+ uint8_t key_data[25] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+ };
+ string key = make_string(key_data);
+ string message(50, 0xcd);
+ uint8_t sha_224_expected[] = {
+ 0x6c, 0x11, 0x50, 0x68, 0x74, 0x01, 0x3c, 0xac, 0x6a, 0x2a, 0xbc, 0x1b, 0xb3, 0x82,
+ 0x62, 0x7c, 0xec, 0x6a, 0x90, 0xd8, 0x6e, 0xfc, 0x01, 0x2d, 0xe7, 0xaf, 0xec, 0x5a,
+ };
+ uint8_t sha_256_expected[] = {
+ 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81,
+ 0x98, 0x99, 0xf2, 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78,
+ 0xf8, 0x07, 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85, 0x19, 0x33, 0xab, 0x62,
+ 0x90, 0xaf, 0x6c, 0xa7, 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c,
+ 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e, 0x68, 0x01, 0xdd, 0x23,
+ 0xc4, 0xa7, 0xd6, 0x79, 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb,
+ };
+ uint8_t sha_512_expected[] = {
+ 0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69, 0x90, 0xe5, 0xa8, 0xc5, 0xf6,
+ 0x1d, 0x4a, 0xf7, 0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d, 0xe7, 0x6f,
+ 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb, 0xa9, 0x1c, 0xa5, 0xc1, 0x1a, 0xa2, 0x5e,
+ 0xb4, 0xd6, 0x79, 0x27, 0x5c, 0xc5, 0x78, 0x80, 0x63, 0xa5, 0xf1, 0x97, 0x41,
+ 0x12, 0x0c, 0x4f, 0x2d, 0xe2, 0xad, 0xeb, 0xeb, 0x10, 0xa2, 0x98, 0xdd,
+ };
+
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ if (!GetParam()->minimal_digest_set()) {
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected));
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase5) {
+ string key(20, 0x0c);
+ string message = "Test With Truncation";
+
+ uint8_t sha_224_expected[] = {
+ 0x0e, 0x2a, 0xea, 0x68, 0xa9, 0x0c, 0x8d, 0x37,
+ 0xc9, 0x88, 0xbc, 0xdb, 0x9f, 0xca, 0x6f, 0xa8,
+ };
+ uint8_t sha_256_expected[] = {
+ 0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0,
+ 0x6e, 0x0c, 0x79, 0x6c, 0x29, 0x55, 0x55, 0x2b,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x3a, 0xbf, 0x34, 0xc3, 0x50, 0x3b, 0x2a, 0x23,
+ 0xa4, 0x6e, 0xfc, 0x61, 0x9b, 0xae, 0xf8, 0x97,
+ };
+ uint8_t sha_512_expected[] = {
+ 0x41, 0x5f, 0xad, 0x62, 0x71, 0x58, 0x0a, 0x53,
+ 0x1d, 0x41, 0x79, 0xbc, 0x89, 0x1d, 0x87, 0xa6,
+ };
+
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ if (!GetParam()->minimal_digest_set()) {
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected));
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+
+#if HMAC_TEST
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase6) {
+ string key(131, 0xaa);
+ string message = "Test Using Larger Than Block-Size Key - Hash Key First";
+
+ uint8_t sha_224_expected[] = {
+ 0x95, 0xe9, 0xa0, 0xdb, 0x96, 0x20, 0x95, 0xad, 0xae, 0xbe, 0x9b, 0x2d, 0x6f, 0x0d,
+ 0xbc, 0xe2, 0xd4, 0x99, 0xf1, 0x12, 0xf2, 0xd2, 0xb7, 0x27, 0x3f, 0xa6, 0x87, 0x0e,
+ };
+ uint8_t sha_256_expected[] = {
+ 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26,
+ 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28,
+ 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, 0x88, 0xd2, 0xc6, 0x3a,
+ 0x04, 0x1b, 0xc5, 0xb4, 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f,
+ 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, 0x0c, 0x2e, 0xf6, 0xab,
+ 0x40, 0x30, 0xfe, 0x82, 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52,
+ };
+ uint8_t sha_512_expected[] = {
+ 0x80, 0xb2, 0x42, 0x63, 0xc7, 0xc1, 0xa3, 0xeb, 0xb7, 0x14, 0x93, 0xc1, 0xdd,
+ 0x7b, 0xe8, 0xb4, 0x9b, 0x46, 0xd1, 0xf4, 0x1b, 0x4a, 0xee, 0xc1, 0x12, 0x1b,
+ 0x01, 0x37, 0x83, 0xf8, 0xf3, 0x52, 0x6b, 0x56, 0xd0, 0x37, 0xe0, 0x5f, 0x25,
+ 0x98, 0xbd, 0x0f, 0xd2, 0x21, 0x5d, 0x6a, 0x1e, 0x52, 0x95, 0xe6, 0x4f, 0x73,
+ 0xf6, 0x3f, 0x0a, 0xec, 0x8b, 0x91, 0x5a, 0x98, 0x5d, 0x78, 0x65, 0x98,
+ };
+
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ if (!GetParam()->minimal_digest_set()) {
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected));
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+
+#if HMAC_TEST
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase7) {
+ string key(131, 0xaa);
+ string message = "This is a test using a larger than block-size key and a larger than "
+ "block-size data. The key needs to be hashed before being used by the HMAC "
+ "algorithm.";
+
+ uint8_t sha_224_expected[] = {
+ 0x3a, 0x85, 0x41, 0x66, 0xac, 0x5d, 0x9f, 0x02, 0x3f, 0x54, 0xd5, 0x17, 0xd0, 0xb3,
+ 0x9d, 0xbd, 0x94, 0x67, 0x70, 0xdb, 0x9c, 0x2b, 0x95, 0xc9, 0xf6, 0xf5, 0x65, 0xd1,
+ };
+ uint8_t sha_256_expected[] = {
+ 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f,
+ 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07,
+ 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2,
+ };
+ uint8_t sha_384_expected[] = {
+ 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, 0x35, 0x1e, 0x2f, 0x25,
+ 0x4e, 0x8f, 0xd3, 0x2c, 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a,
+ 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, 0xa6, 0x78, 0xcc, 0x31,
+ 0xe7, 0x99, 0x17, 0x6d, 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e,
+ };
+ uint8_t sha_512_expected[] = {
+ 0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, 0xa4, 0xdf, 0xa9, 0xf9, 0x6e,
+ 0x5e, 0x3f, 0xfd, 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86, 0x5d, 0xf5,
+ 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, 0xb6, 0x02, 0x2c, 0xac, 0x3c, 0x49, 0x82,
+ 0xb1, 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15, 0x13, 0x46, 0x76, 0xfb,
+ 0x6d, 0xe0, 0x44, 0x60, 0x65, 0xc9, 0x74, 0x40, 0xfa, 0x8c, 0x6a, 0x58,
+ };
+
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_256, make_string(sha_256_expected));
+ if (!GetParam()->minimal_digest_set()) {
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_224, make_string(sha_224_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_384, make_string(sha_384_expected));
+ CheckHmacTestVector(key, message, KM_DIGEST_SHA_2_512, make_string(sha_512_expected));
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacSha256TooLargeMacLength) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_MAC_LENGTH, 264);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_MAC_LENGTH,
+ BeginOperation(KM_PURPOSE_SIGN, begin_params, nullptr /* output_params */));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(SigningOperationsTest, HmacSha256TooSmallMacLength) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_MAC_LENGTH, 120);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ ASSERT_EQ(KM_ERROR_INVALID_MAC_LENGTH,
+ BeginOperation(KM_PURPOSE_SIGN, begin_params, nullptr /* output_params */));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+// TODO(swillden): Add more verification failure tests.
+
+typedef Keymaster2Test VerificationOperationsTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, VerificationOperationsTest, test_params);
+#if RSA_TEST
+TEST_P(VerificationOperationsTest, RsaSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+ VerifyMessage(message, signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, RsaPssSha256Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(768, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PSS)));
+ // Use large message, which won't work without digesting.
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, RsaPssSha224Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_SHA_2_224)
+ .Padding(KM_PAD_RSA_PSS)));
+ // Use large message, which won't work without digesting.
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_224, KM_PAD_RSA_PSS);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_224, KM_PAD_RSA_PSS);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+
+ // Verify with OpenSSL.
+ string pubkey;
+ EXPECT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &pubkey));
+
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(pubkey.data());
+ unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(nullptr /* alloc new */, &p, pubkey.size()));
+ ASSERT_TRUE(pkey.get());
+
+ EVP_MD_CTX digest_ctx;
+ EVP_MD_CTX_init(&digest_ctx);
+ EVP_PKEY_CTX* pkey_ctx;
+ EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, EVP_sha224(), nullptr /* engine */,
+ pkey.get()));
+ EXPECT_EQ(1, EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING));
+ EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(), message.size()));
+ EXPECT_EQ(1,
+ EVP_DigestVerifyFinal(&digest_ctx, reinterpret_cast<const uint8_t*>(signature.data()),
+ signature.size()));
+ EVP_MD_CTX_cleanup(&digest_ctx);
+}
+
+TEST_P(VerificationOperationsTest, RsaPssSha256CorruptSignature) {
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(768, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PSS));
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS);
+ ++signature[signature.size() / 2];
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PSS);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, RsaPssSha256CorruptInput) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(768, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PSS)));
+ // Use large message, which won't work without digesting.
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PSS);
+ ++message[message.size() / 2];
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PSS);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, RsaPkcs1Sha256Success) {
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN));
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, RsaPks1Sha224Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_SHA_2_224)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)));
+ // Use large message, which won't work without digesting.
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_224, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_224, KM_PAD_RSA_PKCS1_1_5_SIGN);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+
+ // Verify with OpenSSL.
+ string pubkey;
+ EXPECT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &pubkey));
+
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(pubkey.data());
+ unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(nullptr /* alloc new */, &p, pubkey.size()));
+ ASSERT_TRUE(pkey.get());
+
+ EVP_MD_CTX digest_ctx;
+ EVP_MD_CTX_init(&digest_ctx);
+ EVP_PKEY_CTX* pkey_ctx;
+ EXPECT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, EVP_sha224(), nullptr /* engine */,
+ pkey.get()));
+ EXPECT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx, message.data(), message.size()));
+ EXPECT_EQ(1,
+ EVP_DigestVerifyFinal(&digest_ctx, reinterpret_cast<const uint8_t*>(signature.data()),
+ signature.size()));
+ EVP_MD_CTX_cleanup(&digest_ctx);
+}
+
+TEST_P(VerificationOperationsTest, RsaPkcs1Sha256CorruptSignature) {
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN));
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ ++signature[signature.size() / 2];
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, RsaPkcs1Sha256CorruptInput) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_SIGN)));
+ // Use large message, which won't work without digesting.
+ string message(1024, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ ++message[message.size() / 2];
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+#endif
+#if CHECK_FAIL
+TEST_P(VerificationOperationsTest, RsaAllDigestAndPadCombinations) {
+ vector<keymaster_digest_t> digests = {
+ KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224,
+ KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512,
+ };
+
+ vector<keymaster_padding_t> padding_modes{
+ KM_PAD_NONE, KM_PAD_RSA_PKCS1_1_5_SIGN, KM_PAD_RSA_PSS,
+ };
+
+ int trial_count = 0;
+ for (keymaster_padding_t padding_mode : padding_modes) {
+ for (keymaster_digest_t digest : digests) {
+ if (digest != KM_DIGEST_NONE && padding_mode == KM_PAD_NONE)
+ // Digesting requires padding
+ continue;
+
+ // Compute key & message size that will work.
+ size_t key_bits = 0;
+ size_t message_len = 1000;
+
+ if (digest == KM_DIGEST_NONE) {
+ key_bits = 256;
+ switch (padding_mode) {
+ case KM_PAD_NONE:
+ // Match key size.
+ message_len = key_bits / 8;
+ break;
+ case KM_PAD_RSA_PKCS1_1_5_SIGN:
+ message_len = key_bits / 8 - 11;
+ break;
+ case KM_PAD_RSA_PSS:
+ // PSS requires a digest.
+ continue;
+ default:
+ FAIL() << "Missing padding";
+ break;
+ }
+ } else {
+ size_t digest_bits;
+ switch (digest) {
+ case KM_DIGEST_MD5:
+ digest_bits = 128;
+ break;
+ case KM_DIGEST_SHA1:
+ digest_bits = 160;
+ break;
+ case KM_DIGEST_SHA_2_224:
+ digest_bits = 224;
+ break;
+ case KM_DIGEST_SHA_2_256:
+ digest_bits = 256;
+ break;
+ case KM_DIGEST_SHA_2_384:
+ digest_bits = 384;
+ break;
+ case KM_DIGEST_SHA_2_512:
+ digest_bits = 512;
+ break;
+ default:
+ FAIL() << "Missing digest";
+ }
+
+ switch (padding_mode) {
+ case KM_PAD_RSA_PKCS1_1_5_SIGN:
+ key_bits = digest_bits + 8 * (11 + 19);
+ break;
+ case KM_PAD_RSA_PSS:
+ key_bits = digest_bits * 2 + 2 * 8;
+ break;
+ default:
+ FAIL() << "Missing padding";
+ break;
+ }
+ }
+
+ // round up to 128 bits because new boringssl supports only 128 bit mulitples
+ key_bits += 127;
+ key_bits &= ~127;
+ GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(key_bits, 3)
+ .Digest(digest)
+ .Padding(padding_mode));
+ string message(message_len, 'a');
+ string signature;
+ SignMessage(message, &signature, digest, padding_mode);
+ VerifyMessage(message, signature, digest, padding_mode);
+ ++trial_count;
+ }
+ }
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(trial_count * 4, GetParam()->keymaster0_calls());
+}
+#endif
+#if EC_TEST
+TEST_P(VerificationOperationsTest, EcdsaSuccess) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(KM_DIGEST_NONE)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE);
+ VerifyMessage(message, signature, KM_DIGEST_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, EcdsaTooShort) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(KM_DIGEST_NONE)));
+ string message = "12345678901234567890";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE);
+ VerifyMessage(message, signature, KM_DIGEST_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, EcdsaSlightlyTooLong) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(521).Digest(KM_DIGEST_NONE)));
+
+ string message(66, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE);
+ VerifyMessage(message, signature, KM_DIGEST_NONE);
+
+ // Modifying low-order bits doesn't matter, because they didn't get signed. Ugh.
+ message[65] ^= 7;
+ VerifyMessage(message, signature, KM_DIGEST_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(5, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, EcdsaSha256Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(256)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Digest(KM_DIGEST_NONE)));
+ string message = "12345678901234567890123456789012";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_256);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+
+ // Just for giggles, try verifying with the wrong digest.
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result));
+}
+
+TEST_P(VerificationOperationsTest, EcdsaSha224Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(
+ KM_DIGEST_SHA_2_224)));
+
+ string message = "12345678901234567890123456789012";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_224);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_224);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+
+ // Just for giggles, try verifying with the wrong digest.
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(signature, &result));
+}
+
+TEST_P(VerificationOperationsTest, EcdsaAllDigestsAndKeySizes) {
+ keymaster_digest_t digests[] = {
+ KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224, KM_DIGEST_SHA_2_256,
+ KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512,
+ };
+ size_t key_sizes[] = {224, 256, 384, 521};
+
+ string message = "1234567890";
+ string signature;
+
+ for (auto key_size : key_sizes) {
+ AuthorizationSetBuilder builder;
+ builder.EcdsaSigningKey(key_size);
+ for (auto digest : digests)
+ builder.Digest(digest);
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(builder));
+
+ for (auto digest : digests) {
+ SignMessage(message, &signature, digest);
+ VerifyMessage(message, signature, digest);
+ }
+ }
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(static_cast<int>(array_length(key_sizes) * (1 + 3 * array_length(digests))),
+ GetParam()->keymaster0_calls());
+}
+#endif
+#if HMAC_TEST
+TEST_P(VerificationOperationsTest, HmacSha1Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate missing digests for HMAC.
+ return;
+
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA1)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128));
+ string message = "123456789012345678901234567890123456789012345678";
+ string signature;
+ MacMessage(message, &signature, 160);
+ VerifyMac(message, signature);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, HmacSha224Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate missing digests for HMAC.
+ return;
+
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_224)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128));
+ string message = "123456789012345678901234567890123456789012345678";
+ string signature;
+ MacMessage(message, &signature, 224);
+ VerifyMac(message, signature);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, HmacSha256Success) {
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128));
+ string message = "123456789012345678901234567890123456789012345678";
+ string signature;
+ MacMessage(message, &signature, 256);
+ VerifyMac(message, signature);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, HmacSha256TooShortMac) {
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128));
+ string message = "123456789012345678901234567890123456789012345678";
+ string signature;
+ MacMessage(message, &signature, 256);
+
+ // Shorten to 128 bits, should still work.
+ signature.resize(128 / 8);
+ VerifyMac(message, signature);
+
+ // Drop one more byte.
+ signature.resize(signature.length() - 1);
+
+ AuthorizationSet begin_params(client_params());
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_VERIFY, begin_params));
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH, FinishOperation(signature, &result));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, HmacSha384Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate missing digests for HMAC.
+ return;
+
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_384)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128));
+ string message = "123456789012345678901234567890123456789012345678";
+ string signature;
+ MacMessage(message, &signature, 384);
+ VerifyMac(message, signature);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(VerificationOperationsTest, HmacSha512Success) {
+ if (GetParam()->minimal_digest_set())
+ // Can't emulate missing digests for HMAC.
+ return;
+
+ GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_512)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128));
+ string message = "123456789012345678901234567890123456789012345678";
+ string signature;
+ MacMessage(message, &signature, 512);
+ VerifyMac(message, signature);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+typedef Keymaster2Test ExportKeyTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, ExportKeyTest, test_params);
+#if RSA_TEST
+TEST_P(ExportKeyTest, RsaSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ string export_data;
+ ASSERT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &export_data));
+ EXPECT_GT(export_data.length(), 0U);
+
+ // TODO(swillden): Verify that the exported key is actually usable to verify signatures.
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+#endif
+#if EC_TEST
+TEST_P(ExportKeyTest, EcdsaSuccess) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE)));
+ string export_data;
+ ASSERT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &export_data));
+ EXPECT_GT(export_data.length(), 0U);
+
+ // TODO(swillden): Verify that the exported key is actually usable to verify signatures.
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(ExportKeyTest, RsaUnsupportedKeyFormat) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ string export_data;
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, ExportKey(KM_KEY_FORMAT_PKCS8, &export_data));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(ExportKeyTest, RsaCorruptedKeyBlob) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)));
+ corrupt_key_blob();
+ string export_data;
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, ExportKey(KM_KEY_FORMAT_X509, &export_data));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+#endif
+#if AES_TEST
+TEST_P(ExportKeyTest, AesKeyExportFails) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().AesEncryptionKey(128)));
+ string export_data;
+
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, ExportKey(KM_KEY_FORMAT_X509, &export_data));
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, ExportKey(KM_KEY_FORMAT_PKCS8, &export_data));
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_KEY_FORMAT, ExportKey(KM_KEY_FORMAT_RAW, &export_data));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+static string read_file(const string& file_name) {
+ ifstream file_stream(file_name, std::ios::binary);
+ istreambuf_iterator<char> file_begin(file_stream);
+ istreambuf_iterator<char> file_end;
+ return string(file_begin, file_end);
+}
+
+typedef Keymaster2Test ImportKeyTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, ImportKeyTest, test_params);
+#if RSA_TEST
+TEST_P(ImportKeyTest, RsaSuccess) {
+ string pk8_key = read_file("rsa_privkey_pk8.der");
+ ASSERT_EQ(633U, pk8_key.size());
+
+ ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+ .RsaSigningKey(1024, 65537)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE),
+ KM_KEY_FORMAT_PKCS8, pk8_key));
+
+ // Check values derived from the key.
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_KEY_SIZE, 1024));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+ // And values provided by AndroidKeymaster
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME));
+
+ string message(1024 / 8, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+ VerifyMessage(message, signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(ImportKeyTest, RsaKeySizeMismatch) {
+ string pk8_key = read_file("rsa_privkey_pk8.der");
+ ASSERT_EQ(633U, pk8_key.size());
+ ASSERT_EQ(KM_ERROR_IMPORT_PARAMETER_MISMATCH,
+ ImportKey(AuthorizationSetBuilder()
+ .RsaSigningKey(2048 /* Doesn't match key */, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE),
+ KM_KEY_FORMAT_PKCS8, pk8_key));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(ImportKeyTest, RsaPublicExponenMismatch) {
+ string pk8_key = read_file("rsa_privkey_pk8.der");
+ ASSERT_EQ(633U, pk8_key.size());
+ ASSERT_EQ(KM_ERROR_IMPORT_PARAMETER_MISMATCH,
+ ImportKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3 /* Doesnt' match key */)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE),
+ KM_KEY_FORMAT_PKCS8, pk8_key));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if EC_TEST
+TEST_P(ImportKeyTest, EcdsaSuccess) {
+ string pk8_key = read_file("ec_privkey_pk8.der");
+ ASSERT_EQ(138U, pk8_key.size());
+
+ ASSERT_EQ(KM_ERROR_OK,
+ ImportKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(KM_DIGEST_NONE),
+ KM_KEY_FORMAT_PKCS8, pk8_key));
+
+ // Check values derived from the key.
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_EC));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_KEY_SIZE, 256));
+
+ // And values provided by AndroidKeymaster
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME));
+
+ string message(32, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE);
+ VerifyMessage(message, signature, KM_DIGEST_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(ImportKeyTest, EcdsaSizeSpecified) {
+ string pk8_key = read_file("ec_privkey_pk8.der");
+ ASSERT_EQ(138U, pk8_key.size());
+
+ ASSERT_EQ(KM_ERROR_OK,
+ ImportKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(KM_DIGEST_NONE),
+ KM_KEY_FORMAT_PKCS8, pk8_key));
+
+ // Check values derived from the key.
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_EC));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_KEY_SIZE, 256));
+
+ // And values provided by AndroidKeymaster
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME));
+
+ string message(32, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE);
+ VerifyMessage(message, signature, KM_DIGEST_NONE);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(ImportKeyTest, EcdsaSizeMismatch) {
+ string pk8_key = read_file("ec_privkey_pk8.der");
+ ASSERT_EQ(138U, pk8_key.size());
+ ASSERT_EQ(KM_ERROR_IMPORT_PARAMETER_MISMATCH,
+ ImportKey(AuthorizationSetBuilder()
+ .EcdsaSigningKey(224 /* Doesn't match key */)
+ .Digest(KM_DIGEST_NONE),
+ KM_KEY_FORMAT_PKCS8, pk8_key));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if AES_TEST
+TEST_P(ImportKeyTest, AesKeySuccess) {
+ char key_data[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ string key(key_data, sizeof(key_data));
+ ASSERT_EQ(KM_ERROR_OK,
+ ImportKey(AuthorizationSetBuilder().AesEncryptionKey(128).EcbMode().Authorization(
+ TAG_PADDING, KM_PAD_PKCS7),
+ KM_KEY_FORMAT_RAW, key));
+
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME));
+
+ string message = "Hello World!";
+ string ciphertext = EncryptMessage(message, KM_MODE_ECB, KM_PAD_PKCS7);
+ string plaintext = DecryptMessage(ciphertext, KM_MODE_ECB, KM_PAD_PKCS7);
+ EXPECT_EQ(message, plaintext);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if HMAC_TEST
+TEST_P(ImportKeyTest, HmacSha256KeySuccess) {
+ char key_data[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ string key(key_data, sizeof(key_data));
+ ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+ .HmacKey(sizeof(key_data) * 8)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Authorization(TAG_MIN_MAC_LENGTH, 256),
+ KM_KEY_FORMAT_RAW, key));
+
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ORIGIN, KM_ORIGIN_IMPORTED));
+ EXPECT_TRUE(contains(sw_enforced(), KM_TAG_CREATION_DATETIME));
+
+ string message = "Hello World!";
+ string signature;
+ MacMessage(message, &signature, 256);
+ VerifyMac(message, signature);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+typedef Keymaster2Test EncryptionOperationsTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, EncryptionOperationsTest, test_params);
+#if RSA_TEST
+TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(256, 3).Padding(KM_PAD_NONE)));
+
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(string(message), KM_PAD_NONE);
+ EXPECT_EQ(256U / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message), KM_PAD_NONE);
+ EXPECT_EQ(256U / 8, ciphertext2.size());
+
+ // Unpadded RSA is deterministic
+ EXPECT_EQ(ciphertext1, ciphertext2);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaNoPaddingTooShort) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(256, 3).Padding(KM_PAD_NONE)));
+
+ string message = "1";
+
+ string ciphertext = EncryptMessage(message, KM_PAD_NONE);
+ EXPECT_EQ(256U / 8, ciphertext.size());
+
+ string expected_plaintext = string(256 / 8 - 1, 0) + message;
+ string plaintext = DecryptMessage(ciphertext, KM_PAD_NONE);
+
+ EXPECT_EQ(expected_plaintext, plaintext);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaNoPaddingTooLong) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(256, 3).Padding(KM_PAD_NONE)));
+
+ string message = "123456789012345678901234567890123";
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, UpdateOperation(message, &result, &input_consumed));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaNoPaddingLargerThanModulus) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(256, 3).Padding(KM_PAD_NONE)));
+
+ string exported;
+ ASSERT_EQ(KM_ERROR_OK, ExportKey(KM_KEY_FORMAT_X509, &exported));
+
+ const uint8_t* p = reinterpret_cast<const uint8_t*>(exported.data());
+ unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(
+ d2i_PUBKEY(nullptr /* alloc new */, &p, exported.size()));
+ unique_ptr<RSA, RSA_Delete> rsa(EVP_PKEY_get1_RSA(pkey.get()));
+
+ size_t modulus_len = BN_num_bytes(rsa->n);
+ ASSERT_EQ(256U / 8, modulus_len);
+ unique_ptr<uint8_t[]> modulus_buf(new uint8_t[modulus_len]);
+ BN_bn2bin(rsa->n, modulus_buf.get());
+
+ // The modulus is too big to encrypt.
+ string message(reinterpret_cast<const char*>(modulus_buf.get()), modulus_len);
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, FinishOperation(&result));
+
+ // One smaller than the modulus is okay.
+ BN_sub(rsa->n, rsa->n, BN_value_one());
+ modulus_len = BN_num_bytes(rsa->n);
+ ASSERT_EQ(256U / 8, modulus_len);
+ BN_bn2bin(rsa->n, modulus_buf.get());
+ message = string(reinterpret_cast<const char*>(modulus_buf.get()), modulus_len);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&result));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(EncryptionOperationsTest, RsaOaepSuccess) {
+ size_t key_size = 768;
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(key_size, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_SHA_2_256)));
+
+ string message = "Hello";
+ string ciphertext1 = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(key_size / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(key_size / 8, ciphertext2.size());
+
+ // OAEP randomizes padding so every result should be different.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(EncryptionOperationsTest, RsaOaepSha224Success) {
+ size_t key_size = 768;
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(key_size, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_SHA_2_224)));
+
+ string message = "Hello";
+ string ciphertext1 = EncryptMessage(string(message), KM_DIGEST_SHA_2_224, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(key_size / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message), KM_DIGEST_SHA_2_224, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(key_size / 8, ciphertext2.size());
+
+ // OAEP randomizes padding so every result should be different.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaOaepRoundTrip) {
+ size_t key_size = 768;
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(key_size, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_SHA_2_256)));
+ string message = "Hello World!";
+ string ciphertext = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(key_size / 8, ciphertext.size());
+
+ string plaintext = DecryptMessage(ciphertext, KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(message, plaintext);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaOaepSha224RoundTrip) {
+ size_t key_size = 768;
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(key_size, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_SHA_2_224)));
+ string message = "Hello World!";
+ string ciphertext = EncryptMessage(string(message), KM_DIGEST_SHA_2_224, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(key_size / 8, ciphertext.size());
+
+ string plaintext = DecryptMessage(ciphertext, KM_DIGEST_SHA_2_224, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(message, plaintext);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaOaepInvalidDigest) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(512, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_NONE)));
+ string message = "Hello World!";
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_NONE);
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_DIGEST, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaOaepUnauthorizedDigest) {
+ if (GetParam()->minimal_digest_set())
+ // We don't have two supported digests, so we can't try authorizing one and using another.
+ return;
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(512, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_SHA_2_256)));
+ string message = "Hello World!";
+ // Works because encryption is a public key operation.
+ EncryptMessage(string(message), KM_DIGEST_SHA1, KM_PAD_RSA_OAEP);
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA1);
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_DIGEST, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaOaepDecryptWithWrongDigest) {
+ if (GetParam()->minimal_digest_set())
+ // We don't have two supported digests, so we can't try encrypting with one and decrypting
+ // with another.
+ return;
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(768, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Digest(KM_DIGEST_SHA_2_384)));
+ string message = "Hello World!";
+ string ciphertext = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP);
+
+ string result;
+ size_t input_consumed;
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_384);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(&result));
+ EXPECT_EQ(0U, result.size());
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaOaepTooLarge) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(512, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_SHA1)));
+ string message = "12345678901234567890123";
+ string result;
+ size_t input_consumed;
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA1);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&result));
+ EXPECT_EQ(0U, result.size());
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaOaepCorruptedDecrypt) {
+ size_t key_size = 768;
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(768, 3)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_SHA_2_256)));
+ string message = "Hello World!";
+ string ciphertext = EncryptMessage(string(message), KM_DIGEST_SHA_2_256, KM_PAD_RSA_OAEP);
+ EXPECT_EQ(key_size / 8, ciphertext.size());
+
+ // Corrupt the ciphertext
+ ciphertext[key_size / 8 / 2]++;
+
+ string result;
+ size_t input_consumed;
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_OAEP);
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(&result));
+ EXPECT_EQ(0U, result.size());
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(EncryptionOperationsTest, RsaPkcs1Success) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(512, 3).Padding(
+ KM_PAD_RSA_PKCS1_1_5_ENCRYPT)));
+ string message = "Hello World!";
+ string ciphertext1 = EncryptMessage(message, KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ EXPECT_EQ(512U / 8, ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(message, KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ EXPECT_EQ(512U / 8, ciphertext2.size());
+
+ // PKCS1 v1.5 randomizes padding so every result should be different.
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaPkcs1RoundTrip) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(512, 3).Padding(
+ KM_PAD_RSA_PKCS1_1_5_ENCRYPT)));
+ string message = "Hello World!";
+ string ciphertext = EncryptMessage(message, KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ EXPECT_EQ(512U / 8, ciphertext.size());
+
+ string plaintext = DecryptMessage(ciphertext, KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ EXPECT_EQ(message, plaintext);
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+#endif
+#if CHECK_FAIL
+TEST_P(EncryptionOperationsTest, RsaRoundTripAllCombinations) {
+ size_t key_size = 2048;
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaEncryptionKey(key_size, 3)
+ .Padding(KM_PAD_RSA_PKCS1_1_5_ENCRYPT)
+ .Padding(KM_PAD_RSA_OAEP)
+ .Digest(KM_DIGEST_NONE)
+ .Digest(KM_DIGEST_MD5)
+ .Digest(KM_DIGEST_SHA1)
+ .Digest(KM_DIGEST_SHA_2_224)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Digest(KM_DIGEST_SHA_2_384)
+ .Digest(KM_DIGEST_SHA_2_512)));
+
+ string message = "Hello World!";
+
+ keymaster_padding_t padding_modes[] = {KM_PAD_RSA_OAEP, KM_PAD_RSA_PKCS1_1_5_ENCRYPT};
+ keymaster_digest_t digests[] = {
+ KM_DIGEST_NONE, KM_DIGEST_MD5, KM_DIGEST_SHA1, KM_DIGEST_SHA_2_224,
+ KM_DIGEST_SHA_2_256, KM_DIGEST_SHA_2_384, KM_DIGEST_SHA_2_512,
+ };
+
+ for (auto padding : padding_modes)
+ for (auto digest : digests) {
+ if (padding == KM_PAD_RSA_OAEP && digest == KM_DIGEST_NONE)
+ // OAEP requires a digest.
+ continue;
+
+ string ciphertext = EncryptMessage(message, digest, padding);
+ EXPECT_EQ(key_size / 8, ciphertext.size());
+
+ string plaintext = DecryptMessage(ciphertext, digest, padding);
+ EXPECT_EQ(message, plaintext);
+ }
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(40, GetParam()->keymaster0_calls());
+}
+#endif
+#if RSA_TEST
+TEST_P(EncryptionOperationsTest, RsaPkcs1TooLarge) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(512, 3).Padding(
+ KM_PAD_RSA_PKCS1_1_5_ENCRYPT)));
+ string message = "123456789012345678901234567890123456789012345678901234";
+ string result;
+ size_t input_consumed;
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&result));
+ EXPECT_EQ(0U, result.size());
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaPkcs1CorruptedDecrypt) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(512, 3).Padding(
+ KM_PAD_RSA_PKCS1_1_5_ENCRYPT)));
+ string message = "Hello World!";
+ string ciphertext = EncryptMessage(string(message), KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ EXPECT_EQ(512U / 8, ciphertext.size());
+
+ // Corrupt the ciphertext
+ ciphertext[512 / 8 / 2]++;
+
+ string result;
+ size_t input_consumed;
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &result, &input_consumed));
+ EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, FinishOperation(&result));
+ EXPECT_EQ(0U, result.size());
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(4, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, RsaEncryptWithSigningKey) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().RsaSigningKey(256, 3).Padding(KM_PAD_NONE)));
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ ASSERT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(2, GetParam()->keymaster0_calls());
+}
+#endif
+#if EC_TEST
+TEST_P(EncryptionOperationsTest, EcdsaEncrypt) {
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(224).Digest(KM_DIGEST_NONE)));
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_ENCRYPT));
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_DECRYPT));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(3, GetParam()->keymaster0_calls());
+}
+#endif
+#if HMAC_TEST
+TEST_P(EncryptionOperationsTest, HmacEncrypt) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .HmacKey(128)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Padding(KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_ENCRYPT));
+ ASSERT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE, BeginOperation(KM_PURPOSE_DECRYPT));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesEcbRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Padding(KM_PAD_NONE)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string ciphertext1 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+
+ string ciphertext2 = EncryptMessage(string(message), KM_MODE_ECB, KM_PAD_NONE);
+ EXPECT_EQ(message.size(), ciphertext2.size());
+
+ // ECB is deterministic.
+ EXPECT_EQ(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1, KM_MODE_ECB, KM_PAD_NONE);
+ EXPECT_EQ(message, plaintext);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesEcbNotAuthorized) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Padding(KM_PAD_NONE)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_BLOCK_MODE, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesEcbNoPaddingWrongInputSize) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Padding(KM_PAD_NONE)));
+ // Message is slightly shorter than two blocks.
+ string message = "1234567890123456789012345678901";
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ string ciphertext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &ciphertext, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_INVALID_INPUT_LENGTH, FinishOperation(&ciphertext));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesEcbPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Authorization(TAG_PADDING, KM_PAD_PKCS7)));
+
+ // Try various message lengths; all should work.
+ for (size_t i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string ciphertext = EncryptMessage(message, KM_MODE_ECB, KM_PAD_PKCS7);
+ EXPECT_EQ(i + 16 - (i % 16), ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext, KM_MODE_ECB, KM_PAD_PKCS7);
+ EXPECT_EQ(message, plaintext);
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesEcbNoPaddingKeyWithPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)));
+
+ // Try various message lengths; all should fail.
+ for (size_t i = 0; i < 32; ++i) {
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB);
+ begin_params.push_back(TAG_PADDING, KM_PAD_PKCS7);
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PADDING_MODE,
+ BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Authorization(TAG_PADDING, KM_PAD_PKCS7)));
+
+ string message = "a";
+ string ciphertext = EncryptMessage(message, KM_MODE_ECB, KM_PAD_PKCS7);
+ EXPECT_EQ(16U, ciphertext.size());
+ EXPECT_NE(ciphertext, message);
+ ++ciphertext[ciphertext.size() / 2];
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB);
+ begin_params.push_back(TAG_PADDING, KM_PAD_PKCS7);
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext, &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, FinishOperation(&plaintext));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesCtrRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR)
+ .Padding(KM_PAD_NONE)));
+ string message = "123";
+ string iv1;
+ string ciphertext1 = EncryptMessage(message, KM_MODE_CTR, KM_PAD_NONE, &iv1);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+ EXPECT_EQ(16U, iv1.size());
+
+ string iv2;
+ string ciphertext2 = EncryptMessage(message, KM_MODE_CTR, KM_PAD_NONE, &iv2);
+ EXPECT_EQ(message.size(), ciphertext2.size());
+ EXPECT_EQ(16U, iv2.size());
+
+ // IVs should be random, so ciphertexts should differ.
+ EXPECT_NE(iv1, iv2);
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1, KM_MODE_CTR, KM_PAD_NONE, iv1);
+ EXPECT_EQ(message, plaintext);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesCtrIncremental) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR)
+ .Padding(KM_PAD_NONE)));
+
+ int increment = 15;
+ string message(239, 'a');
+ AuthorizationSet input_params(client_params());
+ input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR);
+ input_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ AuthorizationSet output_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, input_params, &output_params));
+
+ string ciphertext;
+ size_t input_consumed;
+ for (size_t i = 0; i < message.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(message.substr(i, increment), &ciphertext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+ EXPECT_EQ(message.size(), ciphertext.size());
+
+ // Move TAG_NONCE into input_params
+ input_params.Reinitialize(output_params);
+ input_params.push_back(client_params());
+ input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR);
+ input_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ output_params.Clear();
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, input_params, &output_params));
+ string plaintext;
+ for (size_t i = 0; i < ciphertext.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(ciphertext.substr(i, increment), &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+ EXPECT_EQ(ciphertext.size(), plaintext.size());
+ EXPECT_EQ(message, plaintext);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+struct AesCtrSp80038aTestVector {
+ const char* key;
+ const char* nonce;
+ const char* plaintext;
+ const char* ciphertext;
+};
+
+// These test vectors are taken from
+// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section F.5.
+static const AesCtrSp80038aTestVector kAesCtrSp80038aTestVectors[] = {
+ // AES-128
+ {
+ "2b7e151628aed2a6abf7158809cf4f3c", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+ "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff"
+ "5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee",
+ },
+ // AES-192
+ {
+ "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+ "1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e94"
+ "1e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050",
+ },
+ // AES-256
+ {
+ "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+ "601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"
+ "2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6",
+ },
+};
+
+TEST_P(EncryptionOperationsTest, AesCtrSp80038aTestVector) {
+ for (size_t i = 0; i < 3; i++) {
+ const AesCtrSp80038aTestVector& test(kAesCtrSp80038aTestVectors[i]);
+ const string key = hex2str(test.key);
+ const string nonce = hex2str(test.nonce);
+ const string plaintext = hex2str(test.plaintext);
+ const string ciphertext = hex2str(test.ciphertext);
+ CheckAesCtrTestVector(key, nonce, plaintext, ciphertext);
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesCtrInvalidPaddingMode) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR)
+ .Authorization(TAG_PADDING, KM_PAD_PKCS7)));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PADDING_MODE, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesCtrInvalidCallerNonce) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR)
+ .Authorization(TAG_CALLER_NONCE)
+ .Padding(KM_PAD_NONE)));
+
+ AuthorizationSet input_params(client_params());
+ input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR);
+ input_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ input_params.push_back(TAG_NONCE, "123", 3);
+ EXPECT_EQ(KM_ERROR_INVALID_NONCE, BeginOperation(KM_PURPOSE_ENCRYPT, input_params));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesCbcRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Padding(KM_PAD_NONE)));
+ // Two-block message.
+ string message = "12345678901234567890123456789012";
+ string iv1;
+ string ciphertext1 = EncryptMessage(message, KM_MODE_CBC, KM_PAD_NONE, &iv1);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+
+ string iv2;
+ string ciphertext2 = EncryptMessage(message, KM_MODE_CBC, KM_PAD_NONE, &iv2);
+ EXPECT_EQ(message.size(), ciphertext2.size());
+
+ // IVs should be random, so ciphertexts should differ.
+ EXPECT_NE(iv1, iv2);
+ EXPECT_NE(ciphertext1, ciphertext2);
+
+ string plaintext = DecryptMessage(ciphertext1, KM_MODE_CBC, KM_PAD_NONE, iv1);
+ EXPECT_EQ(message, plaintext);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesCallerNonce) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Authorization(TAG_CALLER_NONCE)
+ .Padding(KM_PAD_NONE)));
+ string message = "12345678901234567890123456789012";
+ string iv1;
+ // Don't specify nonce, should get a random one.
+ string ciphertext1 = EncryptMessage(message, KM_MODE_CBC, KM_PAD_NONE, &iv1);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+ EXPECT_EQ(16U, iv1.size());
+
+ string plaintext = DecryptMessage(ciphertext1, KM_MODE_CBC, KM_PAD_NONE, iv1);
+ EXPECT_EQ(message, plaintext);
+
+ // Now specify a nonce, should also work.
+ AuthorizationSet input_params(client_params());
+ AuthorizationSet update_params;
+ AuthorizationSet output_params;
+ input_params.push_back(TAG_NONCE, "abcdefghijklmnop", 16);
+ input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC);
+ input_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ string ciphertext2 =
+ ProcessMessage(KM_PURPOSE_ENCRYPT, message, input_params, update_params, &output_params);
+
+ // Decrypt with correct nonce.
+ plaintext = ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext2, input_params, update_params,
+ &output_params);
+ EXPECT_EQ(message, plaintext);
+
+ // Now try with wrong nonce.
+ input_params.Reinitialize(client_params());
+ input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC);
+ input_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ input_params.push_back(TAG_NONCE, "aaaaaaaaaaaaaaaa", 16);
+ plaintext = ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext2, input_params, update_params,
+ &output_params);
+ EXPECT_NE(message, plaintext);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesCallerNonceProhibited) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Padding(KM_PAD_NONE)));
+
+ string message = "12345678901234567890123456789012";
+ string iv1;
+ // Don't specify nonce, should get a random one.
+ string ciphertext1 = EncryptMessage(message, KM_MODE_CBC, KM_PAD_NONE, &iv1);
+ EXPECT_EQ(message.size(), ciphertext1.size());
+ EXPECT_EQ(16U, iv1.size());
+
+ string plaintext = DecryptMessage(ciphertext1, KM_MODE_CBC, KM_PAD_NONE, iv1);
+ EXPECT_EQ(message, plaintext);
+
+ // Now specify a nonce, should fail.
+ AuthorizationSet input_params(client_params());
+ AuthorizationSet update_params;
+ AuthorizationSet output_params;
+ input_params.push_back(TAG_NONCE, "abcdefghijklmnop", 16);
+ input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC);
+ input_params.push_back(TAG_PADDING, KM_PAD_NONE);
+
+ EXPECT_EQ(KM_ERROR_CALLER_NONCE_PROHIBITED,
+ BeginOperation(KM_PURPOSE_ENCRYPT, input_params, &output_params));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesCbcIncrementalNoPadding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Padding(KM_PAD_NONE)));
+
+ int increment = 15;
+ string message(240, 'a');
+ AuthorizationSet input_params(client_params());
+ input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC);
+ input_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ AuthorizationSet output_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, input_params, &output_params));
+
+ string ciphertext;
+ size_t input_consumed;
+ for (size_t i = 0; i < message.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(message.substr(i, increment), &ciphertext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+ EXPECT_EQ(message.size(), ciphertext.size());
+
+ // Move TAG_NONCE into input_params
+ input_params.Reinitialize(output_params);
+ input_params.push_back(client_params());
+ input_params.push_back(TAG_BLOCK_MODE, KM_MODE_CBC);
+ input_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ output_params.Clear();
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, input_params, &output_params));
+ string plaintext;
+ for (size_t i = 0; i < ciphertext.size(); i += increment)
+ EXPECT_EQ(KM_ERROR_OK,
+ UpdateOperation(ciphertext.substr(i, increment), &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+ EXPECT_EQ(ciphertext.size(), plaintext.size());
+ EXPECT_EQ(message, plaintext);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesCbcPkcs7Padding) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CBC)
+ .Authorization(TAG_PADDING, KM_PAD_PKCS7)));
+
+ // Try various message lengths; all should work.
+ for (size_t i = 0; i < 32; ++i) {
+ string message(i, 'a');
+ string iv;
+ string ciphertext = EncryptMessage(message, KM_MODE_CBC, KM_PAD_PKCS7, &iv);
+ EXPECT_EQ(i + 16 - (i % 16), ciphertext.size());
+ string plaintext = DecryptMessage(ciphertext, KM_MODE_CBC, KM_PAD_PKCS7, iv);
+ EXPECT_EQ(message, plaintext);
+ }
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesGcmRoundTripSuccess) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ // Grab nonce
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ EXPECT_EQ(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmTooShortTag) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 96);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH,
+ BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmTooShortTagOnDecrypt) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ // Grab nonce
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.Reinitialize(client_params());
+ begin_params.push_back(begin_out_params);
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 96);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_INVALID_MAC_LENGTH, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmCorruptKey) {
+ uint8_t nonce[] = {
+ 0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f,
+ };
+ uint8_t ciphertext[] = {
+ 0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc, 0xd2, 0xcb, 0x16,
+ 0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78, 0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a,
+ 0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d, 0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76,
+ 0x76, 0x5e, 0xfb, 0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd,
+ 0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0,
+ };
+ string ciphertext_str(reinterpret_cast<char*>(ciphertext), sizeof(ciphertext));
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ begin_params.push_back(TAG_NONCE, nonce, sizeof(nonce));
+
+ string plaintext;
+ size_t input_consumed;
+
+ // Import correct key and decrypt
+ uint8_t good_key[] = {
+ 0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+ 0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+ };
+ string good_key_str(reinterpret_cast<char*>(good_key), sizeof(good_key));
+ ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_CALLER_NONCE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128),
+ KM_KEY_FORMAT_RAW, good_key_str));
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext_str, &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ // Import bad key and decrypt
+ uint8_t bad_key[] = {
+ 0xbb, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+ 0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+ };
+ string bad_key_str(reinterpret_cast<char*>(bad_key), sizeof(bad_key));
+ ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128),
+ KM_KEY_FORMAT_RAW, bad_key_str));
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(ciphertext_str, &plaintext, &input_consumed));
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmAadNoData) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string aad = "123456789012345678";
+ string empty_message;
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, empty_message, &update_out_params,
+ &ciphertext, &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ // Grab nonce
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ EXPECT_EQ(empty_message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesGcmIncremental) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "b", 1);
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+
+ // Send AAD, incrementally
+ for (int i = 0; i < 1000; ++i) {
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+ EXPECT_EQ(0U, ciphertext.size());
+ }
+
+ // Now send data, incrementally, no data.
+ AuthorizationSet empty_params;
+ for (int i = 0; i < 1000; ++i) {
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(empty_params, "a", &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(1U, input_consumed);
+ }
+ EXPECT_EQ(1000U, ciphertext.size());
+
+ // And finish.
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+ EXPECT_EQ(1016U, ciphertext.size());
+
+ // Grab nonce
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
+
+ // Send AAD, incrementally, no data
+ for (int i = 0; i < 1000; ++i) {
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "", &update_out_params, &plaintext,
+ &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+ EXPECT_EQ(0U, plaintext.size());
+ }
+
+ // Now send data, incrementally.
+ for (size_t i = 0; i < ciphertext.length(); ++i) {
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(empty_params, string(ciphertext.data() + i, 1),
+ &update_out_params, &plaintext, &input_consumed));
+ EXPECT_EQ(1U, input_consumed);
+ }
+ EXPECT_EQ(1000U, plaintext.size());
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmMultiPartAad) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foo", 3);
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+
+ // No data, AAD only.
+ string ciphertext;
+ size_t input_consumed;
+ AuthorizationSet update_out_params;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, "" /* message */, &update_out_params,
+ &ciphertext, &input_consumed));
+ EXPECT_EQ(0U, input_consumed);
+
+ // AAD and data.
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ // Grab nonce.
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt
+ update_params.Clear();
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foofoo", 6);
+
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params));
+ string plaintext;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&plaintext));
+
+ EXPECT_EQ(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmBadAad) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string message = "12345678901234567890123456789012";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foobar", 6);
+
+ AuthorizationSet finish_params;
+ AuthorizationSet finish_out_params;
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ AuthorizationSet update_out_params;
+ string ciphertext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ // Grab nonce
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ update_params.Clear();
+ update_params.push_back(TAG_ASSOCIATED_DATA, "barfoo" /* Wrong AAD */, 6);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ string plaintext;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(EncryptionOperationsTest, AesGcmWrongNonce) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string message = "12345678901234567890123456789012";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, "foobar", 6);
+
+ // Encrypt
+ AuthorizationSet begin_out_params;
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ AuthorizationSet update_out_params;
+ string ciphertext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ begin_params.push_back(TAG_NONCE, "123456789012", 12);
+
+ // Decrypt
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ string plaintext;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
+
+ // With wrong nonce, should have gotten garbage plaintext.
+ EXPECT_NE(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if AES_TEST
+TEST_P(EncryptionOperationsTest, AesGcmCorruptTag) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+ string aad = "foobar";
+ string message = "123456789012345678901234567890123456";
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_GCM);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ begin_params.push_back(TAG_MAC_LENGTH, 128);
+ AuthorizationSet begin_out_params;
+
+ AuthorizationSet update_params;
+ update_params.push_back(TAG_ASSOCIATED_DATA, aad.data(), aad.size());
+
+ // Encrypt
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params, &begin_out_params));
+ AuthorizationSet update_out_params;
+ string ciphertext;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, &update_out_params, &ciphertext,
+ &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&ciphertext));
+
+ // Corrupt tag
+ (*ciphertext.rbegin())++;
+
+ // Grab nonce.
+ EXPECT_NE(-1, begin_out_params.find(TAG_NONCE));
+ begin_params.push_back(begin_out_params);
+
+ // Decrypt.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_DECRYPT, begin_params, &begin_out_params));
+ string plaintext;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, ciphertext, &update_out_params,
+ &plaintext, &input_consumed));
+ EXPECT_EQ(ciphertext.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_VERIFICATION_FAILED, FinishOperation(&plaintext));
+
+ EXPECT_EQ(message, plaintext);
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+typedef Keymaster2Test MaxOperationsTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, MaxOperationsTest, test_params);
+#if MAX_TEST
+TEST_P(MaxOperationsTest, TestLimit) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .EcbMode()
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MAX_USES_PER_BOOT, 3)));
+
+ string message = "1234567890123456";
+ string ciphertext1 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+ string ciphertext2 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+ string ciphertext3 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+
+ // Fourth time should fail.
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ EXPECT_EQ(KM_ERROR_KEY_MAX_OPS_EXCEEDED, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(MaxOperationsTest, TestAbort) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .EcbMode()
+ .Authorization(TAG_PADDING, KM_PAD_NONE)
+ .Authorization(TAG_MAX_USES_PER_BOOT, 3)));
+
+ string message = "1234567890123456";
+ string ciphertext1 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+ string ciphertext2 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+ string ciphertext3 = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+
+ // Fourth time should fail.
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ EXPECT_EQ(KM_ERROR_KEY_MAX_OPS_EXCEEDED, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+typedef Keymaster2Test AddEntropyTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, AddEntropyTest, test_params);
+#if ENTROPY_TEST
+TEST_P(AddEntropyTest, AddEntropy) {
+ // There's no obvious way to test that entropy is actually added, but we can test that the API
+ // doesn't blow up or return an error.
+ EXPECT_EQ(KM_ERROR_OK,
+ device()->add_rng_entropy(device(), reinterpret_cast<const uint8_t*>("foo"), 3));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+typedef Keymaster2Test Keymaster0AdapterTest;
+#if 0
+INSTANTIATE_TEST_CASE_P(
+ AndroidKeymasterTest, Keymaster0AdapterTest,
+ ::testing::Values(
+ InstanceCreatorPtr(new Keymaster0AdapterTestInstanceCreator(true /* support_ec */)),
+ InstanceCreatorPtr(new Keymaster0AdapterTestInstanceCreator(false /* support_ec */))));
+#endif
+TEST_P(Keymaster0AdapterTest, OldSoftwareKeymaster1RsaBlob) {
+ // Load and use an old-style Keymaster1 software key blob. These blobs contain OCB-encrypted
+ // key data.
+ string km1_sw = read_file("km1_sw_rsa_512.blob");
+ EXPECT_EQ(486U, km1_sw.length());
+
+ uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km1_sw.length()));
+ memcpy(key_data, km1_sw.data(), km1_sw.length());
+ set_key_blob(key_data, km1_sw.length());
+
+ string message(64, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(Keymaster0AdapterTest, UnversionedSoftwareKeymaster1RsaBlob) {
+ // Load and use an old-style Keymaster1 software key blob, without the version byte. These
+ // blobs contain OCB-encrypted key data.
+ string km1_sw = read_file("km1_sw_rsa_512_unversioned.blob");
+ EXPECT_EQ(477U, km1_sw.length());
+
+ uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km1_sw.length()));
+ memcpy(key_data, km1_sw.data(), km1_sw.length());
+ set_key_blob(key_data, km1_sw.length());
+
+ string message(64, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(Keymaster0AdapterTest, OldSoftwareKeymaster1EcdsaBlob) {
+ // Load and use an old-style Keymaster1 software key blob. These blobs contain OCB-encrypted
+ // key data.
+ string km1_sw = read_file("km1_sw_ecdsa_256.blob");
+ EXPECT_EQ(270U, km1_sw.length());
+
+ uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km1_sw.length()));
+ memcpy(key_data, km1_sw.data(), km1_sw.length());
+ set_key_blob(key_data, km1_sw.length());
+
+ string message(32, static_cast<char>(0xFF));
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+struct Malloc_Delete {
+ void operator()(void* p) { free(p); }
+};
+
+TEST_P(Keymaster0AdapterTest, OldSoftwareKeymaster0RsaBlob) {
+ // Load and use an old softkeymaster blob. These blobs contain PKCS#8 key data.
+ string km0_sw = read_file("km0_sw_rsa_512.blob");
+ EXPECT_EQ(333U, km0_sw.length());
+
+ uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km0_sw.length()));
+ memcpy(key_data, km0_sw.data(), km0_sw.length());
+ set_key_blob(key_data, km0_sw.length());
+
+ string message(64, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(Keymaster0AdapterTest, OldSwKeymaster0RsaBlobGetCharacteristics) {
+ // Load and use an old softkeymaster blob. These blobs contain PKCS#8 key data.
+ string km0_sw = read_file("km0_sw_rsa_512.blob");
+ EXPECT_EQ(333U, km0_sw.length());
+
+ uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km0_sw.length()));
+ memcpy(key_data, km0_sw.data(), km0_sw.length());
+ set_key_blob(key_data, km0_sw.length());
+
+ EXPECT_EQ(KM_ERROR_OK, GetCharacteristics());
+ EXPECT_TRUE(contains(sw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_KEY_SIZE, 512));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_RSA_PUBLIC_EXPONENT, 3));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_DIGEST, KM_DIGEST_NONE));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_PADDING, KM_PAD_NONE));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_SIGN));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_VERIFY));
+ EXPECT_TRUE(sw_enforced().GetTagValue(TAG_ALL_USERS));
+ EXPECT_TRUE(sw_enforced().GetTagValue(TAG_NO_AUTH_REQUIRED));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+
+TEST_P(Keymaster0AdapterTest, OldHwKeymaster0RsaBlob) {
+ // Load and use an old softkeymaster blob. These blobs contain PKCS#8 key data.
+ string km0_sw = read_file("km0_sw_rsa_512.blob");
+ EXPECT_EQ(333U, km0_sw.length());
+
+ // The keymaster0 wrapper swaps the old softkeymaster leading 'P' for a 'Q' to make the key not
+ // be recognized as a software key. Do the same here to pretend this is a hardware key.
+ EXPECT_EQ('P', km0_sw[0]);
+ km0_sw[0] = 'Q';
+
+ uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km0_sw.length()));
+ memcpy(key_data, km0_sw.data(), km0_sw.length());
+ set_key_blob(key_data, km0_sw.length());
+
+ string message(64, 'a');
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_NONE, KM_PAD_NONE);
+ VerifyMessage(message, signature, KM_DIGEST_NONE, KM_PAD_NONE);
+
+ EXPECT_EQ(5, GetParam()->keymaster0_calls());
+}
+
+TEST_P(Keymaster0AdapterTest, OldHwKeymaster0RsaBlobGetCharacteristics) {
+ // Load and use an old softkeymaster blob. These blobs contain PKCS#8 key data.
+ string km0_sw = read_file("km0_sw_rsa_512.blob");
+ EXPECT_EQ(333U, km0_sw.length());
+
+ // The keymaster0 wrapper swaps the old softkeymaster leading 'P' for a 'Q' to make the key not
+ // be recognized as a software key. Do the same here to pretend this is a hardware key.
+ EXPECT_EQ('P', km0_sw[0]);
+ km0_sw[0] = 'Q';
+
+ uint8_t* key_data = reinterpret_cast<uint8_t*>(malloc(km0_sw.length()));
+ memcpy(key_data, km0_sw.data(), km0_sw.length());
+ set_key_blob(key_data, km0_sw.length());
+
+ EXPECT_EQ(KM_ERROR_OK, GetCharacteristics());
+ EXPECT_TRUE(contains(hw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_KEY_SIZE, 512));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_RSA_PUBLIC_EXPONENT, 3));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_NONE));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_MD5));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA1));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA_2_224));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA_2_256));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA_2_384));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_DIGEST, KM_DIGEST_SHA_2_512));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_NONE));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_ENCRYPT));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_RSA_PKCS1_1_5_SIGN));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_RSA_OAEP));
+ EXPECT_TRUE(contains(hw_enforced(), TAG_PADDING, KM_PAD_RSA_PSS));
+ EXPECT_EQ(15U, hw_enforced().size());
+
+ EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_SIGN));
+ EXPECT_TRUE(contains(sw_enforced(), TAG_PURPOSE, KM_PURPOSE_VERIFY));
+ EXPECT_TRUE(sw_enforced().GetTagValue(TAG_ALL_USERS));
+ EXPECT_TRUE(sw_enforced().GetTagValue(TAG_NO_AUTH_REQUIRED));
+
+ EXPECT_FALSE(contains(sw_enforced(), TAG_ALGORITHM, KM_ALGORITHM_RSA));
+ EXPECT_FALSE(contains(sw_enforced(), TAG_KEY_SIZE, 512));
+ EXPECT_FALSE(contains(sw_enforced(), TAG_RSA_PUBLIC_EXPONENT, 3));
+ EXPECT_FALSE(contains(sw_enforced(), TAG_DIGEST, KM_DIGEST_NONE));
+ EXPECT_FALSE(contains(sw_enforced(), TAG_PADDING, KM_PAD_NONE));
+
+ EXPECT_EQ(1, GetParam()->keymaster0_calls());
+}
+
+typedef Keymaster2Test AttestationTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, AttestationTest, test_params);
+
+#if ATTESTATIONTEST
+static X509* parse_cert_blob(const keymaster_blob_t& blob) {
+ const uint8_t* p = blob.data;
+ return d2i_X509(nullptr, &p, blob.data_length);
+}
+
+static bool verify_chain(const keymaster_cert_chain_t& chain) {
+ for (size_t i = 0; i < chain.entry_count - 1; ++i) {
+ keymaster_blob_t& key_cert_blob = chain.entries[i];
+ keymaster_blob_t& signing_cert_blob = chain.entries[i + 1];
+
+ X509_Ptr key_cert(parse_cert_blob(key_cert_blob));
+ X509_Ptr signing_cert(parse_cert_blob(signing_cert_blob));
+ EXPECT_TRUE(!!key_cert.get() && !!signing_cert.get());
+ if (!key_cert.get() || !signing_cert.get())
+ return false;
+
+ EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
+ EXPECT_TRUE(!!signing_pubkey.get());
+ if (!signing_pubkey.get())
+ return false;
+
+ EXPECT_EQ(1, X509_verify(key_cert.get(), signing_pubkey.get()))
+ << "Verification of certificate " << i << " failed";
+ }
+
+ return true;
+}
+
+// Extract attestation record from cert. Returned object is still part of cert; don't free it
+// separately.
+static ASN1_OCTET_STRING* get_attestation_record(X509* certificate) {
+ ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
+ EXPECT_TRUE(!!oid.get());
+ if (!oid.get())
+ return nullptr;
+
+ int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
+ EXPECT_NE(-1, location);
+ if (location == -1)
+ return nullptr;
+
+ X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
+ EXPECT_TRUE(!!attest_rec_ext);
+ if (!attest_rec_ext)
+ return nullptr;
+
+ ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
+ EXPECT_TRUE(!!attest_rec);
+ return attest_rec;
+}
+
+static bool verify_attestation_record(const string& challenge,
+ AuthorizationSet expected_sw_enforced,
+ AuthorizationSet expected_tee_enforced,
+ uint32_t expected_keymaster_version,
+ keymaster_security_level_t expected_keymaster_security_level,
+ const keymaster_blob_t& attestation_cert) {
+
+ X509_Ptr cert(parse_cert_blob(attestation_cert));
+ EXPECT_TRUE(!!cert.get());
+ if (!cert.get())
+ return false;
+
+ ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get());
+ EXPECT_TRUE(!!attest_rec);
+ if (!attest_rec)
+ return false;
+
+ AuthorizationSet att_sw_enforced;
+ AuthorizationSet att_tee_enforced;
+ uint32_t att_attestation_version;
+ uint32_t att_keymaster_version;
+ keymaster_security_level_t att_attestation_security_level;
+ keymaster_security_level_t att_keymaster_security_level;
+ keymaster_blob_t att_challenge = {};
+ keymaster_blob_t att_unique_id = {};
+
+ EXPECT_EQ(KM_ERROR_OK, parse_attestation_record(
+ attest_rec->data, attest_rec->length, &att_attestation_version,
+ &att_attestation_security_level, &att_keymaster_version,
+ &att_keymaster_security_level, &att_challenge, &att_sw_enforced,
+ &att_tee_enforced, &att_unique_id));
+
+ EXPECT_EQ(1U, att_attestation_version);
+ EXPECT_EQ(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, att_attestation_security_level);
+ EXPECT_EQ(expected_keymaster_version, att_keymaster_version);
+ EXPECT_EQ(expected_keymaster_security_level, att_keymaster_security_level);
+
+ EXPECT_EQ(challenge.length(), att_challenge.data_length);
+ EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data, challenge.length()));
+
+ // Add TAG_USER_ID to the relevant attestation list, because user IDs are not included in
+ // attestations, since they're meaningless off-device.
+ uint32_t user_id;
+ if (expected_sw_enforced.GetTagValue(TAG_USER_ID, &user_id))
+ att_sw_enforced.push_back(TAG_USER_ID, user_id);
+ if (expected_tee_enforced.GetTagValue(TAG_USER_ID, &user_id))
+ att_tee_enforced.push_back(TAG_USER_ID, user_id);
+
+ // Add TAG_INCLUDE_UNIQUE_ID to the relevant attestation list, because that tag is not included
+ // in the attestation.
+ if (expected_sw_enforced.GetTagValue(TAG_INCLUDE_UNIQUE_ID))
+ att_sw_enforced.push_back(TAG_INCLUDE_UNIQUE_ID);
+ if (expected_tee_enforced.GetTagValue(TAG_INCLUDE_UNIQUE_ID))
+ att_tee_enforced.push_back(TAG_INCLUDE_UNIQUE_ID);
+
+ att_sw_enforced.Sort();
+ expected_sw_enforced.Sort();
+ EXPECT_EQ(expected_sw_enforced, att_sw_enforced);
+
+ att_tee_enforced.Sort();
+ expected_tee_enforced.Sort();
+ EXPECT_EQ(expected_tee_enforced, att_tee_enforced);
+
+ return true;
+}
+#endif
+#if ATTESTATIONTEST
+TEST_P(AttestationTest, RsaAttestation) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .RsaSigningKey(256, 3)
+ .Digest(KM_DIGEST_NONE)
+ .Padding(KM_PAD_NONE)
+ .Authorization(TAG_INCLUDE_UNIQUE_ID)));
+
+ keymaster_cert_chain_t cert_chain;
+ EXPECT_EQ(KM_ERROR_OK, AttestKey("challenge", &cert_chain));
+ EXPECT_EQ(3U, cert_chain.entry_count);
+ EXPECT_TRUE(verify_chain(cert_chain));
+
+ uint32_t expected_keymaster_version;
+ keymaster_security_level_t expected_keymaster_security_level;
+ expected_keymaster_version = 2;
+ expected_keymaster_security_level = KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
+
+ EXPECT_TRUE(verify_attestation_record(
+ "challenge", sw_enforced(), hw_enforced(), expected_keymaster_version,
+ expected_keymaster_security_level, cert_chain.entries[0]));
+
+ keymaster_free_cert_chain(&cert_chain);
+}
+#endif
+#if ATTESTATIONTEST
+TEST_P(AttestationTest, EcAttestation) {
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(
+ KM_DIGEST_SHA_2_256)));
+
+ uint32_t expected_keymaster_version;
+ keymaster_security_level_t expected_keymaster_security_level;
+ expected_keymaster_version = 2;
+ expected_keymaster_security_level = KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
+
+ keymaster_cert_chain_t cert_chain;
+ EXPECT_EQ(KM_ERROR_OK, AttestKey("challenge", &cert_chain));
+ EXPECT_EQ(3U, cert_chain.entry_count);
+ EXPECT_TRUE(verify_chain(cert_chain));
+ EXPECT_TRUE(verify_attestation_record(
+ "challenge", sw_enforced(), hw_enforced(), expected_keymaster_version,
+ expected_keymaster_security_level, cert_chain.entries[0]));
+
+ keymaster_free_cert_chain(&cert_chain);
+}
+#endif
+typedef Keymaster2Test KeyUpgradeTest;
+INSTANTIATE_TEST_CASE_P(AndroidKeymasterTest, KeyUpgradeTest, test_params);
+#if KEYUPGRADETEST
+TEST_P(KeyUpgradeTest, AesVersionUpgrade) {
+ // A workaround for testing TEE based keymaster 2 from normal world
+ keymaster2_device_t* device = (keymaster2_device_t* )GetParam()->keymaster_context();
+ AuthorizationSet version_info(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 1));
+ device->configure(device, &version_info);
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(128)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Padding(KM_PAD_NONE)));
+
+ // Key should operate fine.
+ string message = "1234567890123456";
+ string ciphertext = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+ EXPECT_EQ(message, DecryptMessage(ciphertext, KM_MODE_ECB, KM_PAD_NONE));
+
+ // Increase patch level. Key usage should fail with KM_ERROR_KEY_REQUIRES_UPGRADE.
+ AuthorizationSet version_info_1_2(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 2));
+ device->configure(device, &version_info_1_2);
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_ECB);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ if (GetParam()->is_keymaster1_hw()) {
+ // Keymaster1 hardware can't support version binding. The key will work regardless
+ // of system version. Just abort the remainder of the test.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, AbortOperation());
+ return;
+ }
+ EXPECT_EQ(KM_ERROR_KEY_REQUIRES_UPGRADE, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ // Getting characteristics should also fail
+ EXPECT_EQ(KM_ERROR_KEY_REQUIRES_UPGRADE, GetCharacteristics());
+
+ // Upgrade key.
+ EXPECT_EQ(KM_ERROR_OK, UpgradeKey(client_params()));
+
+ // Key should work again
+ ciphertext = EncryptMessage(message, KM_MODE_ECB, KM_PAD_NONE);
+ EXPECT_EQ(message, DecryptMessage(ciphertext, KM_MODE_ECB, KM_PAD_NONE));
+
+ // Decrease patch level. Key usage should fail with KM_ERROR_INVALID_KEY_BLOB.
+ AuthorizationSet version_info_1_1(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 1));
+ device->configure(device, &version_info_1_1);
+
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, GetCharacteristics());
+
+ // Upgrade should fail
+ EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, UpgradeKey(client_params()));
+
+ EXPECT_EQ(0, GetParam()->keymaster0_calls());
+}
+#endif
+#if 0//KEYUPGRADETEST
+//this test is no longer valid because boring ssl rejects rsa keys less than 256 bits
+TEST_P(KeyUpgradeTest, RsaVersionUpgrade) {
+ keymaster2_device_t* device = (keymaster2_device_t* )GetParam()->keymaster_context();
+ AuthorizationSet version_info(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 1));
+ device->configure(device, &version_info);
+
+ ASSERT_EQ(KM_ERROR_OK,
+ GenerateKey(AuthorizationSetBuilder().RsaEncryptionKey(128, 3).Padding(KM_PAD_NONE)));
+
+ // Key should operate fine.
+ string message = "1234567890123456";
+ string ciphertext = EncryptMessage(message, KM_PAD_NONE);
+ EXPECT_EQ(message, DecryptMessage(ciphertext, KM_PAD_NONE));
+
+ // Increase patch level. Key usage should fail with KM_ERROR_KEY_REQUIRES_UPGRADE.
+ AuthorizationSet version_info_1_2(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 2));
+ device->configure(device, &version_info_1_2);
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ if (GetParam()->is_keymaster1_hw()) {
+ // Keymaster1 hardware can't support version binding. The key will work regardless
+ // of system version. Just abort the remainder of the test.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, AbortOperation());
+ return;
+ }
+ EXPECT_EQ(KM_ERROR_KEY_REQUIRES_UPGRADE, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+
+ // Getting characteristics should also fail
+ EXPECT_EQ(KM_ERROR_KEY_REQUIRES_UPGRADE, GetCharacteristics());
+
+ // Upgrade key.
+ EXPECT_EQ(KM_ERROR_OK, UpgradeKey(client_params()));
+
+ // Key should work again
+ ciphertext = EncryptMessage(message, KM_PAD_NONE);
+ EXPECT_EQ(message, DecryptMessage(ciphertext, KM_PAD_NONE));
+
+ // Decrease patch level. Key usage should fail with KM_ERROR_INVALID_KEY_BLOB.
+ AuthorizationSet version_info_1_1(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 1));
+ device->configure(device, &version_info_1_1);
+
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, GetCharacteristics());
+
+ // Upgrade should fail
+ EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, UpgradeKey(client_params()));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_RSA))
+ EXPECT_EQ(7, GetParam()->keymaster0_calls());
+}
+#endif
+#if KEYUPGRADETEST
+TEST_P(KeyUpgradeTest, EcVersionUpgrade) {
+ keymaster2_device_t* device = (keymaster2_device_t* )GetParam()->keymaster_context();
+ AuthorizationSet version_info(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 1));
+ device->configure(device, &version_info);
+
+ ASSERT_EQ(KM_ERROR_OK, GenerateKey(AuthorizationSetBuilder().EcdsaSigningKey(256).Digest(
+ KM_DIGEST_SHA_2_256)));
+
+ // Key should operate fine.
+ string message = "1234567890123456";
+ string signature;
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_256);
+
+ // Increase patch level. Key usage should fail with KM_ERROR_KEY_REQUIRES_UPGRADE.
+ AuthorizationSet version_info_1_2(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 2));
+ device->configure(device, &version_info_1_2);
+
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_DIGEST, KM_DIGEST_SHA_2_256);
+ if (GetParam()->is_keymaster1_hw()) {
+ // Keymaster1 hardware can't support version binding. The key will work regardless
+ // of system version. Just abort the remainder of the test.
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+ EXPECT_EQ(KM_ERROR_OK, AbortOperation());
+ return;
+ }
+ EXPECT_EQ(KM_ERROR_KEY_REQUIRES_UPGRADE, BeginOperation(KM_PURPOSE_SIGN, begin_params));
+
+ // Getting characteristics should also fail
+ EXPECT_EQ(KM_ERROR_KEY_REQUIRES_UPGRADE, GetCharacteristics());
+
+ // Upgrade key.
+ EXPECT_EQ(KM_ERROR_OK, UpgradeKey(client_params()));
+
+ // Key should work again
+ SignMessage(message, &signature, KM_DIGEST_SHA_2_256);
+ VerifyMessage(message, signature, KM_DIGEST_SHA_2_256);
+
+ // Decrease patch level. Key usage should fail with KM_ERROR_INVALID_KEY_BLOB.
+ AuthorizationSet version_info_1_1(AuthorizationSetBuilder()
+ .Authorization(TAG_OS_VERSION, 1)
+ .Authorization(TAG_OS_PATCHLEVEL, 1));
+ device->configure(device, &version_info_1_1);
+
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, BeginOperation(KM_PURPOSE_ENCRYPT, begin_params));
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB, GetCharacteristics());
+
+ // Upgrade should fail
+ EXPECT_EQ(KM_ERROR_INVALID_ARGUMENT, UpgradeKey(client_params()));
+
+ if (GetParam()->algorithm_in_km0_hardware(KM_ALGORITHM_EC))
+ EXPECT_EQ(7, GetParam()->keymaster0_calls());
+}
+#endif
+#if 0
+TEST(SoftKeymasterWrapperTest, CheckKeymaster2Device) {
+ // Make a good fake device, and wrap it.
+ SoftKeymasterDevice* good_fake(new SoftKeymasterDevice(new TestKeymasterContext));
+
+ // Wrap it and check it.
+ SoftKeymasterDevice* good_fake_wrapper(new SoftKeymasterDevice(new TestKeymasterContext));
+ good_fake_wrapper->SetHardwareDevice(good_fake->keymaster_device());
+ EXPECT_TRUE(good_fake_wrapper->Keymaster1DeviceIsGood());
+
+ // Close and clean up wrapper and wrapped
+ good_fake_wrapper->keymaster_device()->common.close(good_fake_wrapper->hw_device());
+
+ // Make a "bad" (doesn't support all digests) device;
+ keymaster1_device_t* sha256_only_fake = make_device_sha256_only(
+ (new SoftKeymasterDevice(new TestKeymasterContext("256")))->keymaster_device());
+
+ // Wrap it and check it.
+ SoftKeymasterDevice* sha256_only_fake_wrapper(
+ (new SoftKeymasterDevice(new TestKeymasterContext)));
+ sha256_only_fake_wrapper->SetHardwareDevice(sha256_only_fake);
+ EXPECT_FALSE(sha256_only_fake_wrapper->Keymaster1DeviceIsGood());
+
+ // Close and clean up wrapper and wrapped
+ sha256_only_fake_wrapper->keymaster_device()->common.close(
+ sha256_only_fake_wrapper->hw_device());
+}
+#endif
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/android_keymaster_test_utils.cpp b/unit_test/android_keymaster_test_utils.cpp
new file mode 100644
index 0000000..ecaee62
--- a/dev/null
+++ b/unit_test/android_keymaster_test_utils.cpp
@@ -0,0 +1,902 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android_keymaster_test_utils.h"
+
+#include <algorithm>
+
+#include <openssl/rand.h>
+
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/android_keymaster_utils.h>
+
+using std::copy_if;
+using std::find_if;
+using std::is_permutation;
+using std::ostream;
+using std::string;
+using std::vector;
+
+#ifndef KEYMASTER_NAME_TAGS
+#error Keymaster test code requires that KEYMASTER_NAME_TAGS is defined
+#endif
+
+std::ostream& operator<<(std::ostream& os, const keymaster_key_param_t& param) {
+ os << "Tag: " << keymaster::StringifyTag(param.tag);
+ switch (keymaster_tag_get_type(param.tag)) {
+ case KM_INVALID:
+ os << " Invalid";
+ break;
+ case KM_UINT_REP:
+ os << " (Rep)";
+ /* Falls through */
+ case KM_UINT:
+ os << " Int: " << param.integer;
+ break;
+ case KM_ENUM_REP:
+ os << " (Rep)";
+ /* Falls through */
+ case KM_ENUM:
+ os << " Enum: " << param.enumerated;
+ break;
+ case KM_ULONG_REP:
+ os << " (Rep)";
+ /* Falls through */
+ case KM_ULONG:
+ os << " Long: " << param.long_integer;
+ break;
+ case KM_DATE:
+ os << " Date: " << param.date_time;
+ break;
+ case KM_BOOL:
+ os << " Bool: " << param.boolean;
+ break;
+ case KM_BIGNUM:
+ os << " Bignum: ";
+ if (!param.blob.data)
+ os << "(null)";
+ else
+ for (size_t i = 0; i < param.blob.data_length; ++i)
+ os << std::hex << std::setw(2) << static_cast<int>(param.blob.data[i]) << std::dec;
+ break;
+ case KM_BYTES:
+ os << " Bytes: ";
+ if (!param.blob.data)
+ os << "(null)";
+ else
+ for (size_t i = 0; i < param.blob.data_length; ++i)
+ os << std::hex << std::setw(2) << static_cast<int>(param.blob.data[i]) << std::dec;
+ break;
+ }
+ return os;
+}
+
+bool operator==(const keymaster_key_param_t& a, const keymaster_key_param_t& b) {
+ if (a.tag != b.tag) {
+ return false;
+ }
+
+ switch (keymaster_tag_get_type(a.tag)) {
+ case KM_INVALID:
+ return true;
+ case KM_UINT_REP:
+ case KM_UINT:
+ return a.integer == b.integer;
+ case KM_ENUM_REP:
+ case KM_ENUM:
+ return a.enumerated == b.enumerated;
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ return a.long_integer == b.long_integer;
+ case KM_DATE:
+ return a.date_time == b.date_time;
+ case KM_BOOL:
+ return a.boolean == b.boolean;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ if ((a.blob.data == NULL || b.blob.data == NULL) && a.blob.data != b.blob.data)
+ return false;
+ return a.blob.data_length == b.blob.data_length &&
+ (memcmp(a.blob.data, b.blob.data, a.blob.data_length) == 0);
+ }
+
+ return false;
+}
+
+static char hex_value[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9'
+ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f'
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+string hex2str(string a) {
+ string b;
+ size_t num = a.size() / 2;
+ b.resize(num);
+ for (size_t i = 0; i < num; i++) {
+ b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]);
+ }
+ return b;
+}
+
+namespace keymaster {
+
+bool operator==(const AuthorizationSet& a, const AuthorizationSet& b) {
+ if (a.size() != b.size())
+ return false;
+
+ for (size_t i = 0; i < a.size(); ++i)
+ if (!(a[i] == b[i]))
+ return false;
+ return true;
+}
+
+bool operator!=(const AuthorizationSet& a, const AuthorizationSet& b) {
+ return !(a == b);
+}
+
+std::ostream& operator<<(std::ostream& os, const AuthorizationSet& set) {
+ if (set.size() == 0)
+ os << "(Empty)" << std::endl;
+ else {
+ os << "\n";
+ for (size_t i = 0; i < set.size(); ++i)
+ os << set[i] << std::endl;
+ }
+ return os;
+}
+
+namespace test {
+
+Keymaster2Test::Keymaster2Test() : op_handle_(OP_HANDLE_SENTINEL) {
+ memset(&characteristics_, 0, sizeof(characteristics_));
+ blob_.key_material = nullptr;
+ RAND_seed("foobar", 6);
+ blob_.key_material = 0;
+ device_ = GetParam()->CreateDevice();
+}
+
+Keymaster2Test::~Keymaster2Test() {
+ FreeCharacteristics();
+ FreeKeyBlob();
+ device_->common.close(reinterpret_cast<hw_device_t*>(device_));
+}
+
+keymaster2_device_t* Keymaster2Test::device() {
+ return device_;
+}
+
+keymaster_error_t Keymaster2Test::GenerateKey(const AuthorizationSetBuilder& builder) {
+ AuthorizationSet params(builder.build());
+ params.push_back(UserAuthParams());
+ params.push_back(ClientParams());
+
+ FreeKeyBlob();
+ FreeCharacteristics();
+ return device()->generate_key(device(), &params, &blob_, &characteristics_);
+}
+
+keymaster_error_t Keymaster2Test::DeleteKey() {
+ return device()->delete_key(device(), &blob_);
+}
+
+keymaster_error_t Keymaster2Test::ImportKey(const AuthorizationSetBuilder& builder,
+ keymaster_key_format_t format,
+ const string& key_material) {
+ AuthorizationSet params(builder.build());
+ params.push_back(UserAuthParams());
+ params.push_back(ClientParams());
+
+ FreeKeyBlob();
+ FreeCharacteristics();
+ keymaster_blob_t key = {reinterpret_cast<const uint8_t*>(key_material.c_str()),
+ key_material.length()};
+ return device()->import_key(device(), &params, format, &key, &blob_, &characteristics_);
+}
+
+AuthorizationSet Keymaster2Test::UserAuthParams() {
+ AuthorizationSet set;
+ set.push_back(TAG_USER_ID, 7);
+ set.push_back(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD);
+ set.push_back(TAG_AUTH_TIMEOUT, 300);
+ return set;
+}
+
+AuthorizationSet Keymaster2Test::ClientParams() {
+ AuthorizationSet set;
+ set.push_back(TAG_APPLICATION_ID, "app_id", 6);
+ return set;
+}
+
+keymaster_error_t Keymaster2Test::BeginOperation(keymaster_purpose_t purpose) {
+ AuthorizationSet in_params(client_params());
+ keymaster_key_param_set_t out_params;
+ keymaster_error_t error =
+ device()->begin(device(), purpose, &blob_, &in_params, &out_params, &op_handle_);
+ EXPECT_EQ(0U, out_params.length);
+ EXPECT_TRUE(out_params.params == nullptr);
+ return error;
+}
+
+keymaster_error_t Keymaster2Test::BeginOperation(keymaster_purpose_t purpose,
+ const AuthorizationSet& input_set,
+ AuthorizationSet* output_set) {
+ keymaster_key_param_set_t out_params;
+ keymaster_error_t error =
+ device()->begin(device(), purpose, &blob_, &input_set, &out_params, &op_handle_);
+ if (error == KM_ERROR_OK) {
+ if (output_set) {
+ output_set->Reinitialize(out_params);
+ } else {
+ EXPECT_EQ(0U, out_params.length);
+ EXPECT_TRUE(out_params.params == nullptr);
+ }
+ keymaster_free_param_set(&out_params);
+ }
+ return error;
+}
+
+keymaster_error_t Keymaster2Test::UpdateOperation(const string& message, string* output,
+ size_t* input_consumed) {
+ EXPECT_NE(op_handle_, OP_HANDLE_SENTINEL);
+ keymaster_blob_t input = {reinterpret_cast<const uint8_t*>(message.c_str()), message.length()};
+ keymaster_blob_t out_tmp;
+ keymaster_key_param_set_t out_params;
+ keymaster_error_t error = device()->update(device(), op_handle_, nullptr /* params */, &input,
+ input_consumed, &out_params, &out_tmp);
+ if (error == KM_ERROR_OK && out_tmp.data)
+ output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length);
+ free(const_cast<uint8_t*>(out_tmp.data));
+ return error;
+}
+
+keymaster_error_t Keymaster2Test::UpdateOperation(const AuthorizationSet& additional_params,
+ const string& message,
+ AuthorizationSet* output_params, string* output,
+ size_t* input_consumed) {
+ EXPECT_NE(op_handle_, OP_HANDLE_SENTINEL);
+ keymaster_blob_t input = {reinterpret_cast<const uint8_t*>(message.c_str()), message.length()};
+ keymaster_blob_t out_tmp;
+ keymaster_key_param_set_t out_params;
+ keymaster_error_t error = device()->update(device(), op_handle_, &additional_params, &input,
+ input_consumed, &out_params, &out_tmp);
+ if (error == KM_ERROR_OK && out_tmp.data)
+ output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length);
+ free((void*)out_tmp.data);
+ if (output_params)
+ output_params->Reinitialize(out_params);
+ keymaster_free_param_set(&out_params);
+ return error;
+}
+
+keymaster_error_t Keymaster2Test::FinishOperation(string* output) {
+ return FinishOperation("", output);
+}
+
+keymaster_error_t Keymaster2Test::FinishOperation(const string& signature, string* output) {
+ AuthorizationSet additional_params;
+ AuthorizationSet output_params;
+ return FinishOperation(additional_params, signature, &output_params, output);
+}
+
+keymaster_error_t Keymaster2Test::FinishOperation(const AuthorizationSet& additional_params,
+ const string& signature,
+ AuthorizationSet* output_params, string* output) {
+ keymaster_blob_t sig = {reinterpret_cast<const uint8_t*>(signature.c_str()),
+ signature.length()};
+ keymaster_blob_t out_tmp;
+ keymaster_key_param_set_t out_params;
+ keymaster_error_t error = device()->finish(device(), op_handle_, &additional_params,
+ nullptr /* input */, &sig, &out_params, &out_tmp);
+ if (error != KM_ERROR_OK) {
+ EXPECT_TRUE(out_tmp.data == nullptr);
+ EXPECT_TRUE(out_params.params == nullptr);
+ return error;
+ }
+
+ if (out_tmp.data)
+ output->append(reinterpret_cast<const char*>(out_tmp.data), out_tmp.data_length);
+ free((void*)out_tmp.data);
+ if (output_params)
+ output_params->Reinitialize(out_params);
+ keymaster_free_param_set(&out_params);
+ return error;
+}
+
+keymaster_error_t Keymaster2Test::AbortOperation() {
+ return device()->abort(device(), op_handle_);
+}
+
+keymaster_error_t Keymaster2Test::AttestKey(const string& attest_challenge,
+ keymaster_cert_chain_t* cert_chain) {
+ AuthorizationSet attest_params;
+ attest_params.push_back(UserAuthParams());
+ attest_params.push_back(ClientParams());
+ attest_params.push_back(TAG_ATTESTATION_CHALLENGE, attest_challenge.data(),
+ attest_challenge.length());
+ return device()->attest_key(device(), &blob_, &attest_params, cert_chain);
+}
+
+keymaster_error_t Keymaster2Test::UpgradeKey(const AuthorizationSet& upgrade_params) {
+ keymaster_key_blob_t upgraded_blob;
+ keymaster_error_t error =
+ device()->upgrade_key(device(), &blob_, &upgrade_params, &upgraded_blob);
+ if (error == KM_ERROR_OK) {
+ FreeKeyBlob();
+ blob_ = upgraded_blob;
+ }
+ return error;
+}
+
+string Keymaster2Test::ProcessMessage(keymaster_purpose_t purpose, const string& message) {
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, client_params(), NULL /* output_params */));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(&result));
+ return result;
+}
+
+string Keymaster2Test::ProcessMessage(keymaster_purpose_t purpose, const string& message,
+ const AuthorizationSet& begin_params,
+ const AuthorizationSet& update_params,
+ AuthorizationSet* begin_out_params) {
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, begin_params, begin_out_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, nullptr /* output_params */,
+ &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(update_params, "", &result));
+ return result;
+}
+
+string Keymaster2Test::ProcessMessage(keymaster_purpose_t purpose, const string& message,
+ const string& signature, const AuthorizationSet& begin_params,
+ const AuthorizationSet& update_params,
+ AuthorizationSet* output_params) {
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, begin_params, output_params));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(update_params, message, nullptr /* output_params */,
+ &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(update_params, signature, &result));
+ return result;
+}
+
+string Keymaster2Test::ProcessMessage(keymaster_purpose_t purpose, const string& message,
+ const string& signature) {
+ EXPECT_EQ(KM_ERROR_OK, BeginOperation(purpose, client_params(), NULL /* output_params */));
+
+ string result;
+ size_t input_consumed;
+ EXPECT_EQ(KM_ERROR_OK, UpdateOperation(message, &result, &input_consumed));
+ EXPECT_EQ(message.size(), input_consumed);
+ EXPECT_EQ(KM_ERROR_OK, FinishOperation(signature, &result));
+ return result;
+}
+
+void Keymaster2Test::SignMessage(const string& message, string* signature,
+ keymaster_digest_t digest) {
+ SCOPED_TRACE("SignMessage");
+ AuthorizationSet input_params(AuthorizationSet(client_params_, array_length(client_params_)));
+ input_params.push_back(TAG_DIGEST, digest);
+ AuthorizationSet update_params;
+ AuthorizationSet output_params;
+ *signature =
+ ProcessMessage(KM_PURPOSE_SIGN, message, input_params, update_params, &output_params);
+ EXPECT_GT(signature->size(), 0U);
+}
+
+void Keymaster2Test::SignMessage(const string& message, string* signature,
+ keymaster_digest_t digest, keymaster_padding_t padding) {
+ SCOPED_TRACE("SignMessage");
+ AuthorizationSet input_params(AuthorizationSet(client_params_, array_length(client_params_)));
+ input_params.push_back(TAG_DIGEST, digest);
+ input_params.push_back(TAG_PADDING, padding);
+ AuthorizationSet update_params;
+ AuthorizationSet output_params;
+ *signature =
+ ProcessMessage(KM_PURPOSE_SIGN, message, input_params, update_params, &output_params);
+ EXPECT_GT(signature->size(), 0U);
+}
+
+void Keymaster2Test::MacMessage(const string& message, string* signature, size_t mac_length) {
+ SCOPED_TRACE("SignMessage");
+ AuthorizationSet input_params(AuthorizationSet(client_params_, array_length(client_params_)));
+ input_params.push_back(TAG_MAC_LENGTH, mac_length);
+ AuthorizationSet update_params;
+ AuthorizationSet output_params;
+ *signature =
+ ProcessMessage(KM_PURPOSE_SIGN, message, input_params, update_params, &output_params);
+ EXPECT_GT(signature->size(), 0U);
+}
+
+void Keymaster2Test::VerifyMessage(const string& message, const string& signature,
+ keymaster_digest_t digest) {
+ SCOPED_TRACE("VerifyMessage");
+ AuthorizationSet input_params(client_params());
+ input_params.push_back(TAG_DIGEST, digest);
+ AuthorizationSet update_params;
+ AuthorizationSet output_params;
+ ProcessMessage(KM_PURPOSE_VERIFY, message, signature, input_params, update_params,
+ &output_params);
+}
+
+void Keymaster2Test::VerifyMessage(const string& message, const string& signature,
+ keymaster_digest_t digest, keymaster_padding_t padding) {
+ SCOPED_TRACE("VerifyMessage");
+ AuthorizationSet input_params(client_params());
+ input_params.push_back(TAG_DIGEST, digest);
+ input_params.push_back(TAG_PADDING, padding);
+ AuthorizationSet update_params;
+ AuthorizationSet output_params;
+ ProcessMessage(KM_PURPOSE_VERIFY, message, signature, input_params, update_params,
+ &output_params);
+}
+
+void Keymaster2Test::VerifyMac(const string& message, const string& signature) {
+ SCOPED_TRACE("VerifyMac");
+ ProcessMessage(KM_PURPOSE_VERIFY, message, signature);
+}
+
+string Keymaster2Test::EncryptMessage(const string& message, keymaster_padding_t padding,
+ string* generated_nonce) {
+ SCOPED_TRACE("EncryptMessage");
+ AuthorizationSet begin_params(client_params()), output_params;
+ begin_params.push_back(TAG_PADDING, padding);
+ AuthorizationSet update_params;
+ string ciphertext =
+ ProcessMessage(KM_PURPOSE_ENCRYPT, message, begin_params, update_params, &output_params);
+ if (generated_nonce) {
+ keymaster_blob_t nonce_blob;
+ EXPECT_TRUE(output_params.GetTagValue(TAG_NONCE, &nonce_blob));
+ *generated_nonce = make_string(nonce_blob.data, nonce_blob.data_length);
+ } else {
+ EXPECT_EQ(-1, output_params.find(TAG_NONCE));
+ }
+ return ciphertext;
+}
+
+string Keymaster2Test::EncryptMessage(const string& message, keymaster_digest_t digest,
+ keymaster_padding_t padding, string* generated_nonce) {
+ AuthorizationSet update_params;
+ return EncryptMessage(update_params, message, digest, padding, generated_nonce);
+}
+
+string Keymaster2Test::EncryptMessage(const string& message, keymaster_block_mode_t block_mode,
+ keymaster_padding_t padding, string* generated_nonce) {
+ AuthorizationSet update_params;
+ return EncryptMessage(update_params, message, block_mode, padding, generated_nonce);
+}
+
+string Keymaster2Test::EncryptMessage(const AuthorizationSet& update_params, const string& message,
+ keymaster_digest_t digest, keymaster_padding_t padding,
+ string* generated_nonce) {
+ SCOPED_TRACE("EncryptMessage");
+ AuthorizationSet begin_params(client_params()), output_params;
+ begin_params.push_back(TAG_PADDING, padding);
+ begin_params.push_back(TAG_DIGEST, digest);
+ string ciphertext =
+ ProcessMessage(KM_PURPOSE_ENCRYPT, message, begin_params, update_params, &output_params);
+ if (generated_nonce) {
+ keymaster_blob_t nonce_blob;
+ EXPECT_TRUE(output_params.GetTagValue(TAG_NONCE, &nonce_blob));
+ *generated_nonce = make_string(nonce_blob.data, nonce_blob.data_length);
+ } else {
+ EXPECT_EQ(-1, output_params.find(TAG_NONCE));
+ }
+ return ciphertext;
+}
+
+string Keymaster2Test::EncryptMessage(const AuthorizationSet& update_params, const string& message,
+ keymaster_block_mode_t block_mode,
+ keymaster_padding_t padding, string* generated_nonce) {
+ SCOPED_TRACE("EncryptMessage");
+ AuthorizationSet begin_params(client_params()), output_params;
+ begin_params.push_back(TAG_PADDING, padding);
+ begin_params.push_back(TAG_BLOCK_MODE, block_mode);
+ string ciphertext =
+ ProcessMessage(KM_PURPOSE_ENCRYPT, message, begin_params, update_params, &output_params);
+ if (generated_nonce) {
+ keymaster_blob_t nonce_blob;
+ EXPECT_TRUE(output_params.GetTagValue(TAG_NONCE, &nonce_blob));
+ *generated_nonce = make_string(nonce_blob.data, nonce_blob.data_length);
+ } else {
+ EXPECT_EQ(-1, output_params.find(TAG_NONCE));
+ }
+ return ciphertext;
+}
+
+string Keymaster2Test::EncryptMessageWithParams(const string& message,
+ const AuthorizationSet& begin_params,
+ const AuthorizationSet& update_params,
+ AuthorizationSet* output_params) {
+ SCOPED_TRACE("EncryptMessageWithParams");
+ return ProcessMessage(KM_PURPOSE_ENCRYPT, message, begin_params, update_params, output_params);
+}
+
+string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_padding_t padding) {
+ SCOPED_TRACE("DecryptMessage");
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, padding);
+ AuthorizationSet update_params;
+ return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params);
+}
+
+string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_digest_t digest,
+ keymaster_padding_t padding) {
+ SCOPED_TRACE("DecryptMessage");
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, padding);
+ begin_params.push_back(TAG_DIGEST, digest);
+ AuthorizationSet update_params;
+ return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params);
+}
+
+string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_block_mode_t block_mode,
+ keymaster_padding_t padding) {
+ SCOPED_TRACE("DecryptMessage");
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, padding);
+ begin_params.push_back(TAG_BLOCK_MODE, block_mode);
+ AuthorizationSet update_params;
+ return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params);
+}
+
+string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_digest_t digest,
+ keymaster_padding_t padding, const string& nonce) {
+ SCOPED_TRACE("DecryptMessage");
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, padding);
+ begin_params.push_back(TAG_DIGEST, digest);
+ begin_params.push_back(TAG_NONCE, nonce.data(), nonce.size());
+ AuthorizationSet update_params;
+ return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params);
+}
+
+string Keymaster2Test::DecryptMessage(const string& ciphertext, keymaster_block_mode_t block_mode,
+ keymaster_padding_t padding, const string& nonce) {
+ SCOPED_TRACE("DecryptMessage");
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, padding);
+ begin_params.push_back(TAG_BLOCK_MODE, block_mode);
+ begin_params.push_back(TAG_NONCE, nonce.data(), nonce.size());
+ AuthorizationSet update_params;
+ return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params);
+}
+
+string Keymaster2Test::DecryptMessage(const AuthorizationSet& update_params,
+ const string& ciphertext, keymaster_digest_t digest,
+ keymaster_padding_t padding, const string& nonce) {
+ SCOPED_TRACE("DecryptMessage");
+ AuthorizationSet begin_params(client_params());
+ begin_params.push_back(TAG_PADDING, padding);
+ begin_params.push_back(TAG_DIGEST, digest);
+ begin_params.push_back(TAG_NONCE, nonce.data(), nonce.size());
+ return ProcessMessage(KM_PURPOSE_DECRYPT, ciphertext, begin_params, update_params);
+}
+
+keymaster_error_t Keymaster2Test::GetCharacteristics() {
+ FreeCharacteristics();
+ return device()->get_key_characteristics(device(), &blob_, &client_id_, NULL /* app_data */,
+ &characteristics_);
+}
+
+keymaster_error_t Keymaster2Test::ExportKey(keymaster_key_format_t format, string* export_data) {
+ keymaster_blob_t export_tmp;
+ keymaster_error_t error = device()->export_key(device(), format, &blob_, &client_id_,
+ NULL /* app_data */, &export_tmp);
+
+ if (error != KM_ERROR_OK)
+ return error;
+
+ *export_data = string(reinterpret_cast<const char*>(export_tmp.data), export_tmp.data_length);
+ free((void*)export_tmp.data);
+ return error;
+}
+
+void Keymaster2Test::CheckHmacTestVector(const string& key, const string& message, keymaster_digest_t digest,
+ string expected_mac) {
+ ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+ .HmacKey(key.size() * 8)
+ .Authorization(TAG_MIN_MAC_LENGTH, expected_mac.size() * 8)
+ .Digest(digest),
+ KM_KEY_FORMAT_RAW, key));
+ string signature;
+ MacMessage(message, &signature, expected_mac.size() * 8);
+ EXPECT_EQ(expected_mac, signature) << "Test vector didn't match for digest " << (int)digest;
+}
+
+void Keymaster2Test::CheckAesCtrTestVector(const string& key, const string& nonce,
+ const string& message,
+ const string& expected_ciphertext) {
+ ASSERT_EQ(KM_ERROR_OK, ImportKey(AuthorizationSetBuilder()
+ .AesEncryptionKey(key.size() * 8)
+ .Authorization(TAG_BLOCK_MODE, KM_MODE_CTR)
+ .Authorization(TAG_CALLER_NONCE)
+ .Padding(KM_PAD_NONE),
+ KM_KEY_FORMAT_RAW, key));
+
+ AuthorizationSet begin_params(client_params()), update_params, output_params;
+ begin_params.push_back(TAG_NONCE, nonce.data(), nonce.size());
+ begin_params.push_back(TAG_BLOCK_MODE, KM_MODE_CTR);
+ begin_params.push_back(TAG_PADDING, KM_PAD_NONE);
+ string ciphertext =
+ EncryptMessageWithParams(message, begin_params, update_params, &output_params);
+ EXPECT_EQ(expected_ciphertext, ciphertext);
+}
+
+AuthorizationSet Keymaster2Test::hw_enforced() {
+ return AuthorizationSet(characteristics_.hw_enforced);
+}
+
+AuthorizationSet Keymaster2Test::sw_enforced() {
+ return AuthorizationSet(characteristics_.sw_enforced);
+}
+
+void Keymaster2Test::FreeCharacteristics() {
+ keymaster_free_characteristics(&characteristics_);
+}
+
+void Keymaster2Test::FreeKeyBlob() {
+ free(const_cast<uint8_t*>(blob_.key_material));
+ blob_.key_material = NULL;
+}
+
+void Keymaster2Test::corrupt_key_blob() {
+ assert(blob_.key_material);
+ uint8_t* tmp = const_cast<uint8_t*>(blob_.key_material);
+ ++tmp[blob_.key_material_size / 2];
+}
+
+class Sha256OnlyWrapper {
+ public:
+ explicit Sha256OnlyWrapper(const keymaster1_device_t* wrapped_device) : wrapped_device_(wrapped_device) {
+
+ new_module = *wrapped_device_->common.module;
+ new_module_name = std::string("SHA 256-only ") + wrapped_device_->common.module->name;
+ new_module.name = new_module_name.c_str();
+
+ memset(&device_, 0, sizeof(device_));
+ device_.common.module = &new_module;
+
+ device_.common.close = close_device;
+ device_.get_supported_algorithms = get_supported_algorithms;
+ device_.get_supported_block_modes = get_supported_block_modes;
+ device_.get_supported_padding_modes = get_supported_padding_modes;
+ device_.get_supported_digests = get_supported_digests;
+ device_.get_supported_import_formats = get_supported_import_formats;
+ device_.get_supported_export_formats = get_supported_export_formats;
+ device_.add_rng_entropy = add_rng_entropy;
+ device_.generate_key = generate_key;
+ device_.get_key_characteristics = get_key_characteristics;
+ device_.import_key = import_key;
+ device_.export_key = export_key;
+ device_.begin = begin;
+ device_.update = update;
+ device_.finish = finish;
+ device_.abort = abort;
+ }
+
+ keymaster1_device_t* keymaster_device() { return &device_; }
+
+ static bool is_supported(keymaster_digest_t digest) {
+ return digest == KM_DIGEST_NONE || digest == KM_DIGEST_SHA_2_256;
+ }
+
+ static bool all_digests_supported(const keymaster_key_param_set_t* params) {
+ for (size_t i = 0; i < params->length; ++i)
+ if (params->params[i].tag == TAG_DIGEST)
+ if (!is_supported(static_cast<keymaster_digest_t>(params->params[i].enumerated)))
+ return false;
+ return true;
+ }
+
+ static const keymaster_key_param_t*
+ get_algorithm_param(const keymaster_key_param_set_t* params) {
+ keymaster_key_param_t* end = params->params + params->length;
+ auto alg_ptr = std::find_if(params->params, end, [](keymaster_key_param_t& p) {
+ return p.tag == KM_TAG_ALGORITHM;
+ });
+ if (alg_ptr == end)
+ return nullptr;
+ return alg_ptr;
+ }
+
+ static int close_device(hw_device_t* dev) {
+ Sha256OnlyWrapper* wrapper = reinterpret_cast<Sha256OnlyWrapper*>(dev);
+ const keymaster1_device_t* wrapped_device = wrapper->wrapped_device_;
+ delete wrapper;
+ return wrapped_device->common.close(const_cast<hw_device_t*>(&wrapped_device->common));
+ }
+
+ static const keymaster1_device_t* unwrap(const keymaster1_device_t* dev) {
+ return reinterpret_cast<const Sha256OnlyWrapper*>(dev)->wrapped_device_;
+ }
+
+ static keymaster_error_t get_supported_algorithms(const struct keymaster1_device* dev,
+ keymaster_algorithm_t** algorithms,
+ size_t* algorithms_length) {
+ return unwrap(dev)->get_supported_algorithms(unwrap(dev), algorithms, algorithms_length);
+ }
+ static keymaster_error_t get_supported_block_modes(const struct keymaster1_device* dev,
+ keymaster_algorithm_t algorithm,
+ keymaster_purpose_t purpose,
+ keymaster_block_mode_t** modes,
+ size_t* modes_length) {
+ return unwrap(dev)->get_supported_block_modes(unwrap(dev), algorithm, purpose, modes,
+ modes_length);
+ }
+ static keymaster_error_t get_supported_padding_modes(const struct keymaster1_device* dev,
+ keymaster_algorithm_t algorithm,
+ keymaster_purpose_t purpose,
+ keymaster_padding_t** modes,
+ size_t* modes_length) {
+ return unwrap(dev)->get_supported_padding_modes(unwrap(dev), algorithm, purpose, modes,
+ modes_length);
+ }
+
+ static keymaster_error_t get_supported_digests(const keymaster1_device_t* dev,
+ keymaster_algorithm_t algorithm,
+ keymaster_purpose_t purpose,
+ keymaster_digest_t** digests,
+ size_t* digests_length) {
+ keymaster_error_t error = unwrap(dev)->get_supported_digests(
+ unwrap(dev), algorithm, purpose, digests, digests_length);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ std::vector<keymaster_digest_t> filtered_digests;
+ std::copy_if(*digests, *digests + *digests_length, std::back_inserter(filtered_digests),
+ [](keymaster_digest_t digest) { return is_supported(digest); });
+
+ free(*digests);
+ *digests_length = filtered_digests.size();
+ *digests = reinterpret_cast<keymaster_digest_t*>(
+ malloc(*digests_length * sizeof(keymaster_digest_t)));
+ std::copy(filtered_digests.begin(), filtered_digests.end(), *digests);
+
+ return KM_ERROR_OK;
+ }
+
+ static keymaster_error_t get_supported_import_formats(const struct keymaster1_device* dev,
+ keymaster_algorithm_t algorithm,
+ keymaster_key_format_t** formats,
+ size_t* formats_length) {
+ return unwrap(dev)->get_supported_import_formats(unwrap(dev), algorithm, formats,
+ formats_length);
+ }
+ static keymaster_error_t get_supported_export_formats(const struct keymaster1_device* dev,
+ keymaster_algorithm_t algorithm,
+ keymaster_key_format_t** formats,
+ size_t* formats_length) {
+ return unwrap(dev)->get_supported_export_formats(unwrap(dev), algorithm, formats,
+ formats_length);
+ }
+ static keymaster_error_t add_rng_entropy(const struct keymaster1_device* dev,
+ const uint8_t* data, size_t data_length) {
+ return unwrap(dev)->add_rng_entropy(unwrap(dev), data, data_length);
+ }
+
+ static keymaster_error_t generate_key(const keymaster1_device_t* dev,
+ const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob,
+ keymaster_key_characteristics_t** characteristics) {
+ auto alg_ptr = get_algorithm_param(params);
+ if (!alg_ptr)
+ return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ if (alg_ptr->enumerated == KM_ALGORITHM_HMAC && !all_digests_supported(params))
+ return KM_ERROR_UNSUPPORTED_DIGEST;
+
+ return unwrap(dev)->generate_key(unwrap(dev), params, key_blob, characteristics);
+ }
+
+ static keymaster_error_t
+ get_key_characteristics(const struct keymaster1_device* dev,
+ const keymaster_key_blob_t* key_blob, const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_key_characteristics_t** characteristics) {
+ return unwrap(dev)->get_key_characteristics(unwrap(dev), key_blob, client_id, app_data,
+ characteristics);
+ }
+
+ static keymaster_error_t
+ import_key(const keymaster1_device_t* dev, const keymaster_key_param_set_t* params,
+ keymaster_key_format_t key_format, const keymaster_blob_t* key_data,
+ keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t** characteristics) {
+ auto alg_ptr = get_algorithm_param(params);
+ if (!alg_ptr)
+ return KM_ERROR_UNSUPPORTED_ALGORITHM;
+ if (alg_ptr->enumerated == KM_ALGORITHM_HMAC && !all_digests_supported(params))
+ return KM_ERROR_UNSUPPORTED_DIGEST;
+
+ return unwrap(dev)->import_key(unwrap(dev), params, key_format, key_data, key_blob,
+ characteristics);
+ }
+
+ static keymaster_error_t export_key(const struct keymaster1_device* dev, //
+ keymaster_key_format_t export_format,
+ const keymaster_key_blob_t* key_to_export,
+ const keymaster_blob_t* client_id,
+ const keymaster_blob_t* app_data,
+ keymaster_blob_t* export_data) {
+ return unwrap(dev)->export_key(unwrap(dev), export_format, key_to_export, client_id,
+ app_data, export_data);
+ }
+
+ static keymaster_error_t begin(const keymaster1_device_t* dev, //
+ keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) {
+ if (!all_digests_supported(in_params))
+ return KM_ERROR_UNSUPPORTED_DIGEST;
+ return unwrap(dev)->begin(unwrap(dev), purpose, key, in_params, out_params,
+ operation_handle);
+ }
+
+ static keymaster_error_t update(const keymaster1_device_t* dev,
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ return unwrap(dev)->update(unwrap(dev), operation_handle, in_params, input, input_consumed,
+ out_params, output);
+ }
+
+ static keymaster_error_t finish(const struct keymaster1_device* dev, //
+ keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) {
+ return unwrap(dev)->finish(unwrap(dev), operation_handle, in_params, signature, out_params,
+ output);
+ }
+
+ static keymaster_error_t abort(const struct keymaster1_device* dev,
+ keymaster_operation_handle_t operation_handle) {
+ return unwrap(dev)->abort(unwrap(dev), operation_handle);
+ }
+
+ private:
+ keymaster1_device_t device_;
+ const keymaster1_device_t* wrapped_device_;
+ hw_module_t new_module;
+ string new_module_name;
+};
+
+keymaster1_device_t* make_device_sha256_only(keymaster1_device_t* device) {
+ return (new Sha256OnlyWrapper(device))->keymaster_device();
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/android_keymaster_test_utils.h b/unit_test/android_keymaster_test_utils.h
new file mode 100644
index 0000000..5dd0f76
--- a/dev/null
+++ b/unit_test/android_keymaster_test_utils.h
@@ -0,0 +1,470 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_KEYMASTER_ANDROID_KEYMASTER_TEST_UTILS_H_
+#define SYSTEM_KEYMASTER_ANDROID_KEYMASTER_TEST_UTILS_H_
+
+/*
+ * Utilities used to help with testing. Not used in production code.
+ */
+
+#include <stdarg.h>
+
+#include <algorithm>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <hardware/keymaster0.h>
+#include <hardware/keymaster1.h>
+#include <hardware/keymaster2.h>
+#include <hardware/keymaster_defs.h>
+
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/authorization_set.h>
+#include <keymaster/keymaster_context.h>
+#include <keymaster/logger.h>
+
+std::ostream& operator<<(std::ostream& os, const keymaster_key_param_t& param);
+bool operator==(const keymaster_key_param_t& a, const keymaster_key_param_t& b);
+std::string hex2str(std::string);
+
+namespace keymaster {
+
+bool operator==(const AuthorizationSet& a, const AuthorizationSet& b);
+bool operator!=(const AuthorizationSet& a, const AuthorizationSet& b);
+
+std::ostream& operator<<(std::ostream& os, const AuthorizationSet& set);
+
+namespace test {
+
+template <keymaster_tag_t Tag, typename KeymasterEnum>
+bool contains(const AuthorizationSet& set, TypedEnumTag<KM_ENUM, Tag, KeymasterEnum> tag,
+ KeymasterEnum val) {
+ int pos = set.find(tag);
+ return pos != -1 && static_cast<KeymasterEnum>(set[pos].enumerated) == val;
+}
+
+template <keymaster_tag_t Tag, typename KeymasterEnum>
+bool contains(const AuthorizationSet& set, TypedEnumTag<KM_ENUM_REP, Tag, KeymasterEnum> tag,
+ KeymasterEnum val) {
+ int pos = -1;
+ while ((pos = set.find(tag, pos)) != -1)
+ if (static_cast<KeymasterEnum>(set[pos].enumerated) == val)
+ return true;
+ return false;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_UINT, Tag> tag, uint32_t val) {
+ int pos = set.find(tag);
+ return pos != -1 && set[pos].integer == val;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_UINT_REP, Tag> tag, uint32_t val) {
+ int pos = -1;
+ while ((pos = set.find(tag, pos)) != -1)
+ if (set[pos].integer == val)
+ return true;
+ return false;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_ULONG, Tag> tag, uint64_t val) {
+ int pos = set.find(tag);
+ return pos != -1 && set[pos].long_integer == val;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_BYTES, Tag> tag, const std::string& val) {
+ int pos = set.find(tag);
+ return pos != -1 &&
+ std::string(reinterpret_cast<const char*>(set[pos].blob.data),
+ set[pos].blob.data_length) == val;
+}
+
+template <keymaster_tag_t Tag>
+bool contains(const AuthorizationSet& set, TypedTag<KM_BIGNUM, Tag> tag, const std::string& val) {
+ int pos = set.find(tag);
+ return pos != -1 &&
+ std::string(reinterpret_cast<const char*>(set[pos].blob.data),
+ set[pos].blob.data_length) == val;
+}
+
+inline bool contains(const AuthorizationSet& set, keymaster_tag_t tag) {
+ return set.find(tag) != -1;
+}
+
+class StdoutLogger : public Logger {
+ public:
+ StdoutLogger() { set_instance(this); }
+
+ int log_msg(LogLevel level, const char* fmt, va_list args) const {
+ int output_len = 0;
+ switch (level) {
+ case DEBUG_LVL:
+ output_len = printf("DEBUG: ");
+ break;
+ case INFO_LVL:
+ output_len = printf("INFO: ");
+ break;
+ case WARNING_LVL:
+ output_len = printf("WARNING: ");
+ break;
+ case ERROR_LVL:
+ output_len = printf("ERROR: ");
+ break;
+ case SEVERE_LVL:
+ output_len = printf("SEVERE: ");
+ break;
+ }
+
+ output_len += vprintf(fmt, args);
+ output_len += printf("\n");
+ return output_len;
+ }
+};
+
+inline std::string make_string(const uint8_t* data, size_t length) {
+ return std::string(reinterpret_cast<const char*>(data), length);
+}
+
+template <size_t N> std::string make_string(const uint8_t (&a)[N]) {
+ return make_string(a, N);
+}
+
+/**
+ * Keymaster2TestInstance is used to parameterize Keymaster2Tests. Its main function is to create a
+ * keymaster2_device_t to which test calls can be directed. It also provides a place to specify
+ * various bits of alternative behavior, in cases where different devices are expected to behave
+ * differently (any such cases are a potential bug, but sometimes they may make sense).
+ */
+class Keymaster2TestInstanceCreator {
+ public:
+ virtual ~Keymaster2TestInstanceCreator(){};
+ virtual keymaster2_device_t* CreateDevice() const = 0;
+
+ virtual bool algorithm_in_km0_hardware(keymaster_algorithm_t algorithm) const = 0;
+ virtual int keymaster0_calls() const = 0;
+ virtual int minimal_digest_set() const { return false; }
+ virtual bool is_keymaster1_hw() const = 0;
+ virtual KeymasterContext* keymaster_context() const = 0;
+};
+
+// Use a shared_ptr because it's copyable.
+typedef std::shared_ptr<Keymaster2TestInstanceCreator> InstanceCreatorPtr;
+
+const uint64_t OP_HANDLE_SENTINEL = 0xFFFFFFFFFFFFFFFF;
+class Keymaster2Test : public testing::TestWithParam<InstanceCreatorPtr> {
+ protected:
+ Keymaster2Test();
+ ~Keymaster2Test();
+
+ keymaster2_device_t* device();
+
+ keymaster_error_t GenerateKey(const AuthorizationSetBuilder& builder);
+
+ keymaster_error_t DeleteKey();
+
+ keymaster_error_t ImportKey(const AuthorizationSetBuilder& builder,
+ keymaster_key_format_t format, const std::string& key_material);
+
+ keymaster_error_t ExportKey(keymaster_key_format_t format, std::string* export_data);
+
+ keymaster_error_t GetCharacteristics();
+
+ keymaster_error_t BeginOperation(keymaster_purpose_t purpose);
+ keymaster_error_t BeginOperation(keymaster_purpose_t purpose, const AuthorizationSet& input_set,
+ AuthorizationSet* output_set = NULL);
+
+ keymaster_error_t UpdateOperation(const std::string& message, std::string* output,
+ size_t* input_consumed);
+ keymaster_error_t UpdateOperation(const AuthorizationSet& additional_params,
+ const std::string& message, AuthorizationSet* output_params,
+ std::string* output, size_t* input_consumed);
+
+ keymaster_error_t FinishOperation(std::string* output);
+ keymaster_error_t FinishOperation(const std::string& signature, std::string* output);
+ keymaster_error_t FinishOperation(const AuthorizationSet& additional_params,
+ const std::string& signature, std::string* output) {
+ return FinishOperation(additional_params, signature, nullptr /* output_params */, output);
+ }
+ keymaster_error_t FinishOperation(const AuthorizationSet& additional_params,
+ const std::string& signature, AuthorizationSet* output_params,
+ std::string* output);
+
+ keymaster_error_t AbortOperation();
+
+ keymaster_error_t AttestKey(const std::string& attest_challenge, keymaster_cert_chain_t* chain);
+
+ keymaster_error_t UpgradeKey(const AuthorizationSet& upgrade_params);
+
+ keymaster_error_t GetVersion(uint8_t* major, uint8_t* minor, uint8_t* subminor);
+
+ std::string ProcessMessage(keymaster_purpose_t purpose, const std::string& message);
+ std::string ProcessMessage(keymaster_purpose_t purpose, const std::string& message,
+ const AuthorizationSet& begin_params,
+ const AuthorizationSet& update_params,
+ AuthorizationSet* output_params = NULL);
+ std::string ProcessMessage(keymaster_purpose_t purpose, const std::string& message,
+ const std::string& signature, const AuthorizationSet& begin_params,
+ const AuthorizationSet& update_params,
+ AuthorizationSet* output_params = NULL);
+ std::string ProcessMessage(keymaster_purpose_t purpose, const std::string& message,
+ const std::string& signature);
+
+ void SignMessage(const std::string& message, std::string* signature, keymaster_digest_t digest);
+ void SignMessage(const std::string& message, std::string* signature, keymaster_digest_t digest,
+ keymaster_padding_t padding);
+ void MacMessage(const std::string& message, std::string* signature, size_t mac_length);
+
+ void VerifyMessage(const std::string& message, const std::string& signature,
+ keymaster_digest_t digest);
+ void VerifyMessage(const std::string& message, const std::string& signature,
+ keymaster_digest_t digest, keymaster_padding_t padding);
+ void VerifyMac(const std::string& message, const std::string& signature);
+
+ std::string EncryptMessage(const std::string& message, keymaster_padding_t padding,
+ std::string* generated_nonce = NULL);
+ std::string EncryptMessage(const std::string& message, keymaster_digest_t digest,
+ keymaster_padding_t padding, std::string* generated_nonce = NULL);
+ std::string EncryptMessage(const std::string& message, keymaster_block_mode_t block_mode,
+ keymaster_padding_t padding, std::string* generated_nonce = NULL);
+ std::string EncryptMessage(const AuthorizationSet& update_params, const std::string& message,
+ keymaster_digest_t digest, keymaster_padding_t padding,
+ std::string* generated_nonce = NULL);
+ std::string EncryptMessage(const AuthorizationSet& update_params, const std::string& message,
+ keymaster_block_mode_t block_mode, keymaster_padding_t padding,
+ std::string* generated_nonce = NULL);
+ std::string EncryptMessageWithParams(const std::string& message,
+ const AuthorizationSet& begin_params,
+ const AuthorizationSet& update_params,
+ AuthorizationSet* output_params);
+
+ std::string DecryptMessage(const std::string& ciphertext, keymaster_padding_t padding);
+ std::string DecryptMessage(const std::string& ciphertext, keymaster_digest_t digest,
+ keymaster_padding_t padding);
+ std::string DecryptMessage(const std::string& ciphertext, keymaster_block_mode_t block_mode,
+ keymaster_padding_t padding);
+ std::string DecryptMessage(const std::string& ciphertext, keymaster_digest_t digest,
+ keymaster_padding_t padding, const std::string& nonce);
+ std::string DecryptMessage(const std::string& ciphertext, keymaster_block_mode_t block_mode,
+ keymaster_padding_t padding, const std::string& nonce);
+ std::string DecryptMessage(const AuthorizationSet& update_params, const std::string& ciphertext,
+ keymaster_digest_t digest, keymaster_padding_t padding,
+ const std::string& nonce);
+ std::string DecryptMessage(const AuthorizationSet& update_params, const std::string& ciphertext,
+ keymaster_block_mode_t block_mode, keymaster_padding_t padding,
+ const std::string& nonce);
+
+ void CheckHmacTestVector(const std::string& key, const std::string& message, keymaster_digest_t digest,
+ std::string expected_mac);
+ void CheckAesOcbTestVector(const std::string& key, const std::string& nonce,
+ const std::string& associated_data, const std::string& message,
+ const std::string& expected_ciphertext);
+ void CheckAesCtrTestVector(const std::string& key, const std::string& nonce,
+ const std::string& message, const std::string& expected_ciphertext);
+ AuthorizationSet UserAuthParams();
+ AuthorizationSet ClientParams();
+
+ template <typename T>
+ bool ResponseContains(const std::vector<T>& expected, const T* values, size_t len) {
+ return expected.size() == len &&
+ std::is_permutation(values, values + len, expected.begin());
+ }
+
+ template <typename T> bool ResponseContains(T expected, const T* values, size_t len) {
+ return (len == 1 && *values == expected);
+ }
+
+ AuthorizationSet hw_enforced();
+ AuthorizationSet sw_enforced();
+
+ void FreeCharacteristics();
+ void FreeKeyBlob();
+
+ void corrupt_key_blob();
+
+ void set_key_blob(const uint8_t* key, size_t key_length) {
+ FreeKeyBlob();
+ blob_.key_material = key;
+ blob_.key_material_size = key_length;
+ }
+
+ AuthorizationSet client_params() {
+ return AuthorizationSet(client_params_, sizeof(client_params_) / sizeof(client_params_[0]));
+ }
+
+ private:
+ keymaster2_device_t* device_;
+ keymaster_blob_t client_id_ = {.data = reinterpret_cast<const uint8_t*>("app_id"),
+ .data_length = 6};
+ keymaster_key_param_t client_params_[1] = {
+ Authorization(TAG_APPLICATION_ID, client_id_.data, client_id_.data_length)};
+
+ uint64_t op_handle_;
+
+ keymaster_key_blob_t blob_;
+ keymaster_key_characteristics_t characteristics_;
+};
+
+struct Keymaster0CountingWrapper : public keymaster0_device_t {
+ explicit Keymaster0CountingWrapper(keymaster0_device_t* device) : device_(device), counter_(0) {
+ common = device_->common;
+ common.close = counting_close_device;
+ client_version = device_->client_version;
+ flags = device_->flags;
+ context = this;
+
+ generate_keypair = counting_generate_keypair;
+ import_keypair = counting_import_keypair;
+ get_keypair_public = counting_get_keypair_public;
+ delete_keypair = counting_delete_keypair;
+ delete_all = counting_delete_all;
+ sign_data = counting_sign_data;
+ verify_data = counting_verify_data;
+ }
+
+ int count() { return counter_; }
+
+ // The blobs generated by the underlying softkeymaster start with "PK#8". Tweak the prefix so
+ // they don't get identified as softkeymaster blobs.
+ static void munge_blob(uint8_t* blob, size_t blob_length) {
+ if (blob && blob_length > 0 && *blob == 'P')
+ *blob = 'Q'; // Mind your Ps and Qs!
+ }
+
+ // Copy and un-modfy the blob. The caller must clean up the return value.
+ static uint8_t* unmunge_blob(const uint8_t* blob, size_t blob_length) {
+ uint8_t* dup_blob = dup_buffer(blob, blob_length);
+ if (dup_blob && blob_length > 0 && *dup_blob == 'Q')
+ *dup_blob = 'P';
+ return dup_blob;
+ }
+
+ static keymaster0_device_t* device(const keymaster0_device_t* dev) {
+ Keymaster0CountingWrapper* wrapper =
+ reinterpret_cast<Keymaster0CountingWrapper*>(dev->context);
+ return wrapper->device_;
+ }
+
+ static void increment(const keymaster0_device_t* dev) {
+ Keymaster0CountingWrapper* wrapper =
+ reinterpret_cast<Keymaster0CountingWrapper*>(dev->context);
+ wrapper->counter_++;
+ }
+
+ static int counting_close_device(hw_device_t* dev) {
+ keymaster0_device_t* k0_dev = reinterpret_cast<keymaster0_device_t*>(dev);
+ increment(k0_dev);
+ Keymaster0CountingWrapper* wrapper =
+ reinterpret_cast<Keymaster0CountingWrapper*>(k0_dev->context);
+ int retval =
+ wrapper->device_->common.close(reinterpret_cast<hw_device_t*>(wrapper->device_));
+ delete wrapper;
+ return retval;
+ }
+
+ static int counting_generate_keypair(const struct keymaster0_device* dev,
+ const keymaster_keypair_t key_type, const void* key_params,
+ uint8_t** key_blob, size_t* key_blob_length) {
+ increment(dev);
+ int result = device(dev)->generate_keypair(device(dev), key_type, key_params, key_blob,
+ key_blob_length);
+ if (result == 0)
+ munge_blob(*key_blob, *key_blob_length);
+ return result;
+ }
+
+ static int counting_import_keypair(const struct keymaster0_device* dev, const uint8_t* key,
+ const size_t key_length, uint8_t** key_blob,
+ size_t* key_blob_length) {
+ increment(dev);
+ int result =
+ device(dev)->import_keypair(device(dev), key, key_length, key_blob, key_blob_length);
+ if (result == 0)
+ munge_blob(*key_blob, *key_blob_length);
+ return result;
+ }
+
+ static int counting_get_keypair_public(const struct keymaster0_device* dev,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ uint8_t** x509_data, size_t* x509_data_length) {
+ increment(dev);
+ std::unique_ptr<uint8_t[]> dup_blob(unmunge_blob(key_blob, key_blob_length));
+ return device(dev)->get_keypair_public(device(dev), dup_blob.get(), key_blob_length,
+ x509_data, x509_data_length);
+ }
+
+ static int counting_delete_keypair(const struct keymaster0_device* dev, const uint8_t* key_blob,
+ const size_t key_blob_length) {
+ increment(dev);
+ if (key_blob && key_blob_length > 0)
+ EXPECT_EQ('Q', *key_blob);
+ if (device(dev)->delete_keypair) {
+ std::unique_ptr<uint8_t[]> dup_blob(unmunge_blob(key_blob, key_blob_length));
+ return device(dev)->delete_keypair(device(dev), dup_blob.get(), key_blob_length);
+ }
+ return 0;
+ }
+
+ static int counting_delete_all(const struct keymaster0_device* dev) {
+ increment(dev);
+ if (device(dev)->delete_all)
+ return device(dev)->delete_all(device(dev));
+ return 0;
+ }
+
+ static int counting_sign_data(const struct keymaster0_device* dev, const void* signing_params,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ const uint8_t* data, const size_t data_length,
+ uint8_t** signed_data, size_t* signed_data_length) {
+ increment(dev);
+ std::unique_ptr<uint8_t[]> dup_blob(unmunge_blob(key_blob, key_blob_length));
+ return device(dev)->sign_data(device(dev), signing_params, dup_blob.get(), key_blob_length,
+ data, data_length, signed_data, signed_data_length);
+ }
+
+ static int counting_verify_data(const struct keymaster0_device* dev, const void* signing_params,
+ const uint8_t* key_blob, const size_t key_blob_length,
+ const uint8_t* signed_data, const size_t signed_data_length,
+ const uint8_t* signature, const size_t signature_length) {
+ increment(dev);
+ std::unique_ptr<uint8_t[]> dup_blob(unmunge_blob(key_blob, key_blob_length));
+ return device(dev)->verify_data(device(dev), signing_params, dup_blob.get(),
+ key_blob_length, signed_data, signed_data_length, signature,
+ signature_length);
+ }
+
+ private:
+ keymaster0_device_t* device_;
+ int counter_;
+};
+
+/**
+ * This function takes a keymaster1_device_t and wraps it in an adapter that supports only
+ * KM_DIGEST_SHA_2_256.
+ */
+keymaster1_device_t* make_device_sha256_only(keymaster1_device_t* device);
+
+} // namespace test
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_ANDROID_KEYMASTER_TEST_UTILS_H_
diff --git a/unit_test/android_keymaster_utils.h b/unit_test/android_keymaster_utils.h
new file mode 100644
index 0000000..b957dd1
--- a/dev/null
+++ b/unit_test/android_keymaster_utils.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_KEYMASTER_ANDROID_KEYMASTER_UTILS_H_
+#define SYSTEM_KEYMASTER_ANDROID_KEYMASTER_UTILS_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <time.h> // for time_t.
+
+#include <UniquePtr.h>
+
+#include <hardware/keymaster_defs.h>
+#include <keymaster/serializable.h>
+
+namespace keymaster {
+
+/**
+ * Convert the specified time value into "Java time", which is a signed 64-bit integer representing
+ * elapsed milliseconds since Jan 1, 1970.
+ */
+inline int64_t java_time(time_t time) {
+ // The exact meaning of a time_t value is implementation-dependent. If this code is ported to a
+ // platform that doesn't define it as "seconds since Jan 1, 1970 UTC", this function will have
+ // to be revised.
+ return time * 1000;
+}
+
+/*
+ * Array Manipulation functions. This set of templated inline functions provides some nice tools
+ * for operating on c-style arrays. C-style arrays actually do have a defined size associated with
+ * them, as long as they are not allowed to decay to a pointer. These template methods exploit this
+ * to allow size-based array operations without explicitly specifying the size. If passed a pointer
+ * rather than an array, they'll fail to compile.
+ */
+
+/**
+ * Return the size in bytes of the array \p a.
+ */
+template <typename T, size_t N> inline size_t array_size(const T(&a)[N]) {
+ return sizeof(a);
+}
+
+/**
+ * Return the number of elements in array \p a.
+ */
+template <typename T, size_t N> inline size_t array_length(const T(&)[N]) {
+ return N;
+}
+
+/**
+ * Duplicate the array \p a. The memory for the new array is allocated and the caller takes
+ * responsibility.
+ */
+template <typename T> inline T* dup_array(const T* a, size_t n) {
+ T* dup = new (std::nothrow) T[n];
+ if (dup)
+ for (size_t i = 0; i < n; ++i)
+ dup[i] = a[i];
+ return dup;
+}
+
+/**
+ * Duplicate the array \p a. The memory for the new array is allocated and the caller takes
+ * responsibility. Note that the dup is necessarily returned as a pointer, so size is lost. Call
+ * array_length() on the original array to discover the size.
+ */
+template <typename T, size_t N> inline T* dup_array(const T(&a)[N]) {
+ return dup_array(a, N);
+}
+
+/**
+ * Duplicate the buffer \p buf. The memory for the new buffer is allocated and the caller takes
+ * responsibility.
+ */
+uint8_t* dup_buffer(const void* buf, size_t size);
+
+/**
+ * Copy the contents of array \p arr to \p dest.
+ */
+template <typename T, size_t N> inline void copy_array(const T(&arr)[N], T* dest) {
+ for (size_t i = 0; i < N; ++i)
+ dest[i] = arr[i];
+}
+
+/**
+ * Search array \p a for value \p val, returning true if found. Note that this function is
+ * early-exit, meaning that it should not be used in contexts where timing analysis attacks could be
+ * a concern.
+ */
+template <typename T, size_t N> inline bool array_contains(const T(&a)[N], T val) {
+ for (size_t i = 0; i < N; ++i) {
+ if (a[i] == val) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Variant of memset() that uses GCC-specific pragmas to disable optimizations, so effect is not
+ * optimized away. This is important because we often need to wipe blocks of sensitive data from
+ * memory. As an additional convenience, this implementation avoids writing to NULL pointers.
+ */
+#ifdef __clang__
+#define OPTNONE __attribute__((optnone))
+#else // not __clang__
+#define OPTNONE __attribute__((optimize("O0")))
+#endif // not __clang__
+inline OPTNONE void* memset_s(void* s, int c, size_t n) {
+ if (!s)
+ return s;
+ return memset(s, c, n);
+}
+#undef OPTNONE
+
+/**
+ * Variant of memcmp that has the same runtime regardless of whether the data matches (i.e. doesn't
+ * short-circuit). Not an exact equivalent to memcmp because it doesn't return <0 if p1 < p2, just
+ * 0 for match and non-zero for non-match.
+ */
+int memcmp_s(const void* p1, const void* p2, size_t length);
+
+/**
+ * Eraser clears buffers. Construct it with a buffer or object and the destructor will ensure that
+ * it is zeroed.
+ */
+class Eraser {
+ public:
+ /* Not implemented. If this gets used, we want a link error. */
+ template <typename T> explicit Eraser(T* t);
+
+ template <typename T>
+ explicit Eraser(T& t)
+ : buf_(reinterpret_cast<uint8_t*>(&t)), size_(sizeof(t)) {}
+
+ template <size_t N> explicit Eraser(uint8_t(&arr)[N]) : buf_(arr), size_(N) {}
+
+ Eraser(void* buf, size_t size) : buf_(static_cast<uint8_t*>(buf)), size_(size) {}
+ ~Eraser() { memset_s(buf_, 0, size_); }
+
+ private:
+ Eraser(const Eraser&);
+ void operator=(const Eraser&);
+
+ uint8_t* buf_;
+ size_t size_;
+};
+
+/**
+ * ArrayWrapper is a trivial wrapper around a C-style array that provides begin() and end()
+ * methods. This is primarily to facilitate range-based iteration on arrays. It does not copy, nor
+ * does it take ownership; it just holds pointers.
+ */
+template <typename T> class ArrayWrapper {
+ public:
+ ArrayWrapper(T* array, size_t size) : begin_(array), end_(array + size) {}
+
+ T* begin() { return begin_; }
+ T* end() { return end_; }
+
+ private:
+ T* begin_;
+ T* end_;
+};
+
+/**
+ * Convert any unsigned integer from network to host order. We implement this here rather than
+ * using the functions from arpa/inet.h because the TEE doesn't have inet.h. This isn't the most
+ * efficient implementation, but the compiler should unroll the loop and tighten it up.
+ */
+template <typename T> T ntoh(T t) {
+ const uint8_t* byte_ptr = reinterpret_cast<const uint8_t*>(&t);
+ T retval = 0;
+ for (size_t i = 0; i < sizeof(t); ++i) {
+ retval <<= 8;
+ retval |= byte_ptr[i];
+ }
+ return retval;
+}
+
+/**
+ * Convert any unsigned integer from host to network order. We implement this here rather than
+ * using the functions from arpa/inet.h because the TEE doesn't have inet.h. This isn't the most
+ * efficient implementation, but the compiler should unroll the loop and tighten it up.
+ */
+template <typename T> T hton(T t) {
+ T retval;
+ uint8_t* byte_ptr = reinterpret_cast<uint8_t*>(&retval);
+ for (size_t i = sizeof(t); i > 0; --i) {
+ byte_ptr[i - 1] = t & 0xFF;
+ t >>= 8;
+ }
+ return retval;
+}
+
+/**
+ * KeymasterKeyBlob is a very simple extension of the C struct keymaster_key_blob_t. It manages its
+ * own memory, which makes avoiding memory leaks much easier.
+ */
+struct KeymasterKeyBlob : public keymaster_key_blob_t {
+ KeymasterKeyBlob() {
+ key_material = nullptr;
+ key_material_size = 0;
+ }
+
+ KeymasterKeyBlob(const uint8_t* data, size_t size) {
+ key_material_size = 0;
+ key_material = dup_buffer(data, size);
+ if (key_material)
+ key_material_size = size;
+ }
+
+ explicit KeymasterKeyBlob(size_t size) {
+ key_material_size = 0;
+ key_material = new (std::nothrow) uint8_t[size];
+ if (key_material)
+ key_material_size = size;
+ }
+
+ explicit KeymasterKeyBlob(const keymaster_key_blob_t& blob) {
+ key_material_size = 0;
+ key_material = dup_buffer(blob.key_material, blob.key_material_size);
+ if (key_material)
+ key_material_size = blob.key_material_size;
+ }
+
+ KeymasterKeyBlob(const KeymasterKeyBlob& blob) {
+ key_material_size = 0;
+ key_material = dup_buffer(blob.key_material, blob.key_material_size);
+ if (key_material)
+ key_material_size = blob.key_material_size;
+ }
+
+ void operator=(const KeymasterKeyBlob& blob) {
+ Clear();
+ key_material = dup_buffer(blob.key_material, blob.key_material_size);
+ key_material_size = blob.key_material_size;
+ }
+
+ ~KeymasterKeyBlob() { Clear(); }
+
+ const uint8_t* begin() const { return key_material; }
+ const uint8_t* end() const { return key_material + key_material_size; }
+
+ void Clear() {
+ memset_s(const_cast<uint8_t*>(key_material), 0, key_material_size);
+ delete[] key_material;
+ key_material = nullptr;
+ key_material_size = 0;
+ }
+
+ const uint8_t* Reset(size_t new_size) {
+ Clear();
+ key_material = new (std::nothrow) uint8_t[new_size];
+ if (key_material)
+ key_material_size = new_size;
+ return key_material;
+ }
+
+ // The key_material in keymaster_key_blob_t is const, which is the right thing in most
+ // circumstances, but occasionally we do need to write into it. This method exposes a non-const
+ // version of the pointer. Use sparingly.
+ uint8_t* writable_data() { return const_cast<uint8_t*>(key_material); }
+
+ keymaster_key_blob_t release() {
+ keymaster_key_blob_t tmp = {key_material, key_material_size};
+ key_material = nullptr;
+ key_material_size = 0;
+ return tmp;
+ }
+
+ size_t SerializedSize() const { return sizeof(uint32_t) + key_material_size; }
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const {
+ return append_size_and_data_to_buf(buf, end, key_material, key_material_size);
+ }
+
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) {
+ Clear();
+ UniquePtr<uint8_t[]> tmp;
+ if (!copy_size_and_data_from_buf(buf_ptr, end, &key_material_size, &tmp)) {
+ key_material = nullptr;
+ key_material_size = 0;
+ return false;
+ }
+ key_material = tmp.release();
+ return true;
+ }
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_ANDROID_KEYMASTER_UTILS_H_
diff --git a/unit_test/attestation_record.cpp b/unit_test/attestation_record.cpp
new file mode 100644
index 0000000..8ba5a8a
--- a/dev/null
+++ b/unit_test/attestation_record.cpp
@@ -0,0 +1,690 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "attestation_record.h"
+
+#include <assert.h>
+
+#include <openssl/asn1t.h>
+
+#include "openssl_err.h"
+#include "openssl_utils.h"
+
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_context.h>
+
+namespace keymaster {
+
+struct stack_st_ASN1_TYPE_Delete {
+ void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); }
+};
+
+struct ASN1_STRING_Delete {
+ void operator()(ASN1_STRING* p) { ASN1_STRING_free(p); }
+};
+
+struct ASN1_TYPE_Delete {
+ void operator()(ASN1_TYPE* p) { ASN1_TYPE_free(p); }
+};
+
+#define ASN1_INTEGER_SET STACK_OF(ASN1_INTEGER)
+
+typedef struct km_root_of_trust {
+ ASN1_OCTET_STRING* verified_boot_key;
+ ASN1_BOOLEAN* device_locked;
+ ASN1_ENUMERATED* verified_boot_state;
+} KM_ROOT_OF_TRUST;
+
+ASN1_SEQUENCE(KM_ROOT_OF_TRUST) = {
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_key, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, device_locked, ASN1_BOOLEAN),
+ ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_state, ASN1_ENUMERATED),
+} ASN1_SEQUENCE_END(KM_ROOT_OF_TRUST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_ROOT_OF_TRUST);
+
+typedef struct km_auth_list {
+ ASN1_INTEGER_SET* purpose;
+ ASN1_INTEGER* algorithm;
+ ASN1_INTEGER* key_size;
+ ASN1_INTEGER_SET* digest;
+ ASN1_INTEGER_SET* padding;
+ ASN1_INTEGER_SET* kdf;
+ ASN1_INTEGER* ec_curve;
+ ASN1_INTEGER* rsa_public_exponent;
+ ASN1_INTEGER* active_date_time;
+ ASN1_INTEGER* origination_expire_date_time;
+ ASN1_INTEGER* usage_expire_date_time;
+ ASN1_NULL* no_auth_required;
+ ASN1_INTEGER* user_auth_type;
+ ASN1_INTEGER* auth_timeout;
+ ASN1_NULL* allow_while_on_body;
+ ASN1_NULL* all_applications;
+ ASN1_OCTET_STRING* application_id;
+ ASN1_INTEGER* creation_date_time;
+ ASN1_INTEGER* origin;
+ ASN1_NULL* rollback_resistant;
+ KM_ROOT_OF_TRUST* root_of_trust;
+ ASN1_INTEGER* os_version;
+ ASN1_INTEGER* os_patchlevel;
+} KM_AUTH_LIST;
+
+ASN1_SEQUENCE(KM_AUTH_LIST) = {
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.masked_tag()),
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.masked_tag()),
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.masked_tag()),
+ ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, kdf, ASN1_INTEGER, TAG_KDF.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER,
+ TAG_RSA_PUBLIC_EXPONENT.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
+ TAG_ORIGINATION_EXPIRE_DATETIME.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER,
+ TAG_USAGE_EXPIRE_DATETIME.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL,
+ TAG_ALLOW_WHILE_ON_BODY.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, all_applications, ASN1_NULL, TAG_ALL_APPLICATIONS.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, application_id, ASN1_OCTET_STRING, TAG_APPLICATION_ID.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER,
+ TAG_CREATION_DATETIME.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistant, ASN1_NULL, TAG_ROLLBACK_RESISTANT.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.masked_tag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.masked_tag()),
+} ASN1_SEQUENCE_END(KM_AUTH_LIST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
+
+typedef struct km_key_description {
+ ASN1_INTEGER* attestation_version;
+ ASN1_ENUMERATED* attestation_security_level;
+ ASN1_INTEGER* keymaster_version;
+ ASN1_ENUMERATED* keymaster_security_level;
+ ASN1_OCTET_STRING* attestation_challenge;
+ KM_AUTH_LIST* software_enforced;
+ KM_AUTH_LIST* tee_enforced;
+ ASN1_INTEGER* unique_id;
+} KM_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(KM_KEY_DESCRIPTION) = {
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_version, ASN1_INTEGER),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_security_level, ASN1_ENUMERATED),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_version, ASN1_INTEGER),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymaster_security_level, ASN1_ENUMERATED),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_challenge, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, unique_id, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, software_enforced, KM_AUTH_LIST),
+ ASN1_SIMPLE(KM_KEY_DESCRIPTION, tee_enforced, KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(KM_KEY_DESCRIPTION);
+IMPLEMENT_ASN1_FUNCTIONS(KM_KEY_DESCRIPTION);
+
+struct KM_AUTH_LIST_Delete {
+ void operator()(KM_AUTH_LIST* p) { KM_AUTH_LIST_free(p); }
+};
+
+struct KM_KEY_DESCRIPTION_Delete {
+ void operator()(KM_KEY_DESCRIPTION* p) { KM_KEY_DESCRIPTION_free(p); }
+};
+
+static uint32_t get_uint32_value(const keymaster_key_param_t& param) {
+ switch (keymaster_tag_get_type(param.tag)) {
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ return param.enumerated;
+ case KM_UINT:
+ case KM_UINT_REP:
+ return param.integer;
+ default:
+ assert(false);
+ return 0xFFFFFFFF;
+ }
+}
+
+// Insert value in either the dest_integer or the dest_integer_set, whichever is provided.
+static keymaster_error_t insert_integer(ASN1_INTEGER* value, ASN1_INTEGER** dest_integer,
+ ASN1_INTEGER_SET** dest_integer_set) {
+ assert((dest_integer == nullptr) ^ (dest_integer_set == nullptr));
+ assert(value);
+
+ if (dest_integer_set) {
+ if (!*dest_integer_set)
+ *dest_integer_set = sk_ASN1_INTEGER_new_null();
+ if (!*dest_integer_set)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ if (!sk_ASN1_INTEGER_push(*dest_integer_set, value))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return KM_ERROR_OK;
+
+ } else if (dest_integer) {
+ if (*dest_integer)
+ ASN1_INTEGER_free(*dest_integer);
+ *dest_integer = value;
+ return KM_ERROR_OK;
+ }
+
+ assert(false); // Should never get here.
+ return KM_ERROR_OK;
+}
+
+// Put the contents of the keymaster AuthorizationSet auth_list in to the ASN.1 record structure,
+// record.
+static keymaster_error_t build_auth_list(const AuthorizationSet& auth_list, KM_AUTH_LIST* record) {
+ assert(record);
+
+ if (auth_list.empty())
+ return KM_ERROR_OK;
+
+ for (auto entry : auth_list) {
+
+ ASN1_INTEGER_SET** integer_set = nullptr;
+ ASN1_INTEGER** integer_ptr = nullptr;
+ ASN1_OCTET_STRING** string_ptr = nullptr;
+ ASN1_NULL** bool_ptr = nullptr;
+
+ switch (entry.tag) {
+
+ /* Ignored tags */
+ case KM_TAG_INVALID:
+ case KM_TAG_ASSOCIATED_DATA:
+ case KM_TAG_NONCE:
+ case KM_TAG_AUTH_TOKEN:
+ case KM_TAG_MAC_LENGTH:
+ case KM_TAG_ALL_USERS:
+ case KM_TAG_USER_ID:
+ case KM_TAG_USER_SECURE_ID:
+ case KM_TAG_EXPORTABLE:
+ case KM_TAG_RESET_SINCE_ID_ROTATION:
+ case KM_TAG_ATTESTATION_CHALLENGE:
+ case KM_TAG_BLOCK_MODE:
+ case KM_TAG_CALLER_NONCE:
+ case KM_TAG_MIN_MAC_LENGTH:
+ case KM_TAG_ECIES_SINGLE_HASH_MODE:
+ case KM_TAG_INCLUDE_UNIQUE_ID:
+ case KM_TAG_BLOB_USAGE_REQUIREMENTS:
+ case KM_TAG_BOOTLOADER_ONLY:
+ case KM_TAG_MIN_SECONDS_BETWEEN_OPS:
+ case KM_TAG_MAX_USES_PER_BOOT:
+ case KM_TAG_APPLICATION_DATA:
+ case KM_TAG_UNIQUE_ID:
+ case KM_TAG_ROOT_OF_TRUST:
+ continue;
+
+ /* Non-repeating enumerations */
+ case KM_TAG_ALGORITHM:
+ integer_ptr = &record->algorithm;
+ break;
+ case KM_TAG_EC_CURVE:
+ integer_ptr = &record->ec_curve;
+ break;
+ case KM_TAG_USER_AUTH_TYPE:
+ integer_ptr = &record->user_auth_type;
+ break;
+ case KM_TAG_ORIGIN:
+ integer_ptr = &record->origin;
+ break;
+
+ /* Repeating enumerations */
+ case KM_TAG_PURPOSE:
+ integer_set = &record->purpose;
+ break;
+ case KM_TAG_PADDING:
+ integer_set = &record->padding;
+ break;
+ case KM_TAG_DIGEST:
+ integer_set = &record->digest;
+ break;
+ case KM_TAG_KDF:
+ integer_set = &record->kdf;
+ break;
+
+ /* Non-repeating unsigned integers */
+ case KM_TAG_KEY_SIZE:
+ integer_ptr = &record->key_size;
+ break;
+ case KM_TAG_AUTH_TIMEOUT:
+ integer_ptr = &record->auth_timeout;
+ break;
+ case KM_TAG_OS_VERSION:
+ integer_ptr = &record->os_version;
+ break;
+ case KM_TAG_OS_PATCHLEVEL:
+ integer_ptr = &record->os_patchlevel;
+ break;
+
+ /* Non-repeating long unsigned integers */
+ case KM_TAG_RSA_PUBLIC_EXPONENT:
+ integer_ptr = &record->rsa_public_exponent;
+ break;
+
+ /* Dates */
+ case KM_TAG_ACTIVE_DATETIME:
+ integer_ptr = &record->active_date_time;
+ break;
+ case KM_TAG_ORIGINATION_EXPIRE_DATETIME:
+ integer_ptr = &record->origination_expire_date_time;
+ break;
+ case KM_TAG_USAGE_EXPIRE_DATETIME:
+ integer_ptr = &record->usage_expire_date_time;
+ break;
+ case KM_TAG_CREATION_DATETIME:
+ integer_ptr = &record->creation_date_time;
+ break;
+
+ /* Booleans */
+ case KM_TAG_NO_AUTH_REQUIRED:
+ bool_ptr = &record->no_auth_required;
+ break;
+ case KM_TAG_ALL_APPLICATIONS:
+ bool_ptr = &record->all_applications;
+ break;
+ case KM_TAG_ROLLBACK_RESISTANT:
+ bool_ptr = &record->rollback_resistant;
+ break;
+ case KM_TAG_ALLOW_WHILE_ON_BODY:
+ bool_ptr = &record->allow_while_on_body;
+ break;
+
+ /* Byte arrays*/
+ case KM_TAG_APPLICATION_ID:
+ string_ptr = &record->application_id;
+ break;
+ default:
+ break;
+ }
+
+ keymaster_tag_type_t type = keymaster_tag_get_type(entry.tag);
+ switch (type) {
+ case KM_ENUM:
+ case KM_ENUM_REP:
+ case KM_UINT:
+ case KM_UINT_REP: {
+ assert((keymaster_tag_repeatable(entry.tag) && integer_set) ||
+ (!keymaster_tag_repeatable(entry.tag) && integer_ptr));
+
+ UniquePtr<ASN1_INTEGER, ASN1_INTEGER_Delete> value(ASN1_INTEGER_new());
+ if (!value.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ if (!ASN1_INTEGER_set(value.get(), get_uint32_value(entry)))
+ return TranslateLastOpenSslError();
+
+ insert_integer(value.release(), integer_ptr, integer_set);
+ break;
+ }
+
+ case KM_ULONG:
+ case KM_ULONG_REP:
+ case KM_DATE: {
+ assert((keymaster_tag_repeatable(entry.tag) && integer_set) ||
+ (!keymaster_tag_repeatable(entry.tag) && integer_ptr));
+
+ UniquePtr<BIGNUM, BIGNUM_Delete> bn_value(BN_new());
+ if (!bn_value.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ if (type == KM_DATE) {
+ if (!BN_set_u64(bn_value.get(), entry.date_time)) {
+ return TranslateLastOpenSslError();
+ }
+ } else {
+ if (!BN_set_u64(bn_value.get(), entry.long_integer)) {
+ return TranslateLastOpenSslError();
+ }
+ }
+
+ UniquePtr<ASN1_INTEGER, ASN1_INTEGER_Delete> value(
+ BN_to_ASN1_INTEGER(bn_value.get(), nullptr));
+ if (!value.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ insert_integer(value.release(), integer_ptr, integer_set);
+ break;
+ }
+
+ case KM_BOOL:
+ assert(bool_ptr);
+ if (!*bool_ptr)
+ *bool_ptr = ASN1_NULL_new();
+ if (!*bool_ptr)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ break;
+
+ /* Byte arrays*/
+ case KM_BYTES:
+ assert(string_ptr);
+ if (!*string_ptr)
+ *string_ptr = ASN1_OCTET_STRING_new();
+ if (!*string_ptr)
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ if (!ASN1_OCTET_STRING_set(*string_ptr, entry.blob.data, entry.blob.data_length))
+ return TranslateLastOpenSslError();
+ break;
+
+ default:
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ }
+
+ keymaster_ec_curve_t ec_curve;
+ uint32_t key_size;
+ if (auth_list.Contains(TAG_ALGORITHM, KM_ALGORITHM_EC) && //
+ !auth_list.Contains(TAG_EC_CURVE) && //
+ auth_list.GetTagValue(TAG_KEY_SIZE, &key_size)) {
+ // This must be a keymaster1 key. It's an EC key with no curve. Insert the curve.
+
+ keymaster_error_t error = EcKeySizeToCurve(key_size, &ec_curve);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ UniquePtr<ASN1_INTEGER, ASN1_INTEGER_Delete> value(ASN1_INTEGER_new());
+ if (!value.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ if (!ASN1_INTEGER_set(value.get(), ec_curve))
+ return TranslateLastOpenSslError();
+
+ insert_integer(value.release(), &record->ec_curve, nullptr);
+ }
+
+ return KM_ERROR_OK;
+}
+
+// Construct an ASN1.1 DER-encoded attestation record containing the values from sw_enforced and
+// tee_enforced.
+keymaster_error_t build_attestation_record(const AuthorizationSet& attestation_params,
+ const AuthorizationSet& sw_enforced,
+ const AuthorizationSet& tee_enforced,
+ const KeymasterContext& context,
+ UniquePtr<uint8_t[]>* asn1_key_desc,
+ size_t* asn1_key_desc_len) {
+ assert(asn1_key_desc && asn1_key_desc_len);
+
+ UniquePtr<KM_KEY_DESCRIPTION, KM_KEY_DESCRIPTION_Delete> key_desc(KM_KEY_DESCRIPTION_new());
+ if (!key_desc.get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ keymaster_security_level_t keymaster_security_level;
+ uint32_t keymaster_version = UINT32_MAX;
+ if (tee_enforced.empty()) {
+ // Software key.
+ keymaster_security_level = KM_SECURITY_LEVEL_SOFTWARE;
+ keymaster_version = 2;
+ } else {
+ keymaster_security_level = KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
+ switch (context.GetSecurityLevel()) {
+ case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
+ // We're running in a TEE, so the key is KM2.
+ keymaster_version = 2;
+ break;
+
+ case KM_SECURITY_LEVEL_SOFTWARE:
+ // We're running in software, wrapping some KM hardware. Is it KM0 or KM1? KM1 keys
+ // have the purpose in the tee_enforced list. It's possible that a key could be created
+ // without a purpose, which would fool this test into reporting it's a KM0 key. That
+ // corner case doesn't matter much, because purpose-less keys are not usable anyway.
+ // Also, KM1 TEEs should disappear rapidly.
+ keymaster_version = tee_enforced.Contains(TAG_PURPOSE) ? 1 : 0;
+ break;
+ }
+
+ if (keymaster_version == UINT32_MAX)
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+
+ if (!ASN1_INTEGER_set(key_desc->attestation_version, 1) ||
+ !ASN1_ENUMERATED_set(key_desc->attestation_security_level, context.GetSecurityLevel()) ||
+ !ASN1_INTEGER_set(key_desc->keymaster_version, keymaster_version) ||
+ !ASN1_ENUMERATED_set(key_desc->keymaster_security_level, keymaster_security_level))
+ return TranslateLastOpenSslError();
+
+ keymaster_blob_t attestation_challenge = {nullptr, 0};
+ if (!attestation_params.GetTagValue(TAG_ATTESTATION_CHALLENGE, &attestation_challenge))
+ return KM_ERROR_ATTESTATION_CHALLENGE_MISSING;
+ if (!ASN1_OCTET_STRING_set(key_desc->attestation_challenge, attestation_challenge.data,
+ attestation_challenge.data_length))
+ return TranslateLastOpenSslError();
+
+ keymaster_error_t error = build_auth_list(sw_enforced, key_desc->software_enforced);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ error = build_auth_list(tee_enforced, key_desc->tee_enforced);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ // Only check tee_enforced for TAG_INCLUDE_UNIQUE_ID. If we don't have hardware we can't
+ // generate unique IDs.
+ if (tee_enforced.GetTagValue(TAG_INCLUDE_UNIQUE_ID)) {
+ uint64_t creation_datetime;
+ // Only check sw_enforced for TAG_CREATION_DATETIME, since it shouldn't be in tee_enforced,
+ // since this implementation has no secure wall clock.
+ if (!sw_enforced.GetTagValue(TAG_CREATION_DATETIME, &creation_datetime)) {
+ LOG_E("Unique ID cannot be created without creation datetime", 0);
+ return KM_ERROR_INVALID_KEY_BLOB;
+ }
+
+ keymaster_blob_t application_id = {nullptr, 0};
+ sw_enforced.GetTagValue(TAG_APPLICATION_ID, &application_id);
+
+ Buffer unique_id;
+ error = context.GenerateUniqueId(
+ creation_datetime, application_id,
+ attestation_params.GetTagValue(TAG_RESET_SINCE_ID_ROTATION), &unique_id);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ key_desc->unique_id = ASN1_OCTET_STRING_new();
+ if (!key_desc->unique_id ||
+ !ASN1_OCTET_STRING_set(key_desc->unique_id, unique_id.peek_read(),
+ unique_id.available_read()))
+ return TranslateLastOpenSslError();
+ }
+
+ int len = i2d_KM_KEY_DESCRIPTION(key_desc.get(), nullptr);
+ if (len < 0)
+ return TranslateLastOpenSslError();
+ *asn1_key_desc_len = len;
+ asn1_key_desc->reset(new uint8_t[*asn1_key_desc_len]);
+ if (!asn1_key_desc->get())
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ uint8_t* p = asn1_key_desc->get();
+ len = i2d_KM_KEY_DESCRIPTION(key_desc.get(), &p);
+ if (len < 0)
+ return TranslateLastOpenSslError();
+
+ return KM_ERROR_OK;
+}
+
+// Copy all enumerated values with the specified tag from stack to auth_list.
+static bool get_repeated_enums(const stack_st_ASN1_INTEGER* stack, keymaster_tag_t tag,
+ AuthorizationSet* auth_list) {
+ assert(keymaster_tag_get_type(tag) == KM_ENUM_REP);
+ for (size_t i = 0; i < sk_ASN1_INTEGER_num(stack); ++i) {
+ if (!auth_list->push_back(
+ keymaster_param_enum(tag, ASN1_INTEGER_get(sk_ASN1_INTEGER_value(stack, i)))))
+ return false;
+ }
+ return true;
+}
+
+// Add the specified integer tag/value pair to auth_list.
+template <keymaster_tag_type_t Type, keymaster_tag_t Tag, typename KeymasterEnum>
+static bool get_enum(const ASN1_INTEGER* asn1_int, TypedEnumTag<Type, Tag, KeymasterEnum> tag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_int)
+ return true;
+ return auth_list->push_back(tag, static_cast<KeymasterEnum>(ASN1_INTEGER_get(asn1_int)));
+}
+
+// Add the specified ulong tag/value pair to auth_list.
+static bool get_ulong(const ASN1_INTEGER* asn1_int, keymaster_tag_t tag,
+ AuthorizationSet* auth_list) {
+ if (!asn1_int)
+ return true;
+ UniquePtr<BIGNUM, BIGNUM_Delete> bn(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+ if (!bn.get())
+ return false;
+ uint64_t ulong = BN_get_word(bn.get());
+ return auth_list->push_back(keymaster_param_long(tag, ulong));
+}
+
+// Extract the values from the specified ASN.1 record and place them in auth_list.
+static keymaster_error_t extract_auth_list(const KM_AUTH_LIST* record,
+ AuthorizationSet* auth_list) {
+ if (!record)
+ return KM_ERROR_OK;
+
+ // Purpose
+ if (!get_repeated_enums(record->purpose, TAG_PURPOSE, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Algorithm
+ if (!get_enum(record->algorithm, TAG_ALGORITHM, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Key size
+ if (record->key_size && !auth_list->push_back(TAG_KEY_SIZE, ASN1_INTEGER_get(record->key_size)))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Digest
+ if (!get_repeated_enums(record->digest, TAG_DIGEST, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Padding
+ if (!get_repeated_enums(record->padding, TAG_PADDING, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // EC curve
+ if (!get_enum(record->ec_curve, TAG_EC_CURVE, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // RSA public exponent
+ if (!get_ulong(record->rsa_public_exponent, TAG_RSA_PUBLIC_EXPONENT, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Active date time
+ if (!get_ulong(record->active_date_time, TAG_ACTIVE_DATETIME, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Origination expire date time
+ if (!get_ulong(record->origination_expire_date_time, TAG_ORIGINATION_EXPIRE_DATETIME,
+ auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Usage Expire date time
+ if (!get_ulong(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // No auth required
+ if (record->no_auth_required && !auth_list->push_back(TAG_NO_AUTH_REQUIRED))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // User auth type
+ if (!get_enum(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Auth timeout
+ if (record->auth_timeout &&
+ !auth_list->push_back(TAG_AUTH_TIMEOUT, ASN1_INTEGER_get(record->auth_timeout)))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // All applications
+ if (record->all_applications && !auth_list->push_back(TAG_ALL_APPLICATIONS))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Application ID
+ if (record->application_id &&
+ !auth_list->push_back(TAG_APPLICATION_ID, record->application_id->data,
+ record->application_id->length))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Creation date time
+ if (!get_ulong(record->creation_date_time, TAG_CREATION_DATETIME, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Origin
+ if (!get_enum(record->origin, TAG_ORIGIN, auth_list))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Rollback resistant
+ if (record->rollback_resistant && !auth_list->push_back(TAG_ROLLBACK_RESISTANT))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // Root of trust
+ if (record->root_of_trust) {
+ KM_ROOT_OF_TRUST* rot = record->root_of_trust;
+ if (!rot->verified_boot_key)
+ return KM_ERROR_INVALID_KEY_BLOB;
+
+ // Other root of trust fields are not mapped to auth set entries.
+ }
+
+ // OS Version
+ if (record->os_version &&
+ !auth_list->push_back(TAG_OS_VERSION, ASN1_INTEGER_get(record->os_version)))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ // OS Patch level
+ if (record->os_patchlevel &&
+ !auth_list->push_back(TAG_OS_PATCHLEVEL, ASN1_INTEGER_get(record->os_patchlevel)))
+ return KM_ERROR_MEMORY_ALLOCATION_FAILED;
+
+ return KM_ERROR_OK;
+}
+
+// Parse the DER-encoded attestation record, placing the results in keymaster_version,
+// attestation_challenge, software_enforced, tee_enforced and unique_id.
+keymaster_error_t parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+ uint32_t* attestation_version, //
+ keymaster_security_level_t* attestation_security_level,
+ uint32_t* keymaster_version,
+ keymaster_security_level_t* keymaster_security_level,
+ keymaster_blob_t* attestation_challenge,
+ AuthorizationSet* software_enforced,
+ AuthorizationSet* tee_enforced,
+ keymaster_blob_t* unique_id) {
+ const uint8_t* p = asn1_key_desc;
+ UniquePtr<KM_KEY_DESCRIPTION, KM_KEY_DESCRIPTION_Delete> record(
+ d2i_KM_KEY_DESCRIPTION(nullptr, &p, asn1_key_desc_len));
+ if (!record.get())
+ return TranslateLastOpenSslError();
+
+ *attestation_version = ASN1_INTEGER_get(record->attestation_version);
+ *attestation_security_level = static_cast<keymaster_security_level_t>(
+ ASN1_ENUMERATED_get(record->attestation_security_level));
+ *keymaster_version = ASN1_INTEGER_get(record->keymaster_version);
+ *keymaster_security_level = static_cast<keymaster_security_level_t>(
+ ASN1_ENUMERATED_get(record->keymaster_security_level));
+
+ attestation_challenge->data =
+ dup_buffer(record->attestation_challenge->data, record->attestation_challenge->length);
+ attestation_challenge->data_length = record->attestation_challenge->length;
+
+ unique_id->data = dup_buffer(record->unique_id->data, record->unique_id->length);
+ unique_id->data_length = record->unique_id->length;
+
+ keymaster_error_t error = extract_auth_list(record->software_enforced, software_enforced);
+ if (error != KM_ERROR_OK)
+ return error;
+
+ return extract_auth_list(record->tee_enforced, tee_enforced);
+}
+
+} // namepace keymaster
diff --git a/unit_test/attestation_record.h b/unit_test/attestation_record.h
new file mode 100644
index 0000000..64acabc
--- a/dev/null
+++ b/unit_test/attestation_record.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_KEYMASTER_ATTESTATION_RECORD_H_
+#define SYSTEM_KEYMASTER_ATTESTATION_RECORD_H_
+
+#include <hardware/keymaster_defs.h>
+
+#include <keymaster/authorization_set.h>
+
+namespace keymaster {
+
+class KeymasterContext;
+
+/**
+ * The OID for Android attestation records. For the curious, it breaks down as follows:
+ *
+ * 1 = ISO
+ * 3 = org
+ * 6 = DoD (Huh? OIDs are weird.)
+ * 1 = IANA
+ * 4 = Private
+ * 1 = Enterprises
+ * 11129 = Google
+ * 2 = Google security
+ * 1 = certificate extension
+ * 17 = Android attestation extension.
+ */
+static const char kAttestionRecordOid[] = "1.3.6.1.4.1.11129.2.1.17";
+
+keymaster_error_t build_attestation_record(const AuthorizationSet& attestation_params,
+ const AuthorizationSet& software_enforced,
+ const AuthorizationSet& tee_enforced,
+ const KeymasterContext& context,
+ UniquePtr<uint8_t[]>* asn1_key_desc,
+ size_t* asn1_key_desc_len);
+
+keymaster_error_t parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+ uint32_t* attestation_version, //
+ keymaster_security_level_t* attestation_security_level,
+ uint32_t* keymaster_version,
+ keymaster_security_level_t* keymaster_security_level,
+ keymaster_blob_t* attestation_challenge,
+ AuthorizationSet* software_enforced,
+ AuthorizationSet* tee_enforced,
+ keymaster_blob_t* unique_id);
+}
+
+#endif // SYSTEM_KEYMASTER_ATTESTATION_RECORD_H_
diff --git a/unit_test/attestation_record_test.cpp b/unit_test/attestation_record_test.cpp
new file mode 100644
index 0000000..1cf8630
--- a/dev/null
+++ b/unit_test/attestation_record_test.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+#include <keymaster/keymaster_context.h>
+
+#include "android_keymaster_test_utils.h"
+#include "attestation_record.h"
+
+#include <keymaster/keymaster_context.h>
+
+namespace keymaster {
+namespace test {
+
+class TestContext : public KeymasterContext {
+ public:
+ keymaster_security_level_t GetSecurityLevel() const override {
+ return KM_SECURITY_LEVEL_SOFTWARE;
+ }
+ keymaster_error_t SetSystemVersion(uint32_t /* os_version */,
+ uint32_t /* os_patchlevel */) override {
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ void GetSystemVersion(uint32_t* os_version, uint32_t* os_patchlevel) const override {
+ *os_version = 0;
+ *os_patchlevel = 0;
+ }
+ KeyFactory* GetKeyFactory(keymaster_algorithm_t /* algorithm */) const override {
+ return nullptr;
+ }
+ OperationFactory* GetOperationFactory(keymaster_algorithm_t /* algorithm */,
+ keymaster_purpose_t /* purpose */) const override {
+ return nullptr;
+ }
+ keymaster_algorithm_t* GetSupportedAlgorithms(size_t* /* algorithms_count */) const override {
+ return nullptr;
+ }
+ keymaster_error_t CreateKeyBlob(const AuthorizationSet& /* key_description */,
+ keymaster_key_origin_t /* origin */,
+ const KeymasterKeyBlob& /* key_material */,
+ KeymasterKeyBlob* /* blob */,
+ AuthorizationSet* /* hw_enforced */,
+ AuthorizationSet* /* sw_enforced */) const override {
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ keymaster_error_t UpgradeKeyBlob(const KeymasterKeyBlob& /* key_to_upgrade */,
+ const AuthorizationSet& /* upgrade_params */,
+ KeymasterKeyBlob* /* upgraded_key */) const override {
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ keymaster_error_t ParseKeyBlob(const KeymasterKeyBlob& /* blob */,
+ const AuthorizationSet& /* additional_params */,
+ KeymasterKeyBlob* /* key_material */,
+ AuthorizationSet* /* hw_enforced */,
+ AuthorizationSet* /* sw_enforced */) const override {
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ keymaster_error_t AddRngEntropy(const uint8_t* /* buf */, size_t /* length */) const override {
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ keymaster_error_t GenerateRandom(uint8_t* /* buf */, size_t /* length */) const override {
+ return KM_ERROR_UNIMPLEMENTED;
+ }
+ KeymasterEnforcement* enforcement_policy() { return nullptr; }
+ EVP_PKEY* AttestationKey(keymaster_algorithm_t /* algorithm */,
+ keymaster_error_t* /* error */) const override {
+ return nullptr;
+ }
+ keymaster_cert_chain_t* AttestationChain(keymaster_algorithm_t /* algorithm */,
+ keymaster_error_t* /* error */) const override {
+ return nullptr;
+ }
+ keymaster_error_t GenerateUniqueId(uint64_t /* creation_date_time */,
+ const keymaster_blob_t& /* application_id */,
+ bool /* reset_since_rotation */, Buffer* unique_id) const {
+ // Finally, the reason for defining this class:
+ unique_id->Reinitialize("foo", 3);
+ return KM_ERROR_OK;
+ }
+};
+
+TEST(AttestTest, Simple) {
+ AuthorizationSet hw_set(AuthorizationSetBuilder()
+ .RsaSigningKey(512, 3)
+ .Digest(KM_DIGEST_SHA_2_256)
+ .Digest(KM_DIGEST_SHA_2_384)
+ .Authorization(TAG_OS_VERSION, 60000)
+ .Authorization(TAG_OS_PATCHLEVEL, 201512)
+ .Authorization(TAG_APPLICATION_ID, "bar", 3));
+ AuthorizationSet sw_set(AuthorizationSetBuilder().Authorization(TAG_ACTIVE_DATETIME, 10));
+
+ UniquePtr<uint8_t[]> asn1;
+ size_t asn1_len;
+ AuthorizationSet attest_params(
+ AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_CHALLENGE, "hello", 5));
+ EXPECT_EQ(KM_ERROR_OK, build_attestation_record(attest_params, sw_set, hw_set, TestContext(),
+ &asn1, &asn1_len));
+ EXPECT_GT(asn1_len, 0U);
+
+ std::ofstream output("attest.der",
+ std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
+ if (output)
+ output.write(reinterpret_cast<const char*>(asn1.get()), asn1_len);
+ output.close();
+
+ AuthorizationSet parsed_hw_set;
+ AuthorizationSet parsed_sw_set;
+ uint32_t attestation_version;
+ uint32_t keymaster_version;
+ keymaster_security_level_t attestation_security_level;
+ keymaster_security_level_t keymaster_security_level;
+ keymaster_blob_t attestation_challenge = {};
+ keymaster_blob_t unique_id = {};
+ EXPECT_EQ(KM_ERROR_OK,
+ parse_attestation_record(asn1.get(), asn1_len, &attestation_version,
+ &attestation_security_level, &keymaster_version,
+ &keymaster_security_level, &attestation_challenge,
+ &parsed_sw_set, &parsed_hw_set, &unique_id));
+
+ hw_set.Sort();
+ sw_set.Sort();
+ parsed_hw_set.Sort();
+ parsed_sw_set.Sort();
+ EXPECT_EQ(hw_set, parsed_hw_set);
+ EXPECT_EQ(sw_set, parsed_sw_set);
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/authorization_set_test.cpp b/unit_test/authorization_set_test.cpp
new file mode 100644
index 0000000..f3f4412
--- a/dev/null
+++ b/unit_test/authorization_set_test.cpp
@@ -0,0 +1,745 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/android_keymaster_utils.h>
+
+#include "android_keymaster_test_utils.h"
+
+namespace keymaster {
+
+namespace test {
+
+TEST(Construction, ListProvided) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6), Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+ EXPECT_EQ(8U, set.size());
+}
+
+TEST(Construction, Copy) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_USER_ID, 7),
+ Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD),
+ Authorization(TAG_APPLICATION_ID, "my_app", 6), Authorization(TAG_KEY_SIZE, 256),
+ Authorization(TAG_AUTH_TIMEOUT, 300),
+ };
+ AuthorizationSet set(params, array_length(params));
+ AuthorizationSet set2(set);
+ EXPECT_EQ(set, set2);
+}
+
+TEST(Construction, NullProvided) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ };
+
+ AuthorizationSet set1(params, 0);
+ EXPECT_EQ(0U, set1.size());
+ EXPECT_EQ(AuthorizationSet::OK, set1.is_valid());
+
+ AuthorizationSet set2(reinterpret_cast<keymaster_key_param_t*>(NULL), array_length(params));
+ EXPECT_EQ(0U, set2.size());
+ EXPECT_EQ(AuthorizationSet::OK, set2.is_valid());
+}
+
+TEST(Lookup, NonRepeated) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_KEY_SIZE, 256)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+
+ EXPECT_EQ(8U, set.size());
+
+ int pos = set.find(TAG_ALGORITHM);
+ ASSERT_NE(-1, pos);
+ EXPECT_EQ(KM_TAG_ALGORITHM, set[pos].tag);
+ EXPECT_EQ(KM_ALGORITHM_RSA, set[pos].enumerated);
+
+ pos = set.find(TAG_MAC_LENGTH);
+ EXPECT_EQ(-1, pos);
+
+ uint32_t int_val = 0;
+ EXPECT_TRUE(set.GetTagValue(TAG_USER_ID, &int_val));
+ EXPECT_EQ(7U, int_val);
+
+ keymaster_blob_t blob_val;
+ EXPECT_TRUE(set.GetTagValue(TAG_APPLICATION_ID, &blob_val));
+ EXPECT_EQ(6U, blob_val.data_length);
+ EXPECT_EQ(0, memcmp(blob_val.data, "my_app", 6));
+}
+
+TEST(Lookup, Repeated) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_SECURE_ID, 47727)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_KEY_SIZE, 256)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+ EXPECT_EQ(9U, set.size());
+
+ int pos = set.find(TAG_PURPOSE);
+ ASSERT_FALSE(pos == -1);
+ EXPECT_EQ(KM_TAG_PURPOSE, set[pos].tag);
+ EXPECT_EQ(KM_PURPOSE_SIGN, set[pos].enumerated);
+
+ pos = set.find(TAG_PURPOSE, pos);
+ EXPECT_EQ(KM_TAG_PURPOSE, set[pos].tag);
+ EXPECT_EQ(KM_PURPOSE_VERIFY, set[pos].enumerated);
+
+ EXPECT_EQ(-1, set.find(TAG_PURPOSE, pos));
+
+ pos = set.find(TAG_USER_SECURE_ID, pos);
+ EXPECT_EQ(KM_TAG_USER_SECURE_ID, set[pos].tag);
+ EXPECT_EQ(47727U, set[pos].long_integer);
+}
+
+TEST(Lookup, Indexed) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_KEY_SIZE, 256)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+ EXPECT_EQ(8U, set.size());
+
+ EXPECT_EQ(KM_TAG_PURPOSE, set[0].tag);
+ EXPECT_EQ(KM_PURPOSE_SIGN, set[0].enumerated);
+
+ // Lookup beyond end doesn't work, just returns zeros, but doens't blow up either (verify by
+ // running under valgrind).
+ EXPECT_EQ(KM_TAG_INVALID, set[10].tag);
+}
+
+TEST(Serialization, RoundTrip) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_KEY_SIZE, 256)
+ .Authorization(TAG_USER_SECURE_ID, 47727)
+ .Authorization(TAG_AUTH_TIMEOUT, 300)
+ .Authorization(TAG_ALL_USERS)
+ .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3)
+ .Authorization(TAG_ACTIVE_DATETIME, 10));
+
+ size_t size = set.SerializedSize();
+ EXPECT_TRUE(size > 0);
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size));
+ AuthorizationSet deserialized(buf.get(), size);
+
+ EXPECT_EQ(AuthorizationSet::OK, deserialized.is_valid());
+ EXPECT_EQ(set, deserialized);
+
+ int pos = deserialized.find(TAG_APPLICATION_ID);
+ ASSERT_NE(-1, pos);
+ EXPECT_EQ(KM_TAG_APPLICATION_ID, deserialized[pos].tag);
+ EXPECT_EQ(6U, deserialized[pos].blob.data_length);
+ EXPECT_EQ(0, memcmp(deserialized[pos].blob.data, "my_app", 6));
+}
+
+TEST(Deserialization, Deserialize) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_KEY_SIZE, 256)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+
+ size_t size = set.SerializedSize();
+ EXPECT_TRUE(size > 0);
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size));
+ AuthorizationSet deserialized;
+ const uint8_t* p = buf.get();
+ EXPECT_TRUE(deserialized.Deserialize(&p, p + size));
+ EXPECT_EQ(p, buf.get() + size);
+
+ EXPECT_EQ(AuthorizationSet::OK, deserialized.is_valid());
+
+ EXPECT_EQ(set.size(), deserialized.size());
+ for (size_t i = 0; i < set.size(); ++i) {
+ EXPECT_EQ(set[i].tag, deserialized[i].tag);
+ }
+
+ int pos = deserialized.find(TAG_APPLICATION_ID);
+ ASSERT_NE(-1, pos);
+ EXPECT_EQ(KM_TAG_APPLICATION_ID, deserialized[pos].tag);
+ EXPECT_EQ(6U, deserialized[pos].blob.data_length);
+ EXPECT_EQ(0, memcmp(deserialized[pos].blob.data, "my_app", 6));
+}
+
+TEST(Deserialization, TooShortBuffer) {
+ uint8_t buf[] = {0, 0, 0};
+ AuthorizationSet deserialized(buf, array_length(buf));
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid());
+
+ const uint8_t* p = buf;
+ EXPECT_FALSE(deserialized.Deserialize(&p, p + array_length(buf)));
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid());
+}
+
+TEST(Deserialization, InvalidLengthField) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_KEY_SIZE, 256)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+
+ size_t size = set.SerializedSize();
+ EXPECT_TRUE(size > 0);
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size));
+ *reinterpret_cast<uint32_t*>(buf.get()) = 9;
+
+ AuthorizationSet deserialized(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid());
+
+ const uint8_t* p = buf.get();
+ EXPECT_FALSE(deserialized.Deserialize(&p, p + size));
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized.is_valid());
+}
+
+static uint32_t read_uint32(const uint8_t* buf) {
+ uint32_t val;
+ memcpy(&val, buf, sizeof(val));
+ return val;
+}
+
+static void add_to_uint32(uint8_t* buf, int delta) {
+ uint32_t val;
+ memcpy(&val, buf, sizeof(val));
+ val += delta;
+ memcpy(buf, &val, sizeof(val));
+}
+
+TEST(Deserialization, MalformedIndirectData) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_APPLICATION_DATA, "foo", 3));
+ size_t size = set.SerializedSize();
+
+ UniquePtr<uint8_t[]> buf(new uint8_t[size]);
+ EXPECT_EQ(buf.get() + size, set.Serialize(buf.get(), buf.get() + size));
+
+ // This sucks. This test, as written, requires intimate knowledge of the serialized layout of
+ // this particular set, which means it's brittle. But it's important to test that we handle
+ // broken serialized data and I can't think of a better way to write this.
+ //
+ // The contents of buf are:
+ //
+ // Bytes: Content:
+ // 0-3 Length of string data, which is 9.
+ // 4-9 "my_app"
+ // 10-12 "foo"
+ // 13-16 Number of elements, which is 2.
+ // 17-20 Length of elements, which is 24.
+ // 21-24 First tag, TAG_APPLICATION_ID
+ // 25-28 Length of string "my_app", 6
+ // 29-32 Offset of string "my_app", 0
+ // 33-36 Second tag, TAG_APPLICATION_DATA
+ // 37-40 Length of string "foo", 3
+ // 41-44 Offset of string "foo", 6
+
+ // Check that stuff is where we think.
+ EXPECT_EQ('m', buf[4]);
+ EXPECT_EQ('f', buf[10]);
+ // Length of "my_app"
+ EXPECT_EQ(6U, read_uint32(buf.get() + 25));
+ // Offset of "my_app"
+ EXPECT_EQ(0U, read_uint32(buf.get() + 29));
+ // Length of "foo"
+ EXPECT_EQ(3U, read_uint32(buf.get() + 37));
+ // Offset of "foo"
+ EXPECT_EQ(6U, read_uint32(buf.get() + 41));
+
+ // Check that deserialization works.
+ AuthorizationSet deserialized1(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::OK, deserialized1.is_valid());
+
+ const uint8_t* p = buf.get();
+ EXPECT_TRUE(deserialized1.Deserialize(&p, p + size));
+ EXPECT_EQ(AuthorizationSet::OK, deserialized1.is_valid());
+
+ //
+ // Now mess them up in various ways:
+ //
+
+ // Move "foo" offset so offset + length goes off the end
+ add_to_uint32(buf.get() + 41, 1);
+ AuthorizationSet deserialized2(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized2.is_valid());
+ add_to_uint32(buf.get() + 41, -1);
+
+ // Shorten the "my_app" length to make a gap between the blobs.
+ add_to_uint32(buf.get() + 25, -1);
+ AuthorizationSet deserialized3(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::MALFORMED_DATA, deserialized3.is_valid());
+ add_to_uint32(buf.get() + 25, 1);
+
+ // Extend the "my_app" length to make them overlap, and decrease the "foo" length to keep the
+ // total length the same. We don't detect this but should.
+ // TODO(swillden): Detect overlaps and holes that leave total size correct.
+ add_to_uint32(buf.get() + 25, 1);
+ add_to_uint32(buf.get() + 37, -1);
+ AuthorizationSet deserialized4(buf.get(), size);
+ EXPECT_EQ(AuthorizationSet::OK, deserialized4.is_valid());
+}
+
+TEST(Growable, SuccessfulRoundTrip) {
+ AuthorizationSet growable;
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)));
+ EXPECT_EQ(1U, growable.size());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)));
+ EXPECT_EQ(2U, growable.size());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)));
+ EXPECT_EQ(3U, growable.size());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_ID, "data", 4)));
+ EXPECT_EQ(4U, growable.size());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_DATA, "some more data", 14)));
+ EXPECT_EQ(5U, growable.size());
+
+ size_t serialize_size = growable.SerializedSize();
+ UniquePtr<uint8_t[]> serialized(new uint8_t[serialize_size]);
+ EXPECT_EQ(serialized.get() + serialize_size,
+ growable.Serialize(serialized.get(), serialized.get() + serialize_size));
+
+ AuthorizationSet deserialized(serialized.get(), serialize_size);
+ EXPECT_EQ(growable, deserialized);
+}
+
+TEST(Growable, InsufficientElemBuf) {
+ AuthorizationSet growable;
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ // First insertion fits.
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)));
+ EXPECT_EQ(1U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ // Second does too.
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_RSA_PUBLIC_EXPONENT, 3)));
+ EXPECT_EQ(2U, growable.size());
+}
+
+TEST(Growable, InsufficientIndirectBuf) {
+ AuthorizationSet growable;
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)));
+ EXPECT_EQ(1U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_ID, "1234567890", 10)));
+ EXPECT_EQ(2U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_APPLICATION_DATA, "1", 1)));
+ EXPECT_EQ(3U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+
+ // Can still add another entry without indirect data. Now it's full.
+ EXPECT_TRUE(growable.push_back(Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)));
+ EXPECT_EQ(4U, growable.size());
+ EXPECT_EQ(AuthorizationSet::OK, growable.is_valid());
+}
+
+TEST(Growable, PushBackSets) {
+ AuthorizationSetBuilder builder;
+ builder.Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_KEY_SIZE, 256)
+ .Authorization(TAG_AUTH_TIMEOUT, 300);
+
+ AuthorizationSet set1(builder.build());
+ AuthorizationSet set2(builder.build());
+
+ AuthorizationSet combined;
+ EXPECT_TRUE(combined.push_back(set1));
+ EXPECT_TRUE(combined.push_back(set2));
+ EXPECT_EQ(set1.size() + set2.size(), combined.size());
+ EXPECT_EQ(12U, combined.indirect_size());
+}
+
+TEST(GetValue, GetInt) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+
+ uint32_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_USER_ID, &val));
+ EXPECT_EQ(7U, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_KEY_SIZE, &val));
+}
+
+TEST(GetValue, GetLong) {
+ AuthorizationSet set1(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3));
+
+ AuthorizationSet set2(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA));
+
+ uint64_t val;
+ EXPECT_TRUE(set1.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &val));
+ EXPECT_EQ(3U, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set2.GetTagValue(TAG_RSA_PUBLIC_EXPONENT, &val));
+}
+
+TEST(GetValue, GetLongRep) {
+ AuthorizationSet set1(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_SECURE_ID, 8338)
+ .Authorization(TAG_USER_SECURE_ID, 4334)
+ .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3));
+
+ AuthorizationSet set2(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA));
+
+ uint64_t val;
+ EXPECT_TRUE(set1.GetTagValue(TAG_USER_SECURE_ID, 0, &val));
+ EXPECT_EQ(8338U, val);
+ EXPECT_TRUE(set1.GetTagValue(TAG_USER_SECURE_ID, 1, &val));
+ EXPECT_EQ(4334U, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set2.GetTagValue(TAG_USER_SECURE_ID, &val));
+}
+
+TEST(GetValue, GetEnum) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+
+ keymaster_algorithm_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_ALGORITHM, &val));
+ EXPECT_EQ(KM_ALGORITHM_RSA, val);
+
+ // Find one that isn't there
+ keymaster_padding_t val2;
+ EXPECT_FALSE(set.GetTagValue(TAG_PADDING, &val2));
+}
+
+TEST(GetValue, GetEnumRep) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+
+ keymaster_purpose_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_PURPOSE, 0, &val));
+ EXPECT_EQ(KM_PURPOSE_SIGN, val);
+ EXPECT_TRUE(set.GetTagValue(TAG_PURPOSE, 1, &val));
+ EXPECT_EQ(KM_PURPOSE_VERIFY, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_PURPOSE, 2, &val));
+}
+
+TEST(GetValue, GetDate) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+
+ uint64_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_ACTIVE_DATETIME, &val));
+ EXPECT_EQ(10U, val);
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_USAGE_EXPIRE_DATETIME, &val));
+}
+
+TEST(GetValue, GetBlob) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_APPLICATION_ID, "my_app", 6)
+ .Authorization(TAG_AUTH_TIMEOUT, 300));
+
+ keymaster_blob_t val;
+ EXPECT_TRUE(set.GetTagValue(TAG_APPLICATION_ID, &val));
+ EXPECT_EQ(6U, val.data_length);
+ EXPECT_EQ(0, memcmp(val.data, "my_app", 6));
+
+ // Find one that isn't there
+ EXPECT_FALSE(set.GetTagValue(TAG_APPLICATION_DATA, &val));
+}
+
+TEST(Deduplication, NoDuplicates) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD));
+ AuthorizationSet copy(set);
+
+ EXPECT_EQ(copy, set);
+ set.Deduplicate();
+ EXPECT_EQ(copy.size(), set.size());
+
+ // Sets no longer compare equal, because of ordering (ugh, maybe it should be
+ // AuthorizationList, not AuthorizationSet).
+ EXPECT_NE(copy, set);
+}
+
+TEST(Deduplication, NoDuplicatesHasInvalid) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_INVALID)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD));
+ AuthorizationSet copy(set);
+
+ EXPECT_EQ(copy, set);
+ set.Deduplicate();
+
+ // Deduplicate should have removed the invalid.
+ EXPECT_EQ(copy.size() - 1, set.size());
+ EXPECT_NE(copy, set);
+}
+
+TEST(Deduplication, DuplicateEnum) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD));
+ AuthorizationSet copy(set);
+
+ EXPECT_EQ(copy, set);
+ set.Deduplicate();
+ EXPECT_EQ(copy.size() - 2, set.size());
+ EXPECT_NE(copy, set);
+}
+
+TEST(Deduplication, DuplicateBlob) {
+ AuthorizationSet set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4)
+ .Authorization(TAG_APPLICATION_DATA, "foo", 3)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD));
+ AuthorizationSet copy(set);
+
+ EXPECT_EQ(copy, set);
+ set.Deduplicate();
+ EXPECT_EQ(copy.size() - 3, set.size());
+ EXPECT_NE(copy, set);
+
+ // The real test here is that valgrind reports no leak.
+}
+
+TEST(Union, Disjoint) {
+ AuthorizationSet set1(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ AuthorizationSet set2(AuthorizationSetBuilder()
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_APPLICATION_DATA, "foo", 3)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD));
+
+ AuthorizationSet expected(AuthorizationSetBuilder()
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4)
+ .Authorization(TAG_APPLICATION_DATA, "foo", 3));
+
+ set1.Union(set2);
+ EXPECT_EQ(expected, set1);
+}
+
+TEST(Union, Overlap) {
+ AuthorizationSet set1(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ AuthorizationSet set2(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ AuthorizationSet expected(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ set1.Union(set2);
+ EXPECT_EQ(expected, set1);
+}
+
+TEST(Union, Empty) {
+ AuthorizationSet set1(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ AuthorizationSet set2;
+
+ AuthorizationSet expected(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ set1.Union(set2);
+ EXPECT_EQ(expected, set1);
+}
+
+TEST(Difference, Disjoint) {
+ AuthorizationSet set1(AuthorizationSetBuilder()
+ .Authorization(TAG_APPLICATION_DATA, "data", 4)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10));
+
+ AuthorizationSet set2(AuthorizationSetBuilder()
+ .Authorization(TAG_USER_ID, 7)
+ .Authorization(TAG_APPLICATION_DATA, "foo", 3)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD));
+
+ // Elements are the same as set1, but happen to be in a different order
+ AuthorizationSet expected(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ set1.Difference(set2);
+ EXPECT_EQ(expected, set1);
+}
+
+TEST(Difference, Overlap) {
+ AuthorizationSet set1(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ AuthorizationSet set2(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ AuthorizationSet empty;
+ set1.Difference(set2);
+ EXPECT_EQ(empty, set1);
+ EXPECT_EQ(0U, set1.size());
+}
+
+TEST(Difference, NullSet) {
+ AuthorizationSet set1(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ AuthorizationSet set2;
+
+ AuthorizationSet expected(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_ACTIVE_DATETIME, 10)
+ .Authorization(TAG_APPLICATION_DATA, "data", 4));
+
+ set1.Difference(set2);
+ EXPECT_EQ(expected, set1);
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/ecies_kem_test.cpp b/unit_test/ecies_kem_test.cpp
new file mode 100644
index 0000000..f653dc0
--- a/dev/null
+++ b/unit_test/ecies_kem_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ecies_kem.h"
+
+#include <gtest/gtest.h>
+#include <openssl/evp.h>
+
+#include <hardware/keymaster_defs.h>
+#include <keymaster/android_keymaster_utils.h>
+
+#include "android_keymaster_test_utils.h"
+#include "nist_curve_key_exchange.h"
+
+using std::string;
+
+namespace keymaster {
+namespace test {
+
+StdoutLogger logger;
+
+static const keymaster_ec_curve_t kEcCurves[] = {KM_EC_CURVE_P_224, KM_EC_CURVE_P_256,
+ KM_EC_CURVE_P_384, KM_EC_CURVE_P_521};
+
+/**
+ * TestConsistency just tests that the basic key encapsulation hold.
+ */
+TEST(EciesKem, TestConsistency) {
+ static const uint32_t kKeyLen = 32;
+ for (auto& curve : kEcCurves) {
+ AuthorizationSet kem_description(AuthorizationSetBuilder()
+ .Authorization(TAG_EC_CURVE, curve)
+ .Authorization(TAG_KDF, KM_KDF_RFC5869_SHA256)
+ .Authorization(TAG_ECIES_SINGLE_HASH_MODE)
+ .Authorization(TAG_KEY_SIZE, kKeyLen));
+ keymaster_error_t error;
+ EciesKem* kem = new EciesKem(kem_description, &error);
+ ASSERT_EQ(KM_ERROR_OK, error);
+
+ NistCurveKeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve);
+ Buffer peer_public_value;
+ ASSERT_TRUE(key_exchange->public_value(&peer_public_value));
+
+ Buffer output_clear_key;
+ Buffer output_encrypted_key;
+ ASSERT_TRUE(kem->Encrypt(peer_public_value, &output_clear_key, &output_encrypted_key));
+ ASSERT_EQ(kKeyLen, output_clear_key.available_read());
+ ASSERT_EQ(peer_public_value.available_read(), output_encrypted_key.available_read());
+
+ Buffer decrypted_clear_key;
+ ASSERT_TRUE(
+ kem->Decrypt(key_exchange->private_key(), output_encrypted_key, &decrypted_clear_key));
+ ASSERT_EQ(kKeyLen, decrypted_clear_key.available_read());
+ EXPECT_EQ(0, memcmp(output_clear_key.peek_read(), decrypted_clear_key.peek_read(),
+ output_clear_key.available_read()));
+ }
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/gtest_main.cpp b/unit_test/gtest_main.cpp
new file mode 100644
index 0000000..6072749
--- a/dev/null
+++ b/unit_test/gtest_main.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <openssl/engine.h>
+
+int main(int argc, char** argv) {
+#if !defined(OPENSSL_IS_BORINGSSL)
+ ERR_load_crypto_strings();
+#endif // not OPENSSL_IS_BORINGSSL
+ ::testing::InitGoogleTest(&argc, argv);
+ int result = RUN_ALL_TESTS();
+#if !defined(OPENSSL_IS_BORINGSSL)
+ // Clean up stuff OpenSSL leaves around, so Valgrind doesn't complain.
+ CRYPTO_cleanup_all_ex_data();
+ ERR_remove_thread_state(NULL);
+ ERR_free_strings();
+#endif // not OPENSSL_IS_BORINGSSL
+ return result;
+}
diff --git a/unit_test/hkdf_test.cpp b/unit_test/hkdf_test.cpp
new file mode 100644
index 0000000..3d3f092
--- a/dev/null
+++ b/unit_test/hkdf_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hkdf.h"
+
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include "android_keymaster_test_utils.h"
+
+using std::string;
+
+namespace keymaster {
+namespace test {
+
+struct HkdfTest {
+ const char* key_hex;
+ const char* salt_hex;
+ const char* info_hex;
+ const char* output_hex;
+};
+
+// These test cases are taken from
+// https://tools.ietf.org/html/rfc5869#appendix-A.
+static const HkdfTest kHkdfTests[] = {
+ {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c",
+ "f0f1f2f3f4f5f6f7f8f9",
+ "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865",
+ },
+ {
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c"
+ "2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f",
+ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c"
+ "8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdc"
+ "dddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e"
+ "590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87",
+ },
+ {
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "", "",
+ "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8",
+ },
+};
+
+TEST(HkdfTest, Hkdf) {
+ for (auto& test : kHkdfTests) {
+ const string key = hex2str(test.key_hex);
+ const string salt = hex2str(test.salt_hex);
+ const string info = hex2str(test.info_hex);
+ const string expected = hex2str(test.output_hex);
+ size_t output_len = expected.size();
+ uint8_t output[output_len];
+ Rfc5869Sha256Kdf hkdf;
+ ASSERT_TRUE(hkdf.Init(reinterpret_cast<const uint8_t*>(key.data()), key.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size()));
+ ASSERT_TRUE(hkdf.GenerateKey(reinterpret_cast<const uint8_t*>(info.data()), info.size(),
+ output, output_len));
+ EXPECT_EQ(0, memcmp(output, expected.data(), output_len));
+ }
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/hmac_test.cpp b/unit_test/hmac_test.cpp
new file mode 100644
index 0000000..04f9356
--- a/dev/null
+++ b/unit_test/hmac_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hmac.h"
+
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include "android_keymaster_test_utils.h"
+
+using std::string;
+
+namespace keymaster {
+
+namespace test {
+
+struct HmacTest {
+ const char* data;
+ const char* key;
+ uint8_t digest[32];
+};
+
+static const HmacTest kHmacTests[] = {
+ {
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ {
+ 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5,
+ 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f,
+ 0x0e, 0xe3, 0x7f, 0x54,
+ },
+ },
+ {
+ "The test message for the MD2, MD5, and SHA-1 hashing algorithms.",
+ "46697265666f7820616e64205468756e64657242697264206172652061776573"
+ "6f6d652100",
+ {
+ 0x05, 0x75, 0x9a, 0x9e, 0x70, 0x5e, 0xe7, 0x44, 0xe2, 0x46, 0x4b, 0x92, 0x22, 0x14,
+ 0x22, 0xe0, 0x1b, 0x92, 0x8a, 0x0c, 0xfe, 0xf5, 0x49, 0xe9, 0xa7, 0x1b, 0x56, 0x7d,
+ 0x1d, 0x29, 0x40, 0x48,
+ },
+ },
+};
+
+TEST(HmacTest, SHA256) {
+ for (size_t i = 0; i < 2; i++) {
+ const HmacTest& test(kHmacTests[i]);
+
+ HmacSha256 hmac;
+ const string key = hex2str(test.key);
+ Buffer key_buffer(key.data(), key.size());
+ ASSERT_TRUE(hmac.Init(key_buffer));
+
+ uint8_t digest_copy[sizeof(test.digest)];
+ memcpy(digest_copy, test.digest, sizeof(test.digest));
+ Buffer digest_buffer(reinterpret_cast<uint8_t*>(digest_copy), sizeof(digest_copy));
+
+ Buffer data_buffer(test.data, strlen(test.data));
+ EXPECT_TRUE(hmac.Verify(data_buffer, digest_buffer));
+
+ digest_copy[16] ^= 0x80;
+ digest_buffer.Reinitialize(reinterpret_cast<uint8_t*>(digest_copy), sizeof(digest_copy));
+ EXPECT_FALSE(hmac.Verify(data_buffer, digest_buffer));
+ }
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/kdf1_test.cpp b/unit_test/kdf1_test.cpp
new file mode 100644
index 0000000..9c8b0d5
--- a/dev/null
+++ b/unit_test/kdf1_test.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kdf1.h"
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include "android_keymaster_test_utils.h"
+
+using std::string;
+
+namespace keymaster {
+
+namespace test {
+
+struct Kdf1Test {
+ const char* key_hex;
+ const char* expected_output_hex;
+ keymaster_digest_t digest_type;
+};
+
+static const Kdf1Test kKdf1Tests[] = {
+ {"032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d764374152e0ac009e509e7ba30cd2f1778"
+ "e113b64e135cf4e2292c75efe5288edfda4",
+ "5f8de105b5e96b2e490ddecbd147dd1def7e3b8e0e6a26eb7b956ccb8b3bdc1ca975bc57c3989e8fbad31a224655d"
+ "800c46954840ff32052cdf0d640562bdfadfa263cfccf3c52b29f2af4a1869959bc77f854cf15bd7a25192985a842"
+ "dbff8e13efee5b7e7e55bbe4d389647c686a9a9ab3fb889b2d7767d3837eea4e0a2f04",
+ KM_DIGEST_SHA1}};
+
+TEST(Kdf1Test, Kdf1) {
+ for (auto& test : kKdf1Tests) {
+ const string key = hex2str(test.key_hex);
+ const string expected_output = hex2str(test.expected_output_hex);
+ size_t output_len = expected_output.size();
+ uint8_t output[output_len];
+
+ Kdf1 kdf1;
+ ASSERT_TRUE(
+ kdf1.Init(test.digest_type, reinterpret_cast<const uint8_t*>(key.data()), key.size()));
+ ASSERT_TRUE(kdf1.GenerateKey(nullptr, 0, output, output_len));
+ EXPECT_EQ(0, memcmp(output, expected_output.data(), output_len));
+ }
+}
+
+} // namespace test
+
+} // namespace keymaster
diff --git a/unit_test/kdf2_test.cpp b/unit_test/kdf2_test.cpp
new file mode 100644
index 0000000..29ff40a
--- a/dev/null
+++ b/unit_test/kdf2_test.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kdf2.h"
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include "android_keymaster_test_utils.h"
+
+using std::string;
+
+namespace keymaster {
+
+namespace test {
+
+struct Kdf2Test {
+ const char* key_hex;
+ const char* info_hex;
+ const char* expected_output_hex;
+ keymaster_digest_t digest_type;
+};
+
+static const Kdf2Test kKdf2Tests[] = {
+ {"032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d764374152e0ac009e509e7ba30cd2f1778"
+ "e113b64e135cf4e2292c75efe5288edfda4",
+ "",
+ "10a2403db42a8743cb989de86e668d168cbe6046e23ff26f741e87949a3bba1311ac179f819a3d18412e9eb45668f"
+ "2923c087c1299005f8d5fd42ca257bc93e8fee0c5a0d2a8aa70185401fbbd99379ec76c663e9a29d0b70f3fe261a5"
+ "9cdc24875a60b4aacb1319fa11c3365a8b79a44669f26fba933d012db213d7e3b16349",
+ KM_DIGEST_SHA_2_256},
+ {"032e45326fa859a72ec235acff929b15d1372e30b207255f0611b8f785d764374152e0ac009e509e7ba30cd2f1778"
+ "e113b64e135cf4e2292c75efe5288edfda4",
+ "",
+ "0e6a26eb7b956ccb8b3bdc1ca975bc57c3989e8fbad31a224655d800c46954840ff32052cdf0d640562bdfadfa263"
+ "cfccf3c52b29f2af4a1869959bc77f854cf15bd7a25192985a842dbff8e13efee5b7e7e55bbe4d389647c686a9a9a"
+ "b3fb889b2d7767d3837eea4e0a2f04b53ca8f50fb31225c1be2d0126c8c7a4753b0807",
+ KM_DIGEST_SHA1},
+ {"CA7C0F8C3FFA87A96E1B74AC8E6AF594347BB40A", "", "744AB703F5BC082E59185F6D049D2D367DB245C2",
+ KM_DIGEST_SHA1},
+ {"0499B502FC8B5BAFB0F4047E731D1F9FD8CD0D8881", "",
+ "03C62280C894E103C680B13CD4B4AE740A5EF0C72547292F82DC6B1777F47D63BA9D1EA73"
+ "2DBF386",
+ KM_DIGEST_SHA1},
+ {"5E10B967A95606853E528F04262AD18A4767C761163971391E17CB05A21668D4CE2B9F151617408042CE091958382"
+ "3FD346D1751FBE2341AF2EE0461B62F100FFAD4F723F70C18B38238ED183E9398C8CA517EE0CBBEFFF9C59471FE27"
+ "8093924089480DBC5A38E9A1A97D23038106847D0D22ECF85F49A861821199BAFCB0D74E6ACFFD7D142765EBF4C71"
+ "2414FE4B6AB957F4CB466B46601289BB82060428272842EE28F113CD11F39431CBFFD823254CE472E2105E49B3D7F"
+ "113B825076E6264585807BC46454665F27C5E4E1A4BD03470486322981FDC894CCA1E2930987C92C15A38BC42EB38"
+ "810E867C4432F07259EC00CDBBB0FB99E1727C706DA58DD",
+ "484D4143204B6579", "BC98EB018CB00EE26D1F97A15AE166912A7AC4C5", KM_DIGEST_SHA1},
+
+};
+
+TEST(Kdf2Test, Kdf2) {
+ for (auto& test : kKdf2Tests) {
+ const string key = hex2str(test.key_hex);
+ const string info = hex2str(test.info_hex);
+ const string expected_output = hex2str(test.expected_output_hex);
+ size_t output_len = expected_output.size();
+ uint8_t output[output_len];
+
+ Kdf2 kdf2;
+ ASSERT_TRUE(
+ kdf2.Init(test.digest_type, reinterpret_cast<const uint8_t*>(key.data()), key.size()));
+ ASSERT_TRUE(kdf2.GenerateKey(reinterpret_cast<const uint8_t*>(info.data()), info.size(),
+ output, output_len));
+ EXPECT_EQ(0, memcmp(output, expected_output.data(), output_len));
+ }
+}
+
+} // namespace test
+
+} // namespace keymaster
diff --git a/unit_test/kdf_test.cpp b/unit_test/kdf_test.cpp
new file mode 100644
index 0000000..f6f4a93
--- a/dev/null
+++ b/unit_test/kdf_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "kdf.h"
+#include <gtest/gtest.h>
+
+namespace keymaster {
+
+namespace test {
+
+class ForTestAbstractKdf : public Kdf {
+ bool GenerateKey(const uint8_t* /* info */, size_t /* info_len */, uint8_t* /* output */,
+ size_t /* output_len */) {
+ return true;
+ }
+};
+
+TEST(KdfTest, Kdf) {
+ ForTestAbstractKdf kdf;
+ uint8_t key[128];
+ uint8_t salt[128];
+ ASSERT_TRUE(kdf.Init(KM_DIGEST_SHA1, key, 128, salt, 128));
+ ASSERT_TRUE(kdf.Init(KM_DIGEST_SHA_2_256, key, 128, salt, 128));
+ ASSERT_TRUE(kdf.Init(KM_DIGEST_SHA1, key, 128, nullptr, 0));
+ ASSERT_FALSE(kdf.Init(KM_DIGEST_MD5, key, 128, salt, 128));
+ ASSERT_FALSE(kdf.Init(KM_DIGEST_SHA1, nullptr, 0, salt, 128));
+ ASSERT_FALSE(kdf.Init(KM_DIGEST_SHA1, nullptr, 128, salt, 128));
+ ASSERT_FALSE(kdf.Init(KM_DIGEST_SHA1, key, 0, salt, 128));
+}
+
+} // namespace test
+
+} // namespace keymaster
diff --git a/unit_test/key_blob_test.cpp b/unit_test/key_blob_test.cpp
new file mode 100644
index 0000000..1e590f0
--- a/dev/null
+++ b/unit_test/key_blob_test.cpp
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include <gtest/gtest.h>
+
+#include <openssl/engine.h>
+#include <openssl/rand.h>
+
+#include <keymaster/authorization_set.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/keymaster_tags.h>
+
+#include "android_keymaster_test_utils.h"
+#include "auth_encrypted_key_blob.h"
+#include "integrity_assured_key_blob.h"
+#include "ocb_utils.h"
+
+namespace keymaster {
+
+namespace test {
+
+const uint8_t master_key_data[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const uint8_t key_data[5] = {21, 22, 23, 24, 25};
+
+class KeyBlobTest : public testing::Test {
+ protected:
+ KeyBlobTest()
+ : master_key_(master_key_data, array_length(master_key_data)),
+ key_material_(key_data, array_length(key_data)) {
+ hw_enforced_.push_back(TAG_ALGORITHM, KM_ALGORITHM_RSA);
+ hw_enforced_.push_back(TAG_KEY_SIZE, 256);
+ hw_enforced_.push_back(TAG_BLOB_USAGE_REQUIREMENTS, KM_BLOB_STANDALONE);
+ hw_enforced_.push_back(TAG_MIN_SECONDS_BETWEEN_OPS, 10);
+ hw_enforced_.push_back(TAG_ALL_USERS);
+ hw_enforced_.push_back(TAG_NO_AUTH_REQUIRED);
+ hw_enforced_.push_back(TAG_ORIGIN, KM_ORIGIN_GENERATED);
+
+ sw_enforced_.push_back(TAG_ACTIVE_DATETIME, 10);
+ sw_enforced_.push_back(TAG_ORIGINATION_EXPIRE_DATETIME, 100);
+ sw_enforced_.push_back(TAG_CREATION_DATETIME, 10);
+
+ hidden_.push_back(TAG_ROOT_OF_TRUST, "foo", 3);
+ hidden_.push_back(TAG_APPLICATION_ID, "my_app", 6);
+
+ nonce_.reserve(OCB_NONCE_LENGTH);
+ EXPECT_EQ(1, RAND_bytes(nonce_.peek_write(), OCB_NONCE_LENGTH));
+ nonce_.advance_write(OCB_NONCE_LENGTH);
+
+ tag_.reserve(OCB_TAG_LENGTH);
+ }
+
+ keymaster_error_t Encrypt() {
+ return OcbEncryptKey(hw_enforced_, sw_enforced_, hidden_, master_key_, key_material_,
+ nonce_, &ciphertext_, &tag_);
+ }
+
+ keymaster_error_t Decrypt() {
+ return OcbDecryptKey(hw_enforced_, sw_enforced_, hidden_, master_key_, ciphertext_, nonce_,
+ tag_, &decrypted_plaintext_);
+ }
+
+ keymaster_error_t Serialize() {
+ return SerializeAuthEncryptedBlob(ciphertext_, hw_enforced_, sw_enforced_, nonce_, tag_,
+ &serialized_blob_);
+ }
+
+ keymaster_error_t Deserialize() {
+ return DeserializeAuthEncryptedBlob(serialized_blob_, &ciphertext_, &hw_enforced_,
+ &sw_enforced_, &nonce_, &tag_);
+ }
+
+ AuthorizationSet hw_enforced_;
+ AuthorizationSet sw_enforced_;
+ AuthorizationSet hidden_;
+ Buffer nonce_, tag_;
+
+ KeymasterKeyBlob master_key_;
+ KeymasterKeyBlob key_material_;
+ KeymasterKeyBlob ciphertext_;
+ KeymasterKeyBlob decrypted_plaintext_;
+ KeymasterKeyBlob serialized_blob_;
+};
+
+TEST_F(KeyBlobTest, EncryptDecrypt) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ // key_data shouldn't be anywhere in the blob, ciphertext should.
+ EXPECT_EQ(serialized_blob_.end(), std::search(serialized_blob_.begin(), serialized_blob_.end(),
+ key_material_.begin(), key_material_.end()));
+ EXPECT_NE(serialized_blob_.end(), std::search(serialized_blob_.begin(), serialized_blob_.end(),
+ ciphertext_.begin(), ciphertext_.end()));
+
+ ciphertext_.Clear();
+ nonce_.Clear();
+ tag_.Clear();
+ AuthorizationSet hw2;
+ AuthorizationSet sw2;
+
+ ASSERT_EQ(KM_ERROR_OK, DeserializeAuthEncryptedBlob(serialized_blob_, &ciphertext_, &hw2, &sw2,
+ &nonce_, &tag_));
+ KeymasterKeyBlob plaintext;
+ OcbDecryptKey(hw2, sw2, hidden_, master_key_, ciphertext_, nonce_, tag_, &plaintext);
+
+ EXPECT_EQ(hw_enforced_, hw2);
+ EXPECT_EQ(sw_enforced_, sw2);
+
+ ASSERT_EQ(key_material_.key_material_size, plaintext.key_material_size);
+ EXPECT_EQ(0, memcmp(plaintext.begin(), key_material_.begin(), plaintext.key_material_size));
+}
+
+TEST_F(KeyBlobTest, WrongKeyLength) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ // Modify the key length, shouldn't be able to parse.
+ serialized_blob_.writable_data()[1 /* version */ + 4 /* nonce len */ + 12 /* nonce */ + 3]++;
+
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Deserialize());
+}
+
+TEST_F(KeyBlobTest, WrongNonce) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ // Find the nonce, then modify it.
+ auto nonce_ptr =
+ std::search(serialized_blob_.begin(), serialized_blob_.end(), nonce_.begin(), nonce_.end());
+ ASSERT_NE(nonce_ptr, serialized_blob_.end());
+ EXPECT_EQ(serialized_blob_.end(),
+ std::search(nonce_ptr + 1, serialized_blob_.end(), nonce_.begin(), nonce_.end()));
+ (*const_cast<uint8_t*>(nonce_ptr))++;
+
+ // Deserialization shouldn't be affected, but decryption should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt());
+}
+
+TEST_F(KeyBlobTest, WrongTag) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ // Find the tag, them modify it.
+ auto tag_ptr =
+ std::search(serialized_blob_.begin(), serialized_blob_.end(), tag_.begin(), tag_.end());
+ ASSERT_NE(tag_ptr, serialized_blob_.end());
+ EXPECT_EQ(serialized_blob_.end(),
+ std::search(tag_ptr + 1, serialized_blob_.end(), tag_.begin(), tag_.end()));
+ (*const_cast<uint8_t*>(tag_ptr))++;
+
+ // Deserialization shouldn't be affected, but decryption should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt());
+}
+
+TEST_F(KeyBlobTest, WrongCiphertext) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ // Find the ciphertext, them modify it.
+ auto ciphertext_ptr = std::search(serialized_blob_.begin(), serialized_blob_.end(),
+ ciphertext_.begin(), ciphertext_.end());
+ ASSERT_NE(ciphertext_ptr, serialized_blob_.end());
+ EXPECT_EQ(serialized_blob_.end(), std::search(ciphertext_ptr + 1, serialized_blob_.end(),
+ ciphertext_.begin(), ciphertext_.end()));
+ (*const_cast<uint8_t*>(ciphertext_ptr))++;
+
+ // Deserialization shouldn't be affected, but decryption should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt());
+}
+
+TEST_F(KeyBlobTest, WrongMasterKey) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ uint8_t wrong_master_data[] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ KeymasterKeyBlob wrong_master(wrong_master_data, array_length(wrong_master_data));
+
+ // Decrypting with wrong master key should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ OcbDecryptKey(hw_enforced_, sw_enforced_, hidden_, wrong_master, ciphertext_, nonce_,
+ tag_, &decrypted_plaintext_));
+}
+
+TEST_F(KeyBlobTest, WrongHwEnforced) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ // Find enforced serialization data and modify it.
+ size_t hw_enforced_size = hw_enforced_.SerializedSize();
+ UniquePtr<uint8_t[]> hw_enforced_data(new uint8_t[hw_enforced_size]);
+ hw_enforced_.Serialize(hw_enforced_data.get(), hw_enforced_data.get() + hw_enforced_size);
+
+ auto hw_enforced_ptr =
+ std::search(serialized_blob_.begin(), serialized_blob_.end(), hw_enforced_data.get(),
+ hw_enforced_data.get() + hw_enforced_size);
+ ASSERT_NE(serialized_blob_.end(), hw_enforced_ptr);
+ EXPECT_EQ(serialized_blob_.end(),
+ std::search(hw_enforced_ptr + 1, serialized_blob_.end(), hw_enforced_data.get(),
+ hw_enforced_data.get() + hw_enforced_size));
+ (*(const_cast<uint8_t*>(hw_enforced_ptr) + hw_enforced_size - 1))++;
+
+ // Deserialization shouldn't be affected, but decryption should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt());
+}
+
+TEST_F(KeyBlobTest, WrongSwEnforced) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ // Find enforced serialization data and modify it.
+ size_t sw_enforced_size = sw_enforced_.SerializedSize();
+ UniquePtr<uint8_t[]> sw_enforced_data(new uint8_t[sw_enforced_size]);
+ sw_enforced_.Serialize(sw_enforced_data.get(), sw_enforced_data.get() + sw_enforced_size);
+
+ auto sw_enforced_ptr =
+ std::search(serialized_blob_.begin(), serialized_blob_.end(), sw_enforced_data.get(),
+ sw_enforced_data.get() + sw_enforced_size);
+ ASSERT_NE(serialized_blob_.end(), sw_enforced_ptr);
+ EXPECT_EQ(serialized_blob_.end(),
+ std::search(sw_enforced_ptr + 1, serialized_blob_.end(), sw_enforced_data.get(),
+ sw_enforced_data.get() + sw_enforced_size));
+ (*(const_cast<uint8_t*>(sw_enforced_ptr) + sw_enforced_size - 1))++;
+
+ // Deserialization shouldn't be affected, but decryption should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, Decrypt());
+}
+
+TEST_F(KeyBlobTest, EmptyHidden) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ AuthorizationSet wrong_hidden;
+
+ // Deserialization shouldn't be affected, but decryption should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ OcbDecryptKey(hw_enforced_, sw_enforced_, wrong_hidden, master_key_, ciphertext_,
+ nonce_, tag_, &decrypted_plaintext_));
+}
+
+TEST_F(KeyBlobTest, WrongRootOfTrust) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ AuthorizationSet wrong_hidden;
+ wrong_hidden.push_back(TAG_ROOT_OF_TRUST, "bar", 2);
+ wrong_hidden.push_back(TAG_APPLICATION_ID, "my_app", 6);
+
+ // Deserialization shouldn't be affected, but decryption should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ OcbDecryptKey(hw_enforced_, sw_enforced_, wrong_hidden, master_key_, ciphertext_,
+ nonce_, tag_, &decrypted_plaintext_));
+}
+
+TEST_F(KeyBlobTest, WrongAppId) {
+ ASSERT_EQ(KM_ERROR_OK, Encrypt());
+ ASSERT_EQ(KM_ERROR_OK, Serialize());
+
+ AuthorizationSet wrong_hidden;
+ wrong_hidden.push_back(TAG_ROOT_OF_TRUST, "foo", 3);
+ wrong_hidden.push_back(TAG_APPLICATION_ID, "your_app", 7);
+
+ // Deserialization shouldn't be affected, but decryption should fail.
+ ASSERT_EQ(KM_ERROR_OK, Deserialize());
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ OcbDecryptKey(hw_enforced_, sw_enforced_, wrong_hidden, master_key_, ciphertext_,
+ nonce_, tag_, &decrypted_plaintext_));
+}
+
+// This test is especially useful when compiled for 32-bit mode and run under valgrind.
+TEST_F(KeyBlobTest, FuzzTest) {
+ time_t now = time(NULL);
+ std::cout << "Seeding rand() with " << now << " for fuzz test." << std::endl;
+ srand(now);
+
+ // Fill large buffer with random bytes.
+ const int kBufSize = 10000;
+ UniquePtr<uint8_t[]> buf(new uint8_t[kBufSize]);
+ for (size_t i = 0; i < kBufSize; ++i)
+ buf[i] = static_cast<uint8_t>(rand());
+
+ // Try to deserialize every offset with multiple methods.
+ size_t deserialize_auth_encrypted_success = 0;
+ for (size_t i = 0; i < kBufSize; ++i) {
+ keymaster_key_blob_t blob = {buf.get() + i, kBufSize - i};
+ KeymasterKeyBlob key_blob(blob);
+
+ // Integrity-assured blob.
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ DeserializeIntegrityAssuredBlob(key_blob, hidden_, &key_material_, &hw_enforced_,
+ &sw_enforced_));
+
+ // Auth-encrypted OCB blob.
+ keymaster_error_t error = DeserializeAuthEncryptedBlob(
+ key_blob, &ciphertext_, &hw_enforced_, &sw_enforced_, &nonce_, &tag_);
+ if (error == KM_ERROR_OK) {
+ // It's possible to deserialize successfully. Decryption should always fail.
+ ++deserialize_auth_encrypted_success;
+ error = OcbDecryptKey(hw_enforced_, sw_enforced_, hidden_, master_key_, ciphertext_,
+ nonce_, tag_, &decrypted_plaintext_);
+ }
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB, error)
+ << "Somehow sucessfully parsed a blob with seed " << now << " at offset " << i;
+ }
+}
+
+TEST_F(KeyBlobTest, UnderflowTest) {
+ uint8_t buf[0];
+ keymaster_key_blob_t blob = {buf, 0};
+ KeymasterKeyBlob key_blob(blob);
+ EXPECT_NE(nullptr, key_blob.key_material);
+ EXPECT_EQ(0U, key_blob.key_material_size);
+
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ DeserializeIntegrityAssuredBlob(key_blob, hidden_, &key_material_, &hw_enforced_,
+ &sw_enforced_));
+
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ DeserializeAuthEncryptedBlob(key_blob, &ciphertext_, &hw_enforced_, &sw_enforced_,
+ &nonce_, &tag_));
+}
+
+TEST_F(KeyBlobTest, DupBufferToolarge) {
+ uint8_t buf[0];
+ keymaster_key_blob_t blob = {buf, 0};
+ blob.key_material_size = 16 * 1024 * 1024 + 1;
+ KeymasterKeyBlob key_blob(blob);
+ EXPECT_EQ(nullptr, key_blob.key_material);
+ EXPECT_EQ(0U, key_blob.key_material_size);
+
+ ASSERT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ DeserializeIntegrityAssuredBlob(key_blob, hidden_, &key_material_, &hw_enforced_,
+ &sw_enforced_));
+
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ DeserializeAuthEncryptedBlob(key_blob, &ciphertext_, &hw_enforced_, &sw_enforced_,
+ &nonce_, &tag_));
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/keymaster0_engine.h b/unit_test/keymaster0_engine.h
new file mode 100644
index 0000000..aedd85c
--- a/dev/null
+++ b/unit_test/keymaster0_engine.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_KEYMASTER_KEYMASTER0_ENGINE_H_
+#define SYSTEM_KEYMASTER_KEYMASTER0_ENGINE_H_
+
+#include <memory>
+
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+#include <openssl/ex_data.h>
+#include <openssl/rsa.h>
+
+#include <hardware/keymaster0.h>
+#include <hardware/keymaster_defs.h>
+
+namespace keymaster {
+
+struct KeymasterKeyBlob;
+
+/* Keymaster0Engine is a BoringSSL ENGINE that implements RSA & EC by forwarding the requested
+ * operations to a keymaster0 module. */
+class Keymaster0Engine {
+ public:
+ /**
+ * Create a Keymaster0Engine, wrapping the provided keymaster0_device. The engine takes
+ * ownership of the device, and will close it during destruction.
+ */
+ explicit Keymaster0Engine(const keymaster0_device_t* keymaster0_device);
+ ~Keymaster0Engine();
+
+ bool supports_ec() const { return supports_ec_; }
+
+ bool GenerateRsaKey(uint64_t public_exponent, uint32_t public_modulus,
+ KeymasterKeyBlob* key_material) const;
+ bool GenerateEcKey(uint32_t key_size, KeymasterKeyBlob* key_material) const;
+
+ bool ImportKey(keymaster_key_format_t key_format, const KeymasterKeyBlob& to_import,
+ KeymasterKeyBlob* imported_key_material) const;
+ bool DeleteKey(const KeymasterKeyBlob& blob) const;
+ bool DeleteAllKeys() const;
+
+ RSA* BlobToRsaKey(const KeymasterKeyBlob& blob) const;
+ EC_KEY* BlobToEcKey(const KeymasterKeyBlob& blob) const;
+
+ const keymaster_key_blob_t* RsaKeyToBlob(const RSA* rsa) const;
+ const keymaster_key_blob_t* EcKeyToBlob(const EC_KEY* rsa) const;
+
+ const keymaster0_device_t* device() { return keymaster0_device_; }
+
+ EVP_PKEY* GetKeymaster0PublicKey(const KeymasterKeyBlob& blob) const;
+
+ private:
+ Keymaster0Engine(const Keymaster0Engine&); // Uncopyable
+ void operator=(const Keymaster0Engine&); // Unassignable
+
+ static int keyblob_dup(CRYPTO_EX_DATA* to, const CRYPTO_EX_DATA* from, void** from_d, int index,
+ long argl, void* argp);
+ static void keyblob_free(void* parent, void* ptr, CRYPTO_EX_DATA* data, int index, long argl,
+ void* argp);
+ static int rsa_private_transform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len);
+ static int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
+ unsigned int* sig_len, EC_KEY* ec_key);
+
+ struct Malloc_Delete {
+ void operator()(void* p) { free(p); }
+ };
+
+ bool Keymaster0Sign(const void* signing_params, const keymaster_key_blob_t& key_blob,
+ const uint8_t* data, const size_t data_length,
+ std::unique_ptr<uint8_t[], Malloc_Delete>* signature,
+ size_t* signature_length) const;
+
+ int RsaPrivateTransform(RSA* rsa, uint8_t* out, const uint8_t* in, size_t len) const;
+ int EcdsaSign(const uint8_t* digest, size_t digest_len, uint8_t* sig, unsigned int* sig_len,
+ EC_KEY* ec_key) const;
+
+ const keymaster0_device_t* keymaster0_device_;
+ ENGINE* const engine_;
+ int rsa_index_, ec_key_index_;
+ bool supports_ec_;
+ RSA_METHOD rsa_method_;
+ ECDSA_METHOD ecdsa_method_;
+
+ static Keymaster0Engine* instance_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KEYMASTER0_ENGINE_H_
diff --git a/unit_test/keymaster1_engine.h b/unit_test/keymaster1_engine.h
new file mode 100644
index 0000000..9e2f13e
--- a/dev/null
+++ b/unit_test/keymaster1_engine.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_KEYMASTER_KEYMASTER1_ENGINE_H_
+#define SYSTEM_KEYMASTER_KEYMASTER1_ENGINE_H_
+
+#include <memory>
+
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+#include <openssl/ex_data.h>
+#include <openssl/rsa.h>
+
+#include <hardware/keymaster1.h>
+#include <hardware/keymaster_defs.h>
+
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/authorization_set.h>
+
+#include "openssl_utils.h"
+
+namespace keymaster {
+
+class Keymaster1Engine {
+ public:
+ /**
+ * Create a Keymaster1Engine, wrapping the provided keymaster1_device. The engine takes
+ * ownership of the device, and will close it during destruction.
+ */
+ explicit Keymaster1Engine(const keymaster1_device_t* keymaster1_device);
+ ~Keymaster1Engine();
+
+ keymaster_error_t GenerateKey(const AuthorizationSet& key_description,
+ KeymasterKeyBlob* key_material, AuthorizationSet* hw_enforced,
+ AuthorizationSet* sw_enforced) const;
+
+ keymaster_error_t ImportKey(const AuthorizationSet& key_description,
+ keymaster_key_format_t input_key_material_format,
+ const KeymasterKeyBlob& input_key_material,
+ KeymasterKeyBlob* output_key_blob, AuthorizationSet* hw_enforced,
+ AuthorizationSet* sw_enforced) const;
+ keymaster_error_t DeleteKey(const KeymasterKeyBlob& blob) const;
+ keymaster_error_t DeleteAllKeys() const;
+
+ struct KeyData {
+ KeyData(const KeymasterKeyBlob& blob, const AuthorizationSet& params)
+ : op_handle(0), begin_params(params), key_material(blob), error(KM_ERROR_OK),
+ expected_openssl_padding(-1) {}
+
+ keymaster_operation_handle_t op_handle;
+ AuthorizationSet begin_params;
+ AuthorizationSet finish_params;
+ KeymasterKeyBlob key_material;
+ keymaster_error_t error;
+ int expected_openssl_padding;
+ };
+
+ RSA* BuildRsaKey(const KeymasterKeyBlob& blob, const AuthorizationSet& additional_params,
+ keymaster_error_t* error) const;
+ EC_KEY* BuildEcKey(const KeymasterKeyBlob& blob, const AuthorizationSet& additional_params,
+ keymaster_error_t* error) const;
+
+ KeyData* GetData(EVP_PKEY* key) const;
+ KeyData* GetData(const RSA* rsa) const;
+ KeyData* GetData(const EC_KEY* rsa) const;
+
+ const keymaster1_device_t* device() const { return keymaster1_device_; }
+
+ EVP_PKEY* GetKeymaster1PublicKey(const KeymasterKeyBlob& blob,
+ const AuthorizationSet& additional_params,
+ keymaster_error_t* error) const;
+
+ private:
+ Keymaster1Engine(const Keymaster1Engine&); // Uncopyable
+ void operator=(const Keymaster1Engine&); // Unassignable
+
+ RSA_METHOD BuildRsaMethod();
+ ECDSA_METHOD BuildEcdsaMethod();
+ void ConfigureEngineForRsa();
+ void ConfigureEngineForEcdsa();
+
+ keymaster_error_t Keymaster1Finish(const KeyData* key_data, const keymaster_blob_t& input,
+ keymaster_blob_t* output);
+
+ static int duplicate_key_data(CRYPTO_EX_DATA* to, const CRYPTO_EX_DATA* from, void** from_d,
+ int index, long argl, void* argp);
+ static void free_key_data(void* parent, void* ptr, CRYPTO_EX_DATA* data, int index, long argl,
+ void* argp);
+
+ static int rsa_sign_raw(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out,
+ const uint8_t* in, size_t in_len, int padding);
+ static int rsa_decrypt(RSA* rsa, size_t* out_len, uint8_t* out, size_t max_out,
+ const uint8_t* in, size_t in_len, int padding);
+ static int ecdsa_sign(const uint8_t* digest, size_t digest_len, uint8_t* sig,
+ unsigned int* sig_len, EC_KEY* ec_key);
+
+ const keymaster1_device_t* const keymaster1_device_;
+ const std::unique_ptr<ENGINE, ENGINE_Delete> engine_;
+ const int rsa_index_;
+ const int ec_key_index_;
+
+ const RSA_METHOD rsa_method_;
+ const ECDSA_METHOD ecdsa_method_;
+
+ static Keymaster1Engine* instance_;
+};
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_KEYMASTER1_ENGINE_H_
diff --git a/unit_test/keymaster_configuration_test.cpp b/unit_test/keymaster_configuration_test.cpp
new file mode 100644
index 0000000..81e9598
--- a/dev/null
+++ b/unit_test/keymaster_configuration_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <keymaster/keymaster_configuration.h>
+
+#ifdef HOST_BUILD
+extern "C" {
+int __android_log_print(int prio, const char* tag, const char* fmt);
+int __android_log_print(int prio, const char* tag, const char* fmt) {
+ (void)prio, (void)tag, (void)fmt;
+ std::cout << fmt << std::endl;
+ return 0;
+}
+} // extern "C"
+#endif // HOST_BUILD
+
+namespace keymaster {
+namespace test {
+
+TEST(VersionParsingTest, Full) {
+ EXPECT_EQ(612334U, GetOsVersion("61.23.34"));
+ EXPECT_EQ(680000U, GetOsVersion("681.23.24"));
+ EXPECT_EQ(682300U, GetOsVersion("68.231.24"));
+ EXPECT_EQ(682324U, GetOsVersion("68.23.241"));
+ EXPECT_EQ(60102U, GetOsVersion("6.1.2-extrajunk"));
+ EXPECT_EQ(0U, GetOsVersion("extra6.1.2"));
+}
+
+TEST(VersionParsingTest, FullWithExtraChars) {}
+
+TEST(VersionParsingTest, MajorOnly) {
+ EXPECT_EQ(60000U, GetOsVersion("6"));
+ EXPECT_EQ(680000U, GetOsVersion("68"));
+ EXPECT_EQ(680000U, GetOsVersion("681"));
+ EXPECT_EQ(60000U, GetOsVersion("6.junk"));
+}
+
+TEST(VersionParsingTest, MajorMinorOnly) {
+ EXPECT_EQ(60100U, GetOsVersion("6.1"));
+ EXPECT_EQ(60100U, GetOsVersion("6.1junk"));
+}
+
+TEST(PatchLevelParsingTest, Full) {
+ EXPECT_EQ(201603U, GetOsPatchlevel("2016-03-23"));
+ EXPECT_EQ(0U, GetOsPatchlevel("2016-13-23"));
+ EXPECT_EQ(0U, GetOsPatchlevel("2016-03"));
+ EXPECT_EQ(0U, GetOsPatchlevel("2016-3-23"));
+ EXPECT_EQ(0U, GetOsPatchlevel("2016-03-23r"));
+ EXPECT_EQ(0U, GetOsPatchlevel("r2016-03-23"));
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/keymaster_enforcement_test.cpp b/unit_test/keymaster_enforcement_test.cpp
new file mode 100644
index 0000000..3874744
--- a/dev/null
+++ b/unit_test/keymaster_enforcement_test.cpp
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <keymaster/android_keymaster.h>
+#include <keymaster/authorization_set.h>
+#include <keymaster/keymaster_enforcement.h>
+
+#include "android_keymaster_test_utils.h"
+
+namespace keymaster {
+namespace test {
+
+class TestKeymasterEnforcement : public KeymasterEnforcement {
+ public:
+ TestKeymasterEnforcement()
+ : KeymasterEnforcement(3, 3), current_time_(10000), report_token_valid_(true) {}
+
+ keymaster_error_t AuthorizeOperation(const keymaster_purpose_t purpose, const km_id_t keyid,
+ const AuthorizationSet& auth_set) {
+ AuthorizationSet empty_set;
+ return KeymasterEnforcement::AuthorizeOperation(
+ purpose, keyid, auth_set, empty_set, 0 /* op_handle */, true /* is_begin_operation */);
+ }
+ using KeymasterEnforcement::AuthorizeOperation;
+
+ uint32_t get_current_time() const override { return current_time_; }
+ bool activation_date_valid(uint64_t activation_date) const override {
+ // Convert java date to time_t, non-portably.
+ time_t activation_time = activation_date / 1000;
+ return difftime(time(NULL), activation_time) >= 0;
+ }
+ bool expiration_date_passed(uint64_t expiration_date) const override {
+ // Convert jave date to time_t, non-portably.
+ time_t expiration_time = expiration_date / 1000;
+ return difftime(time(NULL), expiration_time) > 0;
+ }
+ bool auth_token_timed_out(const hw_auth_token_t& token, uint32_t timeout) const {
+ return current_time_ > ntoh(token.timestamp) + timeout;
+ }
+ bool ValidateTokenSignature(const hw_auth_token_t&) const override {
+ return report_token_valid_;
+ }
+
+ void tick(unsigned seconds = 1) { current_time_ += seconds; }
+ uint32_t current_time() { return current_time_; }
+ void set_report_token_valid(bool report_token_valid) {
+ report_token_valid_ = report_token_valid;
+ }
+
+ private:
+ uint32_t current_time_;
+ bool report_token_valid_;
+};
+
+class KeymasterBaseTest : public ::testing::Test {
+ protected:
+ KeymasterBaseTest() {
+ past_time = 0;
+
+ time_t t = time(NULL);
+ future_tm = localtime(&t);
+ future_tm->tm_year += 1;
+ future_time = static_cast<uint64_t>(mktime(future_tm)) * 1000;
+ sign_param = Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN);
+ }
+ virtual ~KeymasterBaseTest() {}
+
+ TestKeymasterEnforcement kmen;
+
+ tm past_tm;
+ tm* future_tm;
+ uint64_t past_time;
+ uint64_t future_time;
+ static const km_id_t key_id = 0xa;
+ static const uid_t uid = 0xf;
+ keymaster_key_param_t sign_param;
+};
+
+TEST_F(KeymasterBaseTest, TestValidKeyPeriodNoTags) {
+ keymaster_key_param_t params[] = {
+ sign_param,
+ };
+ AuthorizationSet single_auth_set(params, array_length(params));
+
+ keymaster_error_t kmer = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, single_auth_set);
+ ASSERT_EQ(KM_ERROR_OK, kmer);
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidActiveTime) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_NO_AUTH_REQUIRED), Authorization(TAG_ACTIVE_DATETIME, future_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ ASSERT_EQ(KM_ERROR_KEY_NOT_YET_VALID,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+
+ // Pubkey ops allowed.
+ ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestValidActiveTime) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_ACTIVE_DATETIME, past_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer_valid_time = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set);
+ ASSERT_EQ(KM_ERROR_OK, kmer_valid_time);
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidOriginationExpireTime) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_ORIGINATION_EXPIRE_DATETIME, past_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ ASSERT_EQ(KM_ERROR_KEY_EXPIRED, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+
+ // Pubkey ops allowed.
+ ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidOriginationExpireTimeOnUsgae) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_ORIGINATION_EXPIRE_DATETIME, past_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer_invalid_origination =
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set);
+ ASSERT_EQ(KM_ERROR_OK, kmer_invalid_origination);
+}
+
+TEST_F(KeymasterBaseTest, TestValidOriginationExpireTime) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_ORIGINATION_EXPIRE_DATETIME, future_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer_valid_origination =
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set);
+ ASSERT_EQ(KM_ERROR_OK, kmer_valid_origination);
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidUsageExpireTime) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_USAGE_EXPIRE_DATETIME, past_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer_invalid_origination =
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set);
+ ASSERT_EQ(KM_ERROR_KEY_EXPIRED, kmer_invalid_origination);
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidPubkeyUsageExpireTime) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_USAGE_EXPIRE_DATETIME, past_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer_invalid_origination =
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set);
+ // Pubkey ops allowed.
+ ASSERT_EQ(KM_ERROR_OK, kmer_invalid_origination);
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidUsageExpireTimeOnOrigination) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_USAGE_EXPIRE_DATETIME, past_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer_invalid_origination =
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set);
+ ASSERT_EQ(KM_ERROR_OK, kmer_invalid_origination);
+}
+
+TEST_F(KeymasterBaseTest, TestValidUsageExpireTime) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_USAGE_EXPIRE_DATETIME, future_time),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer_valid_usage =
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set);
+ ASSERT_EQ(KM_ERROR_OK, kmer_valid_usage);
+}
+
+TEST_F(KeymasterBaseTest, TestValidSingleUseAccesses) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer1 = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set);
+ keymaster_error_t kmer2 = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set);
+
+ ASSERT_EQ(KM_ERROR_OK, kmer1);
+ ASSERT_EQ(KM_ERROR_OK, kmer2);
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidMaxOps) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_MAX_USES_PER_BOOT, 4),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+ ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+ ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+ ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+ ASSERT_EQ(KM_ERROR_KEY_MAX_OPS_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+ // Pubkey ops allowed.
+ ASSERT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestOverFlowMaxOpsTable) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_MAX_USES_PER_BOOT, 2),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 2 /* key_id */, auth_set));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 3 /* key_id */, auth_set));
+
+ // Key 4 should fail, because table is full.
+ EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 4 /* key_id */, auth_set));
+
+ // Key 1 still works, because it's already in the table and hasn't reached max.
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set));
+
+ // Key 1 no longer works, because it's reached max.
+ EXPECT_EQ(KM_ERROR_KEY_MAX_OPS_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set));
+
+ // Key 4 should fail, because table is (still and forever) full.
+ EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 4 /* key_id */, auth_set));
+
+ // Pubkey ops allowed.
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 4 /* key_id */, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidTimeBetweenOps) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, 10),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ keymaster_error_t kmer1 = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set);
+ keymaster_error_t kmer2 = kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set);
+ keymaster_error_t kmer3 = kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set);
+
+ ASSERT_EQ(KM_ERROR_OK, kmer1);
+ kmen.tick(2);
+ ASSERT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED, kmer2);
+
+ // Allowed because it's a pubkey op.
+ ASSERT_EQ(KM_ERROR_OK, kmer3);
+}
+
+TEST_F(KeymasterBaseTest, TestValidTimeBetweenOps) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN), Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, 2),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set));
+ kmen.tick();
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+ kmen.tick();
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestOptTimeoutTableOverflow) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES),
+ Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, 4),
+ Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+
+ kmen.tick();
+
+ // Key 1 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set));
+
+ kmen.tick();
+
+ // Key 1 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+ // Key 2 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set));
+
+ kmen.tick();
+
+ // Key 1 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+ // Key 2 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set));
+ // Key 3 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set));
+ // Key 4 fails because the table is full
+ EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 4 /* key_id */, auth_set));
+
+ kmen.tick();
+
+ // Key 4 succeeds because key 1 expired.
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 4 /* key_id */, auth_set));
+
+ // Key 1 fails because the table is full... and key 1 is no longer in it.
+ EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+ // Key 2 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set));
+ // Key 3 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set));
+
+ kmen.tick();
+
+ // Key 1 succeeds because key 2 expired
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+ // Key 2 fails because the table is full... and key 2 is no longer in it.
+ EXPECT_EQ(KM_ERROR_TOO_MANY_OPERATIONS,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set));
+ // Key 3 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set));
+ // Key 4 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 4 /* key_id */, auth_set));
+
+ kmen.tick(4);
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 2 /* key_id */, auth_set));
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 3 /* key_id */, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestPubkeyOptTimeoutTableOverflow) {
+ keymaster_key_param_t params[] = {
+ Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, 4), Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN),
+ };
+
+ AuthorizationSet auth_set(params, array_length(params));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set));
+
+ kmen.tick();
+
+ // Key 1 fails because it's too soon
+ EXPECT_EQ(KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, 1 /* key_id */, auth_set));
+ // Too soo, but pubkey ops allowed.
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, 1 /* key_id */, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidPurpose) {
+ keymaster_purpose_t invalidPurpose1 = static_cast<keymaster_purpose_t>(-1);
+ keymaster_purpose_t invalidPurpose2 = static_cast<keymaster_purpose_t>(4);
+
+ AuthorizationSet auth_set(
+ AuthorizationSetBuilder().Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY));
+
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE,
+ kmen.AuthorizeOperation(invalidPurpose1, key_id, auth_set));
+ EXPECT_EQ(KM_ERROR_UNSUPPORTED_PURPOSE,
+ kmen.AuthorizeOperation(invalidPurpose2, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestIncompatiblePurposeSymmetricKey) {
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set));
+
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE,
+ kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, auth_set));
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE,
+ kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestIncompatiblePurposeAssymmetricKey) {
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_VERIFY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set));
+
+ // This one is allowed because it's a pubkey op.
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, auth_set));
+ EXPECT_EQ(KM_ERROR_INCOMPATIBLE_PURPOSE,
+ kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidCallerNonce) {
+ AuthorizationSet no_caller_nonce(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_ENCRYPT)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_DECRYPT)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES));
+ AuthorizationSet caller_nonce(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_ENCRYPT)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_DECRYPT)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_HMAC)
+ .Authorization(TAG_CALLER_NONCE));
+ AuthorizationSet begin_params(AuthorizationSetBuilder().Authorization(TAG_NONCE, "foo", 3));
+
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, caller_nonce, begin_params,
+ 0 /* challenge */, true /* is_begin_operation */));
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, caller_nonce, begin_params,
+ 0 /* challenge */, true /* is_begin_operation */));
+ EXPECT_EQ(KM_ERROR_CALLER_NONCE_PROHIBITED,
+ kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, no_caller_nonce, begin_params,
+ 0 /* challenge */, true /* is_begin_operation */));
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, no_caller_nonce, begin_params,
+ 0 /* challenge */, true /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestBootloaderOnly) {
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_BOOTLOADER_ONLY));
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+
+ // Pubkey ops allowed.
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestInvalidTag) {
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_INVALID)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthPerOpSuccess) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = 0;
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthPerOpInvalidTokenSignature) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = 0;
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ kmen.set_report_token_valid(false);
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ false /* is_begin_operation */));
+ // Pubkey ops allowed.
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params,
+ token.challenge, false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthPerOpWrongChallenge) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = 0;
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params,
+ token.challenge + 1 /* doesn't match token */,
+ false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthPerOpNoAuthType) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = 0;
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ false /* is_begin_operation */));
+ // Pubkey ops allowed.
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params,
+ token.challenge, false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthPerOpWrongAuthType) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = 0;
+
+ AuthorizationSet auth_set(
+ AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_FINGERPRINT /* doesn't match token */)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ false /* is_begin_operation */));
+ // Pubkey ops allowed.
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params,
+ token.challenge, false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthPerOpWrongSid) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = 0;
+
+ AuthorizationSet auth_set(
+ AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id + 1 /* doesn't match token */)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ false /* is_begin_operation */));
+ // Pubkey op allowed.
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params,
+ token.challenge, false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthPerOpSuccessAlternateSid) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 10;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = 0;
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_USER_SECURE_ID, token.authenticator_id)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthPerOpMissingToken) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = 0;
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+
+ // During begin we can skip the auth token
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params,
+ token.challenge, true /* is_begin_operation */));
+ // Afterwards we must have authentication
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ false /* is_begin_operation */));
+ // Pubkey ops allowed
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params,
+ token.challenge, false /* is_begin_operation */));
+
+ auth_set.Reinitialize(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_AES)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN)
+ .build());
+
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params,
+ token.challenge, false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestAuthAndNoAuth) {
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_USER_SECURE_ID, 1)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ EXPECT_EQ(KM_ERROR_INVALID_KEY_BLOB,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set));
+}
+
+TEST_F(KeymasterBaseTest, TestTimedAuthSuccess) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = hton(kmen.current_time());
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_AUTH_TIMEOUT, 1)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params,
+ 0 /* irrelevant */, false /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestTimedAuthTimedOut) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = hton(static_cast<uint64_t>(kmen.current_time()));
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_AUTH_TIMEOUT, 1)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+ op_params.push_back(Authorization(TAG_AUTH_TOKEN, &token, sizeof(token)));
+
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params,
+ 0 /* irrelevant */, false /* is_begin_operation */));
+
+ kmen.tick(1);
+
+ // token still good
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params,
+ 0 /* irrelevant */, false /* is_begin_operation */));
+
+ kmen.tick(1);
+
+ // token expired, not allowed during begin.
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params,
+ 0 /* irrelevant */, true /* is_begin_operation */));
+
+ // token expired, afterwards it's okay.
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params,
+ 0 /* irrelevant */, false /* is_begin_operation */));
+
+ // Pubkey ops allowed.
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params,
+ 0 /* irrelevant */, true /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestTimedAuthMissingToken) {
+ hw_auth_token_t token;
+ memset(&token, 0, sizeof(token));
+ token.version = HW_AUTH_TOKEN_VERSION;
+ token.challenge = 99;
+ token.user_id = 9;
+ token.authenticator_id = 0;
+ token.authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+ token.timestamp = hton(static_cast<uint64_t>(kmen.current_time()));
+
+ AuthorizationSet auth_set(AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, KM_ALGORITHM_RSA)
+ .Authorization(TAG_USER_SECURE_ID, token.user_id)
+ .Authorization(TAG_AUTH_TIMEOUT, 1)
+ .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_ANY)
+ .Authorization(TAG_PURPOSE, KM_PURPOSE_SIGN));
+
+ AuthorizationSet op_params;
+
+ // Unlike auth-per-op, must have the auth token during begin.
+ EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ true /* is_begin_operation */));
+
+ // Later we don't check (though begin would fail, so there wouldn't be a later).
+ EXPECT_EQ(KM_ERROR_OK,
+ kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
+ false /* is_begin_operation */));
+
+ // Pubkey ops allowed.
+ EXPECT_EQ(KM_ERROR_OK, kmen.AuthorizeOperation(KM_PURPOSE_VERIFY, key_id, auth_set, op_params,
+ token.challenge, true /* is_begin_operation */));
+}
+
+TEST_F(KeymasterBaseTest, TestCreateKeyId) {
+ keymaster_key_blob_t blob = {reinterpret_cast<const uint8_t*>("foobar"), 6};
+
+ km_id_t key_id = 0;
+ EXPECT_TRUE(KeymasterEnforcement::CreateKeyId(blob, &key_id));
+ EXPECT_NE(0U, key_id);
+}
+
+}; /* namespace test */
+}; /* namespace keymaster */
diff --git a/unit_test/keymaster_tags.cpp b/unit_test/keymaster_tags.cpp
new file mode 100644
index 0000000..238bc33
--- a/dev/null
+++ b/unit_test/keymaster_tags.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <keymaster/keymaster_tags.h>
+
+namespace keymaster {
+
+#ifdef KEYMASTER_NAME_TAGS
+const char* StringifyTag(keymaster_tag_t tag) {
+ switch (tag) {
+ case KM_TAG_INVALID:
+ return "KM_TAG_INVALID";
+ case KM_TAG_PURPOSE:
+ return "KM_TAG_PURPOSE";
+ case KM_TAG_ALGORITHM:
+ return "KM_TAG_ALGORITHM";
+ case KM_TAG_KEY_SIZE:
+ return "KM_TAG_KEY_SIZE";
+ case KM_TAG_BLOCK_MODE:
+ return "KM_TAG_BLOCK_MODE";
+ case KM_TAG_DIGEST:
+ return "KM_TAG_DIGEST";
+ case KM_TAG_PADDING:
+ return "KM_TAG_PADDING";
+ case KM_TAG_CALLER_NONCE:
+ return "KM_TAG_CALLER_NONCE";
+ case KM_TAG_MIN_MAC_LENGTH:
+ return "KM_TAG_MIN_MAC_LENGTH";
+ case KM_TAG_RSA_PUBLIC_EXPONENT:
+ return "KM_TAG_RSA_PUBLIC_EXPONENT";
+ case KM_TAG_BLOB_USAGE_REQUIREMENTS:
+ return "KM_TAG_BLOB_USAGE_REQUIREMENTS";
+ case KM_TAG_BOOTLOADER_ONLY:
+ return "KM_TAG_BOOTLOADER_ONLY";
+ case KM_TAG_ACTIVE_DATETIME:
+ return "KM_TAG_ACTIVE_DATETIME";
+ case KM_TAG_ORIGINATION_EXPIRE_DATETIME:
+ return "KM_TAG_ORIGINATION_EXPIRE_DATETIME";
+ case KM_TAG_USAGE_EXPIRE_DATETIME:
+ return "KM_TAG_USAGE_EXPIRE_DATETIME";
+ case KM_TAG_MIN_SECONDS_BETWEEN_OPS:
+ return "KM_TAG_MIN_SECONDS_BETWEEN_OPS";
+ case KM_TAG_MAX_USES_PER_BOOT:
+ return "KM_TAG_MAX_USES_PER_BOOT";
+ case KM_TAG_ALL_USERS:
+ return "KM_TAG_ALL_USERS";
+ case KM_TAG_USER_ID:
+ return "KM_TAG_USER_ID";
+ case KM_TAG_USER_SECURE_ID:
+ return "KM_TAG_USER_SECURE_ID";
+ case KM_TAG_NO_AUTH_REQUIRED:
+ return "KM_TAG_NO_AUTH_REQUIRED";
+ case KM_TAG_USER_AUTH_TYPE:
+ return "KM_TAG_USER_AUTH_TYPE";
+ case KM_TAG_AUTH_TIMEOUT:
+ return "KM_TAG_AUTH_TIMEOUT";
+ case KM_TAG_ALL_APPLICATIONS:
+ return "KM_TAG_ALL_APPLICATIONS";
+ case KM_TAG_APPLICATION_ID:
+ return "KM_TAG_APPLICATION_ID";
+ case KM_TAG_APPLICATION_DATA:
+ return "KM_TAG_APPLICATION_DATA";
+ case KM_TAG_CREATION_DATETIME:
+ return "KM_TAG_CREATION_DATETIME";
+ case KM_TAG_ORIGIN:
+ return "KM_TAG_ORIGIN";
+ case KM_TAG_ROLLBACK_RESISTANT:
+ return "KM_TAG_ROLLBACK_RESISTANT";
+ case KM_TAG_ROOT_OF_TRUST:
+ return "KM_TAG_ROOT_OF_TRUST";
+ case KM_TAG_ASSOCIATED_DATA:
+ return "KM_TAG_ASSOCIATED_DATA";
+ case KM_TAG_NONCE:
+ return "KM_TAG_NONCE";
+ case KM_TAG_AUTH_TOKEN:
+ return "KM_TAG_AUTH_TOKEN";
+ case KM_TAG_MAC_LENGTH:
+ return "KM_TAG_MAC_LENGTH";
+ case KM_TAG_KDF:
+ return "KM_TAG_KDF";
+ case KM_TAG_EC_CURVE:
+ return "KM_TAG_EC_CURVE";
+ case KM_TAG_ECIES_SINGLE_HASH_MODE:
+ return "KM_TAG_ECIES_SINGLE_HASH_MODE";
+ case KM_TAG_OS_VERSION:
+ return "KM_TAG_OS_VERSION";
+ case KM_TAG_OS_PATCHLEVEL:
+ return "KM_TAG_OS_PATCHLEVEL";
+ case KM_TAG_EXPORTABLE:
+ return "KM_TAG_EXPORTABLE";
+ case KM_TAG_UNIQUE_ID:
+ return "KM_TAG_UNIQUE_ID";
+ case KM_TAG_INCLUDE_UNIQUE_ID:
+ return "KM_TAG_INCLUDE_UNIQUE_ID";
+ case KM_TAG_RESET_SINCE_ID_ROTATION:
+ return "KM_TAG_RESET_SINCE_ID_ROTATION";
+ case KM_TAG_ALLOW_WHILE_ON_BODY:
+ return "KM_TAG_ALLOW_WHILE_ON_BODY";
+ case KM_TAG_ATTESTATION_CHALLENGE:
+ return "KM_TAG_ATTESTATION_CHALLENGE";
+ }
+ return "<Unknown>";
+}
+#endif // KEYMASTER_NAME_TAGS
+
+// DEFINE_KEYMASTER_TAG is used to create TypedTag instances for each non-enum keymaster tag.
+#define DEFINE_KEYMASTER_TAG(type, name) TypedTag<type, KM_##name> name
+
+DEFINE_KEYMASTER_TAG(KM_INVALID, TAG_INVALID);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_KEY_SIZE);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MAC_LENGTH);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_CALLER_NONCE);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MIN_MAC_LENGTH);
+DEFINE_KEYMASTER_TAG(KM_ULONG, TAG_RSA_PUBLIC_EXPONENT);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ECIES_SINGLE_HASH_MODE);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_INCLUDE_UNIQUE_ID);
+DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ACTIVE_DATETIME);
+DEFINE_KEYMASTER_TAG(KM_DATE, TAG_ORIGINATION_EXPIRE_DATETIME);
+DEFINE_KEYMASTER_TAG(KM_DATE, TAG_USAGE_EXPIRE_DATETIME);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MIN_SECONDS_BETWEEN_OPS);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_MAX_USES_PER_BOOT);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ALL_USERS);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_USER_ID);
+DEFINE_KEYMASTER_TAG(KM_ULONG_REP, TAG_USER_SECURE_ID);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_NO_AUTH_REQUIRED);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_AUTH_TIMEOUT);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ALLOW_WHILE_ON_BODY);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ALL_APPLICATIONS);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_APPLICATION_ID);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_APPLICATION_DATA);
+DEFINE_KEYMASTER_TAG(KM_DATE, TAG_CREATION_DATETIME);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_ROLLBACK_RESISTANT);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_ROOT_OF_TRUST);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_ASSOCIATED_DATA);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_NONCE);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_AUTH_TOKEN);
+DEFINE_KEYMASTER_TAG(KM_BOOL, TAG_BOOTLOADER_ONLY);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_OS_VERSION);
+DEFINE_KEYMASTER_TAG(KM_UINT, TAG_OS_PATCHLEVEL);
+DEFINE_KEYMASTER_TAG(KM_BYTES, TAG_UNIQUE_ID);
+
+// DEFINE_KEYMASTER_ENUM_TAG is used to create TypedEnumTag instances for each enum keymaster tag.
+
+#define DEFINE_KEYMASTER_ENUM_TAG(type, name, enumtype) TypedEnumTag<type, KM_##name, enumtype> name
+
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_PURPOSE, keymaster_purpose_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ALGORITHM, keymaster_algorithm_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_BLOCK_MODE, keymaster_block_mode_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_DIGEST, keymaster_digest_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_DIGEST_OLD, keymaster_digest_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_PADDING, keymaster_padding_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_PADDING_OLD, keymaster_padding_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_BLOB_USAGE_REQUIREMENTS,
+ keymaster_key_blob_usage_requirements_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_ORIGIN, keymaster_key_origin_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_USER_AUTH_TYPE, hw_authenticator_type_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM_REP, TAG_KDF, keymaster_kdf_t);
+DEFINE_KEYMASTER_ENUM_TAG(KM_ENUM, TAG_EC_CURVE, keymaster_ec_curve_t);
+
+} // namespace keymaster
diff --git a/unit_test/nist_curve_key_exchange_test.cpp b/unit_test/nist_curve_key_exchange_test.cpp
new file mode 100644
index 0000000..39ea38b
--- a/dev/null
+++ b/unit_test/nist_curve_key_exchange_test.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nist_curve_key_exchange.h"
+
+#include <gtest/gtest.h>
+#include <openssl/evp.h>
+
+#include <hardware/keymaster_defs.h>
+#include <keymaster/android_keymaster_utils.h>
+
+#include "android_keymaster_test_utils.h"
+
+using std::string;
+
+namespace keymaster {
+namespace test {
+
+StdoutLogger logger;
+
+static const keymaster_ec_curve_t kEcCurves[] = {KM_EC_CURVE_P_224, KM_EC_CURVE_P_256,
+ KM_EC_CURVE_P_384, KM_EC_CURVE_P_521};
+
+/**
+ * SharedKey just tests that the basic key exchange identity holds: that both
+ * parties end up with the same key.
+ */
+TEST(NistCurveKeyExchange, SharedKey) {
+ for (auto& curve : kEcCurves) {
+ AuthorizationSet kex_description(
+ AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve));
+ for (size_t j = 0; j < 5; j++) {
+ NistCurveKeyExchange* alice_keyex = NistCurveKeyExchange::GenerateKeyExchange(curve);
+ NistCurveKeyExchange* bob_keyex = NistCurveKeyExchange::GenerateKeyExchange(curve);
+
+ ASSERT_TRUE(alice_keyex != nullptr);
+ ASSERT_TRUE(bob_keyex != nullptr);
+
+ Buffer alice_public_value;
+ ASSERT_TRUE(alice_keyex->public_value(&alice_public_value));
+ Buffer bob_public_value;
+ ASSERT_TRUE(bob_keyex->public_value(&bob_public_value));
+
+ Buffer alice_shared, bob_shared;
+ ASSERT_TRUE(alice_keyex->CalculateSharedKey(bob_public_value, &alice_shared));
+ ASSERT_TRUE(bob_keyex->CalculateSharedKey(alice_public_value, &bob_shared));
+ EXPECT_EQ(alice_shared.available_read(), bob_shared.available_read());
+ EXPECT_EQ(0, memcmp(alice_shared.peek_read(), bob_shared.peek_read(),
+ alice_shared.available_read()));
+ }
+ }
+}
+
+/*
+ * This test tries a key agreement with a false public key (i.e. with
+ * a point not on the curve.)
+ * The expected result of such a protocol should be that the
+ * key agreement fails and returns an error.
+*/
+static const char* kInvalidPublicKeys[] = {
+ "04" // uncompressed public key
+ "deadbeef7f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
+ "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
+};
+
+TEST(NistCurveKeyExchange, InvalidPublicKey) {
+ for (auto& curve : kEcCurves) {
+ AuthorizationSet kex_description(
+ AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve));
+ KeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve);
+ ASSERT_TRUE(key_exchange != nullptr);
+
+ string peer_public_key = hex2str(kInvalidPublicKeys[0]);
+ Buffer computed_shared_secret;
+ ASSERT_FALSE(key_exchange->CalculateSharedKey(
+ reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(),
+ &computed_shared_secret));
+ }
+}
+
+/**
+ * Test that key exchange fails when peer public key is the point at infinity.
+ */
+TEST(NistCurveKeyExchange, TestInfinity) {
+ for (auto& curve : kEcCurves) {
+ /* Obtain the point at infinity */
+ EC_GROUP* group = ec_get_group(curve);
+ EC_POINT* point_at_infinity = EC_POINT_new(group);
+ EC_POINT_set_to_infinity(group, point_at_infinity);
+ EXPECT_EQ(1, EC_POINT_is_on_curve(group, point_at_infinity, nullptr));
+ size_t field_len_in_bits;
+ ec_get_group_size(group, &field_len_in_bits);
+ size_t field_len = (field_len_in_bits + 7) / 8;
+ size_t public_key_len = (field_len * 2) + 1;
+ uint8_t* public_key = new uint8_t[public_key_len];
+ public_key_len = EC_POINT_point2oct(group, point_at_infinity, POINT_CONVERSION_UNCOMPRESSED,
+ public_key, public_key_len, nullptr /* ctx */);
+
+ /* Perform the key exchange */
+ AuthorizationSet kex_description(
+ AuthorizationSetBuilder().Authorization(TAG_EC_CURVE, curve));
+ NistCurveKeyExchange* key_exchange = NistCurveKeyExchange::GenerateKeyExchange(curve);
+ ASSERT_TRUE(key_exchange != nullptr);
+ Buffer computed_shared_secret;
+ /* It should fail */
+ ASSERT_FALSE(key_exchange->CalculateSharedKey(reinterpret_cast<const uint8_t*>(public_key),
+ public_key_len, &computed_shared_secret));
+
+ /* Explicitly test that ECDH_compute_key fails when the public key is the point at infinity
+ */
+ UniquePtr<uint8_t[]> result(new uint8_t[field_len]);
+ EXPECT_EQ(-1 /* error */, ECDH_compute_key(result.get(), field_len, point_at_infinity,
+ key_exchange->private_key(), nullptr /* kdf */));
+ }
+}
+
+/* Test vectors for P-256, downloaded from NIST. */
+struct NistCurveTest {
+ const keymaster_ec_curve_t curve;
+ const char* peer_public_key;
+ const char* my_private_key;
+ const char* shared_secret;
+};
+
+static const NistCurveTest kNistCurveTests[] = {
+ {
+ KM_EC_CURVE_P_256,
+ "04" // uncompressed public key
+ "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
+ "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
+ // https://tools.ietf.org/html/rfc5915
+ "30770201010420" // DER-encodeded EC private key header
+ "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534" // private key
+ "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID,
+ "04"
+ "ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230"
+ "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141",
+ "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
+ },
+ {
+ KM_EC_CURVE_P_256, "04"
+ "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae"
+ "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3",
+ // https://tools.ietf.org/html/rfc5915
+ "30770201010420" // DER-encodeded EC private key header
+ "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5" // private key
+ "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID,
+ "04"
+ "119f2f047902782ab0c9e27a54aff5eb9b964829ca99c06b02ddba95b0a3f6d0"
+ "8f52b726664cac366fc98ac7a012b2682cbd962e5acb544671d41b9445704d1d",
+ "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67",
+ },
+ {
+ KM_EC_CURVE_P_256, "04"
+ "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed"
+ "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4",
+ // https://tools.ietf.org/html/rfc5915
+ "30770201010420" // DER-encodeded EC private key header
+ "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d" // private key
+ "a00a06082a8648ce3d030107a144034200" // DER-encoded curve OID,
+ "04"
+ "24277c33f450462dcb3d4801d57b9ced05188f16c28eda873258048cd1607e0d"
+ "c4789753e2b1f63b32ff014ec42cd6a69fac81dfe6d0d6fd4af372ae27c46f88",
+ "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024",
+ },
+};
+
+/**
+ * Test that key exchange works with NIST test vectors.
+ */
+TEST(NistCurveKeyExchange, NistTestVectors) {
+ for (auto& test : kNistCurveTests) {
+ string private_key = hex2str(test.my_private_key);
+ string shared_secret = hex2str(test.shared_secret);
+
+ const uint8_t* private_key_data = reinterpret_cast<const uint8_t*>(private_key.data());
+ UniquePtr<EC_KEY, EC_KEY_Delete> ec_key(
+ d2i_ECPrivateKey(nullptr, &private_key_data, private_key.size()));
+ ASSERT_TRUE(ec_key.get() && EC_KEY_check_key(ec_key.get()));
+
+ keymaster_error_t error;
+ NistCurveKeyExchange* key_exchange = new NistCurveKeyExchange(ec_key.release(), &error);
+ EXPECT_EQ(KM_ERROR_OK, error);
+ ASSERT_TRUE(key_exchange != nullptr);
+
+ Buffer computed_shared_secret;
+ string peer_public_key = hex2str(test.peer_public_key);
+ ASSERT_TRUE(key_exchange->CalculateSharedKey(
+ reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(),
+ &computed_shared_secret));
+ EXPECT_EQ(shared_secret.size(), computed_shared_secret.available_read());
+ EXPECT_EQ(0, memcmp(shared_secret.data(), computed_shared_secret.peek_read(),
+ shared_secret.size()));
+
+ for (size_t i = 0; i < peer_public_key.size(); i++) {
+ // randomly flip some bits in the peer public key to make it invalid
+ peer_public_key[i] ^= 0xff;
+ ASSERT_FALSE(key_exchange->CalculateSharedKey(
+ reinterpret_cast<const uint8_t*>(peer_public_key.data()), peer_public_key.size(),
+ &computed_shared_secret));
+ }
+ }
+}
+
+} // namespace test
+} // namespace keymaster
diff --git a/unit_test/openssl_utils.h b/unit_test/openssl_utils.h
new file mode 100644
index 0000000..9fa6ec1
--- a/dev/null
+++ b/unit_test/openssl_utils.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_KEYMASTER_OPENSSL_UTILS_H_
+#define SYSTEM_KEYMASTER_OPENSSL_UTILS_H_
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/engine.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#include <UniquePtr.h>
+
+#include <hardware/keymaster_defs.h>
+
+namespace keymaster {
+
+struct KeymasterKeyBlob;
+
+class EvpMdCtxCleaner {
+ public:
+ explicit EvpMdCtxCleaner(EVP_MD_CTX* ctx) : ctx_(ctx) {}
+ ~EvpMdCtxCleaner() { EVP_MD_CTX_cleanup(ctx_); }
+
+ private:
+ EVP_MD_CTX* ctx_;
+};
+
+template <typename T, void (*FreeFunc)(T*)> struct OpenSslObjectDeleter {
+ void operator()(T* p) { FreeFunc(p); }
+};
+
+#define DEFINE_OPENSSL_OBJECT_POINTER(name) \
+ typedef OpenSslObjectDeleter<name, name##_free> name##_Delete; \
+ typedef UniquePtr<name, name##_Delete> name##_Ptr;
+
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_BIT_STRING)
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_INTEGER)
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OBJECT)
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_OCTET_STRING)
+DEFINE_OPENSSL_OBJECT_POINTER(ASN1_TIME)
+DEFINE_OPENSSL_OBJECT_POINTER(BN_CTX)
+DEFINE_OPENSSL_OBJECT_POINTER(EC_GROUP)
+DEFINE_OPENSSL_OBJECT_POINTER(EC_KEY)
+DEFINE_OPENSSL_OBJECT_POINTER(EC_POINT)
+DEFINE_OPENSSL_OBJECT_POINTER(ENGINE)
+DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY)
+DEFINE_OPENSSL_OBJECT_POINTER(PKCS8_PRIV_KEY_INFO)
+DEFINE_OPENSSL_OBJECT_POINTER(RSA)
+DEFINE_OPENSSL_OBJECT_POINTER(X509)
+DEFINE_OPENSSL_OBJECT_POINTER(X509_EXTENSION)
+DEFINE_OPENSSL_OBJECT_POINTER(X509_NAME)
+
+typedef OpenSslObjectDeleter<BIGNUM, BN_free> BIGNUM_Delete;
+typedef UniquePtr<BIGNUM, BIGNUM_Delete> BIGNUM_Ptr;
+
+keymaster_error_t ec_get_group_size(const EC_GROUP* group, size_t* key_size_bits);
+EC_GROUP* ec_get_group(keymaster_ec_curve_t curve);
+
+/**
+ * Many OpenSSL APIs take ownership of an argument on success but don't free the argument on
+ * failure. This means we need to tell our scoped pointers when we've transferred ownership, without
+ * triggering a warning by not using the result of release().
+ */
+template <typename T, typename Delete_T>
+inline void release_because_ownership_transferred(UniquePtr<T, Delete_T>& p) {
+ T* val __attribute__((unused)) = p.release();
+}
+
+keymaster_error_t convert_pkcs8_blob_to_evp(const uint8_t* key_data, size_t key_length,
+ keymaster_algorithm_t expected_algorithm,
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete>* pkey);
+
+keymaster_error_t KeyMaterialToEvpKey(keymaster_key_format_t key_format,
+ const KeymasterKeyBlob& key_material,
+ keymaster_algorithm_t expected_algorithm,
+ UniquePtr<EVP_PKEY, EVP_PKEY_Delete>* evp_pkey);
+
+keymaster_error_t EvpKeyToKeyMaterial(const EVP_PKEY* evp_pkey, KeymasterKeyBlob* key_blob);
+
+size_t ec_group_size_bits(EC_KEY* ec_key);
+
+} // namespace keymaster
+
+#endif // SYSTEM_KEYMASTER_OPENSSL_UTILS_H_
diff --git a/unit_test/sw_rsa_attest_root.key.pem b/unit_test/sw_rsa_attest_root.key.pem
new file mode 100644
index 0000000..387a852
--- a/dev/null
+++ b/unit_test/sw_rsa_attest_root.key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAsQ+wz
+fNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdLt0GA
+ZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwIDAQAB
+AoGAU8dxXchmqzVNbbvff7zgUa63YErk51Yem/EXzhkMaIXRkMO0edaCtZtnkRvg
+9OQ2qEiLWaCTlUoyU7H/HUn2lwTQsOXyZI7dHijVDRMIv1mmrHCrGW/JC8FXfPLS
+r3L3KoHXQVYL2mslbR8Rpogxq4WwnwK6XqSTH9mynFwQwEkCQQDMX3EZk3ricWVH
+ruXD0BpXOMMpZuLu4rg5+1L51WEJvItIMeSjLuNa+g3AI8AYTYYi/aSLk6XEv82L
+iXFGmJ2XAkEAy3M8k8Z0QzHae4olduqoHVWEarBtDE+fqFQBWgdm8fZhdHWrvlAc
+qwJIXMUVc+dWm/FAQarCjbqWqhCRdaYgnQJBAJ7z7GdUCVNtlrQ2F4ZAqPwFreTZ
+nM7njxmpm1Os3hhQiJPSGl3A7huoOGGkbJd6VEWKuRvF7jwkYZ2RfITH1mkCQAvh
+X9E1Toa5+4spRwTJsSV9X+0m/kcwwx7+QNH0CrPockptsKi9Xt8xk+4u6BDLmogi
+r2DmStQh6DhoHUZkfBUCQQCOgBkqH/15drpdR+BQH3VaP4/ALFfxR9E3G+lS+M5a
+IqJEk9kh8vjuGzTaAZyU5keUmpWNc1gI7OvDMaH4+8vQ
+-----END RSA PRIVATE KEY-----