88 "crypto/x509"
99 "encoding/asn1"
1010 "encoding/hex"
11+ "fmt"
1112 "testing"
1213
1314 "github.com/stretchr/testify/assert"
@@ -355,12 +356,192 @@ func TestAndroidKeyAuthorizationListDecoding(t *testing.T) {
355356 assert .Len (t , decoded .TeeEnforced .RootOfTrust .VerifiedBootHash , 32 )
356357
357358 assert .Equal (t , []int {KM_PURPOSE_SIGN }, decoded .TeeEnforced .Purpose )
358- assert .Equal (t , KM_ORIGIN_GENERATED , decoded .TeeEnforced .Origin )
359+
360+ origin , present , err := authorizationListOrigin (& decoded .TeeEnforced )
361+ require .NoError (t , err )
362+ assert .True (t , present )
363+ assert .Equal (t , KM_ORIGIN_GENERATED , origin )
364+
365+ // A real attestation carries origin on one list only, which is why an absent origin decoding to the zero value
366+ // went unnoticed.
367+ _ , present , err = authorizationListOrigin (& decoded .SoftwareEnforced )
368+ require .NoError (t , err )
369+ assert .False (t , present )
359370
360371 assert .Empty (t , decoded .TeeEnforced .AllApplications .FullBytes )
361372 assert .Empty (t , decoded .SoftwareEnforced .AllApplications .FullBytes )
362373}
363374
375+ // TestAndroidKeyAuthorizationListOrigin asserts the §8.4 origin requirement is evaluated against the union of the two
376+ // authorization lists, matching the adjacent purpose requirement, and that an absent origin does not satisfy it. An
377+ // optional integer decodes to zero when absent, which is the value of KM_ORIGIN_GENERATED.
378+ func TestAndroidKeyAuthorizationListOrigin (t * testing.T ) {
379+ // SEQUENCE { [1] EXPLICIT SET { INTEGER 2 } [, [702] EXPLICIT INTEGER n] } where the purpose is KM_PURPOSE_SIGN.
380+ list := func (t * testing.T , origin string ) authorizationList {
381+ t .Helper ()
382+
383+ body := "a1053103020102"
384+
385+ if origin != "" {
386+ body += "bf853e030201" + origin
387+ }
388+
389+ der , err := hex .DecodeString (fmt .Sprintf ("30%02x" , len (body )/ 2 ) + body )
390+ require .NoError (t , err )
391+
392+ var decoded authorizationList
393+
394+ rest , err := asn1 .Unmarshal (der , & decoded )
395+ require .NoError (t , err )
396+ require .Empty (t , rest )
397+
398+ return decoded
399+ }
400+
401+ generated := list (t , "00" ) // KM_ORIGIN_GENERATED.
402+ imported := list (t , "02" ) // KM_ORIGIN_IMPORTED.
403+ missing := list (t , "" )
404+
405+ testCases := []struct {
406+ name string
407+ tee authorizationList
408+ software authorizationList
409+ err string
410+ }{
411+ {
412+ name : "ShouldAcceptWhenGeneratedInTeeAndAbsentFromSoftware" ,
413+ tee : generated , software : missing ,
414+ },
415+ {
416+ name : "ShouldAcceptWhenGeneratedInSoftwareAndAbsentFromTee" ,
417+ tee : missing , software : generated ,
418+ },
419+ {
420+ name : "ShouldAcceptWhenGeneratedInBoth" ,
421+ tee : generated , software : generated ,
422+ },
423+ {
424+ // The union is satisfied by the teeEnforced list. The previous implementation required both lists to
425+ // agree, which rejected this.
426+ name : "ShouldAcceptWhenGeneratedInTeeAndImportedInSoftware" ,
427+ tee : generated , software : imported ,
428+ },
429+ {
430+ name : "ShouldRejectWhenImportedInBoth" ,
431+ tee : imported , software : imported ,
432+ err : "Attestation certificate extensions contains authorization list with origin not equal KM_ORIGIN_GENERATED" ,
433+ },
434+ {
435+ // Neither list states an origin, so there is no value equal to KM_ORIGIN_GENERATED to verify.
436+ name : "ShouldRejectWhenAbsentFromBothLists" ,
437+ tee : missing , software : missing ,
438+ err : "Attestation certificate extensions contains authorization list with origin not equal KM_ORIGIN_GENERATED" ,
439+ },
440+ {
441+ name : "ShouldRejectWhenImportedInTeeAndAbsentFromSoftware" ,
442+ tee : imported , software : missing ,
443+ err : "Attestation certificate extensions contains authorization list with origin not equal KM_ORIGIN_GENERATED" ,
444+ },
445+ }
446+
447+ for _ , tc := range testCases {
448+ t .Run (tc .name , func (t * testing.T ) {
449+ decoded := keyDescription {TeeEnforced : tc .tee , SoftwareEnforced : tc .software }
450+
451+ protoErr := androidKeyValidateAuthorizationLists (& decoded )
452+
453+ if tc .err != "" {
454+ require .NotNil (t , protoErr )
455+ assert .EqualError (t , protoErr , tc .err )
456+ } else {
457+ assert .Nil (t , protoErr )
458+ }
459+ })
460+ }
461+ }
462+
463+ func TestAuthorizationListOriginDER (t * testing.T ) {
464+ const purpose = "a1053103020102" // [1] EXPLICIT SET { INTEGER 2 }.
465+
466+ sequence := func (t * testing.T , body string ) []byte {
467+ t .Helper ()
468+
469+ der , err := hex .DecodeString (fmt .Sprintf ("30%02x" , len (body )/ 2 ) + body )
470+ require .NoError (t , err )
471+
472+ return der
473+ }
474+
475+ t .Run ("ShouldRejectPresentButEmptyOriginWhileDecoding" , func (t * testing.T ) {
476+ var list authorizationList
477+
478+ _ , err := asn1 .Unmarshal (sequence (t , purpose + "bf853e00" ), & list )
479+ require .EqualError (t , err , "asn1: structure error: explicit tag has no child" )
480+ })
481+
482+ testCases := []struct {
483+ name string
484+ origin string
485+ origins int
486+ present bool
487+ err string
488+ }{
489+ {
490+ name : "ShouldReportAbsentWhenOriginMissing" ,
491+ origin : "" ,
492+ },
493+ {
494+ name : "ShouldDecodeGenerated" ,
495+ origin : "bf853e03020100" ,
496+ origins : KM_ORIGIN_GENERATED ,
497+ present : true ,
498+ },
499+ {
500+ name : "ShouldDecodeImported" ,
501+ origin : "bf853e03020102" ,
502+ origins : KM_ORIGIN_IMPORTED ,
503+ present : true ,
504+ },
505+ {
506+ // [702] EXPLICIT { INTEGER 0, NULL }. The value decodes but a NULL follows it inside the wrapper, which
507+ // was previously discarded along with the remainder returned by asn1.Unmarshal.
508+ name : "ShouldRejectTrailingDataAfterOrigin" ,
509+ origin : "bf853e05" + "020100" + "0500" ,
510+ err : "origin has 2 bytes of trailing data" ,
511+ },
512+ {
513+ // [702] EXPLICIT { INTEGER 0, INTEGER 2 }, so the trailing data is itself a valid origin value.
514+ name : "ShouldRejectTrailingOriginValue" ,
515+ origin : "bf853e06" + "020100" + "020102" ,
516+ err : "origin has 3 bytes of trailing data" ,
517+ },
518+ }
519+
520+ for _ , tc := range testCases {
521+ t .Run (tc .name , func (t * testing.T ) {
522+ var list authorizationList
523+
524+ rest , err := asn1 .Unmarshal (sequence (t , purpose + tc .origin ), & list )
525+ require .NoError (t , err )
526+ require .Empty (t , rest )
527+
528+ origin , present , err := authorizationListOrigin (& list )
529+
530+ if tc .err != "" {
531+ require .EqualError (t , err , tc .err )
532+ assert .False (t , present )
533+ assert .Equal (t , 0 , origin )
534+
535+ return
536+ }
537+
538+ require .NoError (t , err )
539+ assert .Equal (t , tc .present , present )
540+ assert .Equal (t , tc .origins , origin )
541+ })
542+ }
543+ }
544+
364545func TestAndroidKeyAuthorizationListAllApplicationsDER (t * testing.T ) {
365546 // SEQUENCE { [1] EXPLICIT SET { INTEGER 2 }, [702] EXPLICIT INTEGER 0 }
366547 // purpose = KM_PURPOSE_SIGN, origin = KM_ORIGIN_GENERATED.
@@ -386,8 +567,13 @@ func TestAndroidKeyAuthorizationListAllApplicationsDER(t *testing.T) {
386567 // Both lists must otherwise be identical, so a failure below is attributable to allApplications alone.
387568 require .Equal (t , []int {KM_PURPOSE_SIGN }, absent .Purpose )
388569 require .Equal (t , []int {KM_PURPOSE_SIGN }, present .Purpose )
389- require .Equal (t , KM_ORIGIN_GENERATED , absent .Origin )
390- require .Equal (t , KM_ORIGIN_GENERATED , present .Origin )
570+
571+ for _ , list := range []* authorizationList {& absent , & present } {
572+ origin , ok , err := authorizationListOrigin (list )
573+ require .NoError (t , err )
574+ require .True (t , ok )
575+ require .Equal (t , KM_ORIGIN_GENERATED , origin )
576+ }
391577
392578 assert .Empty (t , absent .AllApplications .FullBytes )
393579 assert .NotEmpty (t , present .AllApplications .FullBytes , "allApplications must be detectable in the decoded list" )
0 commit comments