@@ -97,14 +97,14 @@ data class ProvisioningInfoMap(
9797}
9898
9999data class DeviceIdentity (
100- val brand : String? ,
101- val device : String? ,
102- val product : String? ,
103- val serialNumber : String? ,
104- val imeis : Set <String >,
105- val meid : String? ,
106- val manufacturer : String? ,
107- val model : String? ,
100+ val brand : String? = null ,
101+ val device : String? = null ,
102+ val product : String? = null ,
103+ val serialNumber : String? = null ,
104+ val imeis : Set <String > = emptySet() ,
105+ val meid : String? = null ,
106+ val manufacturer : String? = null ,
107+ val model : String? = null ,
108108) {
109109 companion object {
110110 @JvmStatic
@@ -160,23 +160,25 @@ data class KeyDescription(
160160 @JvmField val OID = ASN1ObjectIdentifier (" 1.3.6.1.4.1.11129.2.1.17" )
161161
162162 @JvmStatic
163- fun parseFrom (cert : X509Certificate ) =
163+ @JvmOverloads
164+ fun parseFrom (cert : X509Certificate , logFn : (String ) -> Unit = {}) =
164165 cert
165166 .getExtensionValue(OID .id)
166167 .let { ASN1OctetString .getInstance(it).octets }
167- .let { parseFrom(it) }
168+ .let { parseFrom(it, logFn ) }
168169
169170 @JvmStatic
170- fun parseFrom (bytes : ByteArray ) =
171+ @JvmOverloads
172+ fun parseFrom (bytes : ByteArray , logFn : (String ) -> Unit = {}) =
171173 try {
172- from(ASN1Sequence .getInstance(bytes))
174+ from(ASN1Sequence .getInstance(bytes), logFn )
173175 } catch (e: NullPointerException ) {
174176 // Workaround for a NPE in BouncyCastle.
175177 // https://github.com/bcgit/bc-java/blob/228211ecb973fe87fdd0fc4ab16ba0446ec1a29c/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalType.java#L24
176178 throw IllegalArgumentException (e)
177179 }
178180
179- private fun from (seq : ASN1Sequence ): KeyDescription {
181+ private fun from (seq : ASN1Sequence , logFn : ( String ) -> Unit = {} ): KeyDescription {
180182 require(seq.size() == 8 )
181183 return KeyDescription (
182184 attestationVersion = seq.getObjectAt(0 ).toInt(),
@@ -185,8 +187,8 @@ data class KeyDescription(
185187 keyMintSecurityLevel = seq.getObjectAt(3 ).toSecurityLevel(),
186188 attestationChallenge = seq.getObjectAt(4 ).toByteString(),
187189 uniqueId = seq.getObjectAt(5 ).toByteString(),
188- softwareEnforced = seq.getObjectAt(6 ).toAuthorizationList(),
189- hardwareEnforced = seq.getObjectAt(7 ).toAuthorizationList(),
190+ softwareEnforced = seq.getObjectAt(6 ).toAuthorizationList(logFn ),
191+ hardwareEnforced = seq.getObjectAt(7 ).toAuthorizationList(logFn ),
190192 )
191193 }
192194 }
@@ -218,7 +220,7 @@ enum class Origin(val value: Long) {
218220 RESERVED (3 ),
219221 SECURELY_IMPORTED (4 );
220222
221- internal fun toAsn1 () = ASN1Integer (value)
223+ fun toAsn1 () = ASN1Integer (value)
222224}
223225
224226/* *
@@ -397,7 +399,7 @@ data class AuthorizationList(
397399 .let { DERSequence (it.toTypedArray()) }
398400
399401 internal companion object {
400- fun from (seq : ASN1Sequence , validateTagOrder : Boolean = false ): AuthorizationList {
402+ fun from (seq : ASN1Sequence , logFn : ( String ) -> Unit = { _ -> } ): AuthorizationList {
401403 val objects =
402404 seq.associate {
403405 require(it is ASN1TaggedObject ) {
@@ -417,9 +419,8 @@ data class AuthorizationList(
417419 * 2. within each class of tags, the elements or alternatives shall appear in ascending order
418420 * of their tag numbers.
419421 */
420- // TODO: b/356172932 - Add test data once an example certificate is found in the wild.
421- if (validateTagOrder && ! objects.keys.zipWithNext().all { (lhs, rhs) -> rhs > lhs }) {
422- throw IllegalArgumentException (" AuthorizationList tags must appear in ascending order" )
422+ if (! objects.keys.zipWithNext().all { (lhs, rhs) -> rhs > lhs }) {
423+ logFn(" AuthorizationList tags should appear in ascending order" )
423424 }
424425
425426 return AuthorizationList (
@@ -449,7 +450,7 @@ data class AuthorizationList(
449450 rollbackResistant = if (objects.containsKey(KeyMintTag .ROLLBACK_RESISTANT )) true else null ,
450451 rootOfTrust = objects[KeyMintTag .ROOT_OF_TRUST ]?.toRootOfTrust(),
451452 osVersion = objects[KeyMintTag .OS_VERSION ]?.toInt(),
452- osPatchLevel = objects[KeyMintTag .OS_PATCH_LEVEL ]?.toPatchLevel(),
453+ osPatchLevel = objects[KeyMintTag .OS_PATCH_LEVEL ]?.toPatchLevel(" OS " , logFn ),
453454 attestationApplicationId =
454455 objects[KeyMintTag .ATTESTATION_APPLICATION_ID ]?.toAttestationApplicationId(),
455456 attestationIdBrand = objects[KeyMintTag .ATTESTATION_ID_BRAND ]?.toStr(),
@@ -460,8 +461,8 @@ data class AuthorizationList(
460461 attestationIdMeid = objects[KeyMintTag .ATTESTATION_ID_MEID ]?.toStr(),
461462 attestationIdManufacturer = objects[KeyMintTag .ATTESTATION_ID_MANUFACTURER ]?.toStr(),
462463 attestationIdModel = objects[KeyMintTag .ATTESTATION_ID_MODEL ]?.toStr(),
463- vendorPatchLevel = objects[KeyMintTag .VENDOR_PATCH_LEVEL ]?.toPatchLevel(),
464- bootPatchLevel = objects[KeyMintTag .BOOT_PATCH_LEVEL ]?.toPatchLevel(),
464+ vendorPatchLevel = objects[KeyMintTag .VENDOR_PATCH_LEVEL ]?.toPatchLevel(" vendor " , logFn ),
465+ bootPatchLevel = objects[KeyMintTag .BOOT_PATCH_LEVEL ]?.toPatchLevel(" boot " , logFn ),
465466 attestationIdSecondImei = objects[KeyMintTag .ATTESTATION_ID_SECOND_IMEI ]?.toStr(),
466467 moduleHash = objects[KeyMintTag .MODULE_HASH ]?.toByteString(),
467468 )
@@ -480,14 +481,24 @@ data class PatchLevel(val yearMonth: YearMonth, val version: Int? = null) {
480481 }
481482
482483 companion object {
483- fun from (patchLevel : ASN1Encodable ): PatchLevel ? {
484+ fun from (
485+ patchLevel : ASN1Encodable ,
486+ partitionName : String = "",
487+ logFn : (String ) -> Unit = { _ -> },
488+ ): PatchLevel ? {
484489 check(patchLevel is ASN1Integer ) { " Must be an ASN1Integer, was ${this ::class .simpleName} " }
485- return from(patchLevel.value.toString())
490+ return from(patchLevel.value.toString(), partitionName, logFn )
486491 }
487492
488493 @JvmStatic
489- fun from (patchLevel : String ): PatchLevel ? {
494+ @JvmOverloads
495+ fun from (
496+ patchLevel : String ,
497+ partitionName : String = "",
498+ logFn : (String ) -> Unit = { _ -> },
499+ ): PatchLevel ? {
490500 if (patchLevel.length != 6 && patchLevel.length != 8 ) {
501+ logFn(" Invalid $partitionName patch level: $patchLevel " )
491502 return null
492503 }
493504 try {
@@ -496,6 +507,7 @@ data class PatchLevel(val yearMonth: YearMonth, val version: Int? = null) {
496507 val version = if (patchLevel.length == 8 ) patchLevel.substring(6 ).toInt() else null
497508 return PatchLevel (yearMonth, version)
498509 } catch (e: DateTimeParseException ) {
510+ logFn(" Invalid $partitionName patch level: $patchLevel " )
499511 return null
500512 }
501513 }
@@ -625,12 +637,9 @@ private fun ASN1Encodable.toAttestationApplicationId(): AttestationApplicationId
625637 return AttestationApplicationId .from(ASN1Sequence .getInstance(this .octets))
626638}
627639
628- // TODO: b/356172932 - `validateTagOrder` should default to true after making it user configurable.
629- private fun ASN1Encodable.toAuthorizationList (
630- validateTagOrder : Boolean = false
631- ): AuthorizationList {
640+ private fun ASN1Encodable.toAuthorizationList (logFn : (String ) -> Unit ): AuthorizationList {
632641 check(this is ASN1Sequence ) { " Object must be an ASN1Sequence, was ${this ::class .simpleName} " }
633- return AuthorizationList .from(this , validateTagOrder )
642+ return AuthorizationList .from(this , logFn )
634643}
635644
636645private fun ASN1Encodable.toBoolean (): Boolean {
@@ -657,7 +666,10 @@ private fun ASN1Encodable.toInt(): BigInteger {
657666 return this .value
658667}
659668
660- private fun ASN1Encodable.toPatchLevel (): PatchLevel ? = PatchLevel .from(this )
669+ private fun ASN1Encodable.toPatchLevel (
670+ partitionName : String = "",
671+ logFn : (String ) -> Unit = { _ -> },
672+ ): PatchLevel ? = PatchLevel .from(this , partitionName, logFn)
661673
662674private fun ASN1Encodable.toRootOfTrust (): RootOfTrust {
663675 check(this is ASN1Sequence ) { " Object must be an ASN1Sequence, was ${this ::class .simpleName} " }
0 commit comments