summaryrefslogtreecommitdiff
path: root/security/keymint/support/remote_prov_utils.cpp
diff options
context:
space:
mode:
authorShawn Willden <swillden@google.com>2020-09-30 22:39:22 -0600
committerMax Bires <jbires@google.com>2021-02-16 07:40:59 -0800
commit274bb55f102d1adaac99f41ae4f6dcde9d2c13d2 (patch)
tree703ca1e28eb1bfde864a316396fa6bc3211254e5 /security/keymint/support/remote_prov_utils.cpp
parent06811c8124c8e724ed4a41ae54a33a6b97981f4d (diff)
Add RemotelyProvisionedComponent HAL.
Test: VtsHalRemotelyProvisionedComponentTargetTest Change-Id: I51fb01f4c52949c81f3ad2d694a4afdf0fa67788
Diffstat (limited to 'security/keymint/support/remote_prov_utils.cpp')
-rw-r--r--security/keymint/support/remote_prov_utils.cpp169
1 files changed, 169 insertions, 0 deletions
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
new file mode 100644
index 0000000000..111cb309b0
--- /dev/null
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 <remote_prov/remote_prov_utils.h>
+
+#include <openssl/rand.h>
+
+#include <cppbor.h>
+
+namespace aidl::android::hardware::security::keymint::remote_prov {
+
+bytevec kTestMacKey(32 /* count */, 0 /* byte value */);
+
+bytevec randomBytes(size_t numBytes) {
+ bytevec retval(numBytes);
+ RAND_bytes(retval.data(), numBytes);
+ return retval;
+}
+
+ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
+ auto eekChain = cppbor::Array();
+
+ bytevec prev_priv_key;
+ for (size_t i = 0; i < length - 1; ++i) {
+ bytevec pub_key(ED25519_PUBLIC_KEY_LEN);
+ bytevec priv_key(ED25519_PRIVATE_KEY_LEN);
+
+ ED25519_keypair(pub_key.data(), priv_key.data());
+
+ // The first signing key is self-signed.
+ if (prev_priv_key.empty()) prev_priv_key = priv_key;
+
+ auto coseSign1 = constructCoseSign1(prev_priv_key,
+ cppbor::Map() /* payload CoseKey */
+ .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+ .add(CoseKey::ALGORITHM, EDDSA)
+ .add(CoseKey::CURVE, ED25519)
+ .add(CoseKey::PUBKEY_X, pub_key)
+ .canonicalize()
+ .encode(),
+ {} /* AAD */);
+ if (!coseSign1) return coseSign1.moveMessage();
+ eekChain.add(coseSign1.moveValue());
+ }
+
+ bytevec pub_key(X25519_PUBLIC_VALUE_LEN);
+ bytevec priv_key(X25519_PRIVATE_KEY_LEN);
+ X25519_keypair(pub_key.data(), priv_key.data());
+
+ auto coseSign1 = constructCoseSign1(prev_priv_key,
+ cppbor::Map() /* payload CoseKey */
+ .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+ .add(CoseKey::KEY_ID, eekId)
+ .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256)
+ .add(CoseKey::CURVE, cppcose::X25519)
+ .add(CoseKey::PUBKEY_X, pub_key)
+ .canonicalize()
+ .encode(),
+ {} /* AAD */);
+ if (!coseSign1) return coseSign1.moveMessage();
+ eekChain.add(coseSign1.moveValue());
+
+ return EekChain{eekChain.encode(), pub_key, priv_key};
+}
+
+ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(bool ignoreSignature, 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::Bstr* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asBstr();
+ 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) {
+ return "Unsupported signature algorithm";
+ }
+
+ // TODO(jbires): Handle CWTs as the CoseSign1 payload in a less hacky way. Since the CWT payload
+ // is extremely remote provisioning specific, probably just make a separate
+ // function there.
+ auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(payload);
+ if (!parsedPayload) return payloadErrMsg + " when parsing key";
+ if (!parsedPayload->asMap()) return "CWT must be a map";
+ auto serializedKey = parsedPayload->asMap()->get(-4670552)->clone();
+ if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry";
+
+ if (!ignoreSignature) {
+ bool selfSigned = signingCoseKey.empty();
+ auto key = CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value()
+ : signingCoseKey);
+ if (!key) return "Bad signing key: " + key.moveMessage();
+
+ bytevec signatureInput = cppbor::Array()
+ .add("Signature1")
+ .add(*protectedParams)
+ .add(aad)
+ .add(*payload)
+ .encode();
+
+ if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
+ key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
+ return "Signature verification failed";
+ }
+ }
+
+ return serializedKey->asBstr()->value();
+}
+ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
+ if (!bcc || bcc->size() == 0) return "Invalid BCC";
+
+ std::vector<BccEntryData> result;
+
+ bytevec prevKey;
+ // TODO(jbires): Actually process the pubKey at the start of the new bcc entry
+ 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(false /* ignoreSignature */, 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();
+ }
+
+ return result;
+}
+
+} // namespace aidl::android::hardware::security::keymint::remote_prov