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

Commit 9704ff6b authored by Max Bires's avatar Max Bires
Browse files

Porting IRPC functionality.

This is the change that removes the functionality that has been shifted
over to appropriate classes and contexts in system/keymaster.

Test: atest VtsHalRemotelyProvisionedComponentTargetTest
Change-Id: I491f4ef823868322ea6a804d88ca09662c099a44
parent 6594b5f1
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