summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2020-11-19 15:52:21 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-11-19 15:52:21 +0000
commitd47c62b62af53ddbd1f70e520162d843e0e43356 (patch)
treecd5bb3f70d531146fb40ccae169bce34694d0e0e
parentf344363e64879d066eb29daa868ee5763ec149a9 (diff)
parent34abaaefcbdd4d7e43abdb11f1fd2c5e54a05dde (diff)
downloadplatform_hardware_interfaces-d47c62b62af53ddbd1f70e520162d843e0e43356.tar.gz
platform_hardware_interfaces-d47c62b62af53ddbd1f70e520162d843e0e43356.tar.bz2
platform_hardware_interfaces-d47c62b62af53ddbd1f70e520162d843e0e43356.zip
Merge "identity: Fix attestation and documentation problems."
-rw-r--r--identity/aidl/android/hardware/identity/IIdentityCredential.aidl19
-rw-r--r--identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl39
-rw-r--r--identity/aidl/default/IdentityCredential.cpp46
-rw-r--r--identity/aidl/default/IdentityCredential.h1
-rw-r--r--identity/aidl/default/WritableIdentityCredential.cpp2
-rw-r--r--identity/aidl/vts/Android.bp4
-rw-r--r--identity/aidl/vts/VtsAttestationParserSupport.cpp187
-rw-r--r--identity/aidl/vts/VtsAttestationParserSupport.h122
-rw-r--r--identity/aidl/vts/VtsAttestationTests.cpp46
-rw-r--r--identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp84
-rw-r--r--identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp85
-rw-r--r--identity/aidl/vts/VtsIdentityTestUtils.cpp308
-rw-r--r--identity/aidl/vts/VtsIdentityTestUtils.h16
-rw-r--r--identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h47
-rw-r--r--identity/support/src/IdentityCredentialSupport.cpp224
-rw-r--r--identity/support/tests/IdentityCredentialSupportTest.cpp294
-rw-r--r--keymaster/4.1/support/attestation_record.cpp12
17 files changed, 925 insertions, 611 deletions
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index 730b601c69..702334d0b6 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -55,7 +55,7 @@ interface IIdentityCredential {
* This method may only be called once per instance. If called more than once, STATUS_FAILED
* will be returned.
*
- * @return the unencrypted key-pair in PKCS#8 format.
+ * @return the private key, in DER format as specified in RFC 5915.
*/
byte[] createEphemeralKeyPair();
@@ -88,10 +88,10 @@ interface IIdentityCredential {
* The setRequestedNamespaces() and setVerificationToken() methods will be called before
* this method is called.
*
- * This method be called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(),
- * createAuthChallenge() and before startRetrieveEntry(). This method call is followed by
- * multiple calls of startRetrieveEntryValue(), retrieveEntryValue(), and finally
- * finishRetrieval().
+ * This method is called after createEphemeralKeyPair(), setReaderEphemeralPublicKey(),
+ * createAuthChallenge() (note that those calls are optional) and before startRetrieveEntry().
+ * This method call is followed by multiple calls of startRetrieveEntryValue(),
+ * retrieveEntryValue(), and finally finishRetrieval().
*
* It is permissible to perform data retrievals multiple times using the same instance (e.g.
* startRetrieval(), then multiple calls of startRetrieveEntryValue(), retrieveEntryValue(),
@@ -343,12 +343,13 @@ interface IIdentityCredential {
*
* - signature: must be set to ECDSA.
*
- * - subject: CN shall be set to "Android Identity Credential Authentication Key".
+ * - subject: CN shall be set to "Android Identity Credential Authentication Key". (fixed
+ * value: same on all certs)
*
- * - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
- * values returned in HardwareInformation.
+ * - issuer: CN shall be set to "Android Identity Credential Key". (fixed value:
+ * same on all certs)
*
- * - validity: should be from current time and one year in the future.
+ * - validity: should be from current time and one year in the future (365 days).
*
* - subjectPublicKeyInfo: must contain attested public key.
*
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index 297fd1d8ec..c48cb6682e 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -37,12 +37,12 @@ interface IWritableIdentityCredential {
*
* - signature: must be set to ECDSA.
*
- * - subject: CN shall be set to "Android Identity Credential Key".
+ * - subject: CN shall be set to "Android Identity Credential Key". (fixed value:
+ * same on all certs)
*
- * - issuer: shall be set to "credentialStoreName (credentialStoreAuthorName)" using the
- * values returned in HardwareInformation.
+ * - issuer: Same as the subject field of the batch attestation key.
*
- * - validity: should be from current time and expire at the same time as the
+ * - validity: Should be set to current time and expire at the same time as the
* attestation batch certificate used.
*
* - subjectPublicKeyInfo: must contain attested public key.
@@ -55,19 +55,14 @@ interface IWritableIdentityCredential {
*
* - The attestationSecurityLevel field must be set to either Software (0),
* TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
- * implemented. Only the default AOSP implementation of this HAL may use
- * value 0 (additionally, this implementation must not be used on production
- * devices).
+ * implemented.
*
- * - The keymasterVersion field in the attestation extension must be set to (10*major + minor)
- * where major and minor are the Identity Credential interface major and minor versions.
- * Specifically for this version of the interface (1.0) this value is 10.
+ * - The keymasterVersion field in the attestation extension must be set to the.
+ * same value as used for Android Keystore keys.
*
* - The keymasterSecurityLevel field in the attestation extension must be set to
* either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
- * the Trusted Application backing the HAL implementation is implemented. Only
- * the default AOSP implementation of this HAL may use value 0 (additionally, this
- * implementation must not be used on production devices)
+ * the Trusted Application backing the HAL implementation is implemented.
*
* - The attestationChallenge field must be set to the passed-in challenge.
*
@@ -81,7 +76,8 @@ interface IWritableIdentityCredential {
*
* - Tag::IDENTITY_CREDENTIAL_KEY which indicates that the key is an Identity
* Credential key (which can only sign/MAC very specific messages) and not an Android
- * Keystore key (which can be used to sign/MAC anything).
+ * Keystore key (which can be used to sign/MAC anything). This must not be set
+ * for test credentials.
*
* - Tag::PURPOSE must be set to SIGN
*
@@ -95,10 +91,13 @@ interface IWritableIdentityCredential {
*
* - Tag::EC_CURVE must be set to P_256
*
- * Additional authorizations may be needed in the softwareEnforced and teeEnforced
- * fields - the above is not an exhaustive list. Specifically, authorizations containing
- * information about the root of trust, OS version, verified boot state, and so on should
- * be included.
+ * - Tag::ROOT_OF_TRUST must be set
+ *
+ * - Tag::OS_VERSION and Tag::OS_PATCHLEVEL must be set
+ *
+ * Additional authorizations may be appear in the softwareEnforced and teeEnforced
+ * fields. For example if the device has a boot or vendor partitions, then BOOT_PATCHLEVEL
+ * and VENDOR_PATCHLEVEL should be set.
*
* Since the chain is required to be generated using Keymaster Attestation, the returned
* certificate chain has the following properties:
@@ -112,8 +111,8 @@ interface IWritableIdentityCredential {
* As with any user of attestation, the Issuing Authority (as a relying party) wishing
* to issue a credential to a device using these APIs, must carefully examine the
* returned certificate chain for all of the above (and more). In particular, the Issuing
- * Authority should check the root of trust, verified boot state, patch level,
- * application id, etc.
+ * Authority should check the root of trust (which include verified boot state), patch level,
+ * attestation application id, etc.
*
* This all depends on the needs of the Issuing Authority and the kind of credential but
* in general an Issuing Authority should never issue a credential to a device without
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
index 87d9a935de..dfcd4f557f 100644
--- a/identity/aidl/default/IdentityCredential.cpp
+++ b/identity/aidl/default/IdentityCredential.cpp
@@ -272,6 +272,7 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
const vector<uint8_t>& signingKeyBlob, const vector<uint8_t>& sessionTranscript,
const vector<uint8_t>& readerSignature, const vector<int32_t>& requestCounts) {
+ std::unique_ptr<cppbor::Item> sessionTranscriptItem;
if (sessionTranscript.size() > 0) {
auto [item, _, message] = cppbor::parse(sessionTranscript);
if (item == nullptr) {
@@ -279,7 +280,7 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
IIdentityCredentialStore::STATUS_INVALID_DATA,
"SessionTranscript contains invalid CBOR"));
}
- sessionTranscriptItem_ = std::move(item);
+ sessionTranscriptItem = std::move(item);
}
if (numStartRetrievalCalls_ > 0) {
if (sessionTranscript_ != sessionTranscript) {
@@ -319,7 +320,7 @@ ndk::ScopedAStatus IdentityCredential::startRetrieval(
vector<uint8_t> encodedReaderAuthentication =
cppbor::Array()
.add("ReaderAuthentication")
- .add(sessionTranscriptItem_->clone())
+ .add(std::move(sessionTranscriptItem))
.add(cppbor::Semantic(24, itemsRequestBytes))
.encode();
vector<uint8_t> encodedReaderAuthenticationBytes =
@@ -776,13 +777,6 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
optional<vector<uint8_t>> mac;
if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
readerPublicKey_.size() > 0) {
- cppbor::Array array;
- array.add("DeviceAuthentication");
- array.add(sessionTranscriptItem_->clone());
- array.add(docType_);
- array.add(cppbor::Semantic(24, encodedDeviceNameSpaces));
- vector<uint8_t> deviceAuthenticationBytes = cppbor::Semantic(24, array.encode()).encode();
-
vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
optional<vector<uint8_t>> signingKey =
support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
@@ -792,31 +786,15 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
"Error decrypting signingKeyBlob"));
}
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(readerPublicKey_, signingKey.value());
- if (!sharedSecret) {
- return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED, "Error doing ECDH"));
- }
-
- // Mix-in SessionTranscriptBytes
vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
- vector<uint8_t> sharedSecretWithSessionTranscriptBytes = sharedSecret.value();
- std::copy(sessionTranscriptBytes.begin(), sessionTranscriptBytes.end(),
- std::back_inserter(sharedSecretWithSessionTranscriptBytes));
-
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey =
- support::hkdf(sharedSecretWithSessionTranscriptBytes, salt, info, 32);
- if (!derivedKey) {
+ optional<vector<uint8_t>> eMacKey =
+ support::calcEMacKey(signingKey.value(), readerPublicKey_, sessionTranscriptBytes);
+ if (!eMacKey) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
- IIdentityCredentialStore::STATUS_FAILED,
- "Error deriving key from shared secret"));
+ IIdentityCredentialStore::STATUS_FAILED, "Error calculating EMacKey"));
}
-
- mac = support::coseMac0(derivedKey.value(), {}, // payload
- deviceAuthenticationBytes); // detached content
+ mac = support::calcMac(sessionTranscript_, docType_, encodedDeviceNameSpaces,
+ eMacKey.value());
if (!mac) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
@@ -830,9 +808,9 @@ ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
- string serialDecimal = "0"; // TODO: set serial to something unique
- string issuer = "Android Open Source Project";
- string subject = "Android IdentityCredential Reference Implementation";
+ string serialDecimal = "1";
+ string issuer = "Android Identity Credential Key";
+ string subject = "Android Identity Credential Authentication Key";
time_t validityNotBefore = time(nullptr);
time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h
index a82531dd4b..a8a6409ca0 100644
--- a/identity/aidl/default/IdentityCredential.h
+++ b/identity/aidl/default/IdentityCredential.h
@@ -103,7 +103,6 @@ class IdentityCredential : public BnIdentityCredential {
map<int32_t, int> profileIdToAccessCheckResult_;
vector<uint8_t> signingKeyBlob_;
vector<uint8_t> sessionTranscript_;
- std::unique_ptr<cppbor::Item> sessionTranscriptItem_;
vector<uint8_t> itemsRequest_;
vector<int32_t> requestCountsRemaining_;
map<string, set<string>> requestedNameSpacesAndNames_;
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
index fea289b9ef..141b4deaef 100644
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ b/identity/aidl/default/WritableIdentityCredential.cpp
@@ -74,7 +74,7 @@ ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
- support::createEcKeyPairAndAttestation(challenge, appId);
+ support::createEcKeyPairAndAttestation(challenge, appId, testCredential_);
if (!keyAttestationPair) {
LOG(ERROR) << "Error creating credentialKey and attestation";
return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index c1f44e742e..03966de549 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -9,7 +9,6 @@ cc_test {
"VtsIWritableIdentityCredentialTests.cpp",
"VtsIdentityTestUtils.cpp",
"VtsAttestationTests.cpp",
- "VtsAttestationParserSupport.cpp",
"UserAuthTests.cpp",
"ReaderAuthTests.cpp",
],
@@ -20,13 +19,14 @@ cc_test {
static_libs: [
"libcppbor",
"libkeymaster_portable",
- "libsoft_attestation_cert",
"libpuresoftkeymasterdevice",
"android.hardware.keymaster@4.0",
"android.hardware.identity-support-lib",
"android.hardware.identity-cpp",
"android.hardware.keymaster-cpp",
"android.hardware.keymaster-ndk_platform",
+ "libkeymaster4support",
+ "libkeymaster4_1support",
],
test_suites: [
"general-tests",
diff --git a/identity/aidl/vts/VtsAttestationParserSupport.cpp b/identity/aidl/vts/VtsAttestationParserSupport.cpp
deleted file mode 100644
index 71fe7331ba..0000000000
--- a/identity/aidl/vts/VtsAttestationParserSupport.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright 2019, 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.
- */
-
-#include "VtsAttestationParserSupport.h"
-
-#include <aidl/Gtest.h>
-#include <map>
-
-namespace android::hardware::identity::test_utils {
-
-using std::endl;
-using std::map;
-using std::optional;
-using std::string;
-using std::vector;
-
-using ::android::sp;
-using ::android::String16;
-using ::android::binder::Status;
-
-using ::keymaster::ASN1_OBJECT_Ptr;
-using ::keymaster::AuthorizationSet;
-using ::keymaster::EVP_PKEY_Ptr;
-using ::keymaster::kAttestionRecordOid;
-using ::keymaster::TAG_ATTESTATION_APPLICATION_ID;
-using ::keymaster::TAG_IDENTITY_CREDENTIAL_KEY;
-using ::keymaster::TAG_INCLUDE_UNIQUE_ID;
-using ::keymaster::TypedTag;
-using ::keymaster::X509_Ptr;
-
-using support::certificateChainSplit;
-
-optional<keymaster_cert_chain_t> AttestationCertificateParser::certificateChainToKeymasterChain(
- const vector<Certificate>& certificates) {
- if (certificates.size() <= 0) {
- return {};
- }
-
- keymaster_cert_chain_t kCert;
- kCert.entry_count = certificates.size();
- kCert.entries = (keymaster_blob_t*)malloc(sizeof(keymaster_blob_t) * kCert.entry_count);
-
- int index = 0;
- for (const auto& c : certificates) {
- kCert.entries[index].data_length = c.encodedCertificate.size();
- uint8_t* data = (uint8_t*)malloc(c.encodedCertificate.size());
-
- memcpy(data, c.encodedCertificate.data(), c.encodedCertificate.size());
- kCert.entries[index].data = (const uint8_t*)data;
- index++;
- }
-
- return kCert;
-}
-
-bool AttestationCertificateParser::parse() {
- optional<keymaster_cert_chain_t> cert_chain = certificateChainToKeymasterChain(origCertChain_);
- if (!cert_chain) {
- return false;
- }
-
- if (cert_chain.value().entry_count < 3) {
- return false;
- }
-
- if (!verifyChain(cert_chain.value())) {
- return false;
- }
-
- if (!verifyAttestationRecord(cert_chain.value().entries[0])) {
- return false;
- }
-
- keymaster_free_cert_chain(&cert_chain.value());
- return true;
-}
-
-ASN1_OCTET_STRING* AttestationCertificateParser::getAttestationRecord(X509* certificate) {
- ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1));
- if (!oid.get()) return nullptr;
-
- int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1);
- if (location == -1) return nullptr;
-
- X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
- if (!attest_rec_ext) return nullptr;
-
- ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
- return attest_rec;
-}
-
-X509* AttestationCertificateParser::parseCertBlob(const keymaster_blob_t& blob) {
- const uint8_t* p = blob.data;
- return d2i_X509(nullptr, &p, blob.data_length);
-}
-
-bool AttestationCertificateParser::verifyAttestationRecord(
- const keymaster_blob_t& attestation_cert) {
- X509_Ptr cert(parseCertBlob(attestation_cert));
- if (!cert.get()) {
- return false;
- }
-
- ASN1_OCTET_STRING* attest_rec = getAttestationRecord(cert.get());
- if (!attest_rec) {
- return false;
- }
-
- keymaster_blob_t att_unique_id = {};
- keymaster_blob_t att_challenge;
- keymaster_error_t ret = parse_attestation_record(
- attest_rec->data, attest_rec->length, &att_attestation_version_,
- &att_attestation_security_level_, &att_keymaster_version_,
- &att_keymaster_security_level_, &att_challenge, &att_sw_enforced_, &att_hw_enforced_,
- &att_unique_id);
- if (ret) {
- return false;
- }
-
- att_challenge_.assign(att_challenge.data, att_challenge.data + att_challenge.data_length);
- return true;
-}
-
-uint32_t AttestationCertificateParser::getKeymasterVersion() {
- return att_keymaster_version_;
-}
-
-uint32_t AttestationCertificateParser::getAttestationVersion() {
- return att_attestation_version_;
-}
-
-vector<uint8_t> AttestationCertificateParser::getAttestationChallenge() {
- return att_challenge_;
-}
-
-keymaster_security_level_t AttestationCertificateParser::getKeymasterSecurityLevel() {
- return att_keymaster_security_level_;
-}
-
-keymaster_security_level_t AttestationCertificateParser::getAttestationSecurityLevel() {
- return att_attestation_security_level_;
-}
-
-// Verify the Attestation certificates are correctly chained.
-bool AttestationCertificateParser::verifyChain(const keymaster_cert_chain_t& chain) {
- for (size_t i = 0; i < chain.entry_count - 1; ++i) {
- keymaster_blob_t& key_cert_blob = chain.entries[i];
- keymaster_blob_t& signing_cert_blob = chain.entries[i + 1];
-
- X509_Ptr key_cert(parseCertBlob(key_cert_blob));
- X509_Ptr signing_cert(parseCertBlob(signing_cert_blob));
- if (!key_cert.get() || !signing_cert.get()) {
- return false;
- }
-
- EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
- if (!signing_pubkey.get()) return false;
-
- if (X509_verify(key_cert.get(), signing_pubkey.get()) != 1) {
- return false;
- }
-
- if (i + 1 == chain.entry_count - 1) {
- // Last entry is self-signed.
- if (X509_verify(signing_cert.get(), signing_pubkey.get()) != 1) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-} // namespace android::hardware::identity::test_utils
diff --git a/identity/aidl/vts/VtsAttestationParserSupport.h b/identity/aidl/vts/VtsAttestationParserSupport.h
deleted file mode 100644
index 7c7e1b6ec1..0000000000
--- a/identity/aidl/vts/VtsAttestationParserSupport.h
+++ /dev/null
@@ -1,122 +0,0 @@
-
-/*
- * Copyright 2019, 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.
- */
-
-#ifndef VTS_ATTESTATION_PARSER_SUPPORT_H
-#define VTS_ATTESTATION_PARSER_SUPPORT_H
-
-//#include <aidl/Gtest.h>
-#include <android/hardware/identity/IIdentityCredentialStore.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-#include <android/hardware/keymaster/4.0/types.h>
-#include <hardware/keymaster_defs.h>
-#include <keymaster/android_keymaster_utils.h>
-#include <keymaster/authorization_set.h>
-#include <keymaster/contexts/pure_soft_keymaster_context.h>
-#include <keymaster/contexts/soft_attestation_cert.h>
-#include <keymaster/keymaster_tags.h>
-#include <keymaster/km_openssl/attestation_utils.h>
-#include <vector>
-
-namespace android::hardware::identity::test_utils {
-
-using ::std::optional;
-using ::std::string;
-using ::std::vector;
-
-using ::keymaster::AuthorizationSet;
-using ::keymaster::TypedTag;
-
-class AttestationCertificateParser {
- public:
- AttestationCertificateParser(const vector<Certificate>& certChain)
- : origCertChain_(certChain) {}
-
- bool parse();
-
- uint32_t getKeymasterVersion();
- uint32_t getAttestationVersion();
- vector<uint8_t> getAttestationChallenge();
- keymaster_security_level_t getKeymasterSecurityLevel();
- keymaster_security_level_t getAttestationSecurityLevel();
-
- template <keymaster_tag_t Tag>
- bool getSwEnforcedBool(TypedTag<KM_BOOL, Tag> tag) {
- if (att_sw_enforced_.GetTagValue(tag)) {
- return true;
- }
-
- return false;
- }
-
- template <keymaster_tag_t Tag>
- bool getHwEnforcedBool(TypedTag<KM_BOOL, Tag> tag) {
- if (att_hw_enforced_.GetTagValue(tag)) {
- return true;
- }
-
- return false;
- }
-
- template <keymaster_tag_t Tag>
- optional<vector<uint8_t>> getHwEnforcedBlob(TypedTag<KM_BYTES, Tag> tag) {
- keymaster_blob_t blob;
- if (att_hw_enforced_.GetTagValue(tag, &blob)) {
- return {};
- }
-
- vector<uint8_t> ret(blob.data, blob.data + blob.data_length);
- return ret;
- }
-
- template <keymaster_tag_t Tag>
- optional<vector<uint8_t>> getSwEnforcedBlob(TypedTag<KM_BYTES, Tag> tag) {
- keymaster_blob_t blob;
- if (!att_sw_enforced_.GetTagValue(tag, &blob)) {
- return {};
- }
-
- vector<uint8_t> ret(blob.data, blob.data + blob.data_length);
- return ret;
- }
-
- private:
- // Helper functions.
- bool verifyChain(const keymaster_cert_chain_t& chain);
-
- ASN1_OCTET_STRING* getAttestationRecord(X509* certificate);
-
- X509* parseCertBlob(const keymaster_blob_t& blob);
-
- bool verifyAttestationRecord(const keymaster_blob_t& attestation_cert);
-
- optional<keymaster_cert_chain_t> certificateChainToKeymasterChain(
- const vector<Certificate>& certificates);
-
- // Private variables.
- vector<Certificate> origCertChain_;
- AuthorizationSet att_sw_enforced_;
- AuthorizationSet att_hw_enforced_;
- uint32_t att_attestation_version_;
- uint32_t att_keymaster_version_;
- keymaster_security_level_t att_attestation_security_level_;
- keymaster_security_level_t att_keymaster_security_level_;
- vector<uint8_t> att_challenge_;
-};
-
-} // namespace android::hardware::identity::test_utils
-
-#endif // VTS_ATTESTATION_PARSER_SUPPORT_H
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index 673d08bb42..5529853009 100644
--- a/identity/aidl/vts/VtsAttestationTests.cpp
+++ b/identity/aidl/vts/VtsAttestationTests.cpp
@@ -29,7 +29,6 @@
#include <future>
#include <map>
-#include "VtsAttestationParserSupport.h"
#include "VtsIdentityTestUtils.h"
namespace android::hardware::identity {
@@ -44,7 +43,6 @@ using ::android::sp;
using ::android::String16;
using ::android::binder::Status;
-using test_utils::AttestationCertificateParser;
using test_utils::setupWritableCredential;
using test_utils::validateAttestationCertificate;
@@ -61,38 +59,12 @@ class VtsAttestationTests : public testing::TestWithParam<std::string> {
sp<IIdentityCredentialStore> credentialStore_;
};
-TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeEmptyId) {
- Status result;
-
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
- sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
-
- string challenge = "NotSoRandomChallenge";
- vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
- vector<Certificate> attestationCertificate;
- vector<uint8_t> attestationApplicationId = {};
-
- result = writableCredential->getAttestationCertificate(
- attestationApplicationId, attestationChallenge, &attestationCertificate);
-
- ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
- << endl;
-
- EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
- attestationApplicationId, hwInfo));
-}
-
TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeNonemptyId) {
Status result;
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1";
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
@@ -106,18 +78,16 @@ TEST_P(VtsAttestationTests, verifyAttestationWithNonemptyChallengeNonemptyId) {
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
- EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
- attestationApplicationId, hwInfo));
+ validateAttestationCertificate(attestationCertificate, attestationChallenge,
+ attestationApplicationId, false);
}
TEST_P(VtsAttestationTests, verifyAttestationWithVeryShortChallengeAndId) {
Status result;
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "c";
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
@@ -131,8 +101,8 @@ TEST_P(VtsAttestationTests, verifyAttestationWithVeryShortChallengeAndId) {
ASSERT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
- EXPECT_TRUE(validateAttestationCertificate(attestationCertificate, attestationChallenge,
- attestationApplicationId, hwInfo));
+ validateAttestationCertificate(attestationCertificate, attestationChallenge,
+ attestationApplicationId, false);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsAttestationTests);
diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
index 500b79ff8e..cdecb97989 100644
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
@@ -174,16 +174,17 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
string cborPretty;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ true /* testCredential */));
string challenge = "attestationChallenge";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {1} /* atteestationApplicationId */);
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
- EXPECT_TRUE(validateAttestationCertificate(attData.attestationCertificate,
- attData.attestationChallenge,
- attData.attestationApplicationId, hwInfo));
+ validateAttestationCertificate(attData.attestationCertificate, attData.attestationChallenge,
+ attData.attestationApplicationId, true);
// This is kinda of a hack but we need to give the size of
// ProofOfProvisioning that we'll expect to receive.
@@ -368,6 +369,7 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
optional<vector<uint8_t>> signingPubKey =
support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
EXPECT_TRUE(signingPubKey);
+ test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
// Since we're using a test-credential we know storageKey meaning we can get the
// private key. Do this, derive the public key from it, and check this matches what
@@ -418,9 +420,9 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
}
vector<uint8_t> mac;
- vector<uint8_t> deviceNameSpacesBytes;
- ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ vector<uint8_t> deviceNameSpacesEncoded;
+ ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ(
"{\n"
" 'PersonalData' : {\n"
@@ -435,37 +437,19 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
" },\n"
"}",
cborPretty);
- // The data that is MACed is ["DeviceAuthentication", sessionTranscript, docType,
- // deviceNameSpacesBytes] so build up that structure
- cppbor::Array deviceAuthentication;
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
string docType = "org.iso.18013-5.2019.mdl";
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- vector<uint8_t> deviceAuthenticationBytes =
- cppbor::Semantic(24, deviceAuthentication.encode()).encode();
- // Derive the key used for MACing.
optional<vector<uint8_t>> readerEphemeralPrivateKey =
support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
- optional<vector<uint8_t>> sharedSecret =
- support::ecdh(signingPubKey.value(), readerEphemeralPrivateKey.value());
- ASSERT_TRUE(sharedSecret);
- // Mix-in SessionTranscriptBytes
- vector<uint8_t> sessionTranscriptBytes =
- cppbor::Semantic(24, sessionTranscript.encode()).encode();
- vector<uint8_t> sharedSecretWithSessionTranscriptBytes = sharedSecret.value();
- std::copy(sessionTranscriptBytes.begin(), sessionTranscriptBytes.end(),
- std::back_inserter(sharedSecretWithSessionTranscriptBytes));
- vector<uint8_t> salt = {0x00};
- vector<uint8_t> info = {};
- optional<vector<uint8_t>> derivedKey =
- support::hkdf(sharedSecretWithSessionTranscriptBytes, salt, info, 32);
- ASSERT_TRUE(derivedKey);
+ optional<vector<uint8_t>> eMacKey = support::calcEMacKey(
+ readerEphemeralPrivateKey.value(), // Private Key
+ signingPubKey.value(), // Public Key
+ cppbor::Semantic(24, sessionTranscript.encode()).encode()); // SessionTranscriptBytes
optional<vector<uint8_t>> calculatedMac =
- support::coseMac0(derivedKey.value(), {}, // payload
- deviceAuthenticationBytes); // detached content
+ support::calcMac(sessionTranscript.encode(), // SessionTranscript
+ docType, // DocType
+ deviceNameSpacesEncoded, // DeviceNamespaces
+ eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
@@ -480,18 +464,14 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
signingKeyBlob, sessionTranscriptEncoded, {}, // readerSignature,
testEntriesEntryCounts)
.isOk());
- ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ("{}", cborPretty);
// Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
- deviceAuthentication = cppbor::Array();
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- deviceAuthenticationBytes = cppbor::Semantic(24, deviceAuthentication.encode()).encode();
- calculatedMac = support::coseMac0(derivedKey.value(), {}, // payload
- deviceAuthenticationBytes); // detached content
+ calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
+ docType, // DocType
+ deviceNameSpacesEncoded, // DeviceNamespaces
+ eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
@@ -506,18 +486,14 @@ TEST_P(IdentityAidl, createAndRetrieveCredential) {
signingKeyBlob, sessionTranscriptEncoded, {}, // readerSignature,
testEntriesEntryCounts)
.isOk());
- ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesBytes).isOk());
- cborPretty = support::cborPrettyPrint(deviceNameSpacesBytes, 32, {});
+ ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+ cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
ASSERT_EQ("{}", cborPretty);
// Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
- deviceAuthentication = cppbor::Array();
- deviceAuthentication.add("DeviceAuthentication");
- deviceAuthentication.add(sessionTranscript.clone());
- deviceAuthentication.add(docType);
- deviceAuthentication.add(cppbor::Semantic(24, deviceNameSpacesBytes));
- deviceAuthenticationBytes = cppbor::Semantic(24, deviceAuthentication.encode()).encode();
- calculatedMac = support::coseMac0(derivedKey.value(), {}, // payload
- deviceAuthenticationBytes); // detached content
+ calculatedMac = support::calcMac(sessionTranscript.encode(), // SessionTranscript
+ docType, // DocType
+ deviceNameSpacesEncoded, // DeviceNamespaces
+ eMacKey.value()); // EMacKey
ASSERT_TRUE(calculatedMac);
EXPECT_EQ(mac, calculatedMac);
}
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 8c35952cd6..56e17bac77 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -61,7 +61,8 @@ TEST_P(IdentityCredentialTests, verifyAttestationWithEmptyChallenge) {
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
vector<uint8_t> attestationChallenge;
vector<Certificate> attestationCertificate;
@@ -82,12 +83,13 @@ TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithChallenge) {
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge1NotSoRandomChallenge1NotSoRandomChallenge1";
vector<uint8_t> attestationChallenge(challenge.begin(), challenge.end());
vector<Certificate> attestationCertificate;
- vector<uint8_t> attestationApplicationId = {};
+ vector<uint8_t> attestationApplicationId = {1};
result = writableCredential->getAttestationCertificate(
attestationApplicationId, attestationChallenge, &attestationCertificate);
@@ -95,27 +97,27 @@ TEST_P(IdentityCredentialTests, verifyAttestationSuccessWithChallenge) {
EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
<< endl;
- EXPECT_TRUE(test_utils::validateAttestationCertificate(
- attestationCertificate, attestationChallenge, attestationApplicationId, hwInfo));
+ test_utils::validateAttestationCertificate(attestationCertificate, attestationChallenge,
+ attestationApplicationId, false);
}
TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) {
Status result;
- HardwareInformation hwInfo;
- ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge1";
- test_utils::AttestationData attData(writableCredential, challenge, {});
- ASSERT_TRUE(test_utils::validateAttestationCertificate(
- attData.attestationCertificate, attData.attestationChallenge,
- attData.attestationApplicationId, hwInfo));
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {1} /* atteestationApplicationId */);
+ test_utils::validateAttestationCertificate(attData.attestationCertificate,
+ attData.attestationChallenge,
+ attData.attestationApplicationId, false);
string challenge2 = "NotSoRandomChallenge2";
- test_utils::AttestationData attData2(writableCredential, challenge2, {});
+ test_utils::AttestationData attData2(writableCredential, challenge2,
+ {} /* atteestationApplicationId */);
EXPECT_FALSE(attData2.result.isOk()) << attData2.result.exceptionCode() << "; "
<< attData2.result.exceptionMessage() << endl;
EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, attData2.result.exceptionCode());
@@ -125,7 +127,8 @@ TEST_P(IdentityCredentialTests, verifyAttestationDoubleCallFails) {
TEST_P(IdentityCredentialTests, verifyStartPersonalization) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// First call should go through
const vector<int32_t> entryCounts = {2, 4};
@@ -147,7 +150,8 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalization) {
TEST_P(IdentityCredentialTests, verifyStartPersonalizationMin) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// Verify minimal number of profile count and entry count
const vector<int32_t> entryCounts = {1, 1};
@@ -160,7 +164,8 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalizationMin) {
TEST_P(IdentityCredentialTests, verifyStartPersonalizationOne) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// Verify minimal number of profile count and entry count
const vector<int32_t> entryCounts = {1};
@@ -173,7 +178,8 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalizationOne) {
TEST_P(IdentityCredentialTests, verifyStartPersonalizationLarge) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// Verify set a large number of profile count and entry count is ok
const vector<int32_t> entryCounts = {3000};
@@ -186,7 +192,8 @@ TEST_P(IdentityCredentialTests, verifyStartPersonalizationLarge) {
TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
// Enter mismatched entry and profile numbers
const vector<int32_t> entryCounts = {5, 6};
@@ -224,7 +231,8 @@ TEST_P(IdentityCredentialTests, verifyProfileNumberMismatchShouldFail) {
TEST_P(IdentityCredentialTests, verifyDuplicateProfileId) {
Status result;
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
const vector<int32_t> entryCounts = {3, 6};
writableCredential->setExpectedProofOfProvisioningSize(123456);
@@ -283,10 +291,12 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) {
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge1";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {} /* atteestationApplicationId */);
EXPECT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
@@ -294,7 +304,7 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) {
ASSERT_TRUE(readerCertificate1);
const vector<int32_t> entryCounts = {1u};
- size_t expectedPoPSize = 186 + readerCertificate1.value().size();
+ size_t expectedPoPSize = 185 + readerCertificate1.value().size();
// OK to fail, not available in v1 HAL
writableCredential->setExpectedProofOfProvisioningSize(expectedPoPSize);
result = writableCredential->startPersonalization(1, entryCounts);
@@ -308,7 +318,7 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) {
ASSERT_TRUE(secureProfiles);
const vector<test_utils::TestEntryData> testEntries1 = {
- {"Name Space", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+ {"Name Space", "Last name", string("Turing"), vector<int32_t>{1}},
};
map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
@@ -347,11 +357,11 @@ TEST_P(IdentityCredentialTests, verifyOneProfileAndEntryPass) {
" {\n"
" 'name' : 'Last name',\n"
" 'value' : 'Turing',\n"
- " 'accessControlProfiles' : [0, 1, ],\n"
+ " 'accessControlProfiles' : [1, ],\n"
" },\n"
" ],\n"
" },\n"
- " true,\n"
+ " false,\n"
"]",
cborPretty);
@@ -370,10 +380,12 @@ TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) {
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {} /* atteestationApplicationId */);
EXPECT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
@@ -510,7 +522,7 @@ TEST_P(IdentityCredentialTests, verifyManyProfilesAndEntriesPass) {
" },\n"
" ],\n"
" },\n"
- " true,\n"
+ " false,\n"
"]",
cborPretty);
@@ -529,10 +541,12 @@ TEST_P(IdentityCredentialTests, verifyEmptyNameSpaceMixedWithNonEmptyWorks) {
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {} /* atteestationApplicationId */);
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
@@ -591,10 +605,12 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) {
ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
string challenge = "NotSoRandomChallenge";
- test_utils::AttestationData attData(writableCredential, challenge, {});
+ test_utils::AttestationData attData(writableCredential, challenge,
+ {} /* atteestationApplicationId */);
ASSERT_TRUE(attData.result.isOk())
<< attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
@@ -667,7 +683,8 @@ TEST_P(IdentityCredentialTests, verifyInterleavingEntryNameSpaceOrderingFails) {
TEST_P(IdentityCredentialTests, verifyAccessControlProfileIdOutOfRange) {
sp<IWritableIdentityCredential> writableCredential;
- ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_));
+ ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+ false /* testCredential */));
const vector<int32_t> entryCounts = {1};
writableCredential->setExpectedProofOfProvisioningSize(123456);
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp
index b6ed80f4b1..3b106514b4 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ b/identity/aidl/vts/VtsIdentityTestUtils.cpp
@@ -14,13 +14,17 @@
* limitations under the License.
*/
+#define LOG_TAG "VtsIdentityTestUtils"
+
#include "VtsIdentityTestUtils.h"
#include <aidl/Gtest.h>
+#include <android-base/logging.h>
+#include <keymaster/km_openssl/openssl_utils.h>
+#include <keymasterV4_1/attestation_record.h>
+#include <charconv>
#include <map>
-#include "VtsAttestationParserSupport.h"
-
namespace android::hardware::identity::test_utils {
using std::endl;
@@ -32,15 +36,15 @@ using std::vector;
using ::android::sp;
using ::android::String16;
using ::android::binder::Status;
+using ::keymaster::X509_Ptr;
bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
- sp<IIdentityCredentialStore>& credentialStore) {
+ sp<IIdentityCredentialStore>& credentialStore, bool testCredential) {
if (credentialStore == nullptr) {
return false;
}
string docType = "org.iso.18013-5.2019.mdl";
- bool testCredential = true;
Status result = credentialStore->createCredential(docType, testCredential, &writableCredential);
if (result.isOk() && writableCredential != nullptr) {
@@ -178,61 +182,267 @@ void setImageData(vector<uint8_t>& image) {
}
}
-bool validateAttestationCertificate(const vector<Certificate>& inputCertificates,
- const vector<uint8_t>& expectedChallenge,
- const vector<uint8_t>& expectedAppId,
- const HardwareInformation& hwInfo) {
- AttestationCertificateParser certParser_(inputCertificates);
- bool ret = certParser_.parse();
- EXPECT_TRUE(ret);
- if (!ret) {
- return false;
+string x509NameToRfc2253String(X509_NAME* name) {
+ char* buf;
+ size_t bufSize;
+ BIO* bio;
+
+ bio = BIO_new(BIO_s_mem());
+ X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
+ bufSize = BIO_get_mem_data(bio, &buf);
+ string ret = string(buf, bufSize);
+ BIO_free(bio);
+
+ return ret;
+}
+
+int parseDigits(const char** s, int numDigits) {
+ int result;
+ auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
+ if (ec != std::errc()) {
+ LOG(ERROR) << "Error parsing " << numDigits << " digits "
+ << " from " << s;
+ return 0;
}
+ *s += numDigits;
+ return result;
+}
- // As per the IC HAL, the version of the Identity
- // Credential HAL is 1.0 - and this is encoded as major*10 + minor. This field is used by
- // Keymaster which is known to report integers less than or equal to 4 (for KM up to 4.0)
- // and integers greater or equal than 41 (for KM starting with 4.1).
- //
- // Since we won't get to version 4.0 of the IC HAL for a while, let's also check that a KM
- // version isn't errornously returned.
- EXPECT_LE(10, certParser_.getKeymasterVersion());
- EXPECT_GT(40, certParser_.getKeymasterVersion());
- EXPECT_LE(3, certParser_.getAttestationVersion());
-
- // Verify the app id matches to whatever we set it to be.
- optional<vector<uint8_t>> appId =
- certParser_.getSwEnforcedBlob(::keymaster::TAG_ATTESTATION_APPLICATION_ID);
- if (appId) {
- EXPECT_EQ(expectedAppId.size(), appId.value().size());
- EXPECT_EQ(0, memcmp(expectedAppId.data(), appId.value().data(), expectedAppId.size()));
+bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
+ struct tm tm;
+
+ memset(&tm, '\0', sizeof(tm));
+ const char* timeStr = (const char*)asn1Time->data;
+ const char* s = timeStr;
+ if (asn1Time->type == V_ASN1_UTCTIME) {
+ tm.tm_year = parseDigits(&s, 2);
+ if (tm.tm_year < 70) {
+ tm.tm_year += 100;
+ }
+ } else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
+ tm.tm_year = parseDigits(&s, 4) - 1900;
+ tm.tm_year -= 1900;
} else {
- // app id not found
- EXPECT_EQ(0, expectedAppId.size());
+ LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
+ return false;
+ }
+ tm.tm_mon = parseDigits(&s, 2) - 1;
+ tm.tm_mday = parseDigits(&s, 2);
+ tm.tm_hour = parseDigits(&s, 2);
+ tm.tm_min = parseDigits(&s, 2);
+ tm.tm_sec = parseDigits(&s, 2);
+ // This may need to be updated if someone create certificates using +/- instead of Z.
+ //
+ if (*s != 'Z') {
+ LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
+ return false;
+ }
+
+ time_t t = timegm(&tm);
+ if (t == -1) {
+ LOG(ERROR) << "Error converting broken-down time to time_t";
+ return false;
}
+ *outTime = t;
+ return true;
+}
+
+void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
+ const vector<uint8_t>& expectedChallenge,
+ const vector<uint8_t>& expectedAppId, bool isTestCredential) {
+ ASSERT_GE(credentialKeyCertChain.size(), 2);
+
+ vector<uint8_t> certBytes = credentialKeyCertChain[0].encodedCertificate;
+ const uint8_t* certData = certBytes.data();
+ X509_Ptr cert = X509_Ptr(d2i_X509(nullptr, &certData, certBytes.size()));
+
+ vector<uint8_t> batchCertBytes = credentialKeyCertChain[1].encodedCertificate;
+ const uint8_t* batchCertData = batchCertBytes.data();
+ X509_Ptr batchCert = X509_Ptr(d2i_X509(nullptr, &batchCertData, batchCertBytes.size()));
+
+ // First get some values from the batch certificate which is checked
+ // against the top-level certificate (subject, notAfter)
+ //
+
+ X509_NAME* batchSubject = X509_get_subject_name(batchCert.get());
+ ASSERT_NE(nullptr, batchSubject);
+ time_t batchNotAfter;
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(batchCert.get()), &batchNotAfter));
+
+ // Check all the requirements from IWritableIdentityCredential::getAttestationCertificate()...
+ //
+
+ // - version: INTEGER 2 (means v3 certificate).
+ EXPECT_EQ(2, X509_get_version(cert.get()));
- EXPECT_TRUE(certParser_.getHwEnforcedBool(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
- EXPECT_FALSE(certParser_.getHwEnforcedBool(::keymaster::TAG_INCLUDE_UNIQUE_ID));
+ // - serialNumber: INTEGER 1 (fixed value: same on all certs).
+ EXPECT_EQ(1, ASN1_INTEGER_get(X509_get_serialNumber(cert.get())));
- // Verify the challenge always matches in size and data of what is passed
- // in.
- vector<uint8_t> attChallenge = certParser_.getAttestationChallenge();
- EXPECT_EQ(expectedChallenge.size(), attChallenge.size());
- EXPECT_EQ(0, memcmp(expectedChallenge.data(), attChallenge.data(), expectedChallenge.size()));
+ // - signature: must be set to ECDSA.
+ EXPECT_EQ(NID_ecdsa_with_SHA256, X509_get_signature_nid(cert.get()));
- // Ensure the attestation conveys that it's implemented in secure hardware (with carve-out
- // for the reference implementation which cannot be implemented in secure hardware).
- if (hwInfo.credentialStoreName == "Identity Credential Reference Implementation" &&
- hwInfo.credentialStoreAuthorName == "Google") {
- EXPECT_LE(KM_SECURITY_LEVEL_SOFTWARE, certParser_.getKeymasterSecurityLevel());
- EXPECT_LE(KM_SECURITY_LEVEL_SOFTWARE, certParser_.getAttestationSecurityLevel());
+ // - subject: CN shall be set to "Android Identity Credential Key". (fixed value:
+ // same on all certs)
+ X509_NAME* subject = X509_get_subject_name(cert.get());
+ ASSERT_NE(nullptr, subject);
+ EXPECT_EQ("CN=Android Identity Credential Key", x509NameToRfc2253String(subject));
+ // - issuer: Same as the subject field of the batch attestation key.
+ X509_NAME* issuer = X509_get_issuer_name(cert.get());
+ ASSERT_NE(nullptr, issuer);
+ EXPECT_EQ(x509NameToRfc2253String(batchSubject), x509NameToRfc2253String(issuer));
+
+ // - validity: Should be from current time and expire at the same time as the
+ // attestation batch certificate used.
+ //
+ // Allow for 10 seconds drift to account for the time drift between Secure HW
+ // and this environment plus the difference between when the certificate was
+ // created and until now
+ //
+ time_t notBefore;
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), &notBefore));
+ uint64_t now = time(nullptr);
+ int64_t diffSecs = now - notBefore;
+ int64_t allowDriftSecs = 10;
+ EXPECT_LE(-allowDriftSecs, diffSecs);
+ EXPECT_GE(allowDriftSecs, diffSecs);
+
+ time_t notAfter;
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), &notAfter));
+ EXPECT_EQ(notAfter, batchNotAfter);
+
+ auto [err, attRec] = keymaster::V4_1::parse_attestation_record(certBytes);
+ ASSERT_EQ(keymaster::V4_1::ErrorCode::OK, err);
+
+ // - subjectPublicKeyInfo: must contain attested public key.
+
+ // - The attestationVersion field in the attestation extension must be at least 3.
+ EXPECT_GE(attRec.attestation_version, 3);
+
+ // - The attestationSecurityLevel field must be set to either Software (0),
+ // TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
+ // implemented.
+ EXPECT_GE(attRec.attestation_security_level,
+ keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+ // - The keymasterVersion field in the attestation extension must be set to the.
+ // same value as used for Android Keystore keys.
+ //
+ // Nothing to check here...
+
+ // - The keymasterSecurityLevel field in the attestation extension must be set to
+ // either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
+ // the Trusted Application backing the HAL implementation is implemented.
+ EXPECT_GE(attRec.keymaster_security_level, keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+ // - The attestationChallenge field must be set to the passed-in challenge.
+ EXPECT_EQ(expectedChallenge.size(), attRec.attestation_challenge.size());
+ EXPECT_TRUE(memcmp(expectedChallenge.data(), attRec.attestation_challenge.data(),
+ attRec.attestation_challenge.size()) == 0);
+
+ // - The uniqueId field must be empty.
+ EXPECT_EQ(attRec.unique_id.size(), 0);
+
+ // - The softwareEnforced field in the attestation extension must include
+ // Tag::ATTESTATION_APPLICATION_ID which must be set to the bytes of the passed-in
+ // attestationApplicationId.
+ EXPECT_TRUE(attRec.software_enforced.Contains(keymaster::V4_0::TAG_ATTESTATION_APPLICATION_ID,
+ expectedAppId));
+
+ // - The teeEnforced field in the attestation extension must include
+ //
+ // - Tag::IDENTITY_CREDENTIAL_KEY which indicates that the key is an Identity
+ // Credential key (which can only sign/MAC very specific messages) and not an Android
+ // Keystore key (which can be used to sign/MAC anything). This must not be set
+ // for test credentials.
+ bool hasIcKeyTag =
+ attRec.hardware_enforced.Contains(static_cast<android::hardware::keymaster::V4_0::Tag>(
+ keymaster::V4_1::Tag::IDENTITY_CREDENTIAL_KEY));
+ if (isTestCredential) {
+ EXPECT_FALSE(hasIcKeyTag);
} else {
- // Actual devices should use TrustedEnvironment or StrongBox.
- EXPECT_LE(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, certParser_.getKeymasterSecurityLevel());
- EXPECT_LE(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT, certParser_.getAttestationSecurityLevel());
+ EXPECT_TRUE(hasIcKeyTag);
}
- return true;
+
+ // - Tag::PURPOSE must be set to SIGN
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_PURPOSE,
+ keymaster::V4_0::KeyPurpose::SIGN));
+
+ // - Tag::KEY_SIZE must be set to the appropriate key size, in bits (e.g. 256)
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_KEY_SIZE, 256));
+
+ // - Tag::ALGORITHM must be set to EC
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_ALGORITHM,
+ keymaster::V4_0::Algorithm::EC));
+
+ // - Tag::NO_AUTH_REQUIRED must be set
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_NO_AUTH_REQUIRED));
+
+ // - Tag::DIGEST must be include SHA_2_256
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_DIGEST,
+ keymaster::V4_0::Digest::SHA_2_256));
+
+ // - Tag::EC_CURVE must be set to P_256
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_EC_CURVE,
+ keymaster::V4_0::EcCurve::P_256));
+
+ // - Tag::ROOT_OF_TRUST must be set
+ //
+ EXPECT_GE(attRec.root_of_trust.security_level,
+ keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+ // - Tag::OS_VERSION and Tag::OS_PATCHLEVEL must be set
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_VERSION));
+ EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_PATCHLEVEL));
+
+ // TODO: we could retrieve osVersion and osPatchLevel from Android itself and compare it
+ // with what was reported in the certificate.
+}
+
+void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain) {
+ const uint8_t* data = authKeyCertChain.data();
+ auto cert = X509_Ptr(d2i_X509(nullptr, &data, authKeyCertChain.size()));
+
+ // - version: INTEGER 2 (means v3 certificate).
+ EXPECT_EQ(X509_get_version(cert.get()), 2);
+
+ // - serialNumber: INTEGER 1 (fixed value: same on all certs).
+ EXPECT_EQ(ASN1_INTEGER_get(X509_get_serialNumber(cert.get())), 1);
+
+ // - signature: must be set to ECDSA.
+ EXPECT_EQ(X509_get_signature_nid(cert.get()), NID_ecdsa_with_SHA256);
+
+ // - subject: CN shall be set to "Android Identity Credential Authentication Key". (fixed
+ // value: same on all certs)
+ X509_NAME* subject = X509_get_subject_name(cert.get());
+ ASSERT_NE(subject, nullptr);
+ EXPECT_EQ(x509NameToRfc2253String(subject),
+ "CN=Android Identity Credential Authentication Key");
+
+ // - issuer: CN shall be set to "Android Identity Credential Key". (fixed value:
+ // same on all certs)
+ X509_NAME* issuer = X509_get_issuer_name(cert.get());
+ ASSERT_NE(issuer, nullptr);
+ EXPECT_EQ(x509NameToRfc2253String(issuer), "CN=Android Identity Credential Key");
+
+ // - subjectPublicKeyInfo: must contain attested public key.
+
+ // - validity: should be from current time and one year in the future (365 days).
+ time_t notBefore, notAfter;
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), &notAfter));
+ ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), &notBefore));
+
+ // Allow for 10 seconds drift to account for the time drift between Secure HW
+ // and this environment plus the difference between when the certificate was
+ // created and until now
+ //
+ uint64_t now = time(nullptr);
+ int64_t diffSecs = now - notBefore;
+ int64_t allowDriftSecs = 10;
+ EXPECT_LE(-allowDriftSecs, diffSecs);
+ EXPECT_GE(allowDriftSecs, diffSecs);
+ constexpr uint64_t kSecsInOneYear = 365 * 24 * 60 * 60;
+ EXPECT_EQ(notBefore + kSecsInOneYear, notAfter);
}
vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries) {
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/VtsIdentityTestUtils.h
index 673b736842..85c24f86b5 100644
--- a/identity/aidl/vts/VtsIdentityTestUtils.h
+++ b/identity/aidl/vts/VtsIdentityTestUtils.h
@@ -34,8 +34,8 @@ using ::android::binder::Status;
struct AttestationData {
AttestationData(sp<IWritableIdentityCredential>& writableCredential, string challenge,
- vector<uint8_t> applicationId)
- : attestationApplicationId(applicationId) {
+ vector<uint8_t> attestationAppId)
+ : attestationApplicationId(attestationAppId) {
// ASSERT_NE(writableCredential, nullptr);
if (!challenge.empty()) {
@@ -94,7 +94,7 @@ struct TestProfile {
};
bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
- sp<IIdentityCredentialStore>& credentialStore);
+ sp<IIdentityCredentialStore>& credentialStore, bool testCredential);
optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal);
@@ -111,13 +111,17 @@ bool addEntry(sp<IWritableIdentityCredential>& writableCredential, const TestEnt
void setImageData(vector<uint8_t>& image);
-bool validateAttestationCertificate(const vector<Certificate>& inputCertificates,
+void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
const vector<uint8_t>& expectedChallenge,
- const vector<uint8_t>& expectedAppId,
- const HardwareInformation& hwInfo);
+ const vector<uint8_t>& expectedAppId, bool isTestCredential);
vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries);
+// Verifies that the X.509 certificate for a just created authentication key
+// is valid.
+//
+void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain);
+
} // namespace android::hardware::identity::test_utils
#endif // VTS_IDENTITY_TEST_UTILS_H
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index f7ec7c516f..3aa5bb641e 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -35,6 +35,9 @@ using ::std::tuple;
using ::std::vector;
using ::std::pair;
+// The semantic tag for a bstr which includes Encoded CBOR (RFC 7049, section 2.4)
+const int kSemanticTagEncodedCbor = 24;
+
// ---------------------------------------------------------------------------
// Miscellaneous utilities.
// ---------------------------------------------------------------------------
@@ -108,45 +111,47 @@ optional<vector<uint8_t>> encryptAes128Gcm(const vector<uint8_t>& key, const vec
// ---------------------------------------------------------------------------
// EC crypto functionality / abstraction (only supports P-256).
// ---------------------------------------------------------------------------
+
// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
-// PKCS#8 encoded key-pair. Also generates an attestation
-// certificate using the |challenge| and |applicationId|, and returns the generated
-// certificate in X.509 certificate chain format.
+// DER encoded private key. Also generates an attestation using the |challenge|
+// and |applicationId|, and returns the generated certificate chain.
//
-// The attestation time fields used will be the current time, and expires in one year.
+// The notBeffore field will be the current time and the notAfter will be the same
+// same time as the batch certificate.
//
// The first parameter of the return value is the keyPair generated, second return in
// the pair is the attestation certificate generated.
+//
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
- const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId);
+ const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
+ bool isTestCredential);
-// Like createEcKeyPairAndAttestation() but allows you to choose the public key.
-//
+// (TODO: remove when no longer used by 3rd party.)
optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
const vector<uint8_t>& publicKey, const vector<uint8_t>& challenge,
const vector<uint8_t>& applicationId);
// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
-// PKCS#8 encoded key-pair.
+// private key in DER format (as specified in RFC 5915).
//
optional<vector<uint8_t>> createEcKeyPair();
-// For an EC key |keyPair| encoded in PKCS#8 format, extracts the public key in
+// For an EC key |keyPair| encoded in DER format, extracts the public key in
// uncompressed point form.
//
optional<vector<uint8_t>> ecKeyPairGetPublicKey(const vector<uint8_t>& keyPair);
-// For an EC key |keyPair| encoded in PKCS#8 format, extracts the private key as
+// For an EC key |keyPair| encoded in DER format, extracts the private key as
// an EC uncompressed key.
//
optional<vector<uint8_t>> ecKeyPairGetPrivateKey(const vector<uint8_t>& keyPair);
-// Creates a PKCS#8 encoded key-pair from a private key (which must be uncompressed,
-// e.g. 32 bytes). The public key is derived from the given private key..
+// Creates a DER encoded representation from a private key (which must be uncompressed,
+// e.g. 32 bytes).
//
optional<vector<uint8_t>> ecPrivateKeyToKeyPair(const vector<uint8_t>& privateKey);
-// For an EC key |keyPair| encoded in PKCS#8 format, creates a PKCS#12 structure
+// For an EC key |keyPair| encoded in DER format, creates a PKCS#12 structure
// with the key-pair (not using a password to encrypt the data). The public key
// in the created structure is included as a certificate, using the given fields
// |serialDecimal|, |issuer|, |subject|, |validityNotBefore|, and
@@ -209,6 +214,13 @@ optional<pair<size_t, size_t>> certificateTbsCertificate(const vector<uint8_t>&
//
optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate);
+// Extracts notBefore and notAfter from the top-most certificate in |certificateChain
+// (which should be a concatenated chain of DER-encoded X.509 certificates).
+//
+// Returns notBefore and notAfter in that order.
+//
+optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x509Certificate);
+
// Generates a X.509 certificate for |publicKey| (which must be in the format
// returned by ecKeyPairGetPublicKey()).
//
@@ -351,6 +363,15 @@ optional<vector<uint8_t>> coseMacWithDigest(const vector<uint8_t>& digestToBeMac
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
+optional<vector<uint8_t>> calcMac(const vector<uint8_t>& sessionTranscriptEncoded,
+ const string& docType,
+ const vector<uint8_t>& deviceNameSpacesEncoded,
+ const vector<uint8_t>& eMacKey);
+
+optional<vector<uint8_t>> calcEMacKey(const vector<uint8_t>& privateKey,
+ const vector<uint8_t>& publicKey,
+ const vector<uint8_t>& sessionTranscriptBytes);
+
// Returns the testing AES-128 key where all bits are set to 0.
const vector<uint8_t>& getTestHardwareBoundKey();
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 747f182b76..fbf3ab12a9 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -44,6 +44,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <charconv>
#include <cppbor.h>
#include <cppbor_parse.h>
@@ -870,16 +871,97 @@ optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<ui
return hmac;
}
+int parseDigits(const char** s, int numDigits) {
+ int result;
+ auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
+ if (ec != std::errc()) {
+ LOG(ERROR) << "Error parsing " << numDigits << " digits "
+ << " from " << s;
+ return 0;
+ }
+ *s += numDigits;
+ return result;
+}
+
+bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
+ struct tm tm;
+
+ memset(&tm, '\0', sizeof(tm));
+ const char* timeStr = (const char*)asn1Time->data;
+ const char* s = timeStr;
+ if (asn1Time->type == V_ASN1_UTCTIME) {
+ tm.tm_year = parseDigits(&s, 2);
+ if (tm.tm_year < 70) {
+ tm.tm_year += 100;
+ }
+ } else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
+ tm.tm_year = parseDigits(&s, 4) - 1900;
+ tm.tm_year -= 1900;
+ } else {
+ LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
+ return false;
+ }
+ tm.tm_mon = parseDigits(&s, 2) - 1;
+ tm.tm_mday = parseDigits(&s, 2);
+ tm.tm_hour = parseDigits(&s, 2);
+ tm.tm_min = parseDigits(&s, 2);
+ tm.tm_sec = parseDigits(&s, 2);
+ // This may need to be updated if someone create certificates using +/- instead of Z.
+ //
+ if (*s != 'Z') {
+ LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
+ return false;
+ }
+
+ time_t t = timegm(&tm);
+ if (t == -1) {
+ LOG(ERROR) << "Error converting broken-down time to time_t";
+ return false;
+ }
+ *outTime = t;
+ return true;
+}
+
// Generates the attestation certificate with the parameters passed in. Note
// that the passed in |activeTimeMilliSeconds| |expireTimeMilliSeconds| are in
// milli seconds since epoch. We are setting them to milliseconds due to
// requirement in AuthorizationSet KM_DATE fields. The certificate created is
// actually in seconds.
-optional<vector<vector<uint8_t>>> createAttestation(const EVP_PKEY* key,
- const vector<uint8_t>& applicationId,
- const vector<uint8_t>& challenge,
- uint64_t activeTimeMilliSeconds,
- uint64_t expireTimeMilliSeconds) {
+//
+// If 0 is passed for expiration time, the expiration time from batch
+// certificate will be used.
+//
+optional<vector<vector<uint8_t>>> createAttestation(
+ const EVP_PKEY* key, const vector<uint8_t>& applicationId, const vector<uint8_t>& challenge,
+ uint64_t activeTimeMilliSeconds, uint64_t expireTimeMilliSeconds, bool isTestCredential) {
+ const keymaster_cert_chain_t* attestation_chain =
+ ::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
+ if (attestation_chain == nullptr) {
+ LOG(ERROR) << "Error getting attestation chain";
+ return {};
+ }
+ if (expireTimeMilliSeconds == 0) {
+ if (attestation_chain->entry_count < 1) {
+ LOG(ERROR) << "Expected at least one entry in attestation chain";
+ return {};
+ }
+ keymaster_blob_t* bcBlob = &(attestation_chain->entries[0]);
+ const uint8_t* bcData = bcBlob->data;
+ auto bc = X509_Ptr(d2i_X509(nullptr, &bcData, bcBlob->data_length));
+ time_t bcNotAfter;
+ if (!parseAsn1Time(X509_get0_notAfter(bc.get()), &bcNotAfter)) {
+ LOG(ERROR) << "Error getting notAfter from batch certificate";
+ return {};
+ }
+ expireTimeMilliSeconds = bcNotAfter * 1000;
+ }
+ const keymaster_key_blob_t* attestation_signing_key =
+ ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
+ if (attestation_signing_key == nullptr) {
+ LOG(ERROR) << "Error getting attestation key";
+ return {};
+ }
+
::keymaster::AuthorizationSet auth_set(
::keymaster::AuthorizationSetBuilder()
.Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(),
@@ -901,7 +983,7 @@ optional<vector<vector<uint8_t>>> createAttestation(const EVP_PKEY* key,
::keymaster::AuthorizationSet swEnforced(::keymaster::AuthorizationSetBuilder().Authorization(
::keymaster::TAG_CREATION_DATETIME, activeTimeMilliSeconds));
- ::keymaster::AuthorizationSet hwEnforced(
+ ::keymaster::AuthorizationSetBuilder hwEnforcedBuilder =
::keymaster::AuthorizationSetBuilder()
.Authorization(::keymaster::TAG_PURPOSE, KM_PURPOSE_SIGN)
.Authorization(::keymaster::TAG_KEY_SIZE, 256)
@@ -909,34 +991,29 @@ optional<vector<vector<uint8_t>>> createAttestation(const EVP_PKEY* key,
.Authorization(::keymaster::TAG_NO_AUTH_REQUIRED)
.Authorization(::keymaster::TAG_DIGEST, KM_DIGEST_SHA_2_256)
.Authorization(::keymaster::TAG_EC_CURVE, KM_EC_CURVE_P_256)
- .Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
-
- const keymaster_cert_chain_t* attestation_chain =
- ::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
-
- if (attestation_chain == nullptr) {
- LOG(ERROR) << "Error getting attestation chain";
- return {};
- }
+ .Authorization(::keymaster::TAG_OS_VERSION, 42)
+ .Authorization(::keymaster::TAG_OS_PATCHLEVEL, 43);
- const keymaster_key_blob_t* attestation_signing_key =
- ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
- if (attestation_signing_key == nullptr) {
- LOG(ERROR) << "Error getting attestation key";
- return {};
+ // Only include TAG_IDENTITY_CREDENTIAL_KEY if it's not a test credential
+ if (!isTestCredential) {
+ hwEnforcedBuilder.Authorization(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY);
}
+ ::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
keymaster_error_t error;
::keymaster::CertChainPtr cert_chain_out;
- ::keymaster::PureSoftKeymasterContext context;
- // set identity version to 10 per hal requirements specified in IWriteableCredential.hal
- // For now, the identity version in the attestation is set in the keymaster
- // version field in the portable keymaster lib, which is a bit misleading.
- uint identity_version = 10;
- error = generate_attestation_from_EVP(key, swEnforced, hwEnforced, auth_set, context,
- identity_version, *attestation_chain,
- *attestation_signing_key, &cert_chain_out);
+ // Pretend to be implemented in a trusted environment just so we can pass
+ // the VTS tests. Of course, this is a pretend-only game since hopefully no
+ // relying party is ever going to trust our batch key and those keys above
+ // it.
+ //
+ ::keymaster::PureSoftKeymasterContext context(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
+
+ error = generate_attestation_from_EVP_with_subject_name(
+ key, swEnforced, hwEnforced, auth_set, context, ::keymaster::kCurrentKeymasterVersion,
+ *attestation_chain, *attestation_signing_key, "Android Identity Credential Key",
+ &cert_chain_out);
if (KM_ERROR_OK != error || !cert_chain_out) {
LOG(ERROR) << "Error generate attestation from EVP key" << error;
@@ -957,7 +1034,8 @@ optional<vector<vector<uint8_t>>> createAttestation(const EVP_PKEY* key,
}
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
- const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
+ const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
+ bool isTestCredential) {
auto ec_key = ::keymaster::EC_KEY_Ptr(EC_KEY_new());
auto pkey = ::keymaster::EVP_PKEY_Ptr(EVP_PKEY_new());
auto group = ::keymaster::EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
@@ -978,12 +1056,11 @@ optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAnd
return {};
}
- uint64_t now = time(nullptr);
- uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
- uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
+ uint64_t nowMs = time(nullptr) * 1000;
+ uint64_t expireTimeMs = 0; // Set to same as batch certificate
- optional<vector<vector<uint8_t>>> attestationCert =
- createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
+ optional<vector<vector<uint8_t>>> attestationCert = createAttestation(
+ pkey.get(), applicationId, challenge, nowMs, expireTimeMs, isTestCredential);
if (!attestationCert) {
LOG(ERROR) << "Error create attestation from key and challenge";
return {};
@@ -1031,14 +1108,12 @@ optional<vector<vector<uint8_t>>> createAttestationForEcPublicKey(
return {};
}
- uint64_t now = (std::chrono::duration_cast<std::chrono::nanoseconds>(
- std::chrono::system_clock::now().time_since_epoch()).
- count()/ 1000000000);
- uint64_t secondsInOneYear = 365 * 24 * 60 * 60;
- uint64_t expireTimeMs = (now + secondsInOneYear) * 1000;
+ uint64_t nowMs = time(nullptr) * 1000;
+ uint64_t expireTimeMs = 0; // Set to same as batch certificate
optional<vector<vector<uint8_t>>> attestationCert =
- createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs);
+ createAttestation(pkey.get(), applicationId, challenge, nowMs, expireTimeMs,
+ false /* isTestCredential */);
if (!attestationCert) {
LOG(ERROR) << "Error create attestation from key and challenge";
return {};
@@ -1646,6 +1721,32 @@ optional<pair<size_t, size_t>> certificateTbsCertificate(const vector<uint8_t>&
return std::make_pair(tbsCertificateOffset, tbsCertificateSize);
}
+optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x509Certificate) {
+ vector<X509_Ptr> certs;
+ if (!parseX509Certificates(x509Certificate, certs)) {
+ LOG(ERROR) << "Error parsing certificates";
+ return {};
+ }
+ if (certs.size() < 1) {
+ LOG(ERROR) << "No certificates in chain";
+ return {};
+ }
+
+ time_t notBefore;
+ time_t notAfter;
+ if (!parseAsn1Time(X509_get0_notBefore(certs[0].get()), &notBefore)) {
+ LOG(ERROR) << "Error parsing notBefore";
+ return {};
+ }
+
+ if (!parseAsn1Time(X509_get0_notAfter(certs[0].get()), &notAfter)) {
+ LOG(ERROR) << "Error parsing notAfter";
+ return {};
+ }
+
+ return std::make_pair(notBefore, notAfter);
+}
+
optional<pair<size_t, size_t>> certificateFindSignature(const vector<uint8_t>& x509Certificate) {
vector<X509_Ptr> certs;
if (!parseX509Certificates(x509Certificate, certs)) {
@@ -2218,6 +2319,49 @@ optional<vector<uint8_t>> coseMacWithDigest(const vector<uint8_t>& digestToBeMac
// Utility functions specific to IdentityCredential.
// ---------------------------------------------------------------------------
+optional<vector<uint8_t>> calcEMacKey(const vector<uint8_t>& privateKey,
+ const vector<uint8_t>& publicKey,
+ const vector<uint8_t>& sessionTranscriptBytes) {
+ optional<vector<uint8_t>> sharedSecret = support::ecdh(publicKey, privateKey);
+ if (!sharedSecret) {
+ LOG(ERROR) << "Error performing ECDH";
+ return {};
+ }
+ vector<uint8_t> salt = support::sha256(sessionTranscriptBytes);
+ vector<uint8_t> info = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+ optional<vector<uint8_t>> derivedKey = support::hkdf(sharedSecret.value(), salt, info, 32);
+ if (!derivedKey) {
+ LOG(ERROR) << "Error performing HKDF";
+ return {};
+ }
+ return derivedKey.value();
+}
+
+optional<vector<uint8_t>> calcMac(const vector<uint8_t>& sessionTranscriptEncoded,
+ const string& docType,
+ const vector<uint8_t>& deviceNameSpacesEncoded,
+ const vector<uint8_t>& eMacKey) {
+ auto [sessionTranscriptItem, _, errMsg] = cppbor::parse(sessionTranscriptEncoded);
+ if (sessionTranscriptItem == nullptr) {
+ LOG(ERROR) << "Error parsing sessionTranscriptEncoded: " << errMsg;
+ return {};
+ }
+ // The data that is MACed is ["DeviceAuthentication", sessionTranscript, docType,
+ // deviceNameSpacesBytes] so build up that structure
+ cppbor::Array deviceAuthentication =
+ cppbor::Array()
+ .add("DeviceAuthentication")
+ .add(std::move(sessionTranscriptItem))
+ .add(docType)
+ .add(cppbor::Semantic(kSemanticTagEncodedCbor, deviceNameSpacesEncoded));
+ vector<uint8_t> deviceAuthenticationBytes =
+ cppbor::Semantic(kSemanticTagEncodedCbor, deviceAuthentication.encode()).encode();
+ optional<vector<uint8_t>> calculatedMac =
+ support::coseMac0(eMacKey, {}, // payload
+ deviceAuthenticationBytes); // detached content
+ return calculatedMac;
+}
+
vector<vector<uint8_t>> chunkVector(const vector<uint8_t>& content, size_t maxChunkSize) {
vector<vector<uint8_t>> ret;
diff --git a/identity/support/tests/IdentityCredentialSupportTest.cpp b/identity/support/tests/IdentityCredentialSupportTest.cpp
index c356549d00..266f263203 100644
--- a/identity/support/tests/IdentityCredentialSupportTest.cpp
+++ b/identity/support/tests/IdentityCredentialSupportTest.cpp
@@ -436,6 +436,300 @@ TEST(IdentityCredentialSupport, CoseMac0DetachedContent) {
support::cborPrettyPrint(mac.value()));
}
+// Generates a private key in DER format for a small value of 'd'.
+//
+// Used for test vectors.
+//
+vector<uint8_t> p256PrivateKeyFromD(uint8_t d) {
+ vector<uint8_t> privateUncompressed;
+ privateUncompressed.resize(32);
+ privateUncompressed[31] = d;
+ optional<vector<uint8_t>> privateKey = support::ecPrivateKeyToKeyPair(privateUncompressed);
+ return privateKey.value();
+}
+
+std::pair<vector<uint8_t>, vector<uint8_t>> p256PrivateKeyGetXandY(
+ const vector<uint8_t> privateKey) {
+ optional<vector<uint8_t>> publicUncompressed = support::ecKeyPairGetPublicKey(privateKey);
+ vector<uint8_t> x = vector<uint8_t>(publicUncompressed.value().begin() + 1,
+ publicUncompressed.value().begin() + 33);
+ vector<uint8_t> y = vector<uint8_t>(publicUncompressed.value().begin() + 33,
+ publicUncompressed.value().begin() + 65);
+ return std::make_pair(x, y);
+}
+
+const cppbor::Item* findValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ // TODO: Need cast until libcppbor's Map::get() is marked as const
+ auto [item, found] = ((cppbor::Map*)map)->get(keyValue);
+ if (!found) {
+ return nullptr;
+ }
+ return item.get();
+}
+
+const cppbor::Array* findArrayValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ const cppbor::Item* item = findValueForTstr(map, keyValue);
+ if (item == nullptr) {
+ return nullptr;
+ }
+ return item->asArray();
+}
+
+const cppbor::Map* findMapValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ const cppbor::Item* item = findValueForTstr(map, keyValue);
+ if (item == nullptr) {
+ return nullptr;
+ }
+ return item->asMap();
+}
+
+const cppbor::Semantic* findSemanticValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ const cppbor::Item* item = findValueForTstr(map, keyValue);
+ if (item == nullptr) {
+ return nullptr;
+ }
+ return item->asSemantic();
+}
+
+const std::string findStringValueForTstr(const cppbor::Map* map, const string& keyValue) {
+ const cppbor::Item* item = findValueForTstr(map, keyValue);
+ if (item == nullptr) {
+ return nullptr;
+ }
+ const cppbor::Tstr* tstr = item->asTstr();
+ if (tstr == nullptr) {
+ return "";
+ }
+ return tstr->value();
+}
+
+TEST(IdentityCredentialSupport, testVectors_18013_5) {
+ // This is a test against known vectors for ISO 18013-5.
+ //
+ // The objective of this test is to verify that support::calcEMacKey() and
+ // support::calcMac() agree with the given test vectors.
+ //
+
+ // We're given static device key:
+ //
+ // x: 28412803729898893058558238221310261427084375743576167377786533380249859400145
+ // y: 65403602826180996396520286939226973026599920614829401631985882360676038096704
+ // d: 11
+ //
+ vector<uint8_t> deviceKey = p256PrivateKeyFromD(11);
+ auto [deviceKeyX, deviceKeyY] = p256PrivateKeyGetXandY(deviceKey);
+ EXPECT_EQ(support::encodeHex(deviceKeyX),
+ "3ed113b7883b4c590638379db0c21cda16742ed0255048bf433391d374bc21d1");
+ EXPECT_EQ(support::encodeHex(deviceKeyY),
+ "9099209accc4c8a224c843afa4f4c68a090d04da5e9889dae2f8eefce82a3740");
+
+ // We're given Ephemeral reader key:
+ //
+ // x: 59535862115950685744176693329402396749019581632805653266809849538337418304154
+ // y: 53776829996815113213100700404832701936765102413212294632483274374518863708344
+ // d: 20
+ //
+ vector<uint8_t> ephemeralReaderKey = p256PrivateKeyFromD(20);
+ auto [ephemeralReaderKeyX, ephemeralReaderKeyY] = p256PrivateKeyGetXandY(ephemeralReaderKey);
+ EXPECT_EQ(support::encodeHex(ephemeralReaderKeyX),
+ "83a01a9378395bab9bcd6a0ad03cc56d56e6b19250465a94a234dc4c6b28da9a");
+ EXPECT_EQ(support::encodeHex(ephemeralReaderKeyY),
+ "76e49b6de2f73234ae6a5eb9d612b75c9f2202bb6923f54ff8240aaa86f640b8");
+ vector<uint8_t> ephemeralReaderKeyPublic =
+ support::ecKeyPairGetPublicKey(ephemeralReaderKey).value();
+
+ // We're given SessionEstablishment.
+ //
+ // SessionEstablishment = {
+ // "eReaderKey" : EReaderKeyBytes,
+ // "data" : bstr ; Encrypted mdoc request
+ // }
+ //
+ // Fish out EReaderKey from this.
+ //
+ // Note that the test vector below is incorrect insofar that it uses
+ // "eReaderKeyBytes" instead of just "eReaderKey". This will be corrected in
+ // the future.
+ //
+ optional<vector<uint8_t>> sessionEstablishmentEncoded = support::decodeHex(
+ "a26f655265616465724b65794279746573d818584ba40102200121582083a01a9378395bab9bcd6a0ad03c"
+ "c56d56e6b19250465a94a234dc4c6b28da9a22582076e49b6de2f73234ae6a5eb9d612b75c9f2202bb6923"
+ "f54ff8240aaa86f640b864646174615902d945b31040c57491acb6d46a71f6c1f67a0b837df1bda9089fd0"
+ "3d0b1fdac3eeb2874a4ef6f90c97d03397186ba00a91102faae7e992e15f761d5662c3c37e3c6c2cfd2ebc"
+ "0bf59dbb8795e377bd7dd353230a41ba2d82294b45871a39b42ca531f26b52f46e356fbaf5075c8fd5b8b0"
+ "8a0df4a1d2e1bdd2e5d69169c1efbb51e393e608d833d325bebfbccb2e15ec08f94b264582fa7b93f7cebc"
+ "aa69f4f0cac2744d4fe35b04df26b2ae69273eed33024949080c1c95a6ef046beede959e9494297dd770af"
+ "4ac6fdd56783aa012555c213dc05cf0f41d1c95119720fcfe1621027f80e2ddd56ea3c1fc596f7b2579333"
+ "5a887ec788092b4a69d23b6219e27d0249b50b3fdcb95b5227007689362e0416b3bae3dae7cb56b4394666"
+ "4e3a3f60dce8d0b678fcd754bebf87bd2b0278dd782d952488a46f2874e34c2dd97bb74084a62b850e9719"
+ "252cd1dca7dbf1858193f6cf093cb3735312bbe1138cf29d8f350e285923f8ef07065299926720b42264e8"
+ "fd5d4b133e72f47c4e999ea689c353f8b41e50a59838e1a0d09eca4a557f77a9c389a0591ad1639119ce86"
+ "edc3320130480ee5101effae6066e8c85aac9ead2ae83e49c1e508aab02f753decbb522ea2200d62fd5d26"
+ "094bd35100bffaa1cdc6af9f7e9cfe7b63da6b5671cd5ac2cf5da450c72addc64cde441f3b7f7fdaf930ad"
+ "1e13388e8a7308d8ca4607e59e082db431a232e7e12cb692baeb4b2127e110ff24cea322ffdbc2e4d9c4c6"
+ "bed27753137d07897c8613627a799a560cf1a2d1edb3de029442862940a5ed7785eea8b6ace93aa6af0792"
+ "fd82877f62d07b757d0179ecbb7347004ecc9c0690d41f75f188cb17ffd2cec2ad8c9675466bb33b737a2a"
+ "e7592b2dcb8132aced2e572266f3f5413a5f9d6d4339a1e4662622af2e7e157a4ea3bfd5c4247e2ec91d8c"
+ "5c3c17427d5edfae673d0e0f782a8d40fa805fd8bc82ae3cb21a65cdad863e02309f6b01d1753fa884b778"
+ "f6e019a2004d8964deeb11f1fd478fcb");
+ ASSERT_TRUE(sessionEstablishmentEncoded);
+ auto [sessionEstablishmentItem, _se, _se2] = cppbor::parse(sessionEstablishmentEncoded.value());
+ const cppbor::Map* sessionEstablishment = sessionEstablishmentItem->asMap();
+ ASSERT_NE(sessionEstablishment, nullptr);
+ const cppbor::Semantic* eReaderKeyBytes =
+ findSemanticValueForTstr(sessionEstablishment, "eReaderKeyBytes");
+ ASSERT_NE(eReaderKeyBytes, nullptr);
+ ASSERT_EQ(eReaderKeyBytes->value(), 24);
+ const cppbor::Bstr* eReaderKeyBstr = eReaderKeyBytes->child()->asBstr();
+ ASSERT_NE(eReaderKeyBstr, nullptr);
+ vector<uint8_t> eReaderKeyEncoded = eReaderKeyBstr->value();
+ // TODO: verify this agrees with ephemeralReaderKeyX and ephemeralReaderKeyY
+
+ // We're given DeviceEngagement.
+ //
+ vector<uint8_t> deviceEngagementEncoded =
+ support::decodeHex(
+ "a20063312e30018201d818584ba401022001215820cef66d6b2a3a993e591214d1ea223fb545ca"
+ "6c471c48306e4c36069404c5723f225820878662a229aaae906e123cdd9d3b4c10590ded29fe75"
+ "1eeeca34bbaa44af0773")
+ .value();
+
+ // Now calculate SessionTranscriptBytes. It is defined as
+ //
+ // SessionTranscript = [
+ // DeviceEngagementBytes,
+ // EReaderKeyBytes,
+ // Handover
+ // ]
+ //
+ // SessionTranscriptBytes = #6.24(bstr .cbor SessionTranscript)
+ //
+ cppbor::Array sessionTranscript;
+ sessionTranscript.add(cppbor::Semantic(24, deviceEngagementEncoded));
+ sessionTranscript.add(cppbor::Semantic(24, eReaderKeyEncoded));
+ sessionTranscript.add(cppbor::Null());
+ vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
+ vector<uint8_t> sessionTranscriptBytes =
+ cppbor::Semantic(24, sessionTranscriptEncoded).encode();
+
+ // The expected EMacKey is 4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2
+ //
+ // Verify that support::calcEMacKey() gets the same result.
+ //
+ optional<vector<uint8_t>> eMacKey =
+ support::calcEMacKey(support::ecKeyPairGetPrivateKey(deviceKey).value(), // private key
+ ephemeralReaderKeyPublic, // public key
+ sessionTranscriptBytes); // sessionTranscriptBytes
+ ASSERT_TRUE(eMacKey);
+ ASSERT_EQ(support::encodeHex(eMacKey.value()),
+ "4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2");
+
+ // Also do it the other way around
+ //
+ optional<vector<uint8_t>> eMacKey2 = support::calcEMacKey(
+ support::ecKeyPairGetPrivateKey(ephemeralReaderKey).value(), // private key
+ support::ecKeyPairGetPublicKey(deviceKey).value(), // public key
+ sessionTranscriptBytes); // sessionTranscriptBytes
+ ASSERT_TRUE(eMacKey2);
+ ASSERT_EQ(support::encodeHex(eMacKey2.value()),
+ "4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2");
+
+ // We're given DeviceResponse
+ //
+ vector<uint8_t> deviceResponseEncoded =
+ support::decodeHex(
+ "a36776657273696f6e63312e3069646f63756d656e747381a367646f6354797065756f72672e69"
+ "736f2e31383031332e352e312e6d444c6c6973737565725369676e6564a26a6e616d6553706163"
+ "6573a2716f72672e69736f2e31383031332e352e3181d8185863a4686469676573744944016672"
+ "616e646f6d58208798645b20ea200e19ffabac92624bee6aec63aceedecfb1b80077d22bfc20e9"
+ "71656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456"
+ "616c756563446f656b636f6d2e6578616d706c6581d8185864a468646967657374494401667261"
+ "6e646f6d5820218ecf13521b53f4b96abaebe56417afec0e4c91fc8fb26086cd1e5cdc1a94ff71"
+ "656c656d656e744964656e7469666965726f616e6f746865725f656c656d656e746c656c656d65"
+ "6e7456616c75650a6a697373756572417574688443a10126a118215901d2308201ce30820174a0"
+ "0302010202141f7d44f4f107c5ee3f566049cf5d72de294b0d23300a06082a8648ce3d04030230"
+ "233114301206035504030c0b75746f7069612069616361310b3009060355040613025553301e17"
+ "0d3230313030313030303030305a170d3231313030313030303030305a30213112301006035504"
+ "030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106"
+ "082a8648ce3d03010703420004301d9e502dc7e05da85da026a7ae9aa0fac9db7d52a95b3e3e3f"
+ "9aa0a1b45b8b6551b6f6b3061223e0d23c026b017d72298d9ae46887ca61d58db6aea17ee267a3"
+ "8187308184301e0603551d120417301581136578616d706c65406578616d706c652e636f6d301c"
+ "0603551d1f041530133011a00fa00d820b6578616d706c652e636f6d301d0603551d0e04160414"
+ "7bef4db59a1ffb07592bfc57f4743b8a73aea792300e0603551d0f0101ff040403020780301506"
+ "03551d250101ff040b3009060728818c5d050102300a06082a8648ce3d04030203480030450220"
+ "21d52fb1fbda80e5bfda1e8dfb1bc7bf0acb7261d5c9ff54425af76eb21571c602210082bf301f"
+ "89e0a2cb9ca9c9050352de80b47956764f7a3e07bf6a8cd87528a3b55901d2d8185901cda66776"
+ "657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c75"
+ "6544696765737473a2716f72672e69736f2e31383031332e352e31a20058203b22af1126771f02"
+ "f0ea0d546d4ee3c5b51637381154f5211b79daf5f9facaa8015820f2cba0ce3cde5df901a3da75"
+ "13a4d7f7225fdfe5a306544529bf3dbcce655ca06b636f6d2e6578616d706c65a200582072636d"
+ "ddc282424a63499f4b3927aaa3b74da7b9c0134178bf735e949e4a761e01582006322d3cbe6603"
+ "876bdacc5b6679b51b0fc53d029c244fd5ea719d9028459c916d6465766963654b6579496e666f"
+ "a1696465766963654b6579a4010220012158203ed113b7883b4c590638379db0c21cda16742ed0"
+ "255048bf433391d374bc21d12258209099209accc4c8a224c843afa4f4c68a090d04da5e9889da"
+ "e2f8eefce82a374067646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c"
+ "76616c6964697479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a"
+ "30325a6976616c696446726f6dc074323032302d31302d30315431333a33303a30325a6a76616c"
+ "6964556e74696cc074323032312d31302d30315431333a33303a30325a5840273ec1b59817d571"
+ "b5a2c5c0ab0ea213d42acb18547fd7097afcc888a22ecbb863c6461ce0e240880895b4aaa84308"
+ "784571c7be7aa3a2e7e3a2ea1a145ed1966c6465766963655369676e6564a26a6e616d65537061"
+ "636573d81841a06a64657669636541757468a1696465766963654d61638443a10105a0f6582009"
+ "da7c964ac004ec36ec64edd0c1abf50c03433c215c3ddb144768abcdf20a60667374617475730"
+ "0")
+ .value();
+ auto [deviceResponseItem, _, _2] = cppbor::parse(deviceResponseEncoded);
+ const cppbor::Map* deviceResponse = deviceResponseItem->asMap();
+ ASSERT_NE(deviceResponse, nullptr);
+ const cppbor::Array* documents = findArrayValueForTstr(deviceResponse, "documents");
+ ASSERT_NE(documents, nullptr);
+ ASSERT_EQ(documents->size(), 1);
+ const cppbor::Map* document = ((*documents)[0])->asMap();
+ ASSERT_NE(document, nullptr);
+
+ // Get docType
+ string docType = findStringValueForTstr(document, "docType");
+ ASSERT_EQ(docType, "org.iso.18013.5.1.mDL");
+
+ // Drill down...
+ const cppbor::Map* deviceSigned = findMapValueForTstr(document, "deviceSigned");
+ ASSERT_NE(deviceSigned, nullptr);
+
+ // Dig out the encoded form of DeviceNameSpaces
+ //
+ const cppbor::Semantic* deviceNameSpacesBytes =
+ findSemanticValueForTstr(deviceSigned, "nameSpaces");
+ ASSERT_NE(deviceNameSpacesBytes, nullptr);
+ ASSERT_EQ(deviceNameSpacesBytes->value(), 24);
+ const cppbor::Bstr* deviceNameSpacesBstr = deviceNameSpacesBytes->child()->asBstr();
+ ASSERT_NE(deviceNameSpacesBstr, nullptr);
+ vector<uint8_t> deviceNameSpacesEncoded = deviceNameSpacesBstr->value();
+
+ // (For this version of 18013-5, DeviceNameSpaces is always supposed to be empty, check that.)
+ EXPECT_EQ(deviceNameSpacesEncoded, cppbor::Map().encode());
+
+ const cppbor::Map* deviceAuth = findMapValueForTstr(deviceSigned, "deviceAuth");
+ ASSERT_NE(deviceAuth, nullptr);
+ // deviceMac is is the COSE_Mac0.. dig out the encoded form to check that
+ // support::calcMac() gives exactly the same bytes.
+ //
+ const cppbor::Array* deviceMac = findArrayValueForTstr(deviceAuth, "deviceMac");
+ ASSERT_NE(deviceMac, nullptr);
+ vector<uint8_t> deviceMacEncoded = deviceMac->encode();
+
+ // Now we calculate what it should be..
+ optional<vector<uint8_t>> calculatedMac =
+ support::calcMac(sessionTranscriptEncoded, // SessionTranscript
+ docType, // DocType
+ deviceNameSpacesEncoded, // DeviceNamespaces
+ eMacKey.value()); // EMacKey
+ ASSERT_TRUE(calculatedMac);
+
+ // ... and hopefully it's the same!
+ ASSERT_EQ(calculatedMac.value().size(), deviceMacEncoded.size());
+ EXPECT_TRUE(memcmp(calculatedMac.value().data(), deviceMacEncoded.data(),
+ deviceMacEncoded.size()) == 0);
+}
+
} // namespace identity
} // namespace hardware
} // namespace android
diff --git a/keymaster/4.1/support/attestation_record.cpp b/keymaster/4.1/support/attestation_record.cpp
index 598b6b50c4..207a7e8264 100644
--- a/keymaster/4.1/support/attestation_record.cpp
+++ b/keymaster/4.1/support/attestation_record.cpp
@@ -102,6 +102,7 @@ typedef struct km_auth_list {
ASN1_INTEGER* boot_patchlevel;
ASN1_NULL* early_boot_only;
ASN1_NULL* device_unique_attestation;
+ ASN1_NULL* identity_credential_key;
} KM_AUTH_LIST;
ASN1_SEQUENCE(KM_AUTH_LIST) = {
@@ -145,6 +146,8 @@ ASN1_SEQUENCE(KM_AUTH_LIST) = {
ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
ASN1_EXP_OPT(KM_AUTH_LIST, device_unique_attestation, ASN1_NULL,
TAG_DEVICE_UNIQUE_ATTESTATION.maskedTag()),
+ ASN1_EXP_OPT(KM_AUTH_LIST, identity_credential_key, ASN1_NULL,
+ TAG_IDENTITY_CREDENTIAL_KEY.maskedTag()),
} ASN1_SEQUENCE_END(KM_AUTH_LIST);
IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
@@ -285,6 +288,7 @@ static ErrorCode extract_auth_list(const KM_AUTH_LIST* record, AuthorizationSet*
copyAuthTag(record->unlocked_device_required, TAG_UNLOCKED_DEVICE_REQUIRED, auth_list);
copyAuthTag(record->early_boot_only, TAG_EARLY_BOOT_ONLY, auth_list);
copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
+ copyAuthTag(record->identity_credential_key, TAG_IDENTITY_CREDENTIAL_KEY, auth_list);
return ErrorCode::OK;
}
@@ -327,7 +331,10 @@ std::tuple<ErrorCode, AttestationRecord> parse_attestation_record(const hidl_vec
p = attest_rec->data;
KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, attest_rec->length));
- if (!record.get()) return {ErrorCode::UNKNOWN_ERROR, {}};
+ if (!record.get()) {
+ LOG(ERROR) << "Unable to get key description";
+ return {ErrorCode::UNKNOWN_ERROR, {}};
+ }
AttestationRecord result;
@@ -352,10 +359,12 @@ std::tuple<ErrorCode, AttestationRecord> parse_attestation_record(const hidl_vec
if (error != ErrorCode::OK) return {error, {}};
KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+ SecurityLevel root_of_trust_security_level = SecurityLevel::TRUSTED_ENVIRONMENT;
if (record->tee_enforced && record->tee_enforced->root_of_trust) {
root_of_trust = record->tee_enforced->root_of_trust;
} else if (record->software_enforced && record->software_enforced->root_of_trust) {
root_of_trust = record->software_enforced->root_of_trust;
+ root_of_trust_security_level = SecurityLevel::SOFTWARE;
} else {
LOG(ERROR) << AT << " Failed root of trust parsing";
return {ErrorCode::INVALID_ARGUMENT, {}};
@@ -373,6 +382,7 @@ std::tuple<ErrorCode, AttestationRecord> parse_attestation_record(const hidl_vec
rot.verified_boot_state = static_cast<keymaster_verified_boot_t>(
ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
rot.device_locked = root_of_trust->device_locked;
+ rot.security_level = root_of_trust_security_level;
auto& vb_hash = root_of_trust->verified_boot_hash;
if (!vb_hash) {