1616
1717package com.android.keyattestation.verifier.provider
1818
19+ import com.android.keyattestation.verifier.SecurityLevel
1920import com.android.keyattestation.verifier.asX509Certificate
2021import com.google.protobuf.ByteString
2122import java.security.cert.CertPath
2223import java.security.cert.CertificateException
2324import java.security.cert.X509Certificate
25+ import javax.security.auth.x500.X500Principal
2426
2527/* *
2628 * [CertPath] representing an Android key attestation certificate chain.
@@ -29,9 +31,9 @@ import java.security.cert.X509Certificate
2931 * `KeyStore.getCertificateChain()`) in the following order:
3032 * 1. Leaf certificate (containing the extension)
3133 * 2. Attestation certificate (contains the ProvisioningInfo extension if remotely provisioned)
32- * 3. Intermediate certificate
33- * 5 . [Intermediate certificate] (if remotely provisioned)
34- * 4 . Root certificate
34+ * 3. Intermediate certificate (not present if software-backed attestation)
35+ * 4 . [Intermediate certificate] (only present if remotely provisioned)
36+ * 5 . Root certificate
3537 *
3638 * The last certificate in the chain is the trust anchor and is not included in the resulting
3739 * [CertPath]: "By convention, the certificates in a CertPath object of type X.509 are ordered
@@ -45,15 +47,8 @@ class KeyAttestationCertPath(certs: List<X509Certificate>) : CertPath("X.509") {
4547 val certificatesWithAnchor: List <X509Certificate >
4648
4749 init {
50+ // < 3 check needed to support parsing software-backed certs.
4851 if (certs.size < 3 ) throw CertificateException (" At least 3 certificates are required" )
49- when (certs.indexOfLast { it.hasAttestationExtension() }) {
50- 0 -> {} // expected value
51- - 1 -> throw CertificateException (" Attestation extension not found" )
52- else ->
53- if (certs[0 ].hasAttestationExtension())
54- throw CertificateException (" Additional attestation extension found" )
55- else throw CertificateException (" Certificate after target certificate" )
56- }
5752 if (! certs.last().isSelfIssued()) throw CertificateException (" Root certificate not found" )
5853 this .certificatesWithAnchor = certs
5954 }
@@ -68,7 +63,43 @@ class KeyAttestationCertPath(certs: List<X509Certificate>) : CertPath("X.509") {
6863
6964 override fun getCertificates (): List <X509Certificate > = certificatesWithAnchor.dropLast(1 )
7065
71- fun provisioningMethod (): ProvisioningMethod = intermediateCert().provisioningMethod()
66+ fun provisioningMethod () =
67+ when {
68+ isFactoryProvisioned() -> ProvisioningMethod .FACTORY_PROVISIONED
69+ isRemoteProvisioned() -> ProvisioningMethod .REMOTELY_PROVISIONED
70+ else -> ProvisioningMethod .UNKNOWN
71+ }
72+
73+ fun securityLevel () =
74+ when (provisioningMethod()) {
75+ ProvisioningMethod .FACTORY_PROVISIONED ->
76+ when (
77+ parseDN(intermediateCert().subjectX500Principal.getName(X500Principal .RFC1779 ))[
78+ " OID.2.5.4.12" ]
79+ ) {
80+ " TEE" -> SecurityLevel .TRUSTED_ENVIRONMENT
81+ " StrongBox" -> SecurityLevel .STRONG_BOX
82+ else -> SecurityLevel .SOFTWARE // Should never happen because isFactoryProvisioned()
83+ }
84+ ProvisioningMethod .REMOTELY_PROVISIONED -> {
85+ try {
86+ when (
87+ parseDN(
88+ certificates[certificates.size - 2 ]
89+ .subjectX500Principal
90+ .getName(X500Principal .RFC1779 )
91+ )[" O" ]
92+ ) {
93+ " TEE" -> SecurityLevel .TRUSTED_ENVIRONMENT
94+ " StrongBox" -> SecurityLevel .STRONG_BOX
95+ else -> SecurityLevel .SOFTWARE
96+ }
97+ } catch (e: Exception ) {
98+ SecurityLevel .SOFTWARE
99+ }
100+ }
101+ else -> SecurityLevel .SOFTWARE
102+ }
72103
73104 /* *
74105 * Returns the leaf certificate from the certificate chain.
@@ -84,6 +115,17 @@ class KeyAttestationCertPath(certs: List<X509Certificate>) : CertPath("X.509") {
84115
85116 fun intermediateCert (): X509Certificate = certificates.last()
86117
118+ private fun isFactoryProvisioned (): Boolean {
119+ val rdn = parseDN(this .intermediateCert().subjectX500Principal.getName(X500Principal .RFC1779 ))
120+ return rdn.containsKey(" OID.2.5.4.5" ) && rdn[" OID.2.5.4.12" ] in setOf (" TEE" , " StrongBox" )
121+ }
122+
123+ // TODO(google-internal bug): Update this to use fields in the RKP root.
124+ private fun isRemoteProvisioned (): Boolean {
125+ val rdn = parseDN(this .intermediateCert().subjectX500Principal.getName(X500Principal .RFC1779 ))
126+ return rdn[" CN" ] == " Droid CA2" && rdn[" O" ] == " Google LLC"
127+ }
128+
87129 companion object {
88130 @JvmStatic
89131 @Throws(CertificateException ::class )
@@ -93,3 +135,22 @@ class KeyAttestationCertPath(certs: List<X509Certificate>) : CertPath("X.509") {
93135 private fun X509Certificate.isSelfIssued () = issuerX500Principal == subjectX500Principal
94136 }
95137}
138+
139+ enum class ProvisioningMethod {
140+ UNKNOWN ,
141+ FACTORY_PROVISIONED ,
142+ REMOTELY_PROVISIONED ,
143+ }
144+
145+ private fun parseDN (dn : String ): Map <String , String > {
146+ val attributes = mutableMapOf<String , String >()
147+ val parts = dn.split(" ," )
148+
149+ for (part in parts) {
150+ val keyValue = part.trim().split(" =" , limit = 2 )
151+ if (keyValue.size == 2 ) {
152+ attributes[keyValue[0 ].trim()] = keyValue[1 ].trim()
153+ }
154+ }
155+ return attributes
156+ }
0 commit comments