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,36 @@ 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
83+ }
84+ ProvisioningMethod .REMOTELY_PROVISIONED ->
85+ when (
86+ parseDN(
87+ certificates[certificates.size - 2 ].subjectX500Principal.getName(X500Principal .RFC1779 )
88+ )[" O" ]
89+ ) {
90+ " TEE" -> SecurityLevel .TRUSTED_ENVIRONMENT
91+ " StrongBox" -> SecurityLevel .STRONG_BOX
92+ else -> SecurityLevel .SOFTWARE
93+ }
94+ else -> SecurityLevel .SOFTWARE
95+ }
7296
7397 /* *
7498 * Returns the leaf certificate from the certificate chain.
@@ -84,6 +108,17 @@ class KeyAttestationCertPath(certs: List<X509Certificate>) : CertPath("X.509") {
84108
85109 fun intermediateCert (): X509Certificate = certificates.last()
86110
111+ private fun isFactoryProvisioned (): Boolean {
112+ val rdn = parseDN(this .intermediateCert().subjectX500Principal.getName(X500Principal .RFC1779 ))
113+ return rdn.containsKey(" OID.2.5.4.5" ) && rdn[" OID.2.5.4.12" ] in setOf (" TEE" , " StrongBox" )
114+ }
115+
116+ // TODO(keyattestation): Update this to use fields in the RKP root.
117+ private fun isRemoteProvisioned (): Boolean {
118+ val rdn = parseDN(this .intermediateCert().subjectX500Principal.getName(X500Principal .RFC1779 ))
119+ return rdn[" CN" ] == " Droid CA2" && rdn[" O" ] == " Google LLC"
120+ }
121+
87122 companion object {
88123 @JvmStatic
89124 @Throws(CertificateException ::class )
@@ -93,3 +128,22 @@ class KeyAttestationCertPath(certs: List<X509Certificate>) : CertPath("X.509") {
93128 private fun X509Certificate.isSelfIssued () = issuerX500Principal == subjectX500Principal
94129 }
95130}
131+
132+ enum class ProvisioningMethod {
133+ UNKNOWN ,
134+ FACTORY_PROVISIONED ,
135+ REMOTELY_PROVISIONED ,
136+ }
137+
138+ private fun parseDN (dn : String ): Map <String , String > {
139+ val attributes = mutableMapOf<String , String >()
140+ val parts = dn.split(" ," )
141+
142+ for (part in parts) {
143+ val keyValue = part.trim().split(" =" , limit = 2 )
144+ if (keyValue.size == 2 ) {
145+ attributes[keyValue[0 ].trim()] = keyValue[1 ].trim()
146+ }
147+ }
148+ return attributes
149+ }
0 commit comments