Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93bba950 authored by Selene Huang's avatar Selene Huang Committed by Gerrit Code Review
Browse files

Merge "Add attestation certificate generation and identity credential tags."

parents 19b9aad8 459cb808
Loading
Loading
Loading
Loading
+35 −84
Original line number Original line Diff line number Diff line
@@ -16,6 +16,9 @@


#define LOG_TAG "WritableIdentityCredential"
#define LOG_TAG "WritableIdentityCredential"


#include "WritableIdentityCredential.h"
#include "IdentityCredentialStore.h"

#include <android/hardware/identity/support/IdentityCredentialSupport.h>
#include <android/hardware/identity/support/IdentityCredentialSupport.h>


#include <android-base/logging.h>
#include <android-base/logging.h>
@@ -23,6 +26,8 @@
#include <cppbor/cppbor.h>
#include <cppbor/cppbor.h>
#include <cppbor/cppbor_parse.h>
#include <cppbor/cppbor_parse.h>


#include <utility>

#include "IdentityCredentialStore.h"
#include "IdentityCredentialStore.h"
#include "Util.h"
#include "Util.h"
#include "WritableIdentityCredential.h"
#include "WritableIdentityCredential.h"
@@ -33,26 +38,6 @@ using ::std::optional;
using namespace ::android::hardware::identity;
using namespace ::android::hardware::identity;


bool WritableIdentityCredential::initialize() {
bool WritableIdentityCredential::initialize() {
    optional<vector<uint8_t>> keyPair = support::createEcKeyPair();
    if (!keyPair) {
        LOG(ERROR) << "Error creating credentialKey";
        return false;
    }

    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
    if (!pubKey) {
        LOG(ERROR) << "Error getting public part of credentialKey";
        return false;
    }
    credentialPubKey_ = pubKey.value();

    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair.value());
    if (!privKey) {
        LOG(ERROR) << "Error getting private part of credentialKey";
        return false;
    }
    credentialPrivKey_ = privKey.value();

    optional<vector<uint8_t>> random = support::getRandom(16);
    optional<vector<uint8_t>> random = support::getRandom(16);
    if (!random) {
    if (!random) {
        LOG(ERROR) << "Error creating storageKey";
        LOG(ERROR) << "Error creating storageKey";
@@ -63,88 +48,54 @@ bool WritableIdentityCredential::initialize() {
    return true;
    return true;
}
}


// TODO: use |attestationApplicationId| and |attestationChallenge| and also
// This function generates the attestation certificate using the passed in
//       ensure the returned certificate chain satisfy the requirements listed in
// |attestationApplicationId| and |attestationChallenge|.  It will generate an
//       the docs for IWritableIdentityCredential::getAttestationCertificate()
// 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(
ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
        const vector<int8_t>& /*attestationApplicationId*/,
        const vector<int8_t>& attestationApplicationId,  //
        const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* outCertificateChain) {
        const vector<int8_t>& attestationChallenge,      //
    // For now, we dynamically generate an attestion key on each and every
        vector<Certificate>* outCertificateChain) {
    // request and use that to sign CredentialKey. In a real implementation this
    if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
    // would look very differently.
    optional<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
    if (!attestationKeyPair) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey"));
                IIdentityCredentialStore::STATUS_FAILED,
                "Error attestation certificate previously generated"));
    }
    }


    optional<vector<uint8_t>> attestationPubKey =
    vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
            support::ecKeyPairGetPublicKey(attestationKeyPair.value());
    vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
    if (!attestationPubKey) {

    optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
            support::createEcKeyPairAndAttestation(challenge, appId);
    if (!keyAttestationPair) {
        LOG(ERROR) << "Error creating credentialKey and attestation";
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_FAILED,
                IIdentityCredentialStore::STATUS_FAILED,
                "Error getting public part of attestationKey"));
                "Error creating credentialKey and attestation"));
    }
    }


    optional<vector<uint8_t>> attestationPrivKey =
    vector<uint8_t> keyPair = keyAttestationPair.value().first;
            support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
    certificateChain_ = keyAttestationPair.value().second;
    if (!attestationPrivKey) {

        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
                IIdentityCredentialStore::STATUS_FAILED,
    if (!pubKey) {
                "Error getting private part of attestationKey"));
    }

    string serialDecimal;
    string issuer;
    string subject;
    time_t validityNotBefore = time(nullptr);
    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;

    // 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<vector<uint8_t>> credentialPubKeyCertificate = support::ecPublicKeyGenerateCertificate(
            credentialPubKey_, attestationPrivKey.value(), serialDecimal, issuer, subject,
            validityNotBefore, validityNotAfter);
    if (!credentialPubKeyCertificate) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_FAILED,
                "Error creating certificate for credentialPubKey"));
    }

    // 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<vector<uint8_t>> attestationKeyCertificate = support::ecPublicKeyGenerateCertificate(
            attestationPubKey.value(), attestationPrivKey.value(), serialDecimal, issuer, subject,
            validityNotBefore, validityNotAfter);
    if (!attestationKeyCertificate) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_FAILED,
                IIdentityCredentialStore::STATUS_FAILED,
                "Error creating certificate for attestationPubKey"));
                "Error getting public part of credentialKey"));
    }
    }
    credentialPubKey_ = pubKey.value();


    // Concatenate the certificates to form the chain.
    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
    vector<uint8_t> certificateChain;
    if (!privKey) {
    certificateChain.insert(certificateChain.end(), credentialPubKeyCertificate.value().begin(),
                            credentialPubKeyCertificate.value().end());
    certificateChain.insert(certificateChain.end(), attestationKeyCertificate.value().begin(),
                            attestationKeyCertificate.value().end());

    optional<vector<vector<uint8_t>>> splitCertChain =
            support::certificateChainSplit(certificateChain);
    if (!splitCertChain) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_FAILED, "Error splitting certificate chain"));
                IIdentityCredentialStore::STATUS_FAILED,
                "Error getting private part of credentialKey"));
    }
    }
    credentialPrivKey_ = privKey.value();

    // convert from vector<vector<uint8_t>>> to vector<Certificate>*
    *outCertificateChain = vector<Certificate>();
    *outCertificateChain = vector<Certificate>();
    for (const vector<uint8_t>& cert : splitCertChain.value()) {
    for (const vector<uint8_t>& cert : certificateChain_) {
        Certificate c = Certificate();
        Certificate c = Certificate();
        c.encodedCertificate = byteStringToSigned(cert);
        c.encodedCertificate = byteStringToSigned(cert);
        outCertificateChain->push_back(std::move(c));
        outCertificateChain->push_back(std::move(c));
+4 −1
Original line number Original line Diff line number Diff line
@@ -64,10 +64,13 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
    string docType_;
    string docType_;
    bool testCredential_;
    bool testCredential_;


    // These are set in initialize().
    // This is set in initialize().
    vector<uint8_t> storageKey_;
    vector<uint8_t> storageKey_;

    // These are set in getAttestationCertificate().
    vector<uint8_t> credentialPrivKey_;
    vector<uint8_t> credentialPrivKey_;
    vector<uint8_t> credentialPubKey_;
    vector<uint8_t> credentialPubKey_;
    vector<vector<uint8_t>> certificateChain_;


    // These fields are initialized during startPersonalization()
    // These fields are initialized during startPersonalization()
    size_t numAccessControlProfileRemaining_;
    size_t numAccessControlProfileRemaining_;
+4 −0
Original line number Original line Diff line number Diff line
@@ -23,10 +23,14 @@ cc_library {
        "include",
        "include",
    ],
    ],
    shared_libs: [
    shared_libs: [
        "android.hardware.keymaster@4.0",
        "libcrypto",
        "libcrypto",
        "libbase",
        "libbase",
        "libhidlbase",
        "libhidlbase",
        "libhardware",
        "libhardware",
        "libkeymaster_portable",
        "libsoft_attestation_cert",
        "libpuresoftkeymasterdevice",
    ],
    ],
    static_libs: [
    static_libs: [
        "libcppbor",
        "libcppbor",
+12 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@
#include <optional>
#include <optional>
#include <string>
#include <string>
#include <tuple>
#include <tuple>
#include <utility>
#include <vector>
#include <vector>


namespace android {
namespace android {
@@ -106,6 +107,17 @@ optional<vector<uint8_t>> encryptAes128Gcm(const vector<uint8_t>& key, const vec
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// EC crypto functionality / abstraction (only supports P-256).
// 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<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
        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
// Creates an 256-bit EC key using the NID_X9_62_prime256v1 curve, returns the
// PKCS#8 encoded key-pair.
// PKCS#8 encoded key-pair.
+139 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,13 @@
#include <cppbor.h>
#include <cppbor.h>
#include <cppbor_parse.h>
#include <cppbor_parse.h>


#include <android/hardware/keymaster/4.0/types.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>

namespace android {
namespace android {
namespace hardware {
namespace hardware {
namespace identity {
namespace identity {
@@ -816,6 +823,138 @@ optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<ui
    return hmac;
    return hmac;
}
}


// 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) {
    ::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<uint8_t>.
    vector<vector<uint8_t>> attestationCertificate;
    for (int i = 0; i < cert_chain_out->entry_count; i++) {
        attestationCertificate.insert(
                attestationCertificate.end(),
                vector<uint8_t>(
                        cert_chain_out->entries[i].data,
                        cert_chain_out->entries[i].data + cert_chain_out->entries[i].data_length));
    }

    return attestationCertificate;
}

optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> createEcKeyPairAndAttestation(
        const vector<uint8_t>& challenge, const vector<uint8_t>& 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<vector<vector<uint8_t>>> 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<uint8_t> keyPair(size);
    unsigned char* p = keyPair.data();
    i2d_PrivateKey(pkey.get(), &p);

    return make_pair(keyPair, attestationCert.value());
}

optional<vector<uint8_t>> createEcKeyPair() {
optional<vector<uint8_t>> createEcKeyPair() {
    auto ec_key = EC_KEY_Ptr(EC_KEY_new());
    auto ec_key = EC_KEY_Ptr(EC_KEY_new());
    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
Loading