summaryrefslogtreecommitdiff
path: root/security/keymint/support/include/cppcose/cppcose.h
diff options
context:
space:
mode:
Diffstat (limited to 'security/keymint/support/include/cppcose/cppcose.h')
-rw-r--r--security/keymint/support/include/cppcose/cppcose.h288
1 files changed, 288 insertions, 0 deletions
diff --git a/security/keymint/support/include/cppcose/cppcose.h b/security/keymint/support/include/cppcose/cppcose.h
new file mode 100644
index 0000000000..a936bfdb5a
--- /dev/null
+++ b/security/keymint/support/include/cppcose/cppcose.h
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include <openssl/cipher.h>
+#include <openssl/curve25519.h>
+#include <openssl/digest.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/mem.h>
+#include <openssl/sha.h>
+
+namespace cppcose {
+
+using bytevec = std::vector<uint8_t>;
+
+constexpr int kCoseSign1EntryCount = 4;
+constexpr int kCoseSign1ProtectedParams = 0;
+constexpr int kCoseSign1UnprotectedParams = 1;
+constexpr int kCoseSign1Payload = 2;
+constexpr int kCoseSign1Signature = 3;
+
+constexpr int kCoseMac0EntryCount = 4;
+constexpr int kCoseMac0ProtectedParams = 0;
+constexpr int kCoseMac0UnprotectedParams = 1;
+constexpr int kCoseMac0Payload = 2;
+constexpr int kCoseMac0Tag = 3;
+
+constexpr int kCoseEncryptEntryCount = 4;
+constexpr int kCoseEncryptProtectedParams = 0;
+constexpr int kCoseEncryptUnprotectedParams = 1;
+constexpr int kCoseEncryptPayload = 2;
+constexpr int kCoseEncryptRecipients = 3;
+
+enum Label : int {
+ ALGORITHM = 1,
+ KEY_ID = 4,
+ IV = 5,
+ COSE_KEY = -1,
+};
+
+enum CoseKeyAlgorithm : int {
+ AES_GCM_256 = 3,
+ HMAC_256 = 5,
+ ES256 = -7, // ECDSA with SHA-256
+ EDDSA = -8,
+ ECDH_ES_HKDF_256 = -25,
+};
+
+enum CoseKeyCurve : int { P256 = 1, X25519 = 4, ED25519 = 6 };
+enum CoseKeyType : int { OCTET_KEY_PAIR = 1, EC2 = 2, SYMMETRIC_KEY = 4 };
+enum CoseKeyOps : int { SIGN = 1, VERIFY = 2, ENCRYPT = 3, DECRYPT = 4 };
+
+constexpr int kAesGcmNonceLength = 12;
+constexpr int kAesGcmTagSize = 16;
+constexpr int kAesGcmKeySize = 32;
+
+template <typename T>
+class ErrMsgOr {
+ public:
+ ErrMsgOr(std::string errMsg) : errMsg_(std::move(errMsg)) {}
+ ErrMsgOr(const char* errMsg) : errMsg_(errMsg) {}
+ ErrMsgOr(T val) : value_(std::move(val)) {}
+
+ operator bool() const { return value_.has_value(); }
+
+ T* operator->() & {
+ assert(value_);
+ return &value_.value();
+ }
+ T& operator*() & {
+ assert(value_);
+ return value_.value();
+ };
+ T&& operator*() && {
+ assert(value_);
+ return std::move(value_).value();
+ };
+
+ const std::string& message() { return errMsg_; }
+ std::string moveMessage() { return std::move(errMsg_); }
+
+ T moveValue() {
+ assert(value_);
+ return std::move(value_).value();
+ }
+
+ private:
+ std::string errMsg_;
+ std::optional<T> value_;
+};
+
+class CoseKey {
+ public:
+ CoseKey() {}
+ CoseKey(const CoseKey&) = delete;
+ CoseKey(CoseKey&&) = default;
+
+ enum Label : int {
+ KEY_TYPE = 1,
+ KEY_ID = 2,
+ ALGORITHM = 3,
+ KEY_OPS = 4,
+ CURVE = -1,
+ PUBKEY_X = -2,
+ PUBKEY_Y = -3,
+ PRIVATE_KEY = -4,
+ TEST_KEY = -70000 // Application-defined
+ };
+
+ static ErrMsgOr<CoseKey> parse(const bytevec& coseKey) {
+ auto [parsedKey, _, errMsg] = cppbor::parse(coseKey);
+ if (!parsedKey) return errMsg + " when parsing key";
+ if (!parsedKey->asMap()) return "CoseKey must be a map";
+ return CoseKey(static_cast<cppbor::Map*>(parsedKey.release()));
+ }
+
+ static ErrMsgOr<CoseKey> parse(const bytevec& coseKey, CoseKeyType expectedKeyType,
+ CoseKeyAlgorithm expectedAlgorithm, CoseKeyCurve expectedCurve) {
+ auto key = parse(coseKey);
+ if (!key) return key;
+
+ if (!key->checkIntValue(CoseKey::KEY_TYPE, expectedKeyType) ||
+ !key->checkIntValue(CoseKey::ALGORITHM, expectedAlgorithm) ||
+ !key->checkIntValue(CoseKey::CURVE, expectedCurve)) {
+ return "Unexpected key type:";
+ }
+
+ return key;
+ }
+
+ static ErrMsgOr<CoseKey> parseEd25519(const bytevec& coseKey) {
+ auto key = parse(coseKey, OCTET_KEY_PAIR, EDDSA, ED25519);
+ if (!key) return key;
+
+ auto& pubkey = key->getMap().get(PUBKEY_X);
+ if (!pubkey || !pubkey->asBstr() ||
+ pubkey->asBstr()->value().size() != ED25519_PUBLIC_KEY_LEN) {
+ return "Invalid Ed25519 public key";
+ }
+
+ return key;
+ }
+
+ static ErrMsgOr<CoseKey> parseX25519(const bytevec& coseKey, bool requireKid) {
+ auto key = parse(coseKey, OCTET_KEY_PAIR, ECDH_ES_HKDF_256, X25519);
+ if (!key) return key;
+
+ auto& pubkey = key->getMap().get(PUBKEY_X);
+ if (!pubkey || !pubkey->asBstr() ||
+ pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) {
+ return "Invalid X25519 public key";
+ }
+
+ auto& kid = key->getMap().get(KEY_ID);
+ if (requireKid && (!kid || !kid->asBstr())) {
+ return "Missing KID";
+ }
+
+ return key;
+ }
+
+ static ErrMsgOr<CoseKey> parseP256(const bytevec& coseKey) {
+ auto key = parse(coseKey, EC2, ES256, P256);
+ if (!key) return key;
+
+ auto& pubkey_x = key->getMap().get(PUBKEY_X);
+ auto& pubkey_y = key->getMap().get(PUBKEY_Y);
+ if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() ||
+ pubkey_x->asBstr()->value().size() != 32 || pubkey_y->asBstr()->value().size() != 32) {
+ return "Invalid P256 public key";
+ }
+
+ return key;
+ }
+
+ std::optional<int> getIntValue(Label label) {
+ const auto& value = key_->get(label);
+ if (!value || !value->asInt()) return {};
+ return value->asInt()->value();
+ }
+
+ std::optional<bytevec> getBstrValue(Label label) {
+ const auto& value = key_->get(label);
+ if (!value || !value->asBstr()) return {};
+ return value->asBstr()->value();
+ }
+
+ const cppbor::Map& getMap() const { return *key_; }
+ cppbor::Map&& moveMap() { return std::move(*key_); }
+
+ bool checkIntValue(Label label, int expectedValue) {
+ const auto& value = key_->get(label);
+ return value && value->asInt() && value->asInt()->value() == expectedValue;
+ }
+
+ void add(Label label, int value) { key_->add(label, value); }
+ void add(Label label, bytevec value) { key_->add(label, std::move(value)); }
+
+ bytevec encode() { return key_->canonicalize().encode(); }
+
+ private:
+ CoseKey(cppbor::Map* parsedKey) : key_(parsedKey) {}
+
+ // This is the full parsed key structure.
+ std::unique_ptr<cppbor::Map> key_;
+};
+
+ErrMsgOr<bytevec> generateCoseMac0Mac(const bytevec& macKey, const bytevec& externalAad,
+ const bytevec& payload);
+ErrMsgOr<cppbor::Array> constructCoseMac0(const bytevec& macKey, const bytevec& externalAad,
+ const bytevec& payload);
+ErrMsgOr<bytevec /* payload */> parseCoseMac0(const cppbor::Item* macItem);
+ErrMsgOr<bytevec /* payload */> verifyAndParseCoseMac0(const cppbor::Item* macItem,
+ const bytevec& macKey);
+
+ErrMsgOr<bytevec> createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams,
+ const bytevec& payload, const bytevec& aad);
+ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, const bytevec& payload,
+ const bytevec& aad);
+ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields,
+ const bytevec& payload, const bytevec& aad);
+/**
+ * Verify and parse a COSE_Sign1 message, returning the payload.
+ *
+ * @param ignoreSignature indicates whether signature verification should be skipped. If true, no
+ * verification of the signature will be done.
+ *
+ * @param coseSign1 is the COSE_Sign1 to verify and parse.
+ *
+ * @param signingCoseKey is a CBOR-encoded COSE_Key to use to verify the signature. The bytevec may
+ * be empty, in which case the function assumes that coseSign1's payload is the COSE_Key to
+ * use, i.e. that coseSign1 is a self-signed "certificate".
+ */
+ErrMsgOr<bytevec /* payload */> verifyAndParseCoseSign1(bool ignoreSignature,
+ const cppbor::Array* coseSign1,
+ const bytevec& signingCoseKey,
+ const bytevec& aad);
+
+ErrMsgOr<bytevec> createCoseEncryptCiphertext(const bytevec& key, const bytevec& nonce,
+ const bytevec& protectedParams, const bytevec& aad);
+ErrMsgOr<cppbor::Array> constructCoseEncrypt(const bytevec& key, const bytevec& nonce,
+ const bytevec& plaintextPayload, const bytevec& aad,
+ cppbor::Array recipients);
+ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>> getSenderPubKeyFromCoseEncrypt(
+ const cppbor::Item* encryptItem);
+inline ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>>
+getSenderPubKeyFromCoseEncrypt(const std::unique_ptr<cppbor::Item>& encryptItem) {
+ return getSenderPubKeyFromCoseEncrypt(encryptItem.get());
+}
+
+ErrMsgOr<bytevec /* plaintextPayload */> decryptCoseEncrypt(const bytevec& key,
+ const cppbor::Item* encryptItem,
+ const bytevec& aad);
+
+ErrMsgOr<bytevec> x25519_HKDF_DeriveKey(const bytevec& senderPubKey, const bytevec& senderPrivKey,
+ const bytevec& recipientPubKey, bool senderIsA);
+
+ErrMsgOr<bytevec /* ciphertextWithTag */> aesGcmEncrypt(const bytevec& key, const bytevec& nonce,
+ const bytevec& aad,
+ const bytevec& plaintext);
+ErrMsgOr<bytevec /* plaintext */> aesGcmDecrypt(const bytevec& key, const bytevec& nonce,
+ const bytevec& aad,
+ const bytevec& ciphertextWithTag);
+
+} // namespace cppcose