Skip to content

Commit 31fbda4

Browse files
sethmoocopybara-github
authored andcommitted
Ignore expiry checks for factory certificate chains
Some of the oldest factory chains are about to expires, but there is not a practical mechanism to rotate. Given that, ignore the expiry on these certs. If we find some day that the certs are not trustworthy, we'll revoke them. PiperOrigin-RevId: 902798819
1 parent cd45eb4 commit 31fbda4

5 files changed

Lines changed: 65 additions & 7 deletions

File tree

src/main/kotlin/provider/KeyAttestationCertPathValidator.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,10 @@ private class BasicChecker(
303303
BasicReason.NOT_YET_VALID,
304304
)
305305
} catch (e: CertificateExpiredException) {
306+
// Ignore validity on factory-provisioned certificate chains because it is not possible to
307+
// safely rotate the keys.
308+
if (certPath.provisioningMethod() == ProvisioningMethod.FACTORY_PROVISIONED) return
309+
306310
throw CertPathValidatorException(
307311
"Validity check failed",
308312
e,

src/main/kotlin/testing/Certs.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,9 @@ object Chains {
481481
)
482482
}
483483

484-
/* A chain where the attestation certificate has expired. */
484+
/* A factory-provisioned chain where the attestation certificate has expired. */
485485
@JvmStatic
486-
val expired by lazy {
486+
val expiredFactoryProvisioned by lazy {
487487
KeyAttestationCertPath(
488488
certFactory.generateLeafCert(),
489489
certFactory.generateAttestationCert(
@@ -495,6 +495,24 @@ object Chains {
495495
)
496496
}
497497

498+
/* A remotely-provisioned chain where the attestation certificate has expired. */
499+
@JvmStatic
500+
val expiredRemotelyProvisioned by lazy {
501+
val rkpAttestationCert =
502+
certFactory.generateRkpAttestationCert(
503+
serialNumber = BigInteger.valueOf(0x1234567890),
504+
notBefore = fakeCalendar.lastWeek(),
505+
notAfter = fakeCalendar.lastWeek(),
506+
)
507+
KeyAttestationCertPath(
508+
certFactory.generateLeafCert(issuer = rkpAttestationCert.subject),
509+
rkpAttestationCert,
510+
certFactory.rkpIntermediate,
511+
Certs.remoteIntermediate,
512+
certFactory.root,
513+
)
514+
}
515+
498516
/* A chain where the leaf certificate has expired. This will pass. */
499517
val expiredLeaf by lazy {
500518
KeyAttestationCertPath(

src/main/kotlin/testing/KeyAttestationCertFactory.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,16 @@ internal class KeyAttestationCertFactory(val fakeCalendar: FakeCalendar = FakeCa
117117
internal fun generateRkpAttestationCert(
118118
securityLevel: SecurityLevel = SecurityLevel.TRUSTED_ENVIRONMENT,
119119
serialNumber: BigInteger,
120+
notBefore: Date = fakeCalendar.lastWeek(),
121+
notAfter: Date = fakeCalendar.nextWeek(),
120122
) =
121123
generateAttestationCert(
122124
signingKey = rkpKey.private,
123125
subject = rkpAttestationName(securityLevel, serialNumber),
124126
issuer = rkpIntermediate.subject,
125127
serialNumber,
128+
notBefore,
129+
notAfter,
126130
extraExtension =
127131
Extension(
128132
ProvisioningInfoMap.OID,

src/main/kotlin/testing/X509CertificateExt.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ import org.bouncycastle.cert.jcajce.JcaX500NameUtil
2323

2424
private fun X500Principal.asX500Name() = JcaX500NameUtil.getX500Name(this)
2525

26-
internal val X509Certificate.subject: X500Name
26+
val X509Certificate.subject: X500Name
2727
get() = subjectX500Principal.asX500Name()

src/test/kotlin/provider/KeyAttestationCertPathValidatorTest.kt

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.android.keyattestation.verifier.testing.Certs.rootAnchor as testAncho
2222
import com.android.keyattestation.verifier.testing.Chains
2323
import com.android.keyattestation.verifier.testing.FakeCalendar
2424
import com.android.keyattestation.verifier.testing.TestUtils.prodAnchors
25+
import com.android.keyattestation.verifier.testing.TestUtils.readCertPath
2526
import com.google.common.truth.Truth.assertThat
2627
import java.security.InvalidAlgorithmParameterException
2728
import java.security.Security
@@ -34,6 +35,8 @@ import java.security.cert.PKIXCertPathChecker
3435
import java.security.cert.PKIXCertPathValidatorResult
3536
import java.security.cert.PKIXParameters
3637
import java.security.cert.PKIXReason
38+
import java.security.cert.TrustAnchor
39+
import java.time.LocalDate
3740
import kotlin.test.assertFailsWith
3841
import org.junit.BeforeClass
3942
import org.junit.Test
@@ -84,14 +87,14 @@ class KeyAttestationCertPathValidatorTest {
8487
val exception =
8588
assertFailsWith<CertPathValidatorException> {
8689
certPathValidator.validate(
87-
Chains.validFactoryProvisioned,
90+
Chains.validRemotelyProvisioned,
8891
PKIXParameters(setOf(testAnchor)),
8992
)
9093
}
9194
val pkixException =
9295
assertFailsWith<CertPathValidatorException> {
9396
pkixCertPathValidator.validate(
94-
Chains.validFactoryProvisioned,
97+
Chains.validRemotelyProvisioned,
9598
PKIXParameters(setOf(testAnchor)),
9699
)
97100
}
@@ -205,15 +208,44 @@ class KeyAttestationCertPathValidatorTest {
205208
}
206209

207210
@Test
208-
fun expired_throwsCertPathValidatorException() {
209-
val certPath = Chains.expired
211+
fun expiredFactory_succeeds() {
212+
certPathValidator.validate(Chains.expiredFactoryProvisioned, testParams)
213+
}
214+
215+
@Test
216+
fun expiredRkp_throwsCertPathValidatorException() {
217+
val certPath = Chains.expiredRemotelyProvisioned
210218
val exception =
211219
assertFailsWith<CertPathValidatorException> {
212220
certPathValidator.validate(certPath, testParams)
213221
}
214222
assertThat(exception.reason).isEqualTo(BasicReason.EXPIRED)
215223
}
216224

225+
@Test
226+
fun bluelineSdk28_factoryProvisioned_expiryIgnored() {
227+
val certPath = readCertPath("blueline/sdk28/TEE_EC_NONE.pem")
228+
val root = certPath.certificatesWithAnchor.last()
229+
val params =
230+
PKIXParameters(setOf(TrustAnchor(root, null))).apply {
231+
date = FakeCalendar(LocalDate.of(2030, 1, 1)).today()
232+
}
233+
certPathValidator.validate(certPath, params)
234+
}
235+
236+
@Test
237+
fun caimanSdk36_remoteProvisioned_expiryHonored() {
238+
val certPath = readCertPath("caiman/sdk36/TEE_EC_RKP.pem")
239+
val root = certPath.certificatesWithAnchor.last()
240+
val params =
241+
PKIXParameters(setOf(TrustAnchor(root, null))).apply {
242+
date = FakeCalendar(LocalDate.of(2030, 1, 1)).today()
243+
}
244+
val exception =
245+
assertFailsWith<CertPathValidatorException> { certPathValidator.validate(certPath, params) }
246+
assertThat(exception.reason).isEqualTo(BasicReason.EXPIRED)
247+
}
248+
217249
@Test
218250
fun forgedKeybox_throwsCertPathValidatorException() {
219251
val certPath = Chains.forgedKeybox

0 commit comments

Comments
 (0)