/* * Copyright (c) 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace aidl::android::hardware::security::keymint::remote_prov { constexpr uint32_t kBccPayloadIssuer = 1; constexpr uint32_t kBccPayloadSubject = 2; constexpr int32_t kBccPayloadSubjPubKey = -4670552; constexpr int32_t kBccPayloadKeyUsage = -4670553; constexpr int kP256AffinePointSize = 32; using EC_KEY_Ptr = bssl::UniquePtr; using EVP_PKEY_Ptr = bssl::UniquePtr; using EVP_PKEY_CTX_Ptr = bssl::UniquePtr; ErrMsgOr ecKeyGetPrivateKey(const EC_KEY* ecKey) { // Extract private key. const BIGNUM* bignum = EC_KEY_get0_private_key(ecKey); if (bignum == nullptr) { return "Error getting bignum from private key"; } // Pad with zeros in case the length is lesser than 32. bytevec privKey(32, 0); BN_bn2binpad(bignum, privKey.data(), privKey.size()); return privKey; } ErrMsgOr ecKeyGetPublicKey(const EC_KEY* ecKey) { // Extract public key. auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); if (group.get() == nullptr) { return "Error creating EC group by curve name"; } const EC_POINT* point = EC_KEY_get0_public_key(ecKey); if (point == nullptr) return "Error getting ecpoint from public key"; int size = EC_POINT_point2oct(group.get(), point, POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, nullptr); if (size == 0) { return "Error generating public key encoding"; } bytevec publicKey; publicKey.resize(size); EC_POINT_point2oct(group.get(), point, POINT_CONVERSION_UNCOMPRESSED, publicKey.data(), publicKey.size(), nullptr); return publicKey; } ErrMsgOr> getAffineCoordinates(const bytevec& pubKey) { auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); if (group.get() == nullptr) { return "Error creating EC group by curve name"; } auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); if (EC_POINT_oct2point(group.get(), point.get(), pubKey.data(), pubKey.size(), nullptr) != 1) { return "Error decoding publicKey"; } BIGNUM_Ptr x(BN_new()); BIGNUM_Ptr y(BN_new()); BN_CTX_Ptr ctx(BN_CTX_new()); if (!ctx.get()) return "Failed to create BN_CTX instance"; if (!EC_POINT_get_affine_coordinates_GFp(group.get(), point.get(), x.get(), y.get(), ctx.get())) { return "Failed to get affine coordinates from ECPoint"; } bytevec pubX(kP256AffinePointSize); bytevec pubY(kP256AffinePointSize); if (BN_bn2binpad(x.get(), pubX.data(), kP256AffinePointSize) != kP256AffinePointSize) { return "Error in converting absolute value of x coordinate to big-endian"; } if (BN_bn2binpad(y.get(), pubY.data(), kP256AffinePointSize) != kP256AffinePointSize) { return "Error in converting absolute value of y coordinate to big-endian"; } return std::make_tuple(std::move(pubX), std::move(pubY)); } ErrMsgOr> generateEc256KeyPair() { auto ec_key = EC_KEY_Ptr(EC_KEY_new()); if (ec_key.get() == nullptr) { return "Failed to allocate ec key"; } auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); if (group.get() == nullptr) { return "Error creating EC group by curve name"; } 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) { return "Error generating key"; } auto privKey = ecKeyGetPrivateKey(ec_key.get()); if (!privKey) return privKey.moveMessage(); auto pubKey = ecKeyGetPublicKey(ec_key.get()); if (!pubKey) return pubKey.moveMessage(); return std::make_tuple(pubKey.moveValue(), privKey.moveValue()); } ErrMsgOr> generateX25519KeyPair() { /* Generate X25519 key pair */ bytevec pubKey(X25519_PUBLIC_VALUE_LEN); bytevec privKey(X25519_PRIVATE_KEY_LEN); X25519_keypair(pubKey.data(), privKey.data()); return std::make_tuple(std::move(pubKey), std::move(privKey)); } ErrMsgOr> generateED25519KeyPair() { /* Generate ED25519 key pair */ bytevec pubKey(ED25519_PUBLIC_KEY_LEN); bytevec privKey(ED25519_PRIVATE_KEY_LEN); ED25519_keypair(pubKey.data(), privKey.data()); return std::make_tuple(std::move(pubKey), std::move(privKey)); } ErrMsgOr> generateKeyPair(int32_t supportedEekCurve, bool isEek) { switch (supportedEekCurve) { case RpcHardwareInfo::CURVE_25519: if (isEek) { return generateX25519KeyPair(); } return generateED25519KeyPair(); case RpcHardwareInfo::CURVE_P256: return generateEc256KeyPair(); default: return "Unknown EEK Curve."; } } ErrMsgOr constructCoseKey(int32_t supportedEekCurve, const bytevec& eekId, const bytevec& pubKey) { CoseKeyType keyType; CoseKeyAlgorithm algorithm; CoseKeyCurve curve; bytevec pubX; bytevec pubY; switch (supportedEekCurve) { case RpcHardwareInfo::CURVE_25519: keyType = OCTET_KEY_PAIR; algorithm = (eekId.empty()) ? EDDSA : ECDH_ES_HKDF_256; curve = (eekId.empty()) ? ED25519 : cppcose::X25519; pubX = pubKey; break; case RpcHardwareInfo::CURVE_P256: { keyType = EC2; algorithm = (eekId.empty()) ? ES256 : ECDH_ES_HKDF_256; curve = P256; auto affineCoordinates = getAffineCoordinates(pubKey); if (!affineCoordinates) return affineCoordinates.moveMessage(); std::tie(pubX, pubY) = affineCoordinates.moveValue(); } break; default: return "Unknown EEK Curve."; } cppbor::Map coseKey = cppbor::Map() .add(CoseKey::KEY_TYPE, keyType) .add(CoseKey::ALGORITHM, algorithm) .add(CoseKey::CURVE, curve) .add(CoseKey::PUBKEY_X, pubX); if (!pubY.empty()) coseKey.add(CoseKey::PUBKEY_Y, pubY); if (!eekId.empty()) coseKey.add(CoseKey::KEY_ID, eekId); return coseKey.canonicalize().encode(); } bytevec kTestMacKey(32 /* count */, 0 /* byte value */); bytevec randomBytes(size_t numBytes) { bytevec retval(numBytes); RAND_bytes(retval.data(), numBytes); return retval; } ErrMsgOr constructCoseSign1(int32_t supportedEekCurve, const bytevec& key, const bytevec& payload, const bytevec& aad) { if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { return constructECDSACoseSign1(key, {} /* protectedParams */, payload, aad); } else { return cppcose::constructCoseSign1(key, payload, aad); } } ErrMsgOr generateEekChain(int32_t supportedEekCurve, size_t length, const bytevec& eekId) { if (length < 2) { return "EEK chain must contain at least 2 certs."; } auto eekChain = cppbor::Array(); bytevec prev_priv_key; for (size_t i = 0; i < length - 1; ++i) { auto keyPair = generateKeyPair(supportedEekCurve, false); if (!keyPair) return keyPair.moveMessage(); auto [pub_key, priv_key] = keyPair.moveValue(); // The first signing key is self-signed. if (prev_priv_key.empty()) prev_priv_key = priv_key; auto coseKey = constructCoseKey(supportedEekCurve, {}, pub_key); if (!coseKey) return coseKey.moveMessage(); auto coseSign1 = constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), {} /* AAD */); if (!coseSign1) return coseSign1.moveMessage(); eekChain.add(coseSign1.moveValue()); prev_priv_key = priv_key; } auto keyPair = generateKeyPair(supportedEekCurve, true); if (!keyPair) return keyPair.moveMessage(); auto [pub_key, priv_key] = keyPair.moveValue(); auto coseKey = constructCoseKey(supportedEekCurve, eekId, pub_key); if (!coseKey) return coseKey.moveMessage(); auto coseSign1 = constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), {} /* AAD */); if (!coseSign1) return coseSign1.moveMessage(); eekChain.add(coseSign1.moveValue()); if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { // convert ec public key to x and y co-ordinates. auto affineCoordinates = getAffineCoordinates(pub_key); if (!affineCoordinates) return affineCoordinates.moveMessage(); auto [pubX, pubY] = affineCoordinates.moveValue(); pub_key.clear(); pub_key.insert(pub_key.begin(), pubX.begin(), pubX.end()); pub_key.insert(pub_key.end(), pubY.begin(), pubY.end()); } return EekChain{eekChain.encode(), pub_key, priv_key}; } bytevec getProdEekChain(int32_t supportedEekCurve) { cppbor::Array chain; if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedEcdsa256RootCert), std::end(kCoseEncodedEcdsa256RootCert)))); chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedEcdsa256GeekCert), std::end(kCoseEncodedEcdsa256GeekCert)))); } else { chain.add(cppbor::EncodedItem( bytevec(std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert)))); chain.add(cppbor::EncodedItem( bytevec(std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert)))); } return chain.encode(); } ErrMsgOr validatePayloadAndFetchPubKey(const cppbor::Map* payload) { const auto& issuer = payload->get(kBccPayloadIssuer); if (!issuer || !issuer->asTstr()) return "Issuer is not present or not a tstr."; const auto& subject = payload->get(kBccPayloadSubject); if (!subject || !subject->asTstr()) return "Subject is not present or not a tstr."; const auto& keyUsage = payload->get(kBccPayloadKeyUsage); if (!keyUsage || !keyUsage->asBstr()) return "Key usage is not present or not a bstr."; const auto& serializedKey = payload->get(kBccPayloadSubjPubKey); if (!serializedKey || !serializedKey->asBstr()) return "Key is not present or not a bstr."; return serializedKey->asBstr()->value(); } ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, const bytevec& signingCoseKey, const bytevec& aad) { if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) { return "Invalid COSE_Sign1"; } const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr(); const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap(); const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr(); const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr(); if (!protectedParams || !unprotectedParams || !payload || !signature) { return "Invalid COSE_Sign1"; } auto [parsedProtParams, _, errMsg] = cppbor::parse(protectedParams); if (!parsedProtParams) { return errMsg + " when parsing protected params."; } if (!parsedProtParams->asMap()) { return "Protected params must be a map"; } auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); if (!algorithm || !algorithm->asInt() || (algorithm->asInt()->value() != EDDSA && algorithm->asInt()->value() != ES256)) { return "Unsupported signature algorithm"; } auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(payload); if (!parsedPayload) return payloadErrMsg + " when parsing key"; if (!parsedPayload->asMap()) return "CWT must be a map"; auto serializedKey = validatePayloadAndFetchPubKey(parsedPayload->asMap()); if (!serializedKey) { return "CWT validation failed: " + serializedKey.moveMessage(); } bool selfSigned = signingCoseKey.empty(); bytevec signatureInput = cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); if (algorithm->asInt()->value() == EDDSA) { auto key = CoseKey::parseEd25519(selfSigned ? *serializedKey : signingCoseKey); if (!key) return "Bad signing key: " + key.moveMessage(); if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), key->getBstrValue(CoseKey::PUBKEY_X)->data())) { return "Signature verification failed"; } } else { // P256 auto key = CoseKey::parseP256(selfSigned ? *serializedKey : signingCoseKey); if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() || key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) { return "Bad signing key: " + key.moveMessage(); } auto publicKey = key->getEcPublicKey(); if (!publicKey) return publicKey.moveMessage(); auto ecdsaDerSignature = ecdsaCoseSignatureToDer(signature->value()); if (!ecdsaDerSignature) return ecdsaDerSignature.moveMessage(); // convert public key to uncompressed form. publicKey->insert(publicKey->begin(), 0x04); if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), *ecdsaDerSignature)) { return "Signature verification failed"; } } return serializedKey.moveValue(); } ErrMsgOr> validateBcc(const cppbor::Array* bcc) { if (!bcc || bcc->size() == 0) return "Invalid BCC"; std::vector result; const auto& devicePubKey = bcc->get(0); if (!devicePubKey->asMap()) return "Invalid device public key at the 1st entry in the BCC"; bytevec prevKey; for (size_t i = 1; i < bcc->size(); ++i) { const cppbor::Array* entry = bcc->get(i)->asArray(); if (!entry || entry->size() != kCoseSign1EntryCount) { return "Invalid BCC entry " + std::to_string(i) + ": " + prettyPrint(entry); } auto payload = verifyAndParseCoseSign1Cwt(entry, std::move(prevKey), bytevec{} /* AAD */); if (!payload) { return "Failed to verify entry " + std::to_string(i) + ": " + payload.moveMessage(); } auto& certProtParms = entry->get(kCoseSign1ProtectedParams); if (!certProtParms || !certProtParms->asBstr()) return "Invalid prot params"; auto [parsedProtParms, _, errMsg] = cppbor::parse(certProtParms->asBstr()->value()); if (!parsedProtParms || !parsedProtParms->asMap()) return "Invalid prot params"; result.push_back(BccEntryData{*payload}); // This entry's public key is the signing key for the next entry. prevKey = payload.moveValue(); if (i == 1) { auto [parsedRootKey, _, errMsg] = cppbor::parse(prevKey); if (!parsedRootKey || !parsedRootKey->asMap()) return "Invalid payload entry in BCC."; if (*parsedRootKey != *devicePubKey) { return "Device public key doesn't match BCC root."; } } } return result; } JsonOutput jsonEncodeCsrWithBuild(const std::string instance_name, const cppbor::Array& csr) { const std::string kFingerprintProp = "ro.build.fingerprint"; if (!::android::base::WaitForPropertyCreation(kFingerprintProp)) { return JsonOutput::Error("Unable to read build fingerprint"); } bytevec csrCbor = csr.encode(); size_t base64Length; int rc = EVP_EncodedLength(&base64Length, csrCbor.size()); if (!rc) { return JsonOutput::Error("Error getting base64 length. Size overflow?"); } std::vector base64(base64Length); rc = EVP_EncodeBlock(reinterpret_cast(base64.data()), csrCbor.data(), csrCbor.size()); ++rc; // Account for NUL, which BoringSSL does not for some reason. if (rc != base64Length) { return JsonOutput::Error("Error writing base64. Expected " + std::to_string(base64Length) + " bytes to be written, but " + std::to_string(rc) + " bytes were actually written."); } Json::Value json(Json::objectValue); json["name"] = instance_name; json["build_fingerprint"] = ::android::base::GetProperty(kFingerprintProp, /*default=*/""); json["csr"] = base64.data(); // Boring writes a NUL-terminated c-string Json::StreamWriterBuilder factory; factory["indentation"] = ""; // disable pretty formatting return JsonOutput::Ok(Json::writeString(factory, json)); } } // namespace aidl::android::hardware::security::keymint::remote_prov