Skip to content

Commit 203f99b

Browse files
carmenyhcopybara-github
authored andcommitted
Add mechanism for adding debug logging.
PiperOrigin-RevId: 769130000
1 parent 5fd5d86 commit 203f99b

File tree

10 files changed

+458
-81
lines changed

10 files changed

+458
-81
lines changed

build.gradle.kts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies {
2727
implementation("com.google.code.gson:gson:2.11.0")
2828
implementation("com.google.guava:guava:33.3.1-android")
2929
implementation("com.google.protobuf:protobuf-javalite:4.28.3")
30+
implementation("com.google.protobuf:protobuf-kotlin-lite:4.28.3")
3031
implementation("org.bouncycastle:bcpkix-jdk18on:1.78.1")
3132
implementation("org.jetbrains.kotlin:kotlin-stdlib:2.2.0")
3233

@@ -49,17 +50,20 @@ tasks {
4950

5051
val generatedSourcesDir = layout.buildDirectory.dir("generated")
5152

52-
val googleTrustAnchors by tasks.registering {
53-
val jsonFile = file("roots.json")
54-
val json = jsonFile.readText()
55-
val generatedFile = generatedSourcesDir.get().file("main/kotlin/GoogleTrustAnchors.kt")
53+
val googleTrustAnchors by
54+
tasks.registering {
55+
val jsonFile = file("roots.json")
56+
val json = jsonFile.readText()
57+
val generatedFile = generatedSourcesDir.get().file("main/kotlin/GoogleTrustAnchors.kt")
5658

57-
inputs.files(jsonFile)
58-
outputs.file(generatedFile)
59+
inputs.files(jsonFile)
60+
outputs.file(generatedFile)
5961

60-
doLast {
61-
generatedFile.getAsFile().writeText(
62-
"""
62+
doLast {
63+
generatedFile
64+
.getAsFile()
65+
.writeText(
66+
"""
6367
package com.android.keyattestation.verifier
6468
6569
import com.android.keyattestation.verifier.asX509Certificate
@@ -81,22 +85,18 @@ val googleTrustAnchors by tasks.registering {
8185
}
8286
}
8387
"""
84-
)
88+
)
89+
}
8590
}
86-
}
87-
88-
val generateSources by tasks.registering {
89-
outputs.dir(generatedSourcesDir)
90-
dependsOn(tasks.named("googleTrustAnchors"))
91-
}
9291

93-
sourceSets {
94-
main {
95-
kotlin.srcDir(generateSources)
92+
val generateSources by
93+
tasks.registering {
94+
outputs.dir(generatedSourcesDir)
95+
dependsOn(tasks.named("googleTrustAnchors"))
9696
}
97-
}
97+
98+
sourceSets { main { kotlin.srcDir(generateSources) } }
9899

99100
tasks.named("compileKotlin").configure {
100-
dependsOn("generateSources")
101+
dependsOn("generateSources")
101102
}
102-

src/main/kotlin/Extension.kt

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,14 @@ data class ProvisioningInfoMap(
9797
}
9898

9999
data 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

626635
private 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

652664
private fun ASN1Encodable.toRootOfTrust(): RootOfTrust {
653665
check(this is ASN1Sequence) { "Object must be an ASN1Sequence, was ${this::class.simpleName}" }

0 commit comments

Comments
 (0)