diff options
Diffstat (limited to 'identity')
16 files changed, 914 insertions, 610 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()), ¬Before)); + 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()), ¬After)); + 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()), ¬After)); + ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), ¬Before)); + + // 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()), ¬Before)) { + LOG(ERROR) << "Error parsing notBefore"; + return {}; + } + + if (!parseAsn1Time(X509_get0_notAfter(certs[0].get()), ¬After)) { + 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 |