summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/security/attestationverification/AttestationVerificationManager.java102
-rw-r--r--core/java/android/security/attestationverification/AttestationVerificationService.java4
-rw-r--r--core/java/android/security/attestationverification/VerificationToken.java205
-rw-r--r--services/companion/java/com/android/server/companion/securechannel/SecureChannel.java10
-rw-r--r--services/core/java/com/android/server/security/AttestationVerificationManagerService.java18
-rw-r--r--services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java156
-rw-r--r--services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java80
-rw-r--r--tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt17
-rw-r--r--tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt39
-rw-r--r--tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt48
10 files changed, 294 insertions, 385 deletions
diff --git a/core/java/android/security/attestationverification/AttestationVerificationManager.java b/core/java/android/security/attestationverification/AttestationVerificationManager.java
index acf33822b3c7..ca4d417ad8fe 100644
--- a/core/java/android/security/attestationverification/AttestationVerificationManager.java
+++ b/core/java/android/security/attestationverification/AttestationVerificationManager.java
@@ -79,9 +79,7 @@ public class AttestationVerificationManager {
* is also associated with a particular connection.
*
* <p>The {@code callback} is called with a result and {@link VerificationToken} (which may be
- * null). The result is an integer (see constants in this class with the prefix {@code RESULT_}.
- * The result is {@link #RESULT_SUCCESS} when at least one verifier has passed its checks. The
- * token may be used in calls to other parts of the system.
+ * null). The result is an integer (see constants in {@link VerificationResultFlags}).
*
* <p>It's expected that a verifier will be able to decode and understand the passed values,
* otherwise fail to verify. {@code attestation} should contain some type data to prevent parse
@@ -108,7 +106,7 @@ public class AttestationVerificationManager {
@NonNull Bundle requirements,
@NonNull byte[] attestation,
@NonNull @CallbackExecutor Executor executor,
- @NonNull BiConsumer<@VerificationResult Integer, VerificationToken> callback) {
+ @NonNull BiConsumer<@VerificationResultFlags Integer, VerificationToken> callback) {
try {
AndroidFuture<IVerificationResult> resultCallback = new AndroidFuture<>();
resultCallback.thenAccept(result -> {
@@ -155,7 +153,7 @@ public class AttestationVerificationManager {
*/
@RequiresPermission(Manifest.permission.USE_ATTESTATION_VERIFICATION_SERVICE)
@CheckResult
- @VerificationResult
+ @VerificationResultFlags
public int verifyToken(
@NonNull AttestationProfile profile,
@LocalBindingType int localBindingType,
@@ -280,30 +278,66 @@ public class AttestationVerificationManager {
*/
public static final int TYPE_CHALLENGE = 3;
- /** @hide */
- @IntDef(
- prefix = {"RESULT_"},
- value = {
- RESULT_UNKNOWN,
- RESULT_SUCCESS,
- RESULT_FAILURE,
- })
- @Retention(RetentionPolicy.SOURCE)
+ /**
+ * Verification result returned from {@link #verifyAttestation}.
+ *
+ * A value of {@code 0} indicates success. Otherwise, a bit flag is set from first failing stage
+ * below:
+ * <ol>
+ * <li> The received attestation's integrity (e.g. the certificate signatures) is validated.
+ * If this fails, {@link #FLAG_FAILURE_CERTS} will be returned with all other bits unset.
+ * <li> The local binding requirements are checked. If this fails,
+ * {@link #FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS} is returned with all other bits unset.
+ * <li> The profile requirements are checked. If this fails, a bit flag will be returned with
+ * some of the these bits set to indicate the type of profile requirement failure:
+ * {@link #FLAG_FAILURE_UNSUPPORTED_PROFILE}, {@link #FLAG_FAILURE_KEYSTORE_REQUIREMENTS},
+ * {@link #FLAG_FAILURE_BOOT_STATE}, and {@link #FLAG_FAILURE_PATCH_LEVEL_DIFF}.
+ * </ol>
+ *
+ * Note: The reason of the failure must be not be provided to the remote device.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = {"FLAG_FAILURE_"},
+ value = {
+ FLAG_FAILURE_UNKNOWN,
+ FLAG_FAILURE_UNSUPPORTED_PROFILE,
+ FLAG_FAILURE_CERTS,
+ FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS,
+ FLAG_FAILURE_KEYSTORE_REQUIREMENTS,
+ FLAG_FAILURE_BOOT_STATE,
+ FLAG_FAILURE_PATCH_LEVEL_DIFF,
+ })
@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
- public @interface VerificationResult {
- }
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VerificationResultFlags{}
- /** The result of the verification is unknown because it has a value unknown to this SDK. */
- public static final int RESULT_UNKNOWN = 0;
+ /** Flag: If there are unknown failures e.g. runtime exception. 0 = no, 1 = yes. */
+ public static final int FLAG_FAILURE_UNKNOWN = 1;
- /** The result of the verification was successful. */
- public static final int RESULT_SUCCESS = 1;
+ /** Flag: If the AVF profile is supported. 0 = supported, 1 = not supported */
+ public static final int FLAG_FAILURE_UNSUPPORTED_PROFILE = 1 << 1;
/**
- * The result of the attestation verification was failure. The attestation could not be
- * verified.
+ * Flag: Result bit for certs verification e.g. loading, generating, parsing certs.
+ * 0 = success, 1 = failure
*/
- public static final int RESULT_FAILURE = 2;
+ public static final int FLAG_FAILURE_CERTS = 1 << 2;
+
+ /** Flag: Result bit for local binding requirements verification. 0 = success, 1 = failure. */
+ public static final int FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS = 1 << 3;
+
+ /**
+ * Flag: Result bit for KeyStore requirements verification.
+ * 0 = success, 1 = failure.
+ */
+ public static final int FLAG_FAILURE_KEYSTORE_REQUIREMENTS = 1 << 4;
+
+ /** Flag: Result bit for boot state verification. 0 = success, 1 = failure */
+ public static final int FLAG_FAILURE_BOOT_STATE = 1 << 5;
+
+ /** Flag: Result bit for patch level diff checks. 0 = success, 1 = failure. */
+ public static final int FLAG_FAILURE_PATCH_LEVEL_DIFF = 1 << 6;
/**
* Requirements bundle parameter key for a public key, a byte array.
@@ -351,26 +385,4 @@ public class AttestationVerificationManager {
}
return text + "(" + localBindingType + ")";
}
-
- /** @hide */
- public static String verificationResultCodeToString(@VerificationResult int resultCode) {
- final String text;
- switch (resultCode) {
- case RESULT_UNKNOWN:
- text = "UNKNOWN";
- break;
-
- case RESULT_SUCCESS:
- text = "SUCCESS";
- break;
-
- case RESULT_FAILURE:
- text = "FAILURE";
- break;
-
- default:
- return Integer.toString(resultCode);
- }
- return text + "(" + resultCode + ")";
- }
}
diff --git a/core/java/android/security/attestationverification/AttestationVerificationService.java b/core/java/android/security/attestationverification/AttestationVerificationService.java
index 26c3051f7f38..01668d729282 100644
--- a/core/java/android/security/attestationverification/AttestationVerificationService.java
+++ b/core/java/android/security/attestationverification/AttestationVerificationService.java
@@ -20,7 +20,7 @@ import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.app.Service;
import android.os.Bundle;
-import android.security.attestationverification.AttestationVerificationManager.VerificationResult;
+import android.security.attestationverification.AttestationVerificationManager.VerificationResultFlags;
/**
* A verifier which can be implemented by apps to verify an attestation (as described in {@link
@@ -93,7 +93,7 @@ public abstract class AttestationVerificationService extends Service {
* byte[], java.util.concurrent.Executor, java.util.function.BiConsumer)
*/
@CheckResult
- @VerificationResult
+ @VerificationResultFlags
public abstract int onVerifyPeerDeviceAttestation(
@NonNull Bundle requirements,
@NonNull byte[] attestation);
diff --git a/core/java/android/security/attestationverification/VerificationToken.java b/core/java/android/security/attestationverification/VerificationToken.java
index ae26823a9c7d..015010c52755 100644
--- a/core/java/android/security/attestationverification/VerificationToken.java
+++ b/core/java/android/security/attestationverification/VerificationToken.java
@@ -21,7 +21,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Parcelable;
import android.security.attestationverification.AttestationVerificationManager.LocalBindingType;
-import android.security.attestationverification.AttestationVerificationManager.VerificationResult;
+import android.security.attestationverification.AttestationVerificationManager.VerificationResultFlags;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -41,10 +41,6 @@ import java.util.function.BiConsumer;
* @hide
* @see Bundle#putParcelable(String, Parcelable)
*/
-@DataClass(
- genConstructor = false,
- genHiddenBuilder = true
-)
public final class VerificationToken implements Parcelable {
/**
@@ -69,17 +65,18 @@ public final class VerificationToken implements Parcelable {
private final Bundle mRequirements;
/**
- * The result of the {@link AttestationVerificationManager#verifyAttestation(int, int, Bundle,
- * byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token holders from
- * accidentally reading this value without calling {@code verifyToken}. Do <b>not</b> use this
- * value directly; call {@link AttestationVerificationManager#verifyToken(VerificationToken,
- * Duration)} to verify a valid token and it will return this value.
+ * The result of the {@link AttestationVerificationManager#verifyAttestation(AttestationProfile,
+ * int, Bundle, byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token
+ * holders from accidentally reading this value without calling {@code verifyToken}. Do
+ * <b>not</b> use this value directly; call {@link AttestationVerificationManager#verifyToken(
+ * AttestationProfile, int, Bundle, VerificationToken, Duration)} to verify a valid token and it
+ * will return this value.
*
* If the token is valid, this value is returned directly by {#verifyToken}.
*
* @hide
*/
- @VerificationResult
+ @VerificationResultFlags
private final int mVerificationResult;
/**
@@ -111,28 +108,13 @@ public final class VerificationToken implements Parcelable {
private int mUid;
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/security/attestationverification/VerificationToken.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @DataClass.Generated.Member
- /* package-private */ VerificationToken(
+ VerificationToken(
@NonNull AttestationProfile attestationProfile,
@LocalBindingType int localBindingType,
@NonNull Bundle requirements,
- @VerificationResult int verificationResult,
+ @VerificationResultFlags int verificationResult,
@NonNull java.time.Instant verificationTime,
- @NonNull byte[] hmac,
- int uid) {
+ @NonNull byte[] hmac) {
this.mAttestationProfile = attestationProfile;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mAttestationProfile);
@@ -144,62 +126,61 @@ public final class VerificationToken implements Parcelable {
NonNull.class, null, mRequirements);
this.mVerificationResult = verificationResult;
com.android.internal.util.AnnotationValidations.validate(
- VerificationResult.class, null, mVerificationResult);
+ VerificationResultFlags.class, null, mVerificationResult);
this.mVerificationTime = verificationTime;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVerificationTime);
this.mHmac = hmac;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHmac);
- this.mUid = uid;
-
- // onConstructed(); // You can define this method to get a callback
}
/**
* The attestation profile which was used to perform the verification.
+ *
+ * @hide
*/
- @DataClass.Generated.Member
public @NonNull AttestationProfile getAttestationProfile() {
return mAttestationProfile;
}
/**
* The local binding type of the local binding data used to perform the verification.
+ *
+ * @hide
*/
- @DataClass.Generated.Member
public @LocalBindingType int getLocalBindingType() {
return mLocalBindingType;
}
/**
* The requirements used to perform the verification.
+ *
+ * @hide
*/
- @DataClass.Generated.Member
public @NonNull Bundle getRequirements() {
return mRequirements;
}
/**
- * The result of the {@link AttestationVerificationManager#verifyAttestation(int, int, Bundle,
- * byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token holders from
- * accidentally reading this value without calling {@code verifyToken}. Do <b>not</b> use this
- * value directly; call {@link AttestationVerificationManager#verifyToken(VerificationToken,
- * Duration)} to verify a valid token and it will return this value.
+ * The result of the {@link AttestationVerificationManager#verifyAttestation(AttestationProfile,
+ * int, Bundle, byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token
+ * holders from accidentally reading this value without calling {@code verifyToken}. Do
+ * <b>not</b> use this value directly; call {@link AttestationVerificationManager#verifyToken(
+ * AttestationProfile, int, Bundle, VerificationToken, Duration)} to verify a valid token and it
+ * will return this value.
*
* If the token is valid, this value is returned directly by {#verifyToken}.
*
* @hide
*/
- @DataClass.Generated.Member
- public @VerificationResult int getVerificationResult() {
+ public @VerificationResultFlags int getVerificationResult() {
return mVerificationResult;
}
/**
* Time when the token was generated, set by the system.
*/
- @DataClass.Generated.Member
public @NonNull java.time.Instant getVerificationTime() {
return mVerificationTime;
}
@@ -212,25 +193,10 @@ public final class VerificationToken implements Parcelable {
*
* @hide
*/
- @DataClass.Generated.Member
public @NonNull byte[] getHmac() {
return mHmac;
}
- /**
- * The UID of the process which called {@code verifyAttestation} to create the token, as
- * returned by {@link Binder#getCallingUid()}. Calls to {@code verifyToken} will fail if the UID
- * of calling process does not match this value. This ensures that tokens cannot be shared
- * between UIDs.
- *
- * @hide
- */
- @DataClass.Generated.Member
- public int getUid() {
- return mUid;
- }
-
- @DataClass.Generated.Member
static Parcelling<java.time.Instant> sParcellingForVerificationTime =
Parcelling.Cache.get(
ForInstant.class);
@@ -242,7 +208,6 @@ public final class VerificationToken implements Parcelable {
}
@Override
- @DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
@@ -253,27 +218,24 @@ public final class VerificationToken implements Parcelable {
dest.writeInt(mVerificationResult);
sParcellingForVerificationTime.parcel(mVerificationTime, dest, flags);
dest.writeByteArray(mHmac);
- dest.writeInt(mUid);
}
@Override
- @DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ VerificationToken(@NonNull android.os.Parcel in) {
+ VerificationToken(@NonNull android.os.Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
- AttestationProfile attestationProfile = (AttestationProfile) in.readTypedObject(AttestationProfile.CREATOR);
+ AttestationProfile attestationProfile = (AttestationProfile) in.readTypedObject(
+ AttestationProfile.CREATOR);
int localBindingType = in.readInt();
Bundle requirements = in.readBundle();
int verificationResult = in.readInt();
java.time.Instant verificationTime = sParcellingForVerificationTime.unparcel(in);
byte[] hmac = in.createByteArray();
- int uid = in.readInt();
this.mAttestationProfile = attestationProfile;
com.android.internal.util.AnnotationValidations.validate(
@@ -286,19 +248,15 @@ public final class VerificationToken implements Parcelable {
NonNull.class, null, mRequirements);
this.mVerificationResult = verificationResult;
com.android.internal.util.AnnotationValidations.validate(
- VerificationResult.class, null, mVerificationResult);
+ VerificationResultFlags.class, null, mVerificationResult);
this.mVerificationTime = verificationTime;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVerificationTime);
this.mHmac = hmac;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHmac);
- this.mUid = uid;
-
- // onConstructed(); // You can define this method to get a callback
}
- @DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<VerificationToken> CREATOR
= new Parcelable.Creator<VerificationToken>() {
@Override
@@ -317,16 +275,14 @@ public final class VerificationToken implements Parcelable {
* @hide
*/
@SuppressWarnings("WeakerAccess")
- @DataClass.Generated.Member
public static final class Builder {
private @NonNull AttestationProfile mAttestationProfile;
private @LocalBindingType int mLocalBindingType;
private @NonNull Bundle mRequirements;
- private @VerificationResult int mVerificationResult;
+ private @VerificationResultFlags int mVerificationResult;
private @NonNull java.time.Instant mVerificationTime;
private @NonNull byte[] mHmac;
- private int mUid;
private long mBuilderFieldsSet = 0L;
@@ -340,34 +296,29 @@ public final class VerificationToken implements Parcelable {
* @param requirements
* The requirements used to perform the verification.
* @param verificationResult
- * The result of the {@link AttestationVerificationManager#verifyAttestation(int, int, Bundle,
- * byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token holders from
- * accidentally reading this value without calling {@code verifyToken}. Do <b>not</b> use this
- * value directly; call {@link AttestationVerificationManager#verifyToken(VerificationToken,
- * Duration)} to verify a valid token and it will return this value.
+ * The result of the {@link AttestationVerificationManager#verifyAttestation(
+ * AttestationProfile, int, Bundle, byte[], Executor, BiConsumer)} call. This value is
+ * kept hidden to prevent token holders from accidentally reading this value without
+ * calling {@code verifyToken}. Do <b>not</b> use this value directly; call {@link
+ * AttestationVerificationManager#verifyToken(AttestationProfile, int, Bundle,
+ * VerificationToken, Duration)} to verify a valid token and it will return this value.
*
* If the token is valid, this value is returned directly by {#verifyToken}.
* @param verificationTime
* Time when the token was generated, set by the system.
* @param hmac
- * A Hash-based message authentication code used to verify the contents and authenticity of the
- * rest of the token. The hash is created using a secret key known only to the system server.
- * When verifying the token, the system re-hashes the token and verifies the generated HMAC is
- * the same.
- * @param uid
- * The UID of the process which called {@code verifyAttestation} to create the token, as
- * returned by {@link Binder#getCallingUid()}. Calls to {@code verifyToken} will fail if the UID
- * of calling process does not match this value. This ensures that tokens cannot be shared
- * between UIDs.
+ * A Hash-based message authentication code used to verify the contents and authenticity
+ * of the rest of the token. The hash is created using a secret key known only to the
+ * system server. When verifying the token, the system re-hashes the token and verifies
+ * the generated HMAC is the same.
*/
public Builder(
@NonNull AttestationProfile attestationProfile,
@LocalBindingType int localBindingType,
@NonNull Bundle requirements,
- @VerificationResult int verificationResult,
+ @VerificationResultFlags int verificationResult,
@NonNull java.time.Instant verificationTime,
- @NonNull byte[] hmac,
- int uid) {
+ @NonNull byte[] hmac) {
mAttestationProfile = attestationProfile;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mAttestationProfile);
@@ -379,20 +330,20 @@ public final class VerificationToken implements Parcelable {
NonNull.class, null, mRequirements);
mVerificationResult = verificationResult;
com.android.internal.util.AnnotationValidations.validate(
- VerificationResult.class, null, mVerificationResult);
+ VerificationResultFlags.class, null, mVerificationResult);
mVerificationTime = verificationTime;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVerificationTime);
mHmac = hmac;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHmac);
- mUid = uid;
}
/**
* The attestation profile which was used to perform the verification.
+ *
+ * @hide
*/
- @DataClass.Generated.Member
public @NonNull Builder setAttestationProfile(@NonNull AttestationProfile value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
@@ -402,8 +353,9 @@ public final class VerificationToken implements Parcelable {
/**
* The local binding type of the local binding data used to perform the verification.
+ *
+ * @hide
*/
- @DataClass.Generated.Member
public @NonNull Builder setLocalBindingType(@LocalBindingType int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
@@ -413,8 +365,9 @@ public final class VerificationToken implements Parcelable {
/**
* The requirements used to perform the verification.
+ *
+ * @hide
*/
- @DataClass.Generated.Member
public @NonNull Builder setRequirements(@NonNull Bundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
@@ -423,18 +376,18 @@ public final class VerificationToken implements Parcelable {
}
/**
- * The result of the {@link AttestationVerificationManager#verifyAttestation(int, int, Bundle,
- * byte[], Executor, BiConsumer)} call. This value is kept hidden to prevent token holders from
- * accidentally reading this value without calling {@code verifyToken}. Do <b>not</b> use this
- * value directly; call {@link AttestationVerificationManager#verifyToken(VerificationToken,
- * Duration)} to verify a valid token and it will return this value.
+ * The result of the {@link AttestationVerificationManager#verifyAttestation(
+ * AttestationProfile, int, Bundle, byte[], Executor, BiConsumer)} call. This value is kept
+ * hidden to prevent token holders from accidentally reading this value without calling
+ * {@code verifyToken}. Do <b>not</b> use this value directly; call {@link
+ * AttestationVerificationManager#verifyToken(AttestationProfile, int, Bundle,
+ * VerificationToken, Duration)} to verify a valid token and it will return this value.
*
* If the token is valid, this value is returned directly by {#verifyToken}.
*
* @hide
*/
- @DataClass.Generated.Member
- public @NonNull Builder setVerificationResult(@VerificationResult int value) {
+ public @NonNull Builder setVerificationResult(@VerificationResultFlags int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x8;
mVerificationResult = value;
@@ -444,7 +397,6 @@ public final class VerificationToken implements Parcelable {
/**
* Time when the token was generated, set by the system.
*/
- @DataClass.Generated.Member
public @NonNull Builder setVerificationTime(@NonNull java.time.Instant value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
@@ -453,14 +405,13 @@ public final class VerificationToken implements Parcelable {
}
/**
- * A Hash-based message authentication code used to verify the contents and authenticity of the
- * rest of the token. The hash is created using a secret key known only to the system server.
- * When verifying the token, the system re-hashes the token and verifies the generated HMAC is
- * the same.
+ * A Hash-based message authentication code used to verify the contents and authenticity of
+ * the rest of the token. The hash is created using a secret key known only to the system
+ * server. When verifying the token, the system re-hashes the token and verifies the
+ * generated HMAC is the same.
*
* @hide
*/
- @DataClass.Generated.Member
public @NonNull Builder setHmac(@NonNull byte... value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
@@ -468,26 +419,10 @@ public final class VerificationToken implements Parcelable {
return this;
}
- /**
- * The UID of the process which called {@code verifyAttestation} to create the token, as
- * returned by {@link Binder#getCallingUid()}. Calls to {@code verifyToken} will fail if the UID
- * of calling process does not match this value. This ensures that tokens cannot be shared
- * between UIDs.
- *
- * @hide
- */
- @DataClass.Generated.Member
- public @NonNull Builder setUid(int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x40;
- mUid = value;
- return this;
- }
-
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull VerificationToken build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x80; // Mark builder used
+ mBuilderFieldsSet |= 0x40; // Mark builder used
VerificationToken o = new VerificationToken(
mAttestationProfile,
@@ -495,29 +430,15 @@ public final class VerificationToken implements Parcelable {
mRequirements,
mVerificationResult,
mVerificationTime,
- mHmac,
- mUid);
+ mHmac);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x80) != 0) {
+ if ((mBuilderFieldsSet & 0x40) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
}
}
-
- @DataClass.Generated(
- time = 1633629747234L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/security/attestationverification/VerificationToken.java",
- inputSignatures = "private final @android.annotation.NonNull android.security.attestationverification.AttestationProfile mAttestationProfile\nprivate final @android.security.attestationverification.AttestationVerificationManager.LocalBindingType int mLocalBindingType\nprivate final @android.annotation.NonNull android.os.Bundle mRequirements\nprivate final @android.security.attestationverification.AttestationVerificationManager.VerificationResult int mVerificationResult\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) java.time.Instant mVerificationTime\nprivate final @android.annotation.NonNull byte[] mHmac\nprivate int mUid\nclass VerificationToken extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genHiddenBuilder=true)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
}
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index 71a182225013..dbeca82ade89 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -16,8 +16,6 @@
package com.android.server.companion.securechannel;
-import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
-
import android.annotation.NonNull;
import android.content.Context;
import android.os.Build;
@@ -498,7 +496,7 @@ public class SecureChannel {
private void exchangeAttestation()
throws IOException, GeneralSecurityException, BadHandleException, CryptoException {
- if (mVerificationResult == RESULT_SUCCESS) {
+ if (mVerificationResult == 0) {
Slog.d(TAG, "Remote attestation was already verified.");
return;
}
@@ -530,11 +528,11 @@ public class SecureChannel {
sendMessage(MessageType.AVF_RESULT, verificationResult);
byte[] remoteVerificationResult = readMessage(MessageType.AVF_RESULT);
- if (ByteBuffer.wrap(remoteVerificationResult).getInt() != RESULT_SUCCESS) {
+ if (ByteBuffer.wrap(remoteVerificationResult).getInt() != 0) {
throw new SecureChannelException("Remote device failed to verify local attestation.");
}
- if (mVerificationResult != RESULT_SUCCESS) {
+ if (mVerificationResult != 0) {
throw new SecureChannelException("Failed to verify remote attestation.");
}
@@ -549,7 +547,7 @@ public class SecureChannel {
return false;
}
// Is authenticated
- return mPskVerified || mVerificationResult == RESULT_SUCCESS;
+ return mPskVerified || mVerificationResult == 0;
}
// First byte indicates message type; 0 = CLIENT INIT, 1 = SERVER INIT
diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
index 55f85ea27c82..22a359bced86 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
@@ -17,10 +17,10 @@
package com.android.server.security;
import static android.Manifest.permission.USE_ATTESTATION_VERIFICATION_SERVICE;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_CERTS;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_UNSUPPORTED_PROFILE;
import static android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE;
import static android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED;
-import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
-import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -88,8 +88,8 @@ public class AttestationVerificationManagerService extends SystemService {
public void verifyToken(VerificationToken token, ParcelDuration parcelDuration,
AndroidFuture resultCallback) throws RemoteException {
enforceUsePermission();
- // TODO(b/201696614): Implement
- resultCallback.complete(RESULT_UNKNOWN);
+
+ throw new UnsupportedOperationException();
}
private void enforceUsePermission() {
@@ -123,9 +123,9 @@ public class AttestationVerificationManagerService extends SystemService {
AttestationProfile profile, int localBindingType, Bundle requirements,
byte[] attestation, AndroidFuture<IVerificationResult> resultCallback) {
IVerificationResult result = new IVerificationResult();
- // TODO(b/201696614): Implement
result.token = null;
- switch (profile.getAttestationProfileId()) {
+ int profileId = profile.getAttestationProfileId();
+ switch (profileId) {
case PROFILE_SELF_TRUSTED:
Slog.d(TAG, "Verifying Self Trusted profile.");
try {
@@ -133,7 +133,7 @@ public class AttestationVerificationManagerService extends SystemService {
AttestationVerificationSelfTrustedVerifierForTesting.getInstance()
.verifyAttestation(localBindingType, requirements, attestation);
} catch (Throwable t) {
- result.resultCode = RESULT_FAILURE;
+ result.resultCode = FLAG_FAILURE_CERTS;
}
break;
case PROFILE_PEER_DEVICE:
@@ -142,8 +142,8 @@ public class AttestationVerificationManagerService extends SystemService {
localBindingType, requirements, attestation);
break;
default:
- Slog.d(TAG, "No profile found, defaulting.");
- result.resultCode = RESULT_UNKNOWN;
+ Slog.e(TAG, "Profile [" + profileId + "] is not supported.");
+ result.resultCode = FLAG_FAILURE_UNSUPPORTED_PROFILE;
}
resultCallback.complete(result);
}
diff --git a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
index 41e3d00f3924..dc1f93664f79 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
@@ -16,11 +16,15 @@
package com.android.server.security;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_BOOT_STATE;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_KEYSTORE_REQUIREMENTS;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_CERTS;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_PATCH_LEVEL_DIFF;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_UNKNOWN;
import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE;
import static android.security.attestationverification.AttestationVerificationManager.PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS;
import static android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY;
-import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
-import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE;
import static android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY;
import static android.security.attestationverification.AttestationVerificationManager.localBindingTypeToString;
@@ -35,10 +39,8 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
-import android.security.attestationverification.AttestationVerificationManager;
import android.security.attestationverification.AttestationVerificationManager.LocalBindingType;
import android.util.IndentingPrintWriter;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.R;
@@ -100,7 +102,6 @@ import java.util.Set;
*/
class AttestationVerificationPeerDeviceVerifier {
private static final String TAG = "AVF";
- private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
private static final int MAX_PATCH_AGE_MONTHS = 12;
/**
@@ -188,32 +189,24 @@ class AttestationVerificationPeerDeviceVerifier {
@NonNull byte[] attestation,
@NonNull MyDumpData dumpData) {
if (mCertificateFactory == null) {
- debugVerboseLog("Unable to access CertificateFactory");
- return RESULT_FAILURE;
+ Slog.e(TAG, "Unable to access CertificateFactory");
+ return FLAG_FAILURE_CERTS;
}
dumpData.mCertificationFactoryAvailable = true;
if (mCertPathValidator == null) {
- debugVerboseLog("Unable to access CertPathValidator");
- return RESULT_FAILURE;
+ Slog.e(TAG, "Unable to access CertPathValidator");
+ return FLAG_FAILURE_CERTS;
}
dumpData.mCertPathValidatorAvailable = true;
-
- // Check if the provided local binding type is supported and if the provided requirements
- // "match" the binding type.
- if (!validateAttestationParameters(localBindingType, requirements)) {
- return RESULT_FAILURE;
- }
- dumpData.mAttestationParametersOk = true;
-
// To provide the most information in the dump logs, we track the failure state but keep
// verifying the rest of the attestation. For code safety, there are no transitions past
- // here to set failed = false
- boolean failed = false;
+ // here to set result = 0.
+ int result = 0;
try {
- // First: parse and validate the certificate chain.
+ // 1. parse and validate the certificate chain.
final List<X509Certificate> certificateChain = getCertificates(attestation);
// (returns void, but throws CertificateException and other similar Exceptions)
validateCertificateChain(certificateChain);
@@ -222,33 +215,38 @@ class AttestationVerificationPeerDeviceVerifier {
final var leafCertificate = certificateChain.get(0);
final var attestationExtension = fromCertificate(leafCertificate);
- // Second: verify if the attestation satisfies the "peer device" profile.
- if (!checkAttestationForPeerDeviceProfile(requirements, attestationExtension,
- dumpData)) {
- failed = true;
+ // 2. Check if the provided local binding type is supported and if the provided
+ // requirements "match" the binding type.
+ if (!validateAttestationParameters(localBindingType, requirements)) {
+ return FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS;
}
+ dumpData.mAttestationParametersOk = true;
- // Third: check if the attestation satisfies local binding requirements.
+ // 3. check if the attestation satisfies local binding requirements.
if (!checkLocalBindingRequirements(
leafCertificate, attestationExtension, localBindingType, requirements,
dumpData)) {
- failed = true;
+ result |= FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS;
}
+
+ // 4. verify if the attestation satisfies the "peer device" profile.
+ result |= checkAttestationForPeerDeviceProfile(requirements, attestationExtension,
+ dumpData);
} catch (CertificateException | CertPathValidatorException
| InvalidAlgorithmParameterException | IOException e) {
- // Catch all non-RuntimeExpceptions (all of these are thrown by either getCertificates()
+ // Catch all non-RuntimeExceptions (all of these are thrown by either getCertificates()
// or validateCertificateChain() or
// AndroidKeystoreAttestationVerificationAttributes.fromCertificate())
- debugVerboseLog("Unable to parse/validate Android Attestation certificate(s)", e);
- failed = true;
+ Slog.e(TAG, "Unable to parse/validate Android Attestation certificate(s)", e);
+ result = FLAG_FAILURE_CERTS;
} catch (RuntimeException e) {
- // Catch everyting else (RuntimeExpcetions), since we don't want to throw any exceptions
- // out of this class/method.
- debugVerboseLog("Unexpected error", e);
- failed = true;
+ // Catch everything else (RuntimeExceptions), since we don't want to throw any
+ // exceptions out of this class/method.
+ Slog.e(TAG, "Unexpected error", e);
+ result = FLAG_FAILURE_UNKNOWN;
}
- return failed ? RESULT_FAILURE : RESULT_SUCCESS;
+ return result;
}
@NonNull
@@ -270,22 +268,22 @@ class AttestationVerificationPeerDeviceVerifier {
private boolean validateAttestationParameters(
@LocalBindingType int localBindingType, @NonNull Bundle requirements) {
if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) {
- debugVerboseLog("Binding type is not supported: " + localBindingType);
+ Slog.e(TAG, "Binding type is not supported: " + localBindingType);
return false;
}
if (requirements.size() < 1) {
- debugVerboseLog("At least 1 requirement is required.");
+ Slog.e(TAG, "At least 1 requirement is required.");
return false;
}
if (localBindingType == TYPE_PUBLIC_KEY && !requirements.containsKey(PARAM_PUBLIC_KEY)) {
- debugVerboseLog("Requirements does not contain key: " + PARAM_PUBLIC_KEY);
+ Slog.e(TAG, "Requirements does not contain key: " + PARAM_PUBLIC_KEY);
return false;
}
if (localBindingType == TYPE_CHALLENGE && !requirements.containsKey(PARAM_CHALLENGE)) {
- debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE);
+ Slog.e(TAG, "Requirements does not contain key: " + PARAM_CHALLENGE);
return false;
}
@@ -296,7 +294,7 @@ class AttestationVerificationPeerDeviceVerifier {
throws CertificateException, CertPathValidatorException,
InvalidAlgorithmParameterException {
if (certificates.size() < 2) {
- debugVerboseLog("Certificate chain less than 2 in size.");
+ Slog.e(TAG, "Certificate chain less than 2 in size.");
throw new CertificateException("Certificate chain less than 2 in size.");
}
@@ -355,7 +353,7 @@ class AttestationVerificationPeerDeviceVerifier {
final boolean publicKeyMatches = checkPublicKey(
leafCertificate, requirements.getByteArray(PARAM_PUBLIC_KEY));
if (!publicKeyMatches) {
- debugVerboseLog(
+ Slog.e(TAG,
"Provided public key does not match leaf certificate public key.");
return false;
}
@@ -366,7 +364,7 @@ class AttestationVerificationPeerDeviceVerifier {
final boolean attestationChallengeMatches = checkAttestationChallenge(
attestationAttributes, requirements.getByteArray(PARAM_CHALLENGE));
if (!attestationChallengeMatches) {
- debugVerboseLog(
+ Slog.e(TAG,
"Provided challenge does not match leaf certificate challenge.");
return false;
}
@@ -386,7 +384,7 @@ class AttestationVerificationPeerDeviceVerifier {
final boolean ownedBySystem = checkOwnedBySystem(
leafCertificate, attestationAttributes);
if (!ownedBySystem) {
- debugVerboseLog("Certificate public key is not owned by the AndroidSystem.");
+ Slog.e(TAG, "Certificate public key is not owned by the AndroidSystem.");
return false;
}
dumpData.mSystemOwned = true;
@@ -401,67 +399,67 @@ class AttestationVerificationPeerDeviceVerifier {
return true;
}
- private boolean checkAttestationForPeerDeviceProfile(
+ private int checkAttestationForPeerDeviceProfile(
@NonNull Bundle requirements,
@NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes,
MyDumpData dumpData) {
- boolean result = true;
+ int result = 0;
// Checks for support of Keymaster 4.
if (attestationAttributes.getAttestationVersion() < 3) {
- debugVerboseLog("Attestation version is not at least 3 (Keymaster 4).");
- result = false;
+ Slog.e(TAG, "Attestation version is not at least 3 (Keymaster 4).");
+ result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS;
} else {
dumpData.mAttestationVersionAtLeast3 = true;
}
// Checks for support of Keymaster 4.
if (attestationAttributes.getKeymasterVersion() < 4) {
- debugVerboseLog("Keymaster version is not at least 4.");
- result = false;
+ Slog.e(TAG, "Keymaster version is not at least 4.");
+ result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS;
} else {
dumpData.mKeymasterVersionAtLeast4 = true;
}
// First two characters are Android OS version.
if (attestationAttributes.getKeyOsVersion() < 100000) {
- debugVerboseLog("Android OS version is not 10+.");
- result = false;
+ Slog.e(TAG, "Android OS version is not 10+.");
+ result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS;
} else {
dumpData.mOsVersionAtLeast10 = true;
}
if (!attestationAttributes.isAttestationHardwareBacked()) {
- debugVerboseLog("Key is not HW backed.");
- result = false;
+ Slog.e(TAG, "Key is not HW backed.");
+ result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS;
} else {
dumpData.mKeyHwBacked = true;
}
if (!attestationAttributes.isKeymasterHardwareBacked()) {
- debugVerboseLog("Keymaster is not HW backed.");
- result = false;
+ Slog.e(TAG, "Keymaster is not HW backed.");
+ result |= FLAG_FAILURE_KEYSTORE_REQUIREMENTS;
} else {
dumpData.mKeymasterHwBacked = true;
}
if (attestationAttributes.getVerifiedBootState() != VERIFIED) {
- debugVerboseLog("Boot state not Verified.");
- result = false;
+ Slog.e(TAG, "Boot state not Verified.");
+ result |= FLAG_FAILURE_BOOT_STATE;
} else {
dumpData.mBootStateIsVerified = true;
}
try {
if (!attestationAttributes.isVerifiedBootLocked()) {
- debugVerboseLog("Verified boot state is not locked.");
- result = false;
+ Slog.e(TAG, "Verified boot state is not locked.");
+ result |= FLAG_FAILURE_BOOT_STATE;
} else {
dumpData.mVerifiedBootStateLocked = true;
}
} catch (IllegalStateException e) {
- debugVerboseLog("VerifiedBootLocked is not set.", e);
- result = false;
+ Slog.e(TAG, "VerifiedBootLocked is not set.", e);
+ result = FLAG_FAILURE_BOOT_STATE;
}
int maxPatchLevelDiffMonths = requirements.getInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS,
@@ -470,8 +468,8 @@ class AttestationVerificationPeerDeviceVerifier {
// Patch level integer YYYYMM is expected to be within maxPatchLevelDiffMonths of today.
if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel(),
maxPatchLevelDiffMonths)) {
- debugVerboseLog("OS patch level is not within valid range.");
- result = false;
+ Slog.e(TAG, "OS patch level is not within valid range.");
+ result |= FLAG_FAILURE_PATCH_LEVEL_DIFF;
} else {
dumpData.mOsPatchLevelInRange = true;
}
@@ -479,24 +477,24 @@ class AttestationVerificationPeerDeviceVerifier {
// Patch level integer YYYYMMDD is expected to be within maxPatchLevelDiffMonths of today.
if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel(),
maxPatchLevelDiffMonths)) {
- debugVerboseLog("Boot patch level is not within valid range.");
- result = false;
+ Slog.e(TAG, "Boot patch level is not within valid range.");
+ result |= FLAG_FAILURE_PATCH_LEVEL_DIFF;
} else {
dumpData.mKeyBootPatchLevelInRange = true;
}
if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel(),
maxPatchLevelDiffMonths)) {
- debugVerboseLog("Vendor patch level is not within valid range.");
- result = false;
+ Slog.e(TAG, "Vendor patch level is not within valid range.");
+ result |= FLAG_FAILURE_PATCH_LEVEL_DIFF;
} else {
dumpData.mKeyVendorPatchLevelInRange = true;
}
if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel(),
maxPatchLevelDiffMonths)) {
- debugVerboseLog("Boot patch level is not within valid range.");
- result = false;
+ Slog.e(TAG, "Boot patch level is not within valid range.");
+ result |= FLAG_FAILURE_PATCH_LEVEL_DIFF;
} else {
dumpData.mKeyBootPatchLevelInRange = true;
}
@@ -522,7 +520,7 @@ class AttestationVerificationPeerDeviceVerifier {
final Set<String> ownerPackages =
attestationAttributes.getApplicationPackageNameVersion().keySet();
if (!ANDROID_SYSTEM_PACKAGE_NAME_SET.equals(ownerPackages)) {
- debugVerboseLog("Owner is not system, packages=" + ownerPackages);
+ Slog.e(TAG, "Owner is not system, packages=" + ownerPackages);
return false;
}
@@ -548,7 +546,7 @@ class AttestationVerificationPeerDeviceVerifier {
localPatchDate = LocalDate.parse(Build.VERSION.SECURITY_PATCH);
}
} catch (Throwable t) {
- debugVerboseLog("Build.VERSION.SECURITY_PATCH: "
+ Slog.e(TAG, "Build.VERSION.SECURITY_PATCH: "
+ Build.VERSION.SECURITY_PATCH + " is not in format YYYY-MM-DD");
return false;
}
@@ -563,7 +561,7 @@ class AttestationVerificationPeerDeviceVerifier {
// Convert remote patch dates to LocalDate.
String remoteDeviceDateStr = String.valueOf(patchLevel);
if (remoteDeviceDateStr.length() != 6 && remoteDeviceDateStr.length() != 8) {
- debugVerboseLog("Patch level is not in format YYYYMM or YYYYMMDD");
+ Slog.e(TAG, "Patch level is not in format YYYYMM or YYYYMMDD");
return false;
}
@@ -666,18 +664,6 @@ class AttestationVerificationPeerDeviceVerifier {
}
}
- private static void debugVerboseLog(String str, Throwable t) {
- if (DEBUG) {
- Slog.v(TAG, str, t);
- }
- }
-
- private static void debugVerboseLog(String str) {
- if (DEBUG) {
- Slog.v(TAG, str);
- }
- }
-
/* Mutable data class for tracking dump data from verifications. */
private static class MyDumpData extends AttestationVerificationManagerService.DumpData {
@@ -717,9 +703,7 @@ class AttestationVerificationPeerDeviceVerifier {
@SuppressLint("WrongConstant")
@Override
public void dumpTo(IndentingPrintWriter writer) {
- writer.println(
- "Result: " + AttestationVerificationManager.verificationResultCodeToString(
- mResult));
+ writer.println("Result: " + mResult);
if (!mCertificationFactoryAvailable) {
writer.println("Certificate Factory Unavailable");
return;
diff --git a/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java
index 58df2bd982dc..5039f6f42ac3 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java
@@ -16,17 +16,15 @@
package com.android.server.security;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_CERTS;
+import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS;
import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE;
-import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
-import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE;
import android.annotation.NonNull;
-import android.os.Build;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
@@ -64,7 +62,6 @@ import java.util.Set;
*/
class AttestationVerificationSelfTrustedVerifierForTesting {
private static final String TAG = "AVF";
- private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
// The OID for the extension Android Keymint puts into device-generated certificates.
private static final String ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID =
@@ -99,18 +96,6 @@ class AttestationVerificationSelfTrustedVerifierForTesting {
return sAttestationVerificationSelfTrustedVerifier;
}
- private static void debugVerboseLog(String str, Throwable t) {
- if (DEBUG) {
- Slog.v(TAG, str, t);
- }
- }
-
- private static void debugVerboseLog(String str) {
- if (DEBUG) {
- Slog.v(TAG, str);
- }
- }
-
private AttestationVerificationSelfTrustedVerifierForTesting() throws Exception {
mCertificateFactory = CertificateFactory.getInstance("X.509");
mCertPathValidator = CertPathValidator.getInstance("PKIX");
@@ -142,23 +127,42 @@ class AttestationVerificationSelfTrustedVerifierForTesting {
certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis));
}
} catch (CertificateException e) {
- debugVerboseLog("Unable to parse certificates from attestation", e);
- return RESULT_FAILURE;
+ Slog.e("Unable to parse certificates from attestation", e.getLocalizedMessage());
+ return FLAG_FAILURE_CERTS;
}
- if (localBindingType == TYPE_CHALLENGE
- && validateRequirements(requirements)
- && checkLeafChallenge(requirements, certificates)
- && verifyCertificateChain(certificates)) {
- return RESULT_SUCCESS;
+ int result = 0;
+
+ if (localBindingType != TYPE_CHALLENGE
+ || !validateRequirements(requirements)) {
+ Slog.e(TAG, "Local binding requirements verification failure." + localBindingType);
+ return FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS;
+ }
+
+ // Verify challenge
+ byte[] challenge;
+ try {
+ challenge = getChallengeFromCert(certificates.get(0));
+ } catch (Throwable t) {
+ Slog.e("Unable to parse challenge from certificate.", t.getLocalizedMessage());
+ result |= FLAG_FAILURE_CERTS;
+ return result;
+ }
+ if (!Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE), challenge)) {
+ Slog.e(TAG, "Self-Trusted validation failed; challenge mismatch.");
+ result |= FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS;
}
- return RESULT_FAILURE;
+ if (!verifyCertificateChain(certificates)) {
+ result |= FLAG_FAILURE_CERTS;
+ }
+
+ return result;
}
private boolean verifyCertificateChain(List<X509Certificate> certificates) {
if (certificates.size() < 2) {
- debugVerboseLog("Certificate chain less than 2 in size.");
+ Slog.e(TAG, "Certificate chain less than 2 in size.");
return false;
}
@@ -170,7 +174,7 @@ class AttestationVerificationSelfTrustedVerifierForTesting {
validationParams.setRevocationEnabled(false);
mCertPathValidator.validate(certificatePath, validationParams);
} catch (Throwable t) {
- debugVerboseLog("Invalid certificate chain", t);
+ Slog.e(TAG, "Invalid certificate chain", t);
return false;
}
@@ -183,34 +187,16 @@ class AttestationVerificationSelfTrustedVerifierForTesting {
private boolean validateRequirements(Bundle requirements) {
if (requirements.size() != 1) {
- debugVerboseLog("Requirements does not contain exactly 1 key.");
+ Slog.e(TAG, "Requirements does not contain exactly 1 key.");
return false;
}
if (!requirements.containsKey(PARAM_CHALLENGE)) {
- debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE);
+ Slog.e(TAG, "Requirements does not contain key: " + PARAM_CHALLENGE);
return false;
}
return true;
}
- private boolean checkLeafChallenge(Bundle requirements, List<X509Certificate> certificates) {
- // Verify challenge
- byte[] challenge;
- try {
- challenge = getChallengeFromCert(certificates.get(0));
- } catch (Throwable t) {
- debugVerboseLog("Unable to parse challenge from certificate.", t);
- return false;
- }
-
- if (Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE), challenge)) {
- return true;
- } else {
- debugVerboseLog("Self-Trusted validation failed; challenge mismatch.");
- return false;
- }
- }
-
private byte[] getChallengeFromCert(@NonNull X509Certificate x509Certificate)
throws CertificateEncodingException, IOException {
Certificate certificate = Certificate.getInstance(
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
index ad95fbc36867..88ebf3edc7ed 100644
--- a/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
@@ -2,10 +2,11 @@ package android.security.attestationverification
import android.app.Activity
import android.os.Bundle
+import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_CERTS
+import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS
import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
-import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
import android.security.attestationverification.AttestationVerificationManager.TYPE_UNKNOWN
@@ -54,7 +55,7 @@ class PeerDeviceSystemAttestationVerificationTest {
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
@Test
@@ -66,7 +67,7 @@ class PeerDeviceSystemAttestationVerificationTest {
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
@Test
@@ -80,7 +81,7 @@ class PeerDeviceSystemAttestationVerificationTest {
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
val future2 = CompletableFuture<Int>()
val challengeRequirements = Bundle()
@@ -90,7 +91,7 @@ class PeerDeviceSystemAttestationVerificationTest {
future2.complete(result)
}
- assertThat(future2.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future2.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
@Test
@@ -104,7 +105,7 @@ class PeerDeviceSystemAttestationVerificationTest {
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
@Test
@@ -118,7 +119,7 @@ class PeerDeviceSystemAttestationVerificationTest {
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_CERTS)
}
@Test
@@ -131,7 +132,7 @@ class PeerDeviceSystemAttestationVerificationTest {
invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_CERTS)
}
private fun <T> CompletableFuture<T>.getSoon(): T {
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
index 8f06b4a2ea0a..e77364de8747 100644
--- a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
@@ -2,6 +2,9 @@ package android.security.attestationverification
import android.os.Bundle
import android.app.Activity
+import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_CERTS
+import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS
+import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_UNSUPPORTED_PROFILE
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -14,9 +17,6 @@ import com.google.common.truth.Truth.assertThat
import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
import android.security.attestationverification.AttestationVerificationManager.PROFILE_UNKNOWN
-import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
-import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
-import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
import android.security.keystore.KeyGenParameterSpec
@@ -58,19 +58,19 @@ class SystemAttestationVerificationTest {
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_UNSUPPORTED_PROFILE)
}
@Test
fun verifyAttestation_returnsFailureWithEmptyAttestation() {
val future = CompletableFuture<Int>()
- val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
- avm.verifyAttestation(profile, TYPE_CHALLENGE, Bundle(), ByteArray(0),
- activity.mainExecutor) { result, _ ->
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ selfTrusted.requirements, ByteArray(0), activity.mainExecutor) { result, _ ->
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_CERTS)
}
@Test
@@ -81,7 +81,7 @@ class SystemAttestationVerificationTest {
Bundle(), selfTrusted.attestation, activity.mainExecutor) { result, _ ->
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
@Test
@@ -92,7 +92,7 @@ class SystemAttestationVerificationTest {
selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
@Test
@@ -106,7 +106,7 @@ class SystemAttestationVerificationTest {
wrongKeyRequirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
@Test
@@ -119,7 +119,7 @@ class SystemAttestationVerificationTest {
wrongChallengeRequirements, selfTrusted.attestation, activity.mainExecutor) {
result, _ -> future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ assertThat(future.getSoon()).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
// TODO(b/216144791): Add more failure tests for PROFILE_SELF_TRUSTED.
@@ -131,20 +131,7 @@ class SystemAttestationVerificationTest {
selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
future.complete(result)
}
- assertThat(future.getSoon()).isEqualTo(RESULT_SUCCESS)
- }
-
- @Test
- fun verifyToken_returnsUnknown() {
- val future = CompletableFuture<Int>()
- val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
- avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
- activity.mainExecutor) { _, token ->
- val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
- future.complete(result)
- }
-
- assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
+ assertThat(future.getSoon()).isEqualTo(0)
}
@Test
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
index 4712d6b51bd5..4d1a1a55af74 100644
--- a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
@@ -3,11 +3,12 @@ package com.android.server.security
import android.app.Activity
import android.content.Context
import android.os.Bundle
+import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_CERTS
+import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS
+import android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_PATCH_LEVEL_DIFF
import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
import android.security.attestationverification.AttestationVerificationManager.PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS
import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
-import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
-import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
import android.util.IndentingPrintWriter
@@ -72,7 +73,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_CHALLENGE, challengeRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_SUCCESS)
+ assertThat(result).isEqualTo(0)
}
@Test
@@ -88,7 +89,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_CHALLENGE, challengeRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_SUCCESS)
+ assertThat(result).isEqualTo(0)
}
@Test
@@ -108,7 +109,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_PUBLIC_KEY, pkRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_SUCCESS)
+ assertThat(result).isEqualTo(0)
}
@Test
@@ -126,7 +127,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_SUCCESS)
+ assertThat(result).isEqualTo(0)
}
@Test
@@ -143,7 +144,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_CHALLENGE, challengeRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_FAILURE)
+ assertThat(result).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
@Test
@@ -159,7 +160,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_CHALLENGE, challengeRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_FAILURE)
+ assertThat(result).isEqualTo(FLAG_FAILURE_PATCH_LEVEL_DIFF)
}
@Test
@@ -176,7 +177,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_CHALLENGE, challengeRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_SUCCESS)
+ assertThat(result).isEqualTo(0)
}
@Test
@@ -191,10 +192,28 @@ class AttestationVerificationPeerDeviceVerifierTest {
val result = verifier.verifyAttestation(
TYPE_CHALLENGE, challengeRequirements,
- // The patch date of this file is early 2022
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_FAILURE)
+ assertThat(result).isEqualTo(FLAG_FAILURE_PATCH_LEVEL_DIFF)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureOwnedBySystemAndPatchDataNotWithinMaxPatchDiff() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, dumpLogger, trustAnchors, false, LocalDate.of(2024, 10, 1),
+ LocalDate.of(2024, 9, 1)
+ )
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+ challengeRequirements.putBoolean("android.key_owned_by_system", true)
+ challengeRequirements.putInt(PARAM_MAX_PATCH_LEVEL_DIFF_MONTHS, 24)
+
+ val result = verifier.verifyAttestation(
+ TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
+ )
+ // Both "owned by system" and "patch level diff" checks should fail.
+ assertThat(result).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS or FLAG_FAILURE_PATCH_LEVEL_DIFF)
}
@Test
@@ -210,7 +229,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_CHALLENGE, challengeRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_FAILURE)
+ assertThat(result).isEqualTo(FLAG_FAILURE_CERTS)
}
@Test
@@ -232,7 +251,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_CHALLENGE, challengeRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_FAILURE)
+ assertThat(result).isEqualTo(FLAG_FAILURE_CERTS)
}
fun verifyAttestation_returnsFailureChallenge() {
@@ -247,7 +266,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
TYPE_CHALLENGE, challengeRequirements,
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()
)
- assertThat(result).isEqualTo(RESULT_FAILURE)
+ assertThat(result).isEqualTo(FLAG_FAILURE_LOCAL_BINDING_REQUIREMENTS)
}
private fun String.fromPEMFileToCerts(): Collection<Certificate> {
@@ -281,6 +300,7 @@ class AttestationVerificationPeerDeviceVerifierTest {
companion object {
private const val TAG = "AVFTest"
private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem"
+ // Local patch date is 20220105
private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME =
"test_attestation_with_root_certs.pem"
private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"