diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp index ba2062d7c0..89f7f35696 100644 --- a/identity/aidl/default/WritableIdentityCredential.cpp +++ b/identity/aidl/default/WritableIdentityCredential.cpp @@ -16,6 +16,9 @@ #define LOG_TAG "WritableIdentityCredential" +#include "WritableIdentityCredential.h" +#include "IdentityCredentialStore.h" + #include #include @@ -23,6 +26,8 @@ #include #include +#include + #include "IdentityCredentialStore.h" #include "Util.h" #include "WritableIdentityCredential.h" @@ -33,26 +38,6 @@ using ::std::optional; using namespace ::android::hardware::identity; bool WritableIdentityCredential::initialize() { - optional> keyPair = support::createEcKeyPair(); - if (!keyPair) { - LOG(ERROR) << "Error creating credentialKey"; - return false; - } - - optional> pubKey = support::ecKeyPairGetPublicKey(keyPair.value()); - if (!pubKey) { - LOG(ERROR) << "Error getting public part of credentialKey"; - return false; - } - credentialPubKey_ = pubKey.value(); - - optional> privKey = support::ecKeyPairGetPrivateKey(keyPair.value()); - if (!privKey) { - LOG(ERROR) << "Error getting private part of credentialKey"; - return false; - } - credentialPrivKey_ = privKey.value(); - optional> random = support::getRandom(16); if (!random) { LOG(ERROR) << "Error creating storageKey"; @@ -63,88 +48,54 @@ bool WritableIdentityCredential::initialize() { return true; } -// TODO: use |attestationApplicationId| and |attestationChallenge| and also -// ensure the returned certificate chain satisfy the requirements listed in -// the docs for IWritableIdentityCredential::getAttestationCertificate() -// +// This function generates the attestation certificate using the passed in +// |attestationApplicationId| and |attestationChallenge|. It will generate an +// attestation certificate with current time and expires one year from now. The +// certificate shall contain all values as specified in hal. ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate( - const vector& /*attestationApplicationId*/, - const vector& /*attestationChallenge*/, vector* outCertificateChain) { - // For now, we dynamically generate an attestion key on each and every - // request and use that to sign CredentialKey. In a real implementation this - // would look very differently. - optional> attestationKeyPair = support::createEcKeyPair(); - if (!attestationKeyPair) { - return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( - IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey")); - } - - optional> attestationPubKey = - support::ecKeyPairGetPublicKey(attestationKeyPair.value()); - if (!attestationPubKey) { + const vector& attestationApplicationId, // + const vector& attestationChallenge, // + vector* outCertificateChain) { + if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_FAILED, - "Error getting public part of attestationKey")); + "Error attestation certificate previously generated")); } - optional> attestationPrivKey = - support::ecKeyPairGetPrivateKey(attestationKeyPair.value()); - if (!attestationPrivKey) { + vector challenge(attestationChallenge.begin(), attestationChallenge.end()); + vector appId(attestationApplicationId.begin(), attestationApplicationId.end()); + + optional, vector>>> keyAttestationPair = + support::createEcKeyPairAndAttestation(challenge, appId); + if (!keyAttestationPair) { + LOG(ERROR) << "Error creating credentialKey and attestation"; return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_FAILED, - "Error getting private part of attestationKey")); + "Error creating credentialKey and attestation")); } - string serialDecimal; - string issuer; - string subject; - time_t validityNotBefore = time(nullptr); - time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600; + vector keyPair = keyAttestationPair.value().first; + certificateChain_ = keyAttestationPair.value().second; - // First create a certificate for |credentialPubKey| which is signed by - // |attestationPrivKey|. - // - serialDecimal = "0"; // TODO: set serial to |attestationChallenge| - issuer = "Android Open Source Project"; - subject = "Android IdentityCredential CredentialKey"; - optional> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate( - credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject, - validityNotBefore, validityNotAfter); - if (!credentialPubKeyCertificate) { + optional> pubKey = support::ecKeyPairGetPublicKey(keyPair); + if (!pubKey) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_FAILED, - "Error creating certificate for credentialPubKey")); + "Error getting public part of credentialKey")); } + credentialPubKey_ = pubKey.value(); - // This is followed by a certificate for |attestationPubKey| self-signed by - // |attestationPrivKey|. - serialDecimal = "0"; // TODO: set serial - issuer = "Android Open Source Project"; - subject = "Android IdentityCredential AttestationKey"; - optional> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate( - attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject, - validityNotBefore, validityNotAfter); - if (!attestationKeyCertificate) { + optional> privKey = support::ecKeyPairGetPrivateKey(keyPair); + if (!privKey) { return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( IIdentityCredentialStore::STATUS_FAILED, - "Error creating certificate for attestationPubKey")); + "Error getting private part of credentialKey")); } + credentialPrivKey_ = privKey.value(); - // Concatenate the certificates to form the chain. - vector certificateChain; - certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(), - credentialPubKeyCertificate.value().end()); - certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(), - attestationKeyCertificate.value().end()); - - optional>> splitCertChain = - support::certificateChainSplit(certificateChain); - if (!splitCertChain) { - return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage( - IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain")); - } + // convert from vector>> to vector* *outCertificateChain = vector(); - for (const vector& cert : splitCertChain.value()) { + for (const vector& cert : certificateChain_) { Certificate c = Certificate(); c.encodedCertificate = byteStringToSigned(cert); outCertificateChain->push_back(std::move(c)); diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h index b380f897a1..b182862862 100644 --- a/identity/aidl/default/WritableIdentityCredential.h +++ b/identity/aidl/default/WritableIdentityCredential.h @@ -64,10 +64,13 @@ class WritableIdentityCredential : public BnWritableIdentityCredential { string docType_; bool testCredential_; - // These are set in initialize(). + // This is set in initialize(). vector storageKey_; + + // These are set in getAttestationCertificate(). vector credentialPrivKey_; vector credentialPubKey_; + vector> certificateChain_; // These fields are initialized during startPersonalization() size_t numAccessControlProfileRemaining_; diff --git a/identity/support/Android.bp b/identity/support/Android.bp index 7b4546b20f..2b6c695442 100644 --- a/identity/support/Android.bp +++ b/identity/support/Android.bp @@ -23,10 +23,14 @@ cc_library { "include", ], shared_libs: [ + "android.hardware.keymaster@4.0", "libcrypto", "libbase", "libhidlbase", "libhardware", + "libkeymaster_portable", + "libsoft_attestation_cert", + "libpuresoftkeymasterdevice", ], static_libs: [ "libcppbor", diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h index 4533ad946c..507e914fa3 100644 --- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h +++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h @@ -21,6 +21,7 @@ #include #include #include +#include #include namespace android { @@ -106,6 +107,17 @@ optional> encryptAes128Gcm(const vector& 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. +// +// The attestation time fields used will be the current time, and expires in one year. +// +// The first parameter of the return value is the keyPair generated, second return in +// the pair is the attestation certificate generated. +optional, vector>>> createEcKeyPairAndAttestation( + const vector& challenge, const vector& applicationId); // Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the // PKCS#8 encoded key-pair. diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp index e2828bf2bd..bf6a5c3c45 100644 --- a/identity/support/src/IdentityCredentialSupport.cpp +++ b/identity/support/src/IdentityCredentialSupport.cpp @@ -47,6 +47,13 @@ #include #include +#include +#include +#include +#include +#include +#include + namespace android { namespace hardware { namespace identity { @@ -816,6 +823,138 @@ optional> hmacSha256(const vector& key, const vector>> createAttestation(const EVP_PKEY* key, + const vector& applicationId, + const vector& challenge, + uint64_t activeTimeMilliSeconds, + uint64_t expireTimeMilliSeconds) { + ::keymaster::AuthorizationSet auth_set( + ::keymaster::AuthorizationSetBuilder() + .Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(), + challenge.size()) + .Authorization(::keymaster::TAG_ACTIVE_DATETIME, activeTimeMilliSeconds) + // Even though identity attestation hal said the application + // id should be in software enforced authentication set, + // keymaster portable lib expect the input in this + // parameter because the software enforced in input to keymaster + // refers to the key software enforced properties. And this + // parameter refers to properties of the attestation which + // includes app id. + .Authorization(::keymaster::TAG_ATTESTATION_APPLICATION_ID, + applicationId.data(), applicationId.size()) + .Authorization(::keymaster::TAG_USAGE_EXPIRE_DATETIME, expireTimeMilliSeconds)); + + // Unique id and device id is not applicable for identity credential attestation, + // so we don't need to set those or application id. + ::keymaster::AuthorizationSet swEnforced(::keymaster::AuthorizationSetBuilder().Authorization( + ::keymaster::TAG_CREATION_DATETIME, activeTimeMilliSeconds)); + + ::keymaster::AuthorizationSet hwEnforced( + ::keymaster::AuthorizationSetBuilder() + .Authorization(::keymaster::TAG_PURPOSE, KM_PURPOSE_SIGN) + .Authorization(::keymaster::TAG_KEY_SIZE, 256) + .Authorization(::keymaster::TAG_ALGORITHM, KM_ALGORITHM_EC) + .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 {}; + } + + 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_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); + + if (KM_ERROR_OK != error || !cert_chain_out) { + LOG(ERROR) << "Error generate attestation from EVP key" << error; + return {}; + } + + // translate certificate format from keymaster_cert_chain_t to vector. + vector> attestationCertificate; + for (int i = 0; i < cert_chain_out->entry_count; i++) { + attestationCertificate.insert( + attestationCertificate.end(), + vector( + cert_chain_out->entries[i].data, + cert_chain_out->entries[i].data + cert_chain_out->entries[i].data_length)); + } + + return attestationCertificate; +} + +optional, vector>>> createEcKeyPairAndAttestation( + const vector& challenge, const vector& applicationId) { + 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)); + + if (ec_key.get() == nullptr || pkey.get() == nullptr) { + LOG(ERROR) << "Memory allocation failed"; + return {}; + } + + if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 || + EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) { + LOG(ERROR) << "Error generating key"; + return {}; + } + + if (EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()) != 1) { + LOG(ERROR) << "Error getting private key"; + return {}; + } + + uint64_t now = time(nullptr); + uint64_t secondsInOneYear = 365 * 24 * 60 * 60; + uint64_t expireTimeMs = (now + secondsInOneYear) * 1000; + + optional>> attestationCert = + createAttestation(pkey.get(), applicationId, challenge, now * 1000, expireTimeMs); + if (!attestationCert) { + LOG(ERROR) << "Error create attestation from key and challenge"; + return {}; + } + + int size = i2d_PrivateKey(pkey.get(), nullptr); + if (size == 0) { + LOG(ERROR) << "Error generating public key encoding"; + return {}; + } + + vector keyPair(size); + unsigned char* p = keyPair.data(); + i2d_PrivateKey(pkey.get(), &p); + + return make_pair(keyPair, attestationCert.value()); +} + optional> createEcKeyPair() { auto ec_key = EC_KEY_Ptr(EC_KEY_new()); auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h index 6c186f6429..40eb1426ef 100644 --- a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h +++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h @@ -101,6 +101,7 @@ using V4_0::TAG_VENDOR_PATCHLEVEL; DECLARE_KM_4_1_TYPED_TAG(EARLY_BOOT_ONLY); DECLARE_KM_4_1_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION); DECLARE_KM_4_1_TYPED_TAG(STORAGE_KEY); +DECLARE_KM_4_1_TYPED_TAG(IDENTITY_CREDENTIAL_KEY); } // namespace android::hardware::keymaster::V4_1