@@ -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/* *
@@ -391,7 +393,7 @@ data class AuthorizationList(
391393 .let { DERSequence (it.toTypedArray()) }
392394
393395 internal companion object {
394- fun from (seq : ASN1Sequence , validateTagOrder : Boolean = false ): AuthorizationList {
396+ fun from (seq : ASN1Sequence , logFn : ( String ) -> Unit = { _ -> } ): AuthorizationList {
395397 val objects =
396398 seq.associate {
397399 require(it is ASN1TaggedObject ) {
@@ -411,9 +413,8 @@ data class AuthorizationList(
411413 * 2. within each class of tags, the elements or alternatives shall appear in ascending order
412414 * of their tag numbers.
413415 */
414- // TODO: b/356172932 - Add test data once an example certificate is found in the wild.
415- if (validateTagOrder && ! objects.keys.zipWithNext().all { (lhs, rhs) -> rhs > lhs }) {
416- throw IllegalArgumentException (" AuthorizationList tags must appear in ascending order" )
416+ if (! objects.keys.zipWithNext().all { (lhs, rhs) -> rhs > lhs }) {
417+ logFn(" AuthorizationList tags should appear in ascending order" )
417418 }
418419
419420 return AuthorizationList (
@@ -439,7 +440,7 @@ data class AuthorizationList(
439440 rollbackResistant = if (objects.containsKey(KeyMintTag .ROLLBACK_RESISTANT )) true else null ,
440441 rootOfTrust = objects[KeyMintTag .ROOT_OF_TRUST ]?.toRootOfTrust(),
441442 osVersion = objects[KeyMintTag .OS_VERSION ]?.toInt(),
442- osPatchLevel = objects[KeyMintTag .OS_PATCH_LEVEL ]?.toPatchLevel(),
443+ osPatchLevel = objects[KeyMintTag .OS_PATCH_LEVEL ]?.toPatchLevel(" OS " , logFn ),
443444 attestationApplicationId =
444445 objects[KeyMintTag .ATTESTATION_APPLICATION_ID ]?.toAttestationApplicationId(),
445446 attestationIdBrand = objects[KeyMintTag .ATTESTATION_ID_BRAND ]?.toStr(),
@@ -450,8 +451,8 @@ data class AuthorizationList(
450451 attestationIdMeid = objects[KeyMintTag .ATTESTATION_ID_MEID ]?.toStr(),
451452 attestationIdManufacturer = objects[KeyMintTag .ATTESTATION_ID_MANUFACTURER ]?.toStr(),
452453 attestationIdModel = objects[KeyMintTag .ATTESTATION_ID_MODEL ]?.toStr(),
453- vendorPatchLevel = objects[KeyMintTag .VENDOR_PATCH_LEVEL ]?.toPatchLevel(),
454- bootPatchLevel = objects[KeyMintTag .BOOT_PATCH_LEVEL ]?.toPatchLevel(),
454+ vendorPatchLevel = objects[KeyMintTag .VENDOR_PATCH_LEVEL ]?.toPatchLevel(" vendor " , logFn ),
455+ bootPatchLevel = objects[KeyMintTag .BOOT_PATCH_LEVEL ]?.toPatchLevel(" boot " , logFn ),
455456 attestationIdSecondImei = objects[KeyMintTag .ATTESTATION_ID_SECOND_IMEI ]?.toStr(),
456457 moduleHash = objects[KeyMintTag .MODULE_HASH ]?.toByteString(),
457458 )
@@ -470,14 +471,24 @@ data class PatchLevel(val yearMonth: YearMonth, val version: Int? = null) {
470471 }
471472
472473 companion object {
473- fun from (patchLevel : ASN1Encodable ): PatchLevel ? {
474+ fun from (
475+ patchLevel : ASN1Encodable ,
476+ partitionName : String = "",
477+ logFn : (String ) -> Unit = { _ -> },
478+ ): PatchLevel ? {
474479 check(patchLevel is ASN1Integer ) { " Must be an ASN1Integer, was ${this ::class .simpleName} " }
475- return from(patchLevel.value.toString())
480+ return from(patchLevel.value.toString(), partitionName, logFn )
476481 }
477482
478483 @JvmStatic
479- fun from (patchLevel : String ): PatchLevel ? {
484+ @JvmOverloads
485+ fun from (
486+ patchLevel : String ,
487+ partitionName : String = "",
488+ logFn : (String ) -> Unit = { _ -> },
489+ ): PatchLevel ? {
480490 if (patchLevel.length != 6 && patchLevel.length != 8 ) {
491+ logFn(" Invalid $partitionName patch level: $patchLevel " )
481492 return null
482493 }
483494 try {
@@ -486,6 +497,7 @@ data class PatchLevel(val yearMonth: YearMonth, val version: Int? = null) {
486497 val version = if (patchLevel.length == 8 ) patchLevel.substring(6 ).toInt() else null
487498 return PatchLevel (yearMonth, version)
488499 } catch (e: DateTimeParseException ) {
500+ logFn(" Invalid $partitionName patch level: $patchLevel " )
489501 return null
490502 }
491503 }
@@ -615,12 +627,9 @@ private fun ASN1Encodable.toAttestationApplicationId(): AttestationApplicationId
615627 return AttestationApplicationId .from(ASN1Sequence .getInstance(this .octets))
616628}
617629
618- // TODO: b/356172932 - `validateTagOrder` should default to true after making it user configurable.
619- private fun ASN1Encodable.toAuthorizationList (
620- validateTagOrder : Boolean = false
621- ): AuthorizationList {
630+ private fun ASN1Encodable.toAuthorizationList (logFn : (String ) -> Unit ): AuthorizationList {
622631 check(this is ASN1Sequence ) { " Object must be an ASN1Sequence, was ${this ::class .simpleName} " }
623- return AuthorizationList .from(this , validateTagOrder )
632+ return AuthorizationList .from(this , logFn )
624633}
625634
626635private fun ASN1Encodable.toBoolean (): Boolean {
@@ -647,7 +656,10 @@ private fun ASN1Encodable.toInt(): BigInteger {
647656 return this .value
648657}
649658
650- private fun ASN1Encodable.toPatchLevel (): PatchLevel ? = PatchLevel .from(this )
659+ private fun ASN1Encodable.toPatchLevel (
660+ partitionName : String = "",
661+ logFn : (String ) -> Unit = { _ -> },
662+ ): PatchLevel ? = PatchLevel .from(this , partitionName, logFn)
651663
652664private fun ASN1Encodable.toRootOfTrust (): RootOfTrust {
653665 check(this is ASN1Sequence ) { " Object must be an ASN1Sequence, was ${this ::class .simpleName} " }
0 commit comments