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

Commit ab6ef063 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

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

Merge "Add attestation certificate generation and identity credential tags." am: 93bba950 am: 545148b7

Change-Id: Idd0ef1c5de068f66c6f7896b823404bc3aa8f8b8
parents c7e4fc60 545148b7
Loading
Loading
Loading
Loading
+35 −84
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

#define LOG_TAG "WritableIdentityCredential"

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

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

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

#include <utility>

#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<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);
    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<int8_t>& /*attestationApplicationId*/,
        const vector<int8_t>& /*attestationChallenge*/, vector<Certificate>* 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<vector<uint8_t>> attestationKeyPair = support::createEcKeyPair();
    if (!attestationKeyPair) {
        const vector<int8_t>& attestationApplicationId,  //
        const vector<int8_t>& attestationChallenge,      //
        vector<Certificate>* outCertificateChain) {
    if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_FAILED, "Error creating attestationKey"));
                IIdentityCredentialStore::STATUS_FAILED,
                "Error attestation certificate previously generated"));
    }

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

    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(
                IIdentityCredentialStore::STATUS_FAILED,
                "Error getting public part of attestationKey"));
                "Error creating credentialKey and attestation"));
    }

    optional<vector<uint8_t>> attestationPrivKey =
            support::ecKeyPairGetPrivateKey(attestationKeyPair.value());
    if (!attestationPrivKey) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_FAILED,
                "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) {
    vector<uint8_t> keyPair = keyAttestationPair.value().first;
    certificateChain_ = keyAttestationPair.value().second;

    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
    if (!pubKey) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
                IIdentityCredentialStore::STATUS_FAILED,
                "Error creating certificate for attestationPubKey"));
                "Error getting public part of credentialKey"));
    }
    credentialPubKey_ = pubKey.value();

    // Concatenate the certificates to form the chain.
    vector<uint8_t> certificateChain;
    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) {
    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
    if (!privKey) {
        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>();
    for (const vector<uint8_t>& cert : splitCertChain.value()) {
    for (const vector<uint8_t>& cert : certificateChain_) {
        Certificate c = Certificate();
        c.encodedCertificate = byteStringToSigned(cert);
        outCertificateChain->push_back(std::move(c));
+4 −1
Original line number Diff line number Diff line
@@ -64,10 +64,13 @@ class WritableIdentityCredential : public BnWritableIdentityCredential {
    string docType_;
    bool testCredential_;

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

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

    // These fields are initialized during startPersonalization()
    size_t numAccessControlProfileRemaining_;
+4 −0
Original line number Diff line number Diff line
@@ -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",
+12 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

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).
// ---------------------------------------------------------------------------
// 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
// PKCS#8 encoded key-pair.
+139 −0
Original line number Diff line number Diff line
@@ -47,6 +47,13 @@
#include <cppbor.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 hardware {
namespace identity {
@@ -816,6 +823,138 @@ optional<vector<uint8_t>> hmacSha256(const vector<uint8_t>& key, const vector<ui
    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() {
    auto ec_key = EC_KEY_Ptr(EC_KEY_new());
    auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
Loading