1717package com.android.keyattestation.verifier
1818
1919import androidx.annotation.RequiresApi
20+ import com.android.keyattestation.verifier.provider.KeyAttestationCertPath
2021import com.google.common.collect.ImmutableList
2122import com.google.errorprone.annotations.Immutable
2223import com.google.errorprone.annotations.ThreadSafe
@@ -36,7 +37,7 @@ sealed interface Constraint {
3637 val label: String
3738
3839 /* * Verifies that [description] satisfies this [Constraint]. */
39- fun check (description : KeyDescription ): Result
40+ fun check (description : KeyDescription , certPath : KeyAttestationCertPath ): Result
4041}
4142
4243/* *
@@ -113,15 +114,16 @@ fun constraintConfig(init: ConstraintConfigBuilder.() -> Unit): ConstraintConfig
113114data object IgnoredConstraint : Constraint {
114115 override val label = " Ignored"
115116
116- override fun check (description : KeyDescription ) = Constraint .Satisfied
117+ override fun check (description : KeyDescription , certPath : KeyAttestationCertPath ) =
118+ Constraint .Satisfied
117119}
118120
119121/* * Constraint that checks a single attribute of the [KeyDescription]. */
120122@Immutable(containerOf = [" T" ])
121123sealed class AttributeConstraint <out T >(override val label : String , val mapper : AttributeMapper ? ) :
122124 Constraint {
123125 /* * Evaluates whether the [description] is satisfied by this [AttributeConstraint]. */
124- override fun check (description : KeyDescription ) =
126+ override fun check (description : KeyDescription , certPath : KeyAttestationCertPath ) =
125127 if (isSatisfied(mapper?.invoke(description))) {
126128 Constraint .Satisfied
127129 } else {
@@ -157,21 +159,26 @@ sealed class AttributeConstraint<out T>(override val label: String, val mapper:
157159 */
158160@Immutable
159161@RequiresApi(24 )
160- sealed class SecurityLevelConstraint (val isSatisfied : (KeyDescription ) -> Boolean ) : Constraint {
162+ sealed class SecurityLevelConstraint (
163+ val isSatisfied : (KeyDescription , KeyAttestationCertPath ) -> Boolean
164+ ) : Constraint {
161165 companion object {
162166 const val LABEL = " Security level"
163167 }
164168
165169 override val label = LABEL
166170
167- override fun check (description : KeyDescription ) =
168- if (isSatisfied(description)) {
171+ override fun check (description : KeyDescription , certPath : KeyAttestationCertPath ) =
172+ if (isSatisfied(description, certPath )) {
169173 Constraint .Satisfied
170174 } else {
171- Constraint .Violated (getFailureMessage(description))
175+ Constraint .Violated (getFailureMessage(description, certPath ))
172176 }
173177
174- fun getFailureMessage (description : KeyDescription ): String =
178+ open fun getFailureMessage (
179+ description : KeyDescription ,
180+ certPath : KeyAttestationCertPath ,
181+ ): String =
175182 " Security level violates constraint: " +
176183 " keyMintSecurityLevel=${description.keyMintSecurityLevel} , " +
177184 " attestationSecurityLevel=${description.attestationSecurityLevel} , " +
@@ -185,8 +192,8 @@ sealed class SecurityLevelConstraint(val isSatisfied: (KeyDescription) -> Boolea
185192 */
186193 @Immutable
187194 data class STRICT (val expectedVal : SecurityLevel ) :
188- SecurityLevelConstraint ({
189- it .keyMintSecurityLevel == expectedVal && it .attestationSecurityLevel == expectedVal
195+ SecurityLevelConstraint ({ desc, _ ->
196+ desc .keyMintSecurityLevel == expectedVal && desc .attestationSecurityLevel == expectedVal
190197 })
191198
192199 /* *
@@ -195,9 +202,9 @@ sealed class SecurityLevelConstraint(val isSatisfied: (KeyDescription) -> Boolea
195202 */
196203 @Immutable
197204 data object NOT_SOFTWARE :
198- SecurityLevelConstraint ({
199- it .keyMintSecurityLevel == it .attestationSecurityLevel &&
200- it .attestationSecurityLevel != SecurityLevel .SOFTWARE
205+ SecurityLevelConstraint ({ desc, _ ->
206+ desc .keyMintSecurityLevel == desc .attestationSecurityLevel &&
207+ desc .attestationSecurityLevel != SecurityLevel .SOFTWARE
201208 })
202209
203210 /* *
@@ -206,7 +213,39 @@ sealed class SecurityLevelConstraint(val isSatisfied: (KeyDescription) -> Boolea
206213 */
207214 @Immutable
208215 data object CONSISTENT :
209- SecurityLevelConstraint ({ it.attestationSecurityLevel == it.keyMintSecurityLevel })
216+ SecurityLevelConstraint ({ desc, _ ->
217+ desc.attestationSecurityLevel == desc.keyMintSecurityLevel
218+ })
219+
220+ /* *
221+ * Checks that the keyMintSecurityLevel matches the security level claimed by the certificate.
222+ * this constraint may be used in conjunction with other security level constraints. e.g. it may
223+ * be combined with [STRICT] to verify that the keyMintSecurityLevel is precisely
224+ * [SecurityLevel.STRONG_BOX] and that the security level matches the value claimed by the
225+ * Google-signed certificate.
226+ */
227+ @Immutable
228+ data object MATCHES_CERTIFICATE :
229+ SecurityLevelConstraint ({ desc, certPath ->
230+ when (desc.keyMintSecurityLevel) {
231+ SecurityLevel .SOFTWARE ->
232+ certPath.securityLevel() == null || certPath.securityLevel() == SecurityLevel .SOFTWARE
233+ // Older cert chains do not make a TEE security level claim, so allow null. StrongBox certs
234+ // always explicitly claim the StrongBox security level, so there's no risk of a TEE cert
235+ // chain claiming to be StrongBox.
236+ SecurityLevel .TRUSTED_ENVIRONMENT ->
237+ certPath.securityLevel() == null ||
238+ certPath.securityLevel() == SecurityLevel .TRUSTED_ENVIRONMENT
239+ SecurityLevel .STRONG_BOX -> certPath.securityLevel() == SecurityLevel .STRONG_BOX
240+ }
241+ }) {
242+ override fun getFailureMessage (
243+ description : KeyDescription ,
244+ certPath : KeyAttestationCertPath ,
245+ ): String =
246+ " Security level of KeyMint (${description.keyMintSecurityLevel} ) does not match " +
247+ " attestation certificate (${certPath.securityLevel()} )"
248+ }
210249}
211250
212251/* *
@@ -224,7 +263,7 @@ sealed class TagOrderConstraint : Constraint {
224263 */
225264 @Immutable
226265 data object STRICT : TagOrderConstraint () {
227- override fun check (description : KeyDescription ) =
266+ override fun check (description : KeyDescription , certPath : KeyAttestationCertPath ) =
228267 if (
229268 description.softwareEnforced.areTagsOrdered && description.hardwareEnforced.areTagsOrdered
230269 ) {
0 commit comments