Implementing verification of PROFILE_PEER_DEVICE.
Bug: 216477071 Test: AttestationVerificationTest unit test Change-Id: Ide254de1aaaad24a5ac9e449086192aa9f59a72b
This commit is contained in:
parent
95db721b82
commit
193a27d287
@ -0,0 +1,468 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.security;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import com.android.framework.protobuf.ByteString;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1Boolean;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1Enumerated;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1Set;
|
||||
import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
|
||||
import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Parsed {@link X509Certificate} attestation extension values for Android Keystore attestations.
|
||||
*
|
||||
* Pull fields out of the top-level sequence. A full description of this structure is at
|
||||
* https://source.android.com/security/keystore/attestation.
|
||||
*
|
||||
* If a value is null or empty, then it was not set/found in the extension values.
|
||||
*
|
||||
*/
|
||||
class AndroidKeystoreAttestationVerificationAttributes {
|
||||
// The OID for the extension Android Keymaster puts into device-generated certificates.
|
||||
private static final String ANDROID_KEYMASTER_KEY_DESCRIPTION_EXTENSION_OID =
|
||||
"1.3.6.1.4.1.11129.2.1.17";
|
||||
|
||||
// ASN.1 sequence index values for the Android Keymaster extension.
|
||||
private static final int ATTESTATION_VERSION_INDEX = 0;
|
||||
private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
|
||||
private static final int KEYMASTER_VERSION_INDEX = 2;
|
||||
private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
|
||||
private static final int ATTESTATION_CHALLENGE_INDEX = 4;
|
||||
private static final int KEYMASTER_UNIQUE_ID_INDEX = 5;
|
||||
private static final int SW_ENFORCED_INDEX = 6;
|
||||
private static final int HW_ENFORCED_INDEX = 7;
|
||||
private static final int VERIFIED_BOOT_KEY_INDEX = 0;
|
||||
private static final int VERIFIED_BOOT_LOCKED_INDEX = 1;
|
||||
private static final int VERIFIED_BOOT_STATE_INDEX = 2;
|
||||
private static final int VERIFIED_BOOT_HASH_INDEX = 3;
|
||||
|
||||
// ASN.1 sequence index values for the Android Keystore application id.
|
||||
private static final int PACKAGE_INFO_SET_INDEX = 0;
|
||||
private static final int PACKAGE_SIGNATURE_SET_INDEX = 1;
|
||||
private static final int PACKAGE_INFO_NAME_INDEX = 0;
|
||||
private static final int PACKAGE_INFO_VERSION_INDEX = 1;
|
||||
|
||||
// See these AOSP files: hardware/libhardware/include/hardware/hw_auth_token.h
|
||||
private static final int HW_AUTH_NONE = 0;
|
||||
|
||||
// Some keymaster constants. See this AOSP file:
|
||||
// hardware/libhardware/include/hardware/keymaster_defs.h
|
||||
private static final int KM_TAG_NO_AUTH_REQUIRED = 503;
|
||||
private static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = 509;
|
||||
private static final int KM_TAG_ALL_APPLICATIONS = 600;
|
||||
private static final int KM_TAG_ROOT_OF_TRUST = 704;
|
||||
private static final int KM_TAG_OS_VERSION = 705;
|
||||
private static final int KM_TAG_OS_PATCHLEVEL = 706;
|
||||
private static final int KM_TAG_ATTESTATION_APPLICATION_ID = 709;
|
||||
private static final int KM_TAG_ATTESTATION_ID_BRAND = 710;
|
||||
private static final int KM_TAG_ATTESTATION_ID_DEVICE = 711;
|
||||
private static final int KM_TAG_ATTESTATION_ID_PRODUCT = 712;
|
||||
private static final int KM_TAG_VENDOR_PATCHLEVEL = 718;
|
||||
private static final int KM_TAG_BOOT_PATCHLEVEL = 719;
|
||||
|
||||
private static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
|
||||
private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
|
||||
private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
|
||||
private static final int KM_VERIFIED_BOOT_STATE_VERIFIED = 0;
|
||||
private static final int KM_VERIFIED_BOOT_STATE_SELF_SIGNED = 1;
|
||||
private static final int KM_VERIFIED_BOOT_STATE_UNVERIFIED = 2;
|
||||
private static final int KM_VERIFIED_BOOT_STATE_FAILED = 3;
|
||||
|
||||
private Integer mAttestationVersion = null;
|
||||
private SecurityLevel mAttestationSecurityLevel = null;
|
||||
private boolean mAttestationHardwareBacked = false;
|
||||
private Integer mKeymasterVersion = null;
|
||||
private SecurityLevel mKeymasterSecurityLevel = null;
|
||||
private boolean mKeymasterHardwareBacked = false;
|
||||
private ByteString mAttestationChallenge = null;
|
||||
private ByteString mKeymasterUniqueId = null;
|
||||
private String mDeviceBrand = null;
|
||||
private String mDeviceName = null;
|
||||
private String mDeviceProductName = null;
|
||||
private boolean mKeyAllowedForAllApplications = false;
|
||||
private Integer mKeyAuthenticatorType = null;
|
||||
private Integer mKeyBootPatchLevel = null;
|
||||
private Integer mKeyOsPatchLevel = null;
|
||||
private Integer mKeyOsVersion = null;
|
||||
private Integer mKeyVendorPatchLevel = null;
|
||||
private Boolean mKeyRequiresUnlockedDevice = null;
|
||||
private ByteString mVerifiedBootHash = null;
|
||||
private ByteString mVerifiedBootKey = null;
|
||||
private Boolean mVerifiedBootLocked = null;
|
||||
private VerifiedBootState mVerifiedBootState = null;
|
||||
private Map<String, Long> mApplicationPackageNameVersion = null;
|
||||
private List<ByteString> mApplicationCertificateDigests = null;
|
||||
|
||||
enum VerifiedBootState {
|
||||
VERIFIED,
|
||||
SELF_SIGNED,
|
||||
UNVERIFIED,
|
||||
FAILED
|
||||
}
|
||||
|
||||
enum SecurityLevel {
|
||||
SOFTWARE,
|
||||
TRUSTED_ENVIRONMENT,
|
||||
STRONG_BOX
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts attestation extension properties from {@link X509Certificate}
|
||||
* and returns a {@link AndroidKeystoreAttestationVerificationAttributes} that encapsulates the
|
||||
* properties.
|
||||
*/
|
||||
@NonNull
|
||||
static AndroidKeystoreAttestationVerificationAttributes fromCertificate(
|
||||
@NonNull X509Certificate x509Certificate)
|
||||
throws Exception {
|
||||
return new AndroidKeystoreAttestationVerificationAttributes(x509Certificate);
|
||||
}
|
||||
|
||||
int getAttestationVersion() {
|
||||
return mAttestationVersion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
SecurityLevel getAttestationSecurityLevel() {
|
||||
return mAttestationSecurityLevel;
|
||||
}
|
||||
|
||||
boolean isAttestationHardwareBacked() {
|
||||
return mAttestationHardwareBacked;
|
||||
}
|
||||
|
||||
int getKeymasterVersion() {
|
||||
return mKeymasterVersion;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
SecurityLevel getKeymasterSecurityLevel() {
|
||||
return mKeymasterSecurityLevel;
|
||||
}
|
||||
|
||||
boolean isKeymasterHardwareBacked() {
|
||||
return mKeymasterHardwareBacked;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ByteString getAttestationChallenge() {
|
||||
return mAttestationChallenge;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ByteString getKeymasterUniqueId() {
|
||||
return mKeymasterUniqueId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getDeviceBrand() {
|
||||
return mDeviceBrand;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getDeviceName() {
|
||||
return mDeviceName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getDeviceProductName() {
|
||||
return mDeviceProductName;
|
||||
}
|
||||
|
||||
boolean isKeyAllowedForAllApplications() {
|
||||
return mKeyAllowedForAllApplications;
|
||||
}
|
||||
|
||||
int getKeyAuthenticatorType() {
|
||||
if (mKeyAuthenticatorType == null) {
|
||||
throw new IllegalStateException("KeyAuthenticatorType is not set.");
|
||||
}
|
||||
return mKeyAuthenticatorType;
|
||||
}
|
||||
|
||||
int getKeyBootPatchLevel() {
|
||||
if (mKeyBootPatchLevel == null) {
|
||||
throw new IllegalStateException("KeyBootPatchLevel is not set.");
|
||||
}
|
||||
return mKeyBootPatchLevel;
|
||||
}
|
||||
|
||||
int getKeyOsPatchLevel() {
|
||||
if (mKeyOsPatchLevel == null) {
|
||||
throw new IllegalStateException("KeyOsPatchLevel is not set.");
|
||||
}
|
||||
return mKeyOsPatchLevel;
|
||||
}
|
||||
|
||||
int getKeyVendorPatchLevel() {
|
||||
if (mKeyVendorPatchLevel == null) {
|
||||
throw new IllegalStateException("KeyVendorPatchLevel is not set.");
|
||||
}
|
||||
return mKeyVendorPatchLevel;
|
||||
}
|
||||
|
||||
int getKeyOsVersion() {
|
||||
if (mKeyOsVersion == null) {
|
||||
throw new IllegalStateException("KeyOsVersion is not set.");
|
||||
}
|
||||
return mKeyOsVersion;
|
||||
}
|
||||
|
||||
boolean isKeyRequiresUnlockedDevice() {
|
||||
if (mKeyRequiresUnlockedDevice == null) {
|
||||
throw new IllegalStateException("KeyRequiresUnlockedDevice is not set.");
|
||||
}
|
||||
return mKeyRequiresUnlockedDevice;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ByteString getVerifiedBootHash() {
|
||||
return mVerifiedBootHash;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ByteString getVerifiedBootKey() {
|
||||
return mVerifiedBootKey;
|
||||
}
|
||||
|
||||
boolean isVerifiedBootLocked() {
|
||||
if (mVerifiedBootLocked == null) {
|
||||
throw new IllegalStateException("VerifiedBootLocked is not set.");
|
||||
}
|
||||
return mVerifiedBootLocked;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
VerifiedBootState getVerifiedBootState() {
|
||||
return mVerifiedBootState;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Map<String, Long> getApplicationPackageNameVersion() {
|
||||
return Collections.unmodifiableMap(mApplicationPackageNameVersion);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
List<ByteString> getApplicationCertificateDigests() {
|
||||
return Collections.unmodifiableList(mApplicationCertificateDigests);
|
||||
}
|
||||
|
||||
private AndroidKeystoreAttestationVerificationAttributes(X509Certificate x509Certificate)
|
||||
throws Exception {
|
||||
Certificate certificate = Certificate.getInstance(
|
||||
new ASN1InputStream(x509Certificate.getEncoded()).readObject());
|
||||
ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions()
|
||||
.getExtensionParsedValue(
|
||||
new ASN1ObjectIdentifier(ANDROID_KEYMASTER_KEY_DESCRIPTION_EXTENSION_OID));
|
||||
if (keyAttributes == null) {
|
||||
throw new CertificateEncodingException(
|
||||
"No attestation extension found in certificate.");
|
||||
}
|
||||
this.mAttestationVersion = getIntegerFromAsn1(
|
||||
keyAttributes.getObjectAt(ATTESTATION_VERSION_INDEX));
|
||||
this.mAttestationSecurityLevel = getSecurityLevelEnum(
|
||||
keyAttributes.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
|
||||
this.mAttestationHardwareBacked =
|
||||
this.mAttestationSecurityLevel == SecurityLevel.TRUSTED_ENVIRONMENT;
|
||||
this.mAttestationChallenge = getOctetsFromAsn1(
|
||||
keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX));
|
||||
this.mKeymasterVersion = getIntegerFromAsn1(
|
||||
keyAttributes.getObjectAt(KEYMASTER_VERSION_INDEX));
|
||||
this.mKeymasterUniqueId = getOctetsFromAsn1(
|
||||
keyAttributes.getObjectAt(KEYMASTER_UNIQUE_ID_INDEX));
|
||||
this.mKeymasterSecurityLevel = getSecurityLevelEnum(
|
||||
keyAttributes.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
|
||||
this.mKeymasterHardwareBacked =
|
||||
this.mKeymasterSecurityLevel == SecurityLevel.TRUSTED_ENVIRONMENT;
|
||||
|
||||
ASN1Encodable[] softwareEnforced = ((ASN1Sequence)
|
||||
keyAttributes.getObjectAt(SW_ENFORCED_INDEX)).toArray();
|
||||
for (ASN1Encodable entry : softwareEnforced) {
|
||||
ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
|
||||
switch (taggedEntry.getTagNo()) {
|
||||
case KM_TAG_ATTESTATION_APPLICATION_ID:
|
||||
parseAttestationApplicationId(
|
||||
getOctetsFromAsn1(taggedEntry.getObject()).toByteArray());
|
||||
break;
|
||||
case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
|
||||
this.mKeyRequiresUnlockedDevice = getBoolFromAsn1(taggedEntry.getObject());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ASN1Encodable[] hardwareEnforced = ((ASN1Sequence)
|
||||
keyAttributes.getObjectAt(HW_ENFORCED_INDEX)).toArray();
|
||||
for (ASN1Encodable entry : hardwareEnforced) {
|
||||
ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
|
||||
switch (taggedEntry.getTagNo()) {
|
||||
case KM_TAG_NO_AUTH_REQUIRED:
|
||||
this.mKeyAuthenticatorType = HW_AUTH_NONE;
|
||||
break;
|
||||
case KM_TAG_ALL_APPLICATIONS:
|
||||
this.mKeyAllowedForAllApplications = true;
|
||||
break;
|
||||
case KM_TAG_ROOT_OF_TRUST:
|
||||
ASN1Sequence rootOfTrust = (ASN1Sequence) taggedEntry.getObject();
|
||||
this.mVerifiedBootKey =
|
||||
getOctetsFromAsn1(rootOfTrust.getObjectAt(VERIFIED_BOOT_KEY_INDEX));
|
||||
this.mVerifiedBootLocked =
|
||||
getBoolFromAsn1(rootOfTrust.getObjectAt(VERIFIED_BOOT_LOCKED_INDEX));
|
||||
this.mVerifiedBootState =
|
||||
getVerifiedBootStateEnum(
|
||||
rootOfTrust.getObjectAt(VERIFIED_BOOT_STATE_INDEX));
|
||||
// The verified boot hash was added in structure version 3 (Keymaster 4.0).
|
||||
if (mAttestationVersion >= 3) {
|
||||
this.mVerifiedBootHash =
|
||||
getOctetsFromAsn1(
|
||||
rootOfTrust.getObjectAt(VERIFIED_BOOT_HASH_INDEX));
|
||||
}
|
||||
break;
|
||||
case KM_TAG_OS_VERSION:
|
||||
this.mKeyOsVersion = getIntegerFromAsn1(taggedEntry.getObject());
|
||||
break;
|
||||
case KM_TAG_OS_PATCHLEVEL:
|
||||
this.mKeyOsPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
|
||||
break;
|
||||
case KM_TAG_ATTESTATION_ID_BRAND:
|
||||
this.mDeviceBrand = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
|
||||
break;
|
||||
case KM_TAG_ATTESTATION_ID_DEVICE:
|
||||
this.mDeviceName = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
|
||||
break;
|
||||
case KM_TAG_ATTESTATION_ID_PRODUCT:
|
||||
this.mDeviceProductName = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
|
||||
break;
|
||||
case KM_TAG_VENDOR_PATCHLEVEL:
|
||||
this.mKeyVendorPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
|
||||
break;
|
||||
case KM_TAG_BOOT_PATCHLEVEL:
|
||||
this.mKeyBootPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseAttestationApplicationId(byte [] attestationApplicationId)
|
||||
throws Exception {
|
||||
ASN1Sequence outerSequence = ASN1Sequence.getInstance(
|
||||
new ASN1InputStream(attestationApplicationId).readObject());
|
||||
Map<String, Long> packageNameVersion = new HashMap<>();
|
||||
ASN1Set packageInfoSet = (ASN1Set) outerSequence.getObjectAt(PACKAGE_INFO_SET_INDEX);
|
||||
for (ASN1Encodable packageInfoEntry : packageInfoSet.toArray()) {
|
||||
ASN1Sequence packageInfoSequence = (ASN1Sequence) packageInfoEntry;
|
||||
packageNameVersion.put(
|
||||
getUtf8FromOctetsFromAsn1(
|
||||
packageInfoSequence.getObjectAt(PACKAGE_INFO_NAME_INDEX)),
|
||||
getLongFromAsn1(packageInfoSequence.getObjectAt(PACKAGE_INFO_VERSION_INDEX)));
|
||||
}
|
||||
List<ByteString> certificateDigests = new ArrayList<>();
|
||||
ASN1Set certificateDigestSet =
|
||||
(ASN1Set) outerSequence.getObjectAt(PACKAGE_SIGNATURE_SET_INDEX);
|
||||
for (ASN1Encodable certificateDigestEntry : certificateDigestSet.toArray()) {
|
||||
certificateDigests.add(getOctetsFromAsn1(certificateDigestEntry));
|
||||
}
|
||||
this.mApplicationPackageNameVersion = Collections.unmodifiableMap(packageNameVersion);
|
||||
this.mApplicationCertificateDigests = Collections.unmodifiableList(certificateDigests);
|
||||
|
||||
}
|
||||
|
||||
private VerifiedBootState getVerifiedBootStateEnum(ASN1Encodable asn1) {
|
||||
int verifiedBoot = getEnumFromAsn1(asn1);
|
||||
switch (verifiedBoot) {
|
||||
case KM_VERIFIED_BOOT_STATE_VERIFIED:
|
||||
return VerifiedBootState.VERIFIED;
|
||||
case KM_VERIFIED_BOOT_STATE_SELF_SIGNED:
|
||||
return VerifiedBootState.SELF_SIGNED;
|
||||
case KM_VERIFIED_BOOT_STATE_UNVERIFIED:
|
||||
return VerifiedBootState.UNVERIFIED;
|
||||
case KM_VERIFIED_BOOT_STATE_FAILED:
|
||||
return VerifiedBootState.FAILED;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid verified boot state.");
|
||||
}
|
||||
}
|
||||
|
||||
private SecurityLevel getSecurityLevelEnum(ASN1Encodable asn1) {
|
||||
int securityLevel = getEnumFromAsn1(asn1);
|
||||
switch (securityLevel) {
|
||||
case KM_SECURITY_LEVEL_SOFTWARE:
|
||||
return SecurityLevel.SOFTWARE;
|
||||
case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
|
||||
return SecurityLevel.TRUSTED_ENVIRONMENT;
|
||||
case KM_SECURITY_LEVEL_STRONG_BOX:
|
||||
return SecurityLevel.STRONG_BOX;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid security level.");
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ByteString getOctetsFromAsn1(ASN1Encodable asn1) {
|
||||
return ByteString.copyFrom(((ASN1OctetString) asn1).getOctets());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String getUtf8FromOctetsFromAsn1(ASN1Encodable asn1) {
|
||||
return new String(((ASN1OctetString) asn1).getOctets(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private int getIntegerFromAsn1(ASN1Encodable asn1) {
|
||||
return ((ASN1Integer) asn1).getValue().intValueExact();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private long getLongFromAsn1(ASN1Encodable asn1) {
|
||||
return ((ASN1Integer) asn1).getValue().longValueExact();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private int getEnumFromAsn1(ASN1Encodable asn1) {
|
||||
return ((ASN1Enumerated) asn1).getValue().intValueExact();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Boolean getBoolFromAsn1(ASN1Encodable asn1) {
|
||||
if (asn1 instanceof ASN1Boolean) {
|
||||
return ((ASN1Boolean) asn1).isTrue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.security;
|
||||
|
||||
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;
|
||||
@ -44,9 +45,11 @@ import com.android.server.SystemService;
|
||||
public class AttestationVerificationManagerService extends SystemService {
|
||||
|
||||
private static final String TAG = "AVF";
|
||||
private final AttestationVerificationPeerDeviceVerifier mPeerDeviceVerifier;
|
||||
|
||||
public AttestationVerificationManagerService(final Context context) {
|
||||
public AttestationVerificationManagerService(final Context context) throws Exception {
|
||||
super(context);
|
||||
mPeerDeviceVerifier = new AttestationVerificationPeerDeviceVerifier(context);
|
||||
}
|
||||
|
||||
private final IBinder mService = new IAttestationVerificationManagerService.Stub() {
|
||||
@ -83,7 +86,7 @@ public class AttestationVerificationManagerService extends SystemService {
|
||||
result.token = null;
|
||||
switch (profile.getAttestationProfileId()) {
|
||||
case PROFILE_SELF_TRUSTED:
|
||||
Slog.d(TAG, "Verifying Self trusted profile.");
|
||||
Slog.d(TAG, "Verifying Self Trusted profile.");
|
||||
try {
|
||||
result.resultCode =
|
||||
AttestationVerificationSelfTrustedVerifierForTesting.getInstance()
|
||||
@ -92,6 +95,11 @@ public class AttestationVerificationManagerService extends SystemService {
|
||||
result.resultCode = RESULT_FAILURE;
|
||||
}
|
||||
break;
|
||||
case PROFILE_PEER_DEVICE:
|
||||
Slog.d(TAG, "Verifying Peer Device profile.");
|
||||
result.resultCode = mPeerDeviceVerifier.verifyAttestation(
|
||||
localBindingType, requirements, attestation);
|
||||
break;
|
||||
default:
|
||||
Slog.d(TAG, "No profile found, defaulting.");
|
||||
result.resultCode = RESULT_UNKNOWN;
|
||||
|
@ -0,0 +1,510 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.security;
|
||||
|
||||
import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE;
|
||||
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 com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.cert.CertPath;
|
||||
import java.security.cert.CertPathValidator;
|
||||
import java.security.cert.CertPathValidatorException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.PKIXCertPathChecker;
|
||||
import java.security.cert.PKIXParameters;
|
||||
import java.security.cert.TrustAnchor;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Verifies Android key attestation according to the {@code PROFILE_PEER_DEVICE} profile.
|
||||
*
|
||||
* Trust anchors are vendor-defined via the vendor_required_attestation_certificates.xml resource.
|
||||
* The profile is satisfied by checking all the following:
|
||||
* * TrustAnchor match
|
||||
* * Certificate validity
|
||||
* * Android OS 10 or higher
|
||||
* * Hardware backed key store
|
||||
* * Verified boot locked
|
||||
* * Remote Patch level must be within 1 year of local patch if local patch is less than 1 year old.
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
private final Context mContext;
|
||||
private final Set<TrustAnchor> mTrustAnchors;
|
||||
private final boolean mRevocationEnabled;
|
||||
private final LocalDate mTestSystemDate;
|
||||
private final LocalDate mTestLocalPatchDate;
|
||||
private CertificateFactory mCertificateFactory;
|
||||
private CertPathValidator mCertPathValidator;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
AttestationVerificationPeerDeviceVerifier(@NonNull Context context) throws Exception {
|
||||
mContext = Objects.requireNonNull(context);
|
||||
mCertificateFactory = CertificateFactory.getInstance("X.509");
|
||||
mCertPathValidator = CertPathValidator.getInstance("PKIX");
|
||||
mTrustAnchors = getTrustAnchors();
|
||||
mRevocationEnabled = true;
|
||||
mTestSystemDate = null;
|
||||
mTestLocalPatchDate = null;
|
||||
}
|
||||
|
||||
// Use ONLY for hermetic unit testing.
|
||||
@VisibleForTesting
|
||||
AttestationVerificationPeerDeviceVerifier(@NonNull Context context,
|
||||
Set<TrustAnchor> trustAnchors, boolean revocationEnabled,
|
||||
LocalDate systemDate, LocalDate localPatchDate) throws Exception {
|
||||
mContext = Objects.requireNonNull(context);
|
||||
mCertificateFactory = CertificateFactory.getInstance("X.509");
|
||||
mCertPathValidator = CertPathValidator.getInstance("PKIX");
|
||||
mTrustAnchors = trustAnchors;
|
||||
mRevocationEnabled = revocationEnabled;
|
||||
mTestSystemDate = systemDate;
|
||||
mTestLocalPatchDate = localPatchDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies attestation for public key or challenge local binding.
|
||||
*
|
||||
* The attestations must be suitable for {@link java.security.cert.CertificateFactory}
|
||||
* The certificates in the attestation provided must be DER-encoded and may be supplied in
|
||||
* binary or printable (Base64) encoding. If the certificate is provided in Base64 encoding,
|
||||
* it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and must be bounded at
|
||||
* the end by -----END CERTIFICATE-----.
|
||||
*
|
||||
* @param localBindingType Only {@code TYPE_PUBLIC_KEY} and {@code TYPE_CHALLENGE} supported.
|
||||
* @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported.
|
||||
* @param attestation Certificates should be DER encoded with leaf certificate appended first.
|
||||
*/
|
||||
int verifyAttestation(
|
||||
int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation) {
|
||||
int status = RESULT_FAILURE;
|
||||
|
||||
if (mCertificateFactory == null) {
|
||||
debugVerboseLog("Was unable to initialize CertificateFactory onCreate.");
|
||||
return status;
|
||||
}
|
||||
|
||||
if (mCertPathValidator == null) {
|
||||
debugVerboseLog("Was unable to initialize CertPathValidator onCreate.");
|
||||
return status;
|
||||
}
|
||||
|
||||
List<X509Certificate> certificates;
|
||||
try {
|
||||
certificates = getCertificates(attestation);
|
||||
} catch (CertificateException e) {
|
||||
debugVerboseLog("Unable to parse attestation certificates.", e);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (certificates.isEmpty()) {
|
||||
debugVerboseLog("Attestation contains no certificates.");
|
||||
return status;
|
||||
}
|
||||
|
||||
X509Certificate leafNode = certificates.get(0);
|
||||
if (validateRequirements(localBindingType, requirements)
|
||||
&& validateCertificateChain(certificates)
|
||||
&& checkCertificateAttributes(leafNode, localBindingType, requirements)) {
|
||||
status = RESULT_SUCCESS;
|
||||
} else {
|
||||
status = RESULT_FAILURE;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<X509Certificate> getCertificates(byte[] attestation)
|
||||
throws CertificateException {
|
||||
List<X509Certificate> certificates = new ArrayList<>();
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(attestation);
|
||||
while (bis.available() > 0) {
|
||||
certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis));
|
||||
}
|
||||
|
||||
return certificates;
|
||||
}
|
||||
|
||||
private boolean validateRequirements(int localBindingType, Bundle requirements) {
|
||||
if (requirements.size() != 1) {
|
||||
debugVerboseLog("Requirements does not contain exactly 1 key.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) {
|
||||
debugVerboseLog("Binding type is not supported: " + localBindingType);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (localBindingType == TYPE_PUBLIC_KEY && !requirements.containsKey(PARAM_PUBLIC_KEY)) {
|
||||
debugVerboseLog("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);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validateCertificateChain(List<X509Certificate> certificates) {
|
||||
if (certificates.size() < 2) {
|
||||
debugVerboseLog("Certificate chain less than 2 in size.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
CertPath certificatePath = mCertificateFactory.generateCertPath(certificates);
|
||||
PKIXParameters validationParams = new PKIXParameters(mTrustAnchors);
|
||||
if (mRevocationEnabled) {
|
||||
// Checks Revocation Status List based on
|
||||
// https://developer.android.com/training/articles/security-key-attestation#certificate_status
|
||||
PKIXCertPathChecker checker = new AndroidRevocationStatusListChecker();
|
||||
validationParams.addCertPathChecker(checker);
|
||||
}
|
||||
// Do not use built-in revocation status checker.
|
||||
validationParams.setRevocationEnabled(false);
|
||||
mCertPathValidator.validate(certificatePath, validationParams);
|
||||
} catch (Throwable t) {
|
||||
debugVerboseLog("Invalid certificate chain.", t);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Set<TrustAnchor> getTrustAnchors() throws CertPathValidatorException {
|
||||
Set<TrustAnchor> modifiableSet = new HashSet<>();
|
||||
try {
|
||||
for (String certString: getTrustAnchorResources()) {
|
||||
modifiableSet.add(
|
||||
new TrustAnchor((X509Certificate) mCertificateFactory.generateCertificate(
|
||||
new ByteArrayInputStream(getCertificateBytes(certString))), null));
|
||||
}
|
||||
} catch (CertificateException e) {
|
||||
e.printStackTrace();
|
||||
throw new CertPathValidatorException("Invalid trust anchor certificate.", e);
|
||||
}
|
||||
return Collections.unmodifiableSet(modifiableSet);
|
||||
}
|
||||
|
||||
private byte[] getCertificateBytes(String certString) {
|
||||
String formattedCertString = certString.replaceAll("\\s+", "\n");
|
||||
formattedCertString = formattedCertString.replaceAll(
|
||||
"-BEGIN\\nCERTIFICATE-", "-BEGIN CERTIFICATE-");
|
||||
formattedCertString = formattedCertString.replaceAll(
|
||||
"-END\\nCERTIFICATE-", "-END CERTIFICATE-");
|
||||
return formattedCertString.getBytes(UTF_8);
|
||||
}
|
||||
|
||||
private String[] getTrustAnchorResources() {
|
||||
return mContext.getResources().getStringArray(
|
||||
R.array.vendor_required_attestation_certificates);
|
||||
}
|
||||
|
||||
private boolean checkCertificateAttributes(
|
||||
X509Certificate leafCertificate, int localBindingType, Bundle requirements) {
|
||||
AndroidKeystoreAttestationVerificationAttributes attestationAttributes;
|
||||
try {
|
||||
attestationAttributes =
|
||||
AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
|
||||
leafCertificate);
|
||||
} catch (Throwable t) {
|
||||
debugVerboseLog("Could not get ParsedAttestationAttributes from Certificate.", t);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks for support of Keymaster 4.
|
||||
if (attestationAttributes.getAttestationVersion() < 3) {
|
||||
debugVerboseLog("Attestation version is not at least 3 (Keymaster 4).");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks for support of Keymaster 4.
|
||||
if (attestationAttributes.getKeymasterVersion() < 4) {
|
||||
debugVerboseLog("Keymaster version is not at least 4.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// First two characters are Android OS version.
|
||||
if (attestationAttributes.getKeyOsVersion() < 100000) {
|
||||
debugVerboseLog("Android OS version is not 10+.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!attestationAttributes.isAttestationHardwareBacked()) {
|
||||
debugVerboseLog("Key is not HW backed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!attestationAttributes.isKeymasterHardwareBacked()) {
|
||||
debugVerboseLog("Keymaster is not HW backed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attestationAttributes.getVerifiedBootState() != VERIFIED) {
|
||||
debugVerboseLog("Boot state not Verified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!attestationAttributes.isVerifiedBootLocked()) {
|
||||
debugVerboseLog("Verified boot state is not locked.");
|
||||
return false;
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
debugVerboseLog("VerifiedBootLocked is not set.", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Patch level integer YYYYMM is expected to be within 1 year of today.
|
||||
if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel())) {
|
||||
debugVerboseLog("OS patch level is not within valid range.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Patch level integer YYYYMMDD is expected to be within 1 year of today.
|
||||
if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
|
||||
debugVerboseLog("Boot patch level is not within valid range.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel())) {
|
||||
debugVerboseLog("Vendor patch level is not within valid range.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
|
||||
debugVerboseLog("Boot patch level is not within valid range.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify leaf public key matches provided public key.
|
||||
if (localBindingType == TYPE_PUBLIC_KEY
|
||||
&& !Arrays.equals(requirements.getByteArray(PARAM_PUBLIC_KEY),
|
||||
leafCertificate.getPublicKey().getEncoded())) {
|
||||
debugVerboseLog("Provided public key does not match leaf certificate public key.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify challenge matches provided challenge.
|
||||
if (localBindingType == TYPE_CHALLENGE
|
||||
&& !Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE),
|
||||
attestationAttributes.getAttestationChallenge().toByteArray())) {
|
||||
debugVerboseLog("Provided challenge does not match leaf certificate challenge.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates patchLevel passed is within range of the local device patch date if local patch is
|
||||
* not over one year old. Since the time can be changed on device, just checking the patch date
|
||||
* is not enough. Therefore, we also confirm the patch level for the remote and local device are
|
||||
* similar.
|
||||
*/
|
||||
private boolean isValidPatchLevel(int patchLevel) {
|
||||
LocalDate currentDate = mTestSystemDate != null
|
||||
? mTestSystemDate : LocalDate.now(ZoneId.systemDefault());
|
||||
|
||||
// Convert local patch date to LocalDate.
|
||||
LocalDate localPatchDate;
|
||||
try {
|
||||
if (mTestLocalPatchDate != null) {
|
||||
localPatchDate = mTestLocalPatchDate;
|
||||
} else {
|
||||
localPatchDate = LocalDate.parse(Build.VERSION.SECURITY_PATCH);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
debugVerboseLog("Build.VERSION.SECURITY_PATCH: "
|
||||
+ Build.VERSION.SECURITY_PATCH + " is not in format YYYY-MM-DD");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check local patch date is not in last year of system clock.
|
||||
if (ChronoUnit.MONTHS.between(localPatchDate, currentDate) > MAX_PATCH_AGE_MONTHS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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");
|
||||
return false;
|
||||
}
|
||||
|
||||
int patchYear = Integer.parseInt(remoteDeviceDateStr.substring(0, 4));
|
||||
int patchMonth = Integer.parseInt(remoteDeviceDateStr.substring(4, 6));
|
||||
LocalDate remotePatchDate = LocalDate.of(patchYear, patchMonth, 1);
|
||||
|
||||
// Check patch dates are within 1 year of each other
|
||||
boolean IsRemotePatchWithinOneYearOfLocalPatch;
|
||||
if (remotePatchDate.compareTo(localPatchDate) > 0) {
|
||||
IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between(
|
||||
localPatchDate, remotePatchDate) <= MAX_PATCH_AGE_MONTHS;
|
||||
} else if (remotePatchDate.compareTo(localPatchDate) < 0) {
|
||||
IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between(
|
||||
remotePatchDate, localPatchDate) <= MAX_PATCH_AGE_MONTHS;
|
||||
} else {
|
||||
IsRemotePatchWithinOneYearOfLocalPatch = true;
|
||||
}
|
||||
|
||||
return IsRemotePatchWithinOneYearOfLocalPatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks certificate revocation status.
|
||||
*
|
||||
* Queries status list from android.googleapis.com/attestation/status and checks for
|
||||
* the existence of certificate's serial number. If serial number exists in map, then fail.
|
||||
*/
|
||||
private final class AndroidRevocationStatusListChecker extends PKIXCertPathChecker {
|
||||
private static final String TOP_LEVEL_JSON_PROPERTY_KEY = "entries";
|
||||
private static final String STATUS_PROPERTY_KEY = "status";
|
||||
private static final String REASON_PROPERTY_KEY = "reason";
|
||||
private String mStatusUrl;
|
||||
private JSONObject mJsonStatusMap;
|
||||
|
||||
@Override
|
||||
public void init(boolean forward) throws CertPathValidatorException {
|
||||
mStatusUrl = getRevocationListUrl();
|
||||
if (mStatusUrl == null || mStatusUrl.isEmpty()) {
|
||||
throw new CertPathValidatorException(
|
||||
"R.string.vendor_required_attestation_revocation_list_url is empty.");
|
||||
}
|
||||
// TODO(b/221067843): Update to only pull status map on non critical path and if
|
||||
// out of date (24hrs).
|
||||
mJsonStatusMap = getStatusMap(mStatusUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForwardCheckingSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedExtensions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(Certificate cert, Collection<String> unresolvedCritExts)
|
||||
throws CertPathValidatorException {
|
||||
X509Certificate x509Certificate = (X509Certificate) cert;
|
||||
// The json key is the certificate's serial number converted to lowercase hex.
|
||||
String serialNumber = x509Certificate.getSerialNumber().toString(16);
|
||||
|
||||
if (serialNumber == null) {
|
||||
throw new CertPathValidatorException("Certificate serial number can not be null.");
|
||||
}
|
||||
|
||||
if (mJsonStatusMap.has(serialNumber)) {
|
||||
JSONObject revocationStatus;
|
||||
String status;
|
||||
String reason;
|
||||
try {
|
||||
revocationStatus = mJsonStatusMap.getJSONObject(serialNumber);
|
||||
status = revocationStatus.getString(STATUS_PROPERTY_KEY);
|
||||
reason = revocationStatus.getString(REASON_PROPERTY_KEY);
|
||||
} catch (Throwable t) {
|
||||
throw new CertPathValidatorException("Unable get properties for certificate "
|
||||
+ "with serial number " + serialNumber);
|
||||
}
|
||||
throw new CertPathValidatorException(
|
||||
"Invalid certificate with serial number " + serialNumber
|
||||
+ " has status " + status
|
||||
+ " because reason " + reason);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject getStatusMap(String stringUrl) throws CertPathValidatorException {
|
||||
URL url;
|
||||
try {
|
||||
url = new URL(stringUrl);
|
||||
} catch (Throwable t) {
|
||||
throw new CertPathValidatorException(
|
||||
"Unable to get revocation status from " + mStatusUrl, t);
|
||||
}
|
||||
|
||||
try (InputStream inputStream = url.openStream()) {
|
||||
JSONObject statusListJson = new JSONObject(
|
||||
new String(inputStream.readAllBytes(), UTF_8));
|
||||
return statusListJson.getJSONObject(TOP_LEVEL_JSON_PROPERTY_KEY);
|
||||
} catch (Throwable t) {
|
||||
throw new CertPathValidatorException(
|
||||
"Unable to parse revocation status from " + mStatusUrl, t);
|
||||
}
|
||||
}
|
||||
|
||||
private String getRevocationListUrl() {
|
||||
return mContext.getResources().getString(
|
||||
R.string.vendor_required_attestation_revocation_list_url);
|
||||
}
|
||||
}
|
||||
}
|
@ -40,5 +40,6 @@ android_test {
|
||||
"androidx.test.rules",
|
||||
"androidx.test.ext.junit",
|
||||
"platform-test-annotations",
|
||||
"services.core",
|
||||
],
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner"/>
|
||||
<activity android:name=".SystemAttestationVerificationTest$TestActivity" />
|
||||
<activity android:name=".PeerDeviceSystemAttestationVerificationTest$TestActivity" />
|
||||
</application>
|
||||
|
||||
<!-- self-instrumenting test package. -->
|
||||
|
@ -0,0 +1,81 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICkjCCAjmgAwIBAgIBATAKBggqhkjOPQQDAjA5MQwwCgYDVQQMDANURUUxKTAn
|
||||
BgNVBAUTIDg2ZTQ0MjRhMjY2NDlhZDcyZWZhNWM0MWEwM2IyN2QxMCAXDTcwMDEw
|
||||
MTAwMDAwMFoYDzIxMDYwMjA3MDYyODE1WjAfMR0wGwYDVQQDDBRBbmRyb2lkIEtl
|
||||
eXN0b3JlIEtleTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIlTwcvhe+DLV45X
|
||||
RCTO7HoN20Ib7IbCEhV5+YdMiYOp/0AdKk8oYvsri1XODeC4zcoPfHNdQGt/68i0
|
||||
ADbilJmjggFIMIIBRDAOBgNVHQ8BAf8EBAMCB4AwggEwBgorBgEEAdZ5AgERBIIB
|
||||
IDCCARwCAQMKAQECAQQKAQEECXBsYXllcjQ1NgQAMFe/hT0IAgYBfvkgVei/hUVH
|
||||
BEUwQzEdMBsEFmNvbS5nb29nbGUuYXR0ZXN0YXRpb24CAQExIgQgOqyVXRJUdAGY
|
||||
/XVx8y/uRPiebqlyELt1EpqIz29h5tUwgaehCDEGAgECAgEDogMCAQOjBAICAQCl
|
||||
CDEGAgEEAgEGqgMCAQG/g3cCBQC/hT4DAgEAv4VATDBKBCCEZx8qY8Ys0HC2TqPq
|
||||
74eYPzh5L/agxD7Bn7zVBQHoNAEB/woBAAQguJwoDfWBjRaedzQ6TJPFJJKs+ytr
|
||||
+8Vu2CSmqifFBHW/hUEFAgMB1MC/hUIFAgMDFdm/hU4GAgQBNIjJv4VPBgIEATSI
|
||||
yTAKBggqhkjOPQQDAgNHADBEAiBdGxfMEx59k5+zo+hV3Q9kgjbGi0zU3WH355P5
|
||||
JZttBwIgY4FZsSreUJL8RY3JvfvD8BRw8GuXcB1OQ600hwaYYC4=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB8zCCAXqgAwIBAgIRAOuuukN0OHbNQvKngECkewEwCgYIKoZIzj0EAwIwOTEM
|
||||
MAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA3MDkxMmRmNDYxMDRmYWFlOTQ3ODY0ZTU4
|
||||
MDRmMWY4ZDAeFw0yMDA5MjgyMDI3NTZaFw0zMDA5MjYyMDI3NTZaMDkxDDAKBgNV
|
||||
BAwMA1RFRTEpMCcGA1UEBRMgODZlNDQyNGEyNjY0OWFkNzJlZmE1YzQxYTAzYjI3
|
||||
ZDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT3Mjl05ewv6G8zAR4fXJy2iadU
|
||||
yK7rNvzlECy2+nhEieL8BFXDvo0tx5fYs8qr67j/KvluFBfp2r9s+ckWz3Kzo2Mw
|
||||
YTAdBgNVHQ4EFgQUsVKBzAs1lMXAauQ3mGAJZJqK5tAwHwYDVR0jBBgwFoAUEsQA
|
||||
i8d2oLULSi5Ix4BTGGbvUEkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||
AgQwCgYIKoZIzj0EAwIDZwAwZAIwfFziBCwuM1thLUSUNI61Xx/vnDnNkSv/aX5D
|
||||
yLjxbLlgnFSzIrc+6vf6h6L/+TjYAjAq6h9GKtMn4R0286MoqYqzp/rHn6JD2sqH
|
||||
iM8KZ0oA+Ut242EcmGjAoNfGZGZGddQ=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDkzCCAXugAwIBAgIQNTAX5z3CBac6nD3hQiMDcDANBgkqhkiG9w0BAQsFADAb
|
||||
MRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MB4XDTIwMDkyODIwMjUwMloXDTMw
|
||||
MDkyNjIwMjUwMlowOTEMMAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA3MDkxMmRmNDYx
|
||||
MDRmYWFlOTQ3ODY0ZTU4MDRmMWY4ZDB2MBAGByqGSM49AgEGBSuBBAAiA2IABA/7
|
||||
xZFlFtTjdy2B3p7E+FsrBjyhBSqY4a9FywawXMJRSja3HAK36ruzJjWlEkD+D0vq
|
||||
HI2joY39FHmWoZWwm2cq9gOleFGYOSCpMr4ib7xtq/6nefvKTP5rutxudF97t6Nj
|
||||
MGEwHQYDVR0OBBYEFBLEAIvHdqC1C0ouSMeAUxhm71BJMB8GA1UdIwQYMBaAFDZh
|
||||
4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
|
||||
AgIEMA0GCSqGSIb3DQEBCwUAA4ICAQAaMONDQxJz3PRn9gHQW5KP+TIoBPJZyGa1
|
||||
QFuEBcMDTtIxBxEh5Pj3ivPBc76PrdYu5U47Ve5YYCPsTpUTj7dOxbzGSZjfjvHF
|
||||
fNwy24g1Lah2iAdQRVErhWKBlpnQhBnnRrrNmTTmzhl8NvSExqAPP746dqwm1kQ7
|
||||
YesC5yoEAHpxamhlZpIKAjSxSZeHWace2qV00M8qWd/7lIpqttJjFFrhCjzR0dtr
|
||||
oIIpC5EtmqIWdLeg6yZjJkX+Cjv4F8mRfBtwuNuxFsfALQ3D5l8WKw3iwPebmCy1
|
||||
kEby8Eoq88FxzXQp/XgAaljlrKXyuxptrc1noRuob4g42Oh6wetueYRSCtO6Bkym
|
||||
0UMnld/kG77aeiHOMVVb86wrhNuAGir1vgDGOBsclITVyuu9ka0YVQjjDm3phTpd
|
||||
O8JV16gbei2Phn+FfRV1MSDsZo/wu0i2KVzgs27bfJocMHXv+GzvwfefYgMJ/rYq
|
||||
Bg27lpsWzmFEPv2cyhA5PwwbG8ceswa3RZE/2eS9o7STkz93jr/KsKLcMBY6cX2C
|
||||
q4CBJByKFJtVANOVj+neFNxc2sQgeTT33yYNKbe4b5bm7Ki1FbrhFVckpzUGDnKs
|
||||
gL+AxvALWOoryDGwNbJiW8PRiD3HHByiMvSEQ7e7BSc2KjbsaWbCfYZAMZJEhEsc
|
||||
P1l8lcUVuA==
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
|
||||
BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAz
|
||||
NzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
|
||||
Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
|
||||
tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
|
||||
nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
|
||||
C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
|
||||
oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
|
||||
JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
|
||||
sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
|
||||
igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
|
||||
RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
|
||||
aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
|
||||
AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
|
||||
IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
|
||||
VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnu
|
||||
XKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83U
|
||||
h6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cno
|
||||
L/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2ok
|
||||
QBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vA
|
||||
D32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAI
|
||||
mMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoW
|
||||
Fua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91
|
||||
oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09o
|
||||
jm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUB
|
||||
ZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCH
|
||||
ex0SdDrx+tWUDqG8At2JHA==
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,30 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGCDCCBHCgAwIBAgIBATANBgkqhkiG9w0BAQsFADApMRkwFwYDVQQFExAyZGM1OGIyZDFhMjQx
|
||||
MzI2MQwwCgYDVQQMDANURUUwIBcNNzAwMTAxMDAwMDAwWhgPMjEwNjAyMDcwNjI4MTVaMB8xHTAb
|
||||
BgNVBAMMFEFuZHJvaWQgS2V5c3RvcmUgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEApNVcnyN40MANMbbo2nMGNq2NNysDSjfLm0W3i6wPKf0ffCYkhWM4dCmQKKf50uAZTBeTit4c
|
||||
NwXeZn3qellMlOsIN3Qc384rfN/8cikrRvUAgibz0Jy7STykjwa7x6tKwqITxbO8HqAhKo8/BQXU
|
||||
xzrOdIg5ciy+UM7Vgh7a7ogen0KL2iGgrsalb1ti7Vlzb6vIJ4WzIC3TGD2sCkoPahghwqFDZZCo
|
||||
/FzaLoNY0jAUX2mL+kf8aUaoxz7xA9FTvgara+1pLBR1s4c8xPS2HdZipcVXWfey0wujv1VAKs4+
|
||||
tXjKlHkYBHBBceEjxUtEmrapSQEdpHPv7Xh9Uanq4QIDAQABo4ICwTCCAr0wDgYDVR0PAQH/BAQD
|
||||
AgeAMIICqQYKKwYBBAHWeQIBEQSCApkwggKVAgEDCgEBAgEECgEBBANhYmMEADCCAc2/hT0IAgYB
|
||||
ZOYGEYe/hUWCAbsEggG3MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXlj
|
||||
aGFpbgIBHTAZBBRjb20uYW5kcm9pZC5zZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNl
|
||||
cwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZp
|
||||
Y2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNwb3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxv
|
||||
Y2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci50ZWxlY29tAgEdMCAEG2NvbS5h
|
||||
bmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNTUmVzdGFydERldGVjdG9y
|
||||
AgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS5hbmRyb2lkLnBy
|
||||
b3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcXb9hmqjCB
|
||||
rqEIMQYCAQICAQOiAwIBAaMEAgIIAKUFMQMCAQSmCDEGAgEDAgEFv4FIBQIDAQABv4N3AgUAv4U+
|
||||
AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIHKNsSdP
|
||||
HxzxVx3kOAsEilVKxKOA529TVQg1KQhKk3gBv4VBAwIBAL+FQgUCAwMUs7+FTgUCAwMUs7+FTwUC
|
||||
AwMUszANBgkqhkiG9w0BAQsFAAOCAYEAJMIuzdNUdfrE6sIdmsnMn/scSG2odbphj8FkX9JGdF2S
|
||||
OT599HuDY9qhvkru2Dza4sLKK3f4ViBhuR9lpfeprKvstxbtBO7jkLYfVn0ZRzHRHVEyiW5IVKh+
|
||||
qOXVJ9S1lMShOTlsaYJytLKIlcrRAZBEXZiNbzTuVh1CH6X9Ni1dog14snm+lcOeORdL9fht2CHa
|
||||
u/caRnpWiZbjoAoJp0O89uBrRkXPpln51+3jPY6AFny30grNAvKguauDcPPhNV1yR+ylSsQi2gm3
|
||||
Rs4pgtlxFLMfZLgT0cbkl+9zk/QUqlpBP8ftUBsOI0ARr8xhFN3cvq9kXGLtJ9hEP9PRaflAFREk
|
||||
DK3IBIbVcAFZBFoAQOdE9zy0+F5bQrznPGaZg4Dzhcx33qMDUTgHtWoy+k3ePGQMEtmoTTLgQywW
|
||||
OIkXEoFqqGi9GKJXUT1KYi5NsigaYqu7FoN4Qsvs61pMUEfZSPP2AFwkA8uNFbmb9uxcxaGHCA8i
|
||||
3i9VM6yOLIrP
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,33 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFoDCCA4igAwIBAgIQTfpKgAsLZJhp2V4xUriMADANBgkqhkiG9w0BAQ0FADBp
|
||||
MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLR29vZ2xlIEluYy4xFzAVBgNVBAsMDkFu
|
||||
ZHJvaWQgVGhpbmdzMSswKQYDVQQDDCJBbmRyb2lkIFRoaW5ncyBBdHRlc3RhdGlv
|
||||
biBSb290IENBMCAXDTE3MDYyMTIwMjQzN1oYDzIwNTcwNjExMjAyNDM3WjBpMQsw
|
||||
CQYDVQQGEwJVUzEUMBIGA1UECgwLR29vZ2xlIEluYy4xFzAVBgNVBAsMDkFuZHJv
|
||||
aWQgVGhpbmdzMSswKQYDVQQDDCJBbmRyb2lkIFRoaW5ncyBBdHRlc3RhdGlvbiBS
|
||||
b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuO82oerGivb9
|
||||
G9bWyM8Pg0y6SOnAC8/8b92dp1v4Npnc+QpjPRUKgn8lzjQ9Jo6IGY3OShRBiQYl
|
||||
bbZYkfJnC5HtqbOETdPLZclErVE/G6Oda1IeZWvQVMjNImEYOLL5ct2RxiPttd8v
|
||||
SLyOSNFPf5/SeFqX/La0NcmXMOvPSrTW3qO34brnC+ih7mlpJFLz6Up93N3Umxsl
|
||||
IElz2wCG72t6k3+caWLyIPVgIPmsQrfTeBK/hN5dAJgAN65BsTevLHRP9J610wj3
|
||||
RagSIK1NdTuJRnr5ZyTQrwE2nA8H3IJ7/eo6IlGhXPwLKDhbdxYygPxdlCq6Rl96
|
||||
aVLjfpqDPtJ9ow+QKZuEDbYJ4z4olNXC6O5G7vqnCuULA/2E7y7DZObjzXOrdx2z
|
||||
9YKd8BrIDMTN/5mmw2us8tywiaQhbl8vOtjU+A+iBBnkj/wt9TYyLKopdrDlo5mz
|
||||
wy5l750HOkVZXC3VkeECnp+9duSHdS4qeUf/W1j9nPM7kY0HFLPUVX9AFVp2JXnC
|
||||
iKZC32GQAVsDc1iyAZWAVTqA7E0fBHhk9jUnA0W9b5Lq06oW95ngNR1MIFY871i8
|
||||
aLHCBpIix8DuMe8NB9spCIP6WCQqGiWQQpzbeuBPtoi424xwZTO4oectTd77bs9V
|
||||
Rvunn49fz308KnoWjk/by1N7gWyTb38CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
|
||||
/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMDQ1I0RKwFCI+Fy9uIIJ/HrXuqu
|
||||
MA0GCSqGSIb3DQEBDQUAA4ICAQB09qkyEpEDocdN5pPeXqtjj9d0AXREUGH2LhnC
|
||||
z9KZcUFR+JskwEMHCaOENOmKI3zWRmxT7d8cVywwGk+ExE7EBQoeHlh3Yo44M8aj
|
||||
ZL7RHCvHRYsePhAJkYpJ02IMR60TV+1jhMqE8+BnqFivS7kft4t295EyrnLRZE3b
|
||||
Nfc0t011j02RwUrioR57mdvS9EZBRnMQkobhn+jWt9O+V3mtplW+1A2n4ec6uni1
|
||||
2MMgAWHuO1sKVYd5Sp4JMUpNnfmQAMnNiOMF6VxkpaoF1lZWo4TrLxuDKJG3O8h1
|
||||
fByjCpNVY8kOvvYEadbldzh6Agy/3ppb9yfG7X7FtHr1ghNjuNT6w5VgvbRtoRja
|
||||
/ZSKuJMaKm5emMWNkls/cwVSPJIvTOzPTeYK1BKSyAL2LDJ93HI7x8h79/Q7gKRi
|
||||
kL8qT7GW2FqpWTK0253sJHqCJJP4A5Rxtf2+Afwqadfc6Ga4jJHb7rPXngz4j1ZB
|
||||
gl5yjXgWF9wHGxqrjKWe2EA3d47BC4HG3Rf5L56KQiRPhTqTk5vtZwtwLRLFDLt7
|
||||
Hdff13O1oLhn+2z9xkASUL3rFE/qWajZP7fk3CvzcuXwKDTZomIC4nNaglx4nLdj
|
||||
lHhOq+6ON8MZC46sLStD+D4a9A1HOoihJgI/yGGkwdrp4KQIveRkEBO/x9v3NNBE
|
||||
bMwG9w==
|
||||
-----END CERTIFICATE-----
|
61
tests/AttestationVerificationTest/assets/test_root_certs.pem
Normal file
61
tests/AttestationVerificationTest/assets/test_root_certs.pem
Normal file
@ -0,0 +1,61 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
|
||||
BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYy
|
||||
ODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
|
||||
Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
|
||||
tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
|
||||
nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
|
||||
C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
|
||||
oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
|
||||
JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
|
||||
sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
|
||||
igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
|
||||
RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
|
||||
aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
|
||||
AGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYD
|
||||
VR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAO
|
||||
BgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lk
|
||||
Lmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQAD
|
||||
ggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB
|
||||
Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00m
|
||||
qC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rY
|
||||
DBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPm
|
||||
QUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4u
|
||||
JU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyD
|
||||
CdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79Iy
|
||||
ZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxD
|
||||
qwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23Uaic
|
||||
MDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1
|
||||
wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
|
||||
BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAz
|
||||
NzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
|
||||
Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
|
||||
tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
|
||||
nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
|
||||
C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
|
||||
oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
|
||||
JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
|
||||
sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
|
||||
igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
|
||||
RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
|
||||
aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
|
||||
AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
|
||||
IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
|
||||
VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnu
|
||||
XKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83U
|
||||
h6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cno
|
||||
L/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2ok
|
||||
QBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vA
|
||||
D32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAI
|
||||
mMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoW
|
||||
Fua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91
|
||||
oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09o
|
||||
jm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUB
|
||||
ZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCH
|
||||
ex0SdDrx+tWUDqG8At2JHA==
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,50 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC7DCCApGgAwIBAgIBATAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCVVMxEzAR
|
||||
BgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UE
|
||||
CwwHQW5kcm9pZDE7MDkGA1UEAwwyQW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBB
|
||||
dHRlc3RhdGlvbiBJbnRlcm1lZGlhdGUwHhcNNzAwMTAxMDAwMDAwWhcNNjkxMjMx
|
||||
MjM1OTU5WjAfMR0wGwYDVQQDDBRBbmRyb2lkIEtleXN0b3JlIEtleTBZMBMGByqG
|
||||
SM49AgEGCCqGSM49AwEHA0IABEYtCH28qu+St0F0TixVsQz0L/Y7DcRHgYAU98E6
|
||||
edwOpACFmmseYxMjvmZv/4jURSG2/Z0J1s3A/qFzIY96/tyjggFSMIIBTjALBgNV
|
||||
HQ8EBAMCB4AwggEcBgorBgEEAdZ5AgERBIIBDDCCAQgCAQQKAQACASkKAQAECXBs
|
||||
YXllcjQ1NgQAMIHqoQgxBgIBAgIBA6IDAgEDowQCAgEApQgxBgIBBAIBBqoDAgEB
|
||||
v4N3AgUAv4U9CAIGAX8DoY9Qv4U+AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAv4VBBQIDAa2wv4VCBQIDAxUbv4VFRwRFMEMxHTAbBBZjb20uZ29v
|
||||
Z2xlLmF0dGVzdGF0aW9uAgEBMSIEIDqslV0SVHQBmP11cfMv7kT4nm6pchC7dRKa
|
||||
iM9vYebVMAAwHwYDVR0jBBgwFoAUP/ys1hqxOp6BILjVJRzFZbsekakwCgYIKoZI
|
||||
zj0EAwIDSQAwRgIhAMzs7gWWBIITpeLeEEx9B8ihdhkFqpMGlsYLRO01ZIOeAiEA
|
||||
uKs9xfK3fIOpVAhDmsrp+zE8KUwyvqCU/IS13tXz7Ng=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
|
||||
EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
|
||||
VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
|
||||
ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
|
||||
MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
|
||||
CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
|
||||
bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
|
||||
dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
|
||||
efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
|
||||
U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
|
||||
qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
|
||||
AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
|
||||
wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
|
||||
Xvsiu+f+uXc/WT/7
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
|
||||
EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
|
||||
dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
|
||||
VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
|
||||
HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
|
||||
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
|
||||
BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
|
||||
QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
|
||||
KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
|
||||
dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
|
||||
BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
|
||||
EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
|
||||
SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
|
||||
C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,161 @@
|
||||
package android.security.attestationverification
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
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
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/** Test for system-defined attestation verifiers. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PeerDeviceSystemAttestationVerificationTest {
|
||||
|
||||
@get:Rule
|
||||
val rule = ActivityScenarioRule(TestActivity::class.java)
|
||||
|
||||
private val certifcateFactory = CertificateFactory.getInstance("X.509")
|
||||
private lateinit var activity: Activity
|
||||
private lateinit var avm: AttestationVerificationManager
|
||||
private lateinit var invalidAttestationByteArray: ByteArray
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
rule.getScenario().onActivity {
|
||||
avm = it.getSystemService(AttestationVerificationManager::class.java)
|
||||
activity = it
|
||||
}
|
||||
invalidAttestationByteArray = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToByteArray()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailureWrongBindingType() {
|
||||
val future = CompletableFuture<Int>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
avm.verifyAttestation(profile, TYPE_UNKNOWN, Bundle(),
|
||||
invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
|
||||
future.complete(result)
|
||||
}
|
||||
|
||||
assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailureEmptyRequirements() {
|
||||
val future = CompletableFuture<Int>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(),
|
||||
invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
|
||||
future.complete(result)
|
||||
}
|
||||
|
||||
assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailureMismatchBindingType() {
|
||||
val future = CompletableFuture<Int>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
val publicKeyRequirements = Bundle()
|
||||
publicKeyRequirements.putByteArray(PARAM_PUBLIC_KEY, "publicKeyStr".encodeToByteArray())
|
||||
avm.verifyAttestation(profile, TYPE_CHALLENGE, publicKeyRequirements,
|
||||
invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
|
||||
future.complete(result)
|
||||
}
|
||||
|
||||
assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
|
||||
|
||||
val future2 = CompletableFuture<Int>()
|
||||
val challengeRequirements = Bundle()
|
||||
challengeRequirements.putByteArray(PARAM_CHALLENGE, "challengeStr".encodeToByteArray())
|
||||
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, challengeRequirements,
|
||||
invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
|
||||
future2.complete(result)
|
||||
}
|
||||
|
||||
assertThat(future2.getSoon()).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailureWrongResourceKey() {
|
||||
val future = CompletableFuture<Int>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
val wrongKeyRequirements = Bundle()
|
||||
wrongKeyRequirements.putByteArray("wrongReqKey", "publicKeyStr".encodeToByteArray())
|
||||
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, wrongKeyRequirements,
|
||||
invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
|
||||
future.complete(result)
|
||||
}
|
||||
|
||||
assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailureEmptyAttestation() {
|
||||
val future = CompletableFuture<Int>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
val requirements = Bundle()
|
||||
requirements.putByteArray(PARAM_PUBLIC_KEY, "publicKeyStr".encodeToByteArray())
|
||||
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, requirements, ByteArray(0),
|
||||
activity.mainExecutor) { result, _ ->
|
||||
future.complete(result)
|
||||
}
|
||||
|
||||
assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailureTrustAnchorMismatch() {
|
||||
val future = CompletableFuture<Int>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
val challengeRequirements = Bundle()
|
||||
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
|
||||
avm.verifyAttestation(profile, TYPE_CHALLENGE, challengeRequirements,
|
||||
invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
|
||||
future.complete(result)
|
||||
}
|
||||
assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
private fun <T> CompletableFuture<T>.getSoon(): T {
|
||||
return this.get(1, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
private fun String.fromPEMFileToByteArray(): ByteArray {
|
||||
val certs = certifcateFactory.generateCertificates(
|
||||
InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
|
||||
.open(this))
|
||||
val bos = ByteArrayOutputStream()
|
||||
certs.forEach {
|
||||
bos.write(it.encoded)
|
||||
}
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
class TestActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
|
||||
}
|
||||
}
|
@ -12,8 +12,8 @@ import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
|
||||
import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
|
||||
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
|
||||
@ -52,7 +52,7 @@ class SystemAttestationVerificationTest {
|
||||
@Test
|
||||
fun verifyAttestation_returnsUnknown() {
|
||||
val future = CompletableFuture<Int>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
val profile = AttestationProfile(PROFILE_UNKNOWN)
|
||||
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
|
||||
activity.mainExecutor) { result, _ ->
|
||||
future.complete(result)
|
||||
@ -137,7 +137,7 @@ class SystemAttestationVerificationTest {
|
||||
@Test
|
||||
fun verifyToken_returnsUnknown() {
|
||||
val future = CompletableFuture<Int>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
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)
|
||||
@ -150,7 +150,7 @@ class SystemAttestationVerificationTest {
|
||||
@Test
|
||||
fun verifyToken_tooBigMaxAgeThrows() {
|
||||
val future = CompletableFuture<VerificationToken>()
|
||||
val profile = AttestationProfile(PROFILE_PEER_DEVICE)
|
||||
val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
|
||||
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
|
||||
activity.mainExecutor) { _, token ->
|
||||
future.complete(token)
|
||||
|
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.security;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/** Test for data class holding parsed X509Certificate attestation attributes. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AndroidKeystoreAttestationVerificationAttributesTest {
|
||||
@Rule public ExpectedException mException = ExpectedException.none();
|
||||
private static final String TEST_PHYSCIAL_DEVICE_CERTS =
|
||||
"test_attestation_wrong_root_certs.pem";
|
||||
private static final String TEST_PHYSICAL_DEVICE_CERTS_2 =
|
||||
"test_attestation_with_root_certs.pem";
|
||||
private static final String TEST_VIRTUAL_DEVICE_CERTS =
|
||||
"test_virtual_device_attestation_certs.pem";
|
||||
private static final String TEST_CERT_NO_ATTESTATION_EXTENSION =
|
||||
"test_no_attestation_ext_certs.pem";
|
||||
private static final String TEST_CERTS_NO_ATTESTATION_EXTENSION_2 =
|
||||
"test_root_certs.pem";
|
||||
|
||||
|
||||
private CertificateFactory mFactory;
|
||||
private AndroidKeystoreAttestationVerificationAttributes mPhysicalDeviceAttributes;
|
||||
private AndroidKeystoreAttestationVerificationAttributes mPhysicalDeviceAttributes2;
|
||||
private AndroidKeystoreAttestationVerificationAttributes mVirtualDeviceAttributes;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mFactory = CertificateFactory.getInstance("X.509");
|
||||
mPhysicalDeviceAttributes =
|
||||
AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
|
||||
generateCertificate(TEST_PHYSCIAL_DEVICE_CERTS));
|
||||
mPhysicalDeviceAttributes2 =
|
||||
AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
|
||||
generateCertificates(TEST_PHYSICAL_DEVICE_CERTS_2).get(0));
|
||||
mVirtualDeviceAttributes =
|
||||
AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
|
||||
generateCertificates(TEST_VIRTUAL_DEVICE_CERTS).get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_noAttestationExtension() throws Exception {
|
||||
List<X509Certificate> certsNoAttestation =
|
||||
generateCertificates(TEST_CERTS_NO_ATTESTATION_EXTENSION_2);
|
||||
certsNoAttestation.add(generateCertificate(TEST_CERT_NO_ATTESTATION_EXTENSION));
|
||||
for (X509Certificate cert: certsNoAttestation) {
|
||||
mException.expect(CertificateEncodingException.class);
|
||||
mException.expectMessage(
|
||||
CoreMatchers.containsString("No attestation extension found in certificate."));
|
||||
|
||||
AndroidKeystoreAttestationVerificationAttributes.fromCertificate(cert);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_attestationLevel() {
|
||||
assertThat(mPhysicalDeviceAttributes.getAttestationVersion()).isEqualTo(3);
|
||||
assertThat(mPhysicalDeviceAttributes2.getAttestationVersion()).isEqualTo(3);
|
||||
assertThat(mVirtualDeviceAttributes.getAttestationVersion()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_attestationSecurityLevel() {
|
||||
assertThat(mPhysicalDeviceAttributes.getAttestationSecurityLevel()).isEqualTo(
|
||||
AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
|
||||
assertThat(mPhysicalDeviceAttributes2.getAttestationSecurityLevel()).isEqualTo(
|
||||
AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
|
||||
assertThat(mVirtualDeviceAttributes.getAttestationSecurityLevel()).isEqualTo(
|
||||
AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.SOFTWARE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_isAttestationHardwareBacked() {
|
||||
assertThat(mPhysicalDeviceAttributes.isAttestationHardwareBacked()).isTrue();
|
||||
assertThat(mPhysicalDeviceAttributes2.isAttestationHardwareBacked()).isTrue();
|
||||
assertThat(mVirtualDeviceAttributes.isAttestationHardwareBacked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keymasterLevel() {
|
||||
assertThat(mPhysicalDeviceAttributes.getKeymasterVersion()).isEqualTo(4);
|
||||
assertThat(mPhysicalDeviceAttributes2.getKeymasterVersion()).isEqualTo(4);
|
||||
assertThat(mVirtualDeviceAttributes.getKeymasterVersion()).isEqualTo(41);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keymasterSecurityLevel() {
|
||||
assertThat(mPhysicalDeviceAttributes.getKeymasterSecurityLevel()).isEqualTo(
|
||||
AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
|
||||
assertThat(mPhysicalDeviceAttributes2.getKeymasterSecurityLevel()).isEqualTo(
|
||||
AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
|
||||
assertThat(mVirtualDeviceAttributes.getKeymasterSecurityLevel()).isEqualTo(
|
||||
AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.SOFTWARE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_isKeymasterHardwareBacked() {
|
||||
assertThat(mPhysicalDeviceAttributes.isKeymasterHardwareBacked()).isTrue();
|
||||
assertThat(mPhysicalDeviceAttributes2.isKeymasterHardwareBacked()).isTrue();
|
||||
assertThat(mVirtualDeviceAttributes.isKeymasterHardwareBacked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_attestationChallenge() {
|
||||
assertThat(mPhysicalDeviceAttributes.getAttestationChallenge().toByteArray()).isEqualTo(
|
||||
"abc".getBytes(UTF_8));
|
||||
assertThat(mPhysicalDeviceAttributes2.getAttestationChallenge().toByteArray()).isEqualTo(
|
||||
"player456".getBytes(UTF_8));
|
||||
assertThat(mVirtualDeviceAttributes.getAttestationChallenge().toByteArray()).isEqualTo(
|
||||
"player456".getBytes(UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_verifiedBootState() {
|
||||
assertThat(mPhysicalDeviceAttributes.getVerifiedBootState()).isEqualTo(
|
||||
AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.UNVERIFIED);
|
||||
assertThat(mPhysicalDeviceAttributes2.getVerifiedBootState()).isEqualTo(
|
||||
AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED);
|
||||
assertThat(mVirtualDeviceAttributes.getVerifiedBootState()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyBootPatchLevel() {
|
||||
assertThat(mPhysicalDeviceAttributes.getKeyBootPatchLevel()).isEqualTo(201907);
|
||||
assertThat(mPhysicalDeviceAttributes2.getKeyBootPatchLevel()).isEqualTo(20220105);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyBootPatchLevelNotSetException() {
|
||||
mException.expect(IllegalStateException.class);
|
||||
mException.expectMessage(
|
||||
CoreMatchers.containsString("KeyBootPatchLevel is not set."));
|
||||
|
||||
mVirtualDeviceAttributes.getKeyBootPatchLevel();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyOsPatchLevel() {
|
||||
assertThat(mPhysicalDeviceAttributes.getKeyOsPatchLevel()).isEqualTo(201907);
|
||||
assertThat(mPhysicalDeviceAttributes2.getKeyOsPatchLevel()).isEqualTo(202201);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyOsPatchLevelNotSetException() {
|
||||
mException.expect(IllegalStateException.class);
|
||||
mException.expectMessage(
|
||||
CoreMatchers.containsString("KeyOsPatchLevel is not set."));
|
||||
|
||||
mVirtualDeviceAttributes.getKeyOsPatchLevel();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyVendorPatchLevel() {
|
||||
assertThat(mPhysicalDeviceAttributes.getKeyVendorPatchLevel()).isEqualTo(201907);
|
||||
assertThat(mPhysicalDeviceAttributes2.getKeyVendorPatchLevel()).isEqualTo(20220105);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyVendorPatchLevelNotSetException() {
|
||||
mException.expect(IllegalStateException.class);
|
||||
mException.expectMessage(
|
||||
CoreMatchers.containsString("KeyVendorPatchLevel is not set."));
|
||||
|
||||
mVirtualDeviceAttributes.getKeyVendorPatchLevel();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyAuthenticatorType() {
|
||||
assertThat(mPhysicalDeviceAttributes.getKeyAuthenticatorType()).isEqualTo(0);
|
||||
assertThat(mPhysicalDeviceAttributes2.getKeyAuthenticatorType()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyOsVersion() {
|
||||
assertThat(mPhysicalDeviceAttributes.getKeyOsVersion()).isEqualTo(0);
|
||||
assertThat(mPhysicalDeviceAttributes2.getKeyOsVersion()).isEqualTo(120000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyOsVersionNotSetException() {
|
||||
mException.expect(IllegalStateException.class);
|
||||
mException.expectMessage(
|
||||
CoreMatchers.containsString("KeyOsVersion is not set."));
|
||||
|
||||
mVirtualDeviceAttributes.getKeyOsVersion();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_verifiedBootHash() {
|
||||
assertThat(mPhysicalDeviceAttributes.getVerifiedBootHash()).isNotEmpty();
|
||||
assertThat(mPhysicalDeviceAttributes2.getVerifiedBootHash()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_verifiedBootKey() {
|
||||
assertThat(mPhysicalDeviceAttributes.getVerifiedBootKey()).isNotEmpty();
|
||||
assertThat(mPhysicalDeviceAttributes2.getVerifiedBootKey()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_isVerifiedBootLocked() {
|
||||
assertThat(mPhysicalDeviceAttributes.isVerifiedBootLocked()).isFalse();
|
||||
assertThat(mPhysicalDeviceAttributes2.isVerifiedBootLocked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_isVerifiedBootLockedNotSetException() {
|
||||
mException.expect(IllegalStateException.class);
|
||||
mException.expectMessage(
|
||||
CoreMatchers.containsString("VerifiedBootLocked is not set."));
|
||||
|
||||
mVirtualDeviceAttributes.isVerifiedBootLocked();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_applicationPackageNameVersion() {
|
||||
assertThat(mPhysicalDeviceAttributes.getApplicationPackageNameVersion()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_applicationCertificateDigests() {
|
||||
assertThat(mPhysicalDeviceAttributes.getApplicationCertificateDigests()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_valuesNotSet() {
|
||||
assertThat(mPhysicalDeviceAttributes.getDeviceBrand()).isNull();
|
||||
assertThat(mPhysicalDeviceAttributes.getDeviceName()).isNull();
|
||||
assertThat(mPhysicalDeviceAttributes.getDeviceProductName()).isNull();
|
||||
assertThat(mPhysicalDeviceAttributes.isKeyAllowedForAllApplications()).isFalse();
|
||||
assertThat(mPhysicalDeviceAttributes2.getDeviceBrand()).isNull();
|
||||
assertThat(mPhysicalDeviceAttributes2.getDeviceName()).isNull();
|
||||
assertThat(mPhysicalDeviceAttributes2.getDeviceProductName()).isNull();
|
||||
assertThat(mPhysicalDeviceAttributes2.isKeyAllowedForAllApplications()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCertificate_keyRequiresUnlockedDeviceNotSetException() {
|
||||
mException.expect(IllegalStateException.class);
|
||||
mException.expectMessage(
|
||||
CoreMatchers.containsString("KeyRequiresUnlockedDevice is not set."));
|
||||
|
||||
mPhysicalDeviceAttributes.isKeyRequiresUnlockedDevice();
|
||||
}
|
||||
|
||||
private X509Certificate generateCertificate(String certificateString)
|
||||
throws Exception {
|
||||
return generateCertificates(certificateString).get(0);
|
||||
}
|
||||
|
||||
private List<X509Certificate> generateCertificates(String certificateString)
|
||||
throws Exception {
|
||||
Collection<? extends Certificate> certificates = mFactory.generateCertificates(
|
||||
InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
|
||||
.open(certificateString));
|
||||
|
||||
ArrayList<X509Certificate> x509Certs = new ArrayList<>();
|
||||
for (Certificate cert : certificates) {
|
||||
x509Certs.add((X509Certificate) cert);
|
||||
}
|
||||
return x509Certs;
|
||||
}
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
package com.android.server.security
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
|
||||
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 androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.cert.TrustAnchor
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.LocalDate
|
||||
|
||||
/** Test for Peer Device attestation verifier. */
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AttestationVerificationPeerDeviceVerifierTest {
|
||||
private val certificateFactory = CertificateFactory.getInstance("X.509")
|
||||
@Mock private lateinit var context: Context
|
||||
private lateinit var trustAnchors: HashSet<TrustAnchor>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
val rootCerts = TEST_ROOT_CERT_FILENAME.fromPEMFileToCerts()
|
||||
trustAnchors = HashSet<TrustAnchor>()
|
||||
rootCerts.forEach {
|
||||
trustAnchors.add(TrustAnchor(it as X509Certificate, null))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsSuccessTypeChallenge() {
|
||||
val verifier = AttestationVerificationPeerDeviceVerifier(
|
||||
context, trustAnchors, false, LocalDate.of(2022, 2, 1),
|
||||
LocalDate.of(2021, 8, 1))
|
||||
val challengeRequirements = Bundle()
|
||||
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
|
||||
|
||||
val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
|
||||
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
|
||||
assertThat(result).isEqualTo(RESULT_SUCCESS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() {
|
||||
val verifier = AttestationVerificationPeerDeviceVerifier(
|
||||
context, trustAnchors, false, LocalDate.of(2022, 2, 1),
|
||||
LocalDate.of(2021, 1, 1))
|
||||
val challengeRequirements = Bundle()
|
||||
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
|
||||
|
||||
val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
|
||||
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
|
||||
assertThat(result).isEqualTo(RESULT_SUCCESS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsSuccessTypePublicKey() {
|
||||
val verifier = AttestationVerificationPeerDeviceVerifier(
|
||||
context, trustAnchors, false, LocalDate.of(2022, 2, 1),
|
||||
LocalDate.of(2021, 8, 1))
|
||||
|
||||
val leafCert =
|
||||
(TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0]
|
||||
as X509Certificate
|
||||
val pkRequirements = Bundle()
|
||||
pkRequirements.putByteArray(PARAM_PUBLIC_KEY, leafCert.publicKey.encoded)
|
||||
|
||||
val result = verifier.verifyAttestation(
|
||||
TYPE_PUBLIC_KEY, pkRequirements,
|
||||
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
|
||||
assertThat(result).isEqualTo(RESULT_SUCCESS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() {
|
||||
val verifier = AttestationVerificationPeerDeviceVerifier(
|
||||
context, trustAnchors, false, LocalDate.of(2023, 3, 1),
|
||||
LocalDate.of(2023, 2, 1))
|
||||
val challengeRequirements = Bundle()
|
||||
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
|
||||
|
||||
val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
|
||||
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
|
||||
assertThat(result).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailureTrustedAnchorEmpty() {
|
||||
val verifier = AttestationVerificationPeerDeviceVerifier(
|
||||
context, HashSet(), false, LocalDate.of(2022, 1, 1),
|
||||
LocalDate.of(2022, 1, 1))
|
||||
val challengeRequirements = Bundle()
|
||||
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
|
||||
|
||||
val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
|
||||
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
|
||||
assertThat(result).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAttestation_returnsFailureTrustedAnchorMismatch() {
|
||||
val badTrustAnchorsCerts = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToCerts()
|
||||
val badTrustAnchors = HashSet<TrustAnchor>()
|
||||
badTrustAnchorsCerts.forEach {
|
||||
badTrustAnchors.add(TrustAnchor(it as X509Certificate, null))
|
||||
}
|
||||
|
||||
val verifier = AttestationVerificationPeerDeviceVerifier(
|
||||
context, badTrustAnchors, false, LocalDate.of(2022, 1, 1),
|
||||
LocalDate.of(2022, 1, 1))
|
||||
val challengeRequirements = Bundle()
|
||||
challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
|
||||
|
||||
val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
|
||||
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
|
||||
assertThat(result).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
fun verifyAttestation_returnsFailureChallenge() {
|
||||
val verifier = AttestationVerificationPeerDeviceVerifier(
|
||||
context, trustAnchors, false, LocalDate.of(2022, 1, 1),
|
||||
LocalDate.of(2022, 1, 1))
|
||||
val challengeRequirements = Bundle()
|
||||
challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
|
||||
|
||||
val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
|
||||
TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
|
||||
assertThat(result).isEqualTo(RESULT_FAILURE)
|
||||
}
|
||||
|
||||
private fun String.fromPEMFileToCerts(): Collection<Certificate> {
|
||||
return certificateFactory.generateCertificates(
|
||||
InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
|
||||
.open(this))
|
||||
}
|
||||
|
||||
private fun String.fromPEMFileToByteArray(): ByteArray {
|
||||
val certs = this.fromPEMFileToCerts()
|
||||
val bos = ByteArrayOutputStream()
|
||||
certs.forEach {
|
||||
bos.write(it.encoded)
|
||||
}
|
||||
return bos.toByteArray()
|
||||
}
|
||||
|
||||
class TestActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem"
|
||||
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"
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user