Merge "identity: Fix attestation and documentation problems."

This commit is contained in:
Treehugger Robot
2020-11-19 15:52:21 +00:00
committed by Gerrit Code Review
17 changed files with 931 additions and 617 deletions

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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;

View File

@@ -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_;

View File

@@ -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(

View File

@@ -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",

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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,63 +182,269 @@ 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) {
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;
}
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;
}
// 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()));
} else {
// app id not found
EXPECT_EQ(0, expectedAppId.size());
}
EXPECT_TRUE(certParser_.getHwEnforcedBool(::keymaster::TAG_IDENTITY_CREDENTIAL_KEY));
EXPECT_FALSE(certParser_.getHwEnforcedBool(::keymaster::TAG_INCLUDE_UNIQUE_ID));
// 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()));
// 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());
} 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());
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()));
// - serialNumber: INTEGER 1 (fixed value: same on all certs).
EXPECT_EQ(1, ASN1_INTEGER_get(X509_get_serialNumber(cert.get())));
// - signature: must be set to ECDSA.
EXPECT_EQ(NID_ecdsa_with_SHA256, X509_get_signature_nid(cert.get()));
// - 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 {
EXPECT_TRUE(hasIcKeyTag);
}
// - 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) {
vector<RequestNamespace> ret;
RequestNamespace curNs;

View File

@@ -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

View File

@@ -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);
// Like createEcKeyPairAndAttestation() but allows you to choose the public key.
//
optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId,
bool isTestCredential);
// (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();

View File

@@ -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));
.Authorization(::keymaster::TAG_OS_VERSION, 42)
.Authorization(::keymaster::TAG_OS_PATCHLEVEL, 43);
const keymaster_cert_chain_t* attestation_chain =
::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
if (attestation_chain == nullptr) {
LOG(ERROR) << "Error getting attestation chain";
return {};
}
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;

View File

@@ -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

View File

@@ -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) {