1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <array>
20 #include <memory>
21 #include <optional>
22 #include <string>
23 #include <vector>
24
25 #include <cppbor.h>
26 #include <cppbor_parse.h>
27
28 #include <openssl/cipher.h>
29 #include <openssl/curve25519.h>
30 #include <openssl/digest.h>
31 #include <openssl/hkdf.h>
32 #include <openssl/hmac.h>
33 #include <openssl/mem.h>
34 #include <openssl/sha.h>
35
36 namespace cppcose {
37
38 template <typename T> class ErrMsgOr;
39 using bytevec = std::vector<uint8_t>;
40 using HmacSha256 = std::array<uint8_t, SHA256_DIGEST_LENGTH>;
41 using HmacSha256Function = std::function<ErrMsgOr<HmacSha256>(const bytevec&)>;
42
43 constexpr int kCoseSign1EntryCount = 4;
44 constexpr int kCoseSign1ProtectedParams = 0;
45 constexpr int kCoseSign1UnprotectedParams = 1;
46 constexpr int kCoseSign1Payload = 2;
47 constexpr int kCoseSign1Signature = 3;
48
49 constexpr int kCoseMac0EntryCount = 4;
50 constexpr int kCoseMac0ProtectedParams = 0;
51 constexpr int kCoseMac0UnprotectedParams = 1;
52 constexpr int kCoseMac0Payload = 2;
53 constexpr int kCoseMac0Tag = 3;
54
55 constexpr int kCoseEncryptEntryCount = 4;
56 constexpr int kCoseEncryptProtectedParams = 0;
57 constexpr int kCoseEncryptUnprotectedParams = 1;
58 constexpr int kCoseEncryptPayload = 2;
59 constexpr int kCoseEncryptRecipients = 3;
60
61 enum Label : int {
62 ALGORITHM = 1,
63 KEY_ID = 4,
64 IV = 5,
65 COSE_KEY = -1,
66 };
67
68 enum CoseKeyAlgorithm : int {
69 AES_GCM_256 = 3,
70 HMAC_256 = 5,
71 ES256 = -7, // ECDSA with SHA-256
72 EDDSA = -8,
73 ECDH_ES_HKDF_256 = -25,
74 };
75
76 enum CoseKeyCurve : int { P256 = 1, X25519 = 4, ED25519 = 6 };
77 enum CoseKeyType : int { OCTET_KEY_PAIR = 1, EC2 = 2, SYMMETRIC_KEY = 4 };
78 enum CoseKeyOps : int { SIGN = 1, VERIFY = 2, ENCRYPT = 3, DECRYPT = 4 };
79
80 constexpr int kAesGcmNonceLength = 12;
81 constexpr int kAesGcmTagSize = 16;
82 constexpr int kAesGcmKeySize = 32;
83 constexpr int kAesGcmKeySizeBits = 256;
84
85 template <typename T> class ErrMsgOr {
86 public:
ErrMsgOr(std::string errMsg)87 ErrMsgOr(std::string errMsg) // NOLINT(google-explicit-constructor)
88 : errMsg_(std::move(errMsg)) {}
ErrMsgOr(const char * errMsg)89 ErrMsgOr(const char* errMsg) // NOLINT(google-explicit-constructor)
90 : errMsg_(errMsg) {}
ErrMsgOr(T val)91 ErrMsgOr(T val) // NOLINT(google-explicit-constructor)
92 : value_(std::move(val)) {}
93
94 explicit operator bool() const { return value_.has_value(); }
95
96 T* operator->() & {
97 assert(value_);
98 return &value_.value();
99 }
100 T& operator*() & {
101 assert(value_);
102 return value_.value();
103 };
104 T&& operator*() && {
105 assert(value_);
106 return std::move(value_).value();
107 };
108
message()109 const std::string& message() { return errMsg_; }
moveMessage()110 std::string moveMessage() { return std::move(errMsg_); }
111
moveValue()112 T moveValue() {
113 assert(value_);
114 return std::move(value_).value();
115 }
116
117 private:
118 std::string errMsg_;
119 std::optional<T> value_;
120 };
121
122 class CoseKey {
123 public:
CoseKey()124 CoseKey() {}
125 CoseKey(const CoseKey&) = delete;
126 CoseKey(CoseKey&&) = default;
127
128 enum Label : int {
129 KEY_TYPE = 1,
130 KEY_ID = 2,
131 ALGORITHM = 3,
132 KEY_OPS = 4,
133 CURVE = -1,
134 PUBKEY_X = -2,
135 PUBKEY_Y = -3,
136 PRIVATE_KEY = -4,
137 TEST_KEY = -70000 // Application-defined
138 };
139
parse(const bytevec & coseKey)140 static ErrMsgOr<CoseKey> parse(const bytevec& coseKey) {
141 auto [parsedKey, _, errMsg] = cppbor::parse(coseKey);
142 if (!parsedKey) return errMsg + " when parsing key";
143 if (!parsedKey->asMap()) return "CoseKey must be a map";
144 return CoseKey(static_cast<cppbor::Map*>(parsedKey.release()));
145 }
146
parse(const bytevec & coseKey,CoseKeyType expectedKeyType,CoseKeyAlgorithm expectedAlgorithm,CoseKeyCurve expectedCurve)147 static ErrMsgOr<CoseKey> parse(const bytevec& coseKey, CoseKeyType expectedKeyType,
148 CoseKeyAlgorithm expectedAlgorithm, CoseKeyCurve expectedCurve) {
149 auto key = parse(coseKey);
150 if (!key) return key;
151
152 if (!key->checkIntValue(CoseKey::KEY_TYPE, expectedKeyType) ||
153 !key->checkIntValue(CoseKey::ALGORITHM, expectedAlgorithm) ||
154 !key->checkIntValue(CoseKey::CURVE, expectedCurve)) {
155 return "Unexpected key type:";
156 }
157
158 return key;
159 }
160
parseEd25519(const bytevec & coseKey)161 static ErrMsgOr<CoseKey> parseEd25519(const bytevec& coseKey) {
162 auto key = parse(coseKey, OCTET_KEY_PAIR, EDDSA, ED25519);
163 if (!key) return key;
164
165 auto& pubkey = key->getMap().get(PUBKEY_X);
166 if (!pubkey || !pubkey->asBstr() ||
167 pubkey->asBstr()->value().size() != ED25519_PUBLIC_KEY_LEN) {
168 return "Invalid Ed25519 public key";
169 }
170
171 return key;
172 }
173
parseX25519(const bytevec & coseKey,bool requireKid)174 static ErrMsgOr<CoseKey> parseX25519(const bytevec& coseKey, bool requireKid) {
175 auto key = parse(coseKey, OCTET_KEY_PAIR, ECDH_ES_HKDF_256, X25519);
176 if (!key) return key;
177
178 auto& pubkey = key->getMap().get(PUBKEY_X);
179 if (!pubkey || !pubkey->asBstr() ||
180 pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) {
181 return "Invalid X25519 public key";
182 }
183
184 auto& kid = key->getMap().get(KEY_ID);
185 if (requireKid && (!kid || !kid->asBstr())) {
186 return "Missing KID";
187 }
188
189 return key;
190 }
191
parseP256(const bytevec & coseKey)192 static ErrMsgOr<CoseKey> parseP256(const bytevec& coseKey) {
193 auto key = parse(coseKey, EC2, ES256, P256);
194 if (!key) return key;
195
196 auto& pubkey_x = key->getMap().get(PUBKEY_X);
197 auto& pubkey_y = key->getMap().get(PUBKEY_Y);
198 if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() ||
199 pubkey_x->asBstr()->value().size() != 32 || pubkey_y->asBstr()->value().size() != 32) {
200 return "Invalid P256 public key";
201 }
202
203 return key;
204 }
205
getIntValue(Label label)206 std::optional<int> getIntValue(Label label) {
207 const auto& value = key_->get(label);
208 if (!value || !value->asInt()) return {};
209 return value->asInt()->value();
210 }
211
getBstrValue(Label label)212 std::optional<bytevec> getBstrValue(Label label) {
213 const auto& value = key_->get(label);
214 if (!value || !value->asBstr()) return {};
215 return value->asBstr()->value();
216 }
217
getMap()218 const cppbor::Map& getMap() const { return *key_; }
moveMap()219 cppbor::Map&& moveMap() { return std::move(*key_); }
220
checkIntValue(Label label,int expectedValue)221 bool checkIntValue(Label label, int expectedValue) {
222 const auto& value = key_->get(label);
223 return value && value->asInt() && value->asInt()->value() == expectedValue;
224 }
225
add(Label label,int value)226 void add(Label label, int value) { key_->add(label, value); }
add(Label label,bytevec value)227 void add(Label label, bytevec value) { key_->add(label, std::move(value)); }
228
encode()229 bytevec encode() { return key_->canonicalize().encode(); }
230
231 private:
CoseKey(cppbor::Map * parsedKey)232 explicit CoseKey(cppbor::Map* parsedKey) : key_(parsedKey) {}
233
234 // This is the full parsed key structure.
235 std::unique_ptr<cppbor::Map> key_;
236 };
237
238 // Utility function for generating an HMAC given a key and some input
239 // data. Returns std::nullopt on error
240 ErrMsgOr<HmacSha256> generateHmacSha256(const bytevec& key, const bytevec& data);
241
242 ErrMsgOr<HmacSha256> generateCoseMac0Mac(HmacSha256Function macFunction, const bytevec& externalAad,
243 const bytevec& payload);
244 ErrMsgOr<cppbor::Array> constructCoseMac0(HmacSha256Function macFunction,
245 const bytevec& externalAad, const bytevec& payload);
246 ErrMsgOr<bytevec /* payload */> verifyAndParseCoseMac0(const cppbor::Item* macItem,
247 const bytevec& macKey);
248
249 ErrMsgOr<bytevec> createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams,
250 const bytevec& payload, const bytevec& aad);
251 ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, const bytevec& payload,
252 const bytevec& aad);
253 ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields,
254 const bytevec& payload, const bytevec& aad);
255 /**
256 * Verify and parse a COSE_Sign1 message, returning the payload.
257 *
258 * @param coseSign1 is the COSE_Sign1 to verify and parse.
259 *
260 * @param signingCoseKey is a CBOR-encoded COSE_Key to use to verify the signature. The bytevec may
261 * be empty, in which case the function assumes that coseSign1's payload is the COSE_Key to
262 * use, i.e. that coseSign1 is a self-signed "certificate".
263 */
264 ErrMsgOr<bytevec /* payload */> verifyAndParseCoseSign1(const cppbor::Array* coseSign1,
265 const bytevec& signingCoseKey,
266 const bytevec& aad);
267
268 ErrMsgOr<bytevec> createCoseEncryptCiphertext(const bytevec& key, const bytevec& nonce,
269 const bytevec& protectedParams, const bytevec& aad);
270 ErrMsgOr<cppbor::Array> constructCoseEncrypt(const bytevec& key, const bytevec& nonce,
271 const bytevec& plaintextPayload, const bytevec& aad,
272 cppbor::Array recipients);
273 ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>>
274 getSenderPubKeyFromCoseEncrypt(const cppbor::Item* encryptItem);
275 inline ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>>
getSenderPubKeyFromCoseEncrypt(const std::unique_ptr<cppbor::Item> & encryptItem)276 getSenderPubKeyFromCoseEncrypt(const std::unique_ptr<cppbor::Item>& encryptItem) {
277 return getSenderPubKeyFromCoseEncrypt(encryptItem.get());
278 }
279
280 ErrMsgOr<bytevec /* plaintextPayload */>
281 decryptCoseEncrypt(const bytevec& key, const cppbor::Item* encryptItem, const bytevec& aad);
282
283 ErrMsgOr<bytevec> x25519_HKDF_DeriveKey(const bytevec& senderPubKey, const bytevec& senderPrivKey,
284 const bytevec& recipientPubKey, bool senderIsA);
285
286 ErrMsgOr<bytevec /* ciphertextWithTag */> aesGcmEncrypt(const bytevec& key, const bytevec& nonce,
287 const bytevec& aad,
288 const bytevec& plaintext);
289 ErrMsgOr<bytevec /* plaintext */> aesGcmDecrypt(const bytevec& key, const bytevec& nonce,
290 const bytevec& aad,
291 const bytevec& ciphertextWithTag);
292
293 } // namespace cppcose
294