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

Commit 76c0018a authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Porting IRPC functionality." am: 4b84c912 am: eccf5de9

Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/1666280

Change-Id: I733dceca0095bfb7d504067c6d7fc157534ca6b0
parents 24b2eac1 eccf5de9
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ cc_library_static {
    static_libs: [
        "libbase",
        "libcppbor_external",
        "libcppcose_rkp",
        "libutils",
        "libsoft_attestation_cert",
        "libkeymaster_portable",
@@ -92,6 +93,7 @@ cc_binary {
    static_libs: [
        "libbase",
        "libcppbor_external",
        "libcppcose_rkp",
        "libutils",
        "libsoft_attestation_cert",
        "libkeymaster_portable",
+1 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ cc_test {
    ],
    static_libs: [
        "libcppbor_external",
        "libcppcose_rkp",
        "libkeymaster_portable",
        "libpuresoftkeymasterdevice",
        "android.hardware.keymaster@4.0",
+1 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ cc_library {
        "android.hardware.keymaster@4.0",
        "libcrypto",
        "libbase",
        "libcppcose_rkp",
        "libhidlbase",
        "libhardware",
        "libkeymaster_portable",
+1 −1
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ cc_library {
        "android.hardware.security.keymint-V1-ndk_platform",
        "libbinder_ndk",
        "libcppbor_external",
        "libcppcose",
        "libcppcose_rkp",
        "libcrypto",
        "libkeymaster_portable",
        "libkeymint",
+24 −320
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@
#include <cppbor_parse.h>

#include <KeyMintUtils.h>
#include <cppcose/cppcose.h>
#include <keymaster/cppcose/cppcose.h>
#include <keymaster/keymaster_configuration.h>
#include <remote_prov/remote_prov_utils.h>

@@ -46,18 +46,8 @@ using namespace keymaster;

namespace {

// Hard-coded set of acceptable public keys that can act as roots of EEK chains.
inline const vector<bytevec> kAuthorizedEekRoots = {
        // TODO(drysdale): replace this random value with real root pubkey(s).
        {0x5c, 0xea, 0x4b, 0xd2, 0x31, 0x27, 0x15, 0x5e, 0x62, 0x94, 0x70,
         0x53, 0x94, 0x43, 0x0f, 0x9a, 0x89, 0xd5, 0xc5, 0x0f, 0x82, 0x9b,
         0xcd, 0x10, 0xe0, 0x79, 0xef, 0xf3, 0xfa, 0x40, 0xeb, 0x0a},
};

constexpr auto STATUS_FAILED = RemotelyProvisionedComponent::STATUS_FAILED;
constexpr auto STATUS_INVALID_EEK = RemotelyProvisionedComponent::STATUS_INVALID_EEK;
constexpr auto STATUS_INVALID_MAC = RemotelyProvisionedComponent::STATUS_INVALID_MAC;
constexpr uint32_t kAffinePointLength = 32;

struct AStatusDeleter {
    void operator()(AStatus* p) { AStatus_delete(p); }
};
@@ -125,167 +115,10 @@ class StatusOr {
    std::optional<T> value_;
};

StatusOr<std::pair<bytevec /* EEK pub */, bytevec /* EEK ID */>> validateAndExtractEekPubAndId(
        bool testMode, const bytevec& endpointEncryptionCertChain) {
    auto [item, newPos, errMsg] = cppbor::parse(endpointEncryptionCertChain);

    if (!item || !item->asArray()) {
        return Status("Error parsing EEK chain" + errMsg);
    }

    const cppbor::Array* certArr = item->asArray();
    bytevec lastPubKey;
    for (int i = 0; i < certArr->size(); ++i) {
        auto cosePubKey = verifyAndParseCoseSign1(testMode, certArr->get(i)->asArray(),
                                                  std::move(lastPubKey), bytevec{} /* AAD */);
        if (!cosePubKey) {
            return Status(STATUS_INVALID_EEK,
                          "Failed to validate EEK chain: " + cosePubKey.moveMessage());
        }
        lastPubKey = *std::move(cosePubKey);

        // In prod mode the first pubkey should match a well-known Google public key.
        if (!testMode && i == 0 &&
            std::find(kAuthorizedEekRoots.begin(), kAuthorizedEekRoots.end(), lastPubKey) ==
                    kAuthorizedEekRoots.end()) {
            return Status(STATUS_INVALID_EEK, "Unrecognized root of EEK chain");
        }
    }

    auto eek = CoseKey::parseX25519(lastPubKey, true /* requireKid */);
    if (!eek) return Status(STATUS_INVALID_EEK, "Failed to get EEK: " + eek.moveMessage());

    return std::make_pair(eek->getBstrValue(CoseKey::PUBKEY_X).value(),
                          eek->getBstrValue(CoseKey::KEY_ID).value());
}

StatusOr<bytevec /* pubkeys */> validateAndExtractPubkeys(bool testMode,
                                                          const vector<MacedPublicKey>& keysToSign,
                                                          const bytevec& macKey) {
    auto pubKeysToMac = cppbor::Array();
    for (auto& keyToSign : keysToSign) {
        auto [macedKeyItem, _, coseMacErrMsg] = cppbor::parse(keyToSign.macedKey);
        if (!macedKeyItem || !macedKeyItem->asArray() ||
            macedKeyItem->asArray()->size() != kCoseMac0EntryCount) {
            return Status("Invalid COSE_Mac0 structure");
        }

        auto protectedParms = macedKeyItem->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
        auto unprotectedParms = macedKeyItem->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
        auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr();
        auto tag = macedKeyItem->asArray()->get(kCoseMac0Tag)->asBstr();
        if (!protectedParms || !unprotectedParms || !payload || !tag) {
            return Status("Invalid COSE_Mac0 contents");
        }

        auto [protectedMap, __, errMsg] = cppbor::parse(protectedParms);
        if (!protectedMap || !protectedMap->asMap()) {
            return Status("Invalid Mac0 protected: " + errMsg);
        }
        auto& algo = protectedMap->asMap()->get(ALGORITHM);
        if (!algo || !algo->asInt() || algo->asInt()->value() != HMAC_256) {
            return Status("Unsupported Mac0 algorithm");
        }

        auto pubKey = CoseKey::parse(payload->value(), EC2, ES256, P256);
        if (!pubKey) return Status(pubKey.moveMessage());

        bool testKey = static_cast<bool>(pubKey->getMap().get(CoseKey::TEST_KEY));
        if (testMode && !testKey) {
            return Status(BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST,
                          "Production key in test request");
        } else if (!testMode && testKey) {
            return Status(BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST,
                          "Test key in production request");
        }

        auto macTag = generateCoseMac0Mac(macKey, {} /* external_aad */, payload->value());
        if (!macTag) return Status(STATUS_INVALID_MAC, macTag.moveMessage());
        if (macTag->size() != tag->value().size() ||
            CRYPTO_memcmp(macTag->data(), tag->value().data(), macTag->size()) != 0) {
            return Status(STATUS_INVALID_MAC, "MAC tag mismatch");
        }

        pubKeysToMac.add(pubKey->moveMap());
    }

    return pubKeysToMac.encode();
}

StatusOr<std::pair<bytevec, bytevec>> buildCosePublicKeyFromKmCert(
        const keymaster_blob_t* km_cert) {
    if (km_cert == nullptr) {
        return Status(STATUS_FAILED, "km_cert is a nullptr");
    }
    const uint8_t* temp = km_cert->data;
    X509* cert = d2i_X509(NULL, &temp, km_cert->data_length);
    if (cert == nullptr) {
        return Status(STATUS_FAILED, "d2i_X509 returned null when attempting to get the cert.");
    }
    EVP_PKEY* pubKey = X509_get_pubkey(cert);
    if (pubKey == nullptr) {
        return Status(STATUS_FAILED, "Boringssl failed to get the public key from the cert");
    }
    EC_KEY* ecKey = EVP_PKEY_get0_EC_KEY(pubKey);
    if (ecKey == nullptr) {
        return Status(STATUS_FAILED,
                      "The key in the certificate returned from GenerateKey is not "
                      "an EC key.");
    }
    const EC_POINT* jacobian_coords = EC_KEY_get0_public_key(ecKey);
    BIGNUM x;
    BIGNUM y;
    BN_CTX* ctx = BN_CTX_new();
    if (ctx == nullptr) {
        return Status(STATUS_FAILED, "Memory allocation failure for BN_CTX");
    }
    if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ecKey), jacobian_coords, &x, &y,
                                             ctx)) {
        return Status(STATUS_FAILED, "Failed to get affine coordinates");
    }
    bytevec x_bytestring(kAffinePointLength);
    bytevec y_bytestring(kAffinePointLength);
    if (BN_bn2binpad(&x, x_bytestring.data(), kAffinePointLength) != kAffinePointLength) {
        return Status(STATUS_FAILED, "Wrote incorrect number of bytes for x coordinate");
    }
    if (BN_bn2binpad(&y, y_bytestring.data(), kAffinePointLength) != kAffinePointLength) {
        return Status(STATUS_FAILED, "Wrote incorrect number of bytes for y coordinate");
    }
    BN_CTX_free(ctx);
    return std::make_pair(x_bytestring, y_bytestring);
}

cppbor::Array buildCertReqRecipients(const bytevec& pubkey, const bytevec& kid) {
    return cppbor::Array()                   // Array of recipients
            .add(cppbor::Array()             // Recipient
                         .add(cppbor::Map()  // Protected
                                      .add(ALGORITHM, ECDH_ES_HKDF_256)
                                      .canonicalize()
                                      .encode())
                         .add(cppbor::Map()  // Unprotected
                                      .add(COSE_KEY, cppbor::Map()
                                                             .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
                                                             .add(CoseKey::CURVE, cppcose::X25519)
                                                             .add(CoseKey::PUBKEY_X, pubkey)
                                                             .canonicalize())
                                      .add(KEY_ID, kid)
                                      .canonicalize())
                         .add(cppbor::Null()));  // No ciphertext
}

static keymaster_key_param_t kKeyMintEcdsaP256Params[] = {
        Authorization(TAG_PURPOSE, KM_PURPOSE_ATTEST_KEY),
        Authorization(TAG_ALGORITHM, KM_ALGORITHM_EC), Authorization(TAG_KEY_SIZE, 256),
        Authorization(TAG_DIGEST, KM_DIGEST_SHA_2_256),
        Authorization(TAG_EC_CURVE, KM_EC_CURVE_P_256), Authorization(TAG_NO_AUTH_REQUIRED),
        // The certificate generated by KM will be discarded, these values don't matter.
        Authorization(TAG_CERTIFICATE_NOT_BEFORE, 0), Authorization(TAG_CERTIFICATE_NOT_AFTER, 0)};

}  // namespace

RemotelyProvisionedComponent::RemotelyProvisionedComponent(
        std::shared_ptr<keymint::AndroidKeyMintDevice> keymint) {
    std::tie(devicePrivKey_, bcc_) = generateBcc();
    impl_ = keymint->getKeymasterImpl();
}

@@ -294,43 +127,15 @@ RemotelyProvisionedComponent::~RemotelyProvisionedComponent() {}
ScopedAStatus RemotelyProvisionedComponent::generateEcdsaP256KeyPair(bool testMode,
                                                                     MacedPublicKey* macedPublicKey,
                                                                     bytevec* privateKeyHandle) {
    // TODO(jbires): The following should move from ->GenerateKey to ->GenerateRKPKey and everything
    //              after the GenerateKey call should basically be moved into that new function call
    //              as well once the issue with libcppbor in system/keymaster is sorted out
    GenerateKeyRequest request(impl_->message_version());
    request.key_description.Reinitialize(kKeyMintEcdsaP256Params,
                                         array_length(kKeyMintEcdsaP256Params));
    GenerateKeyResponse response(impl_->message_version());
    impl_->GenerateKey(request, &response);
    GenerateRkpKeyRequest request(impl_->message_version());
    request.test_mode = testMode;
    GenerateRkpKeyResponse response(impl_->message_version());
    impl_->GenerateRkpKey(request, &response);
    if (response.error != KM_ERROR_OK) {
        return km_utils::kmError2ScopedAStatus(response.error);
    }

    if (response.certificate_chain.entry_count != 1) {
        // Error: Need the single non-signed certificate with the public key in it.
        return Status(STATUS_FAILED,
                      "Expected to receive a single certificate from GenerateKey. Instead got: " +
                              std::to_string(response.certificate_chain.entry_count));
        return Status(-static_cast<int32_t>(response.error), "Failure in key generation.");
    }
    auto affineCoords = buildCosePublicKeyFromKmCert(response.certificate_chain.begin());
    if (!affineCoords.isOk()) return affineCoords.moveError();
    cppbor::Map cosePublicKeyMap = cppbor::Map()
                                           .add(CoseKey::KEY_TYPE, EC2)
                                           .add(CoseKey::ALGORITHM, ES256)
                                           .add(CoseKey::CURVE, cppcose::P256)
                                           .add(CoseKey::PUBKEY_X, affineCoords->first)
                                           .add(CoseKey::PUBKEY_Y, affineCoords->second);
    if (testMode) {
        cosePublicKeyMap.add(CoseKey::TEST_KEY, cppbor::Null());
    }

    bytevec cosePublicKey = cosePublicKeyMap.canonicalize().encode();

    auto macedKey = constructCoseMac0(testMode ? remote_prov::kTestMacKey : macKey_,
                                      {} /* externalAad */, cosePublicKey);
    if (!macedKey) return Status(macedKey.moveMessage());

    macedPublicKey->macedKey = macedKey->encode();
    macedPublicKey->macedKey = km_utils::kmBlob2vector(response.maced_public_key);
    *privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);
    return ScopedAStatus::ok();
}
@@ -339,126 +144,25 @@ ScopedAStatus RemotelyProvisionedComponent::generateCertificateRequest(
        bool testMode, const vector<MacedPublicKey>& keysToSign,
        const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,
        ProtectedData* protectedData, bytevec* keysToSignMac) {
    auto pubKeysToSign = validateAndExtractPubkeys(testMode, keysToSign,
                                                   testMode ? remote_prov::kTestMacKey : macKey_);
    if (!pubKeysToSign.isOk()) return pubKeysToSign.moveError();

    bytevec ephemeralMacKey = remote_prov::randomBytes(SHA256_DIGEST_LENGTH);

    auto pubKeysToSignMac = generateCoseMac0Mac(ephemeralMacKey, bytevec{}, *pubKeysToSign);
    if (!pubKeysToSignMac) return Status(pubKeysToSignMac.moveMessage());
    *keysToSignMac = *std::move(pubKeysToSignMac);
    GenerateCsrRequest request(impl_->message_version());
    request.test_mode = testMode;
    request.num_keys = keysToSign.size();
    request.keys_to_sign_array = new KeymasterBlob[keysToSign.size()];
    for (int i = 0; i < keysToSign.size(); i++) {
        request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());
    }
    request.SetEndpointEncCertChain(endpointEncCertChain.data(), endpointEncCertChain.size());
    request.SetChallenge(challenge.data(), challenge.size());
    GenerateCsrResponse response(impl_->message_version());
    impl_->GenerateCsr(request, &response);

    bytevec devicePrivKey;
    cppbor::Array bcc;
    if (testMode) {
        std::tie(devicePrivKey, bcc) = generateBcc();
    } else {
        devicePrivKey = devicePrivKey_;
        bcc = bcc_.clone();
    if (response.error != KM_ERROR_OK) {
        return Status(-static_cast<int32_t>(response.error), "Failure in CSR Generation.");
    }

    std::unique_ptr<cppbor::Map> deviceInfoMap = createDeviceInfo();
    deviceInfo->deviceInfo = deviceInfoMap->encode();
    auto signedMac = constructCoseSign1(devicePrivKey /* Signing key */,  //
                                        ephemeralMacKey /* Payload */,
                                        cppbor::Array() /* AAD */
                                                .add(challenge)
                                                .add(std::move(deviceInfoMap))
                                                .encode());
    if (!signedMac) return Status(signedMac.moveMessage());

    bytevec ephemeralPrivKey(X25519_PRIVATE_KEY_LEN);
    bytevec ephemeralPubKey(X25519_PUBLIC_VALUE_LEN);
    X25519_keypair(ephemeralPubKey.data(), ephemeralPrivKey.data());

    auto eek = validateAndExtractEekPubAndId(testMode, endpointEncCertChain);
    if (!eek.isOk()) return eek.moveError();

    auto sessionKey = x25519_HKDF_DeriveKey(ephemeralPubKey, ephemeralPrivKey, eek->first,
                                            true /* senderIsA */);
    if (!sessionKey) return Status(sessionKey.moveMessage());

    auto coseEncrypted =
            constructCoseEncrypt(*sessionKey, remote_prov::randomBytes(kAesGcmNonceLength),
                                 cppbor::Array()  // payload
                                         .add(signedMac.moveValue())
                                         .add(std::move(bcc))
                                         .encode(),
                                 {},  // aad
                                 buildCertReqRecipients(ephemeralPubKey, eek->second));

    if (!coseEncrypted) return Status(coseEncrypted.moveMessage());
    protectedData->protectedData = coseEncrypted->encode();

    deviceInfo->deviceInfo = km_utils::kmBlob2vector(response.device_info_blob);
    protectedData->protectedData = km_utils::kmBlob2vector(response.protected_data_blob);
    *keysToSignMac = km_utils::kmBlob2vector(response.keys_to_sign_mac);
    return ScopedAStatus::ok();
}

bytevec RemotelyProvisionedComponent::deriveBytesFromHbk(const string& context,
                                                         size_t numBytes) const {
    bytevec fakeHbk(32, 0);
    bytevec result(numBytes);

    // TODO(swillden): Figure out if HKDF can fail.  It doesn't seem like it should be able to,
    // but the function does return an error code.
    HKDF(result.data(), numBytes,               //
         EVP_sha256(),                          //
         fakeHbk.data(), fakeHbk.size(),        //
         nullptr /* salt */, 0 /* salt len */,  //
         reinterpret_cast<const uint8_t*>(context.data()), context.size());

    return result;
}

std::unique_ptr<cppbor::Map> RemotelyProvisionedComponent::createDeviceInfo() const {
    auto result = std::make_unique<cppbor::Map>(cppbor::Map());

    // The following placeholders show how the DeviceInfo map would be populated.
    // result->add(cppbor::Tstr("brand"), cppbor::Tstr("Google"));
    // result->add(cppbor::Tstr("manufacturer"), cppbor::Tstr("Google"));
    // result->add(cppbor::Tstr("product"), cppbor::Tstr("Fake"));
    // result->add(cppbor::Tstr("model"), cppbor::Tstr("Imaginary"));
    // result->add(cppbor::Tstr("board"), cppbor::Tstr("Chess"));
    // result->add(cppbor::Tstr("vb_state"), cppbor::Tstr("orange"));
    // result->add(cppbor::Tstr("bootloader_state"), cppbor::Tstr("unlocked"));
    // result->add(cppbor::Tstr("os_version"), cppbor::Tstr("SC"));
    // result->add(cppbor::Tstr("system_patch_level"), cppbor::Uint(20210331));
    // result->add(cppbor::Tstr("boot_patch_level"), cppbor::Uint(20210331));
    // result->add(cppbor::Tstr("vendor_patch_level"), cppbor::Uint(20210331));

    result->canonicalize();
    return result;
}

std::pair<bytevec /* privKey */, cppbor::Array /* BCC */>
RemotelyProvisionedComponent::generateBcc() {
    bytevec privKey(ED25519_PRIVATE_KEY_LEN);
    bytevec pubKey(ED25519_PUBLIC_KEY_LEN);

    ED25519_keypair(pubKey.data(), privKey.data());

    auto coseKey = cppbor::Map()
                           .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
                           .add(CoseKey::ALGORITHM, EDDSA)
                           .add(CoseKey::CURVE, ED25519)
                           .add(CoseKey::KEY_OPS, VERIFY)
                           .add(CoseKey::PUBKEY_X, pubKey)
                           .canonicalize()
                           .encode();
    auto sign1Payload = cppbor::Map()
                                .add(1 /* Issuer */, "Issuer")
                                .add(2 /* Subject */, "Subject")
                                .add(-4670552 /* Subject Pub Key */, coseKey)
                                .add(-4670553 /* Key Usage (little-endian order) */,
                                     std::vector<uint8_t>{0x20} /* keyCertSign = 1<<5 */)
                                .canonicalize()
                                .encode();
    auto coseSign1 = constructCoseSign1(privKey,       /* signing key */
                                        cppbor::Map(), /* extra protected */
                                        sign1Payload, {} /* AAD */);
    assert(coseSign1);

    return {privKey, cppbor::Array().add(coseKey).add(coseSign1.moveValue())};
}

}  // namespace aidl::android::hardware::security::keymint
Loading