• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <openssl/bn.h>
28 #include <openssl/cipher.h>
29 #include <openssl/curve25519.h>
30 #include <openssl/digest.h>
31 #include <openssl/ec.h>
32 #include <openssl/evp.h>
33 #include <openssl/hkdf.h>
34 #include <openssl/hmac.h>
35 #include <openssl/mem.h>
36 #include <openssl/nid.h>
37 #include <openssl/sha.h>
38 
39 namespace cppcose {
40 
41 using BIGNUM_Ptr = bssl::UniquePtr<BIGNUM>;
42 using EC_GROUP_Ptr = bssl::UniquePtr<EC_GROUP>;
43 using EC_POINT_Ptr = bssl::UniquePtr<EC_POINT>;
44 using BN_CTX_Ptr = bssl::UniquePtr<BN_CTX>;
45 
46 template <typename T> class ErrMsgOr;
47 using bytevec = std::vector<uint8_t>;
48 using HmacSha256 = std::array<uint8_t, SHA256_DIGEST_LENGTH>;
49 using HmacSha256Function = std::function<ErrMsgOr<HmacSha256>(const bytevec&)>;
50 
51 constexpr int kCoseSign1EntryCount = 4;
52 constexpr int kCoseSign1ProtectedParams = 0;
53 constexpr int kCoseSign1UnprotectedParams = 1;
54 constexpr int kCoseSign1Payload = 2;
55 constexpr int kCoseSign1Signature = 3;
56 
57 constexpr int kCoseMac0EntryCount = 4;
58 constexpr int kCoseMac0ProtectedParams = 0;
59 constexpr int kCoseMac0UnprotectedParams = 1;
60 constexpr int kCoseMac0Payload = 2;
61 constexpr int kCoseMac0Tag = 3;
62 
63 constexpr int kCoseEncryptEntryCount = 4;
64 constexpr int kCoseEncryptProtectedParams = 0;
65 constexpr int kCoseEncryptUnprotectedParams = 1;
66 constexpr int kCoseEncryptPayload = 2;
67 constexpr int kCoseEncryptRecipients = 3;
68 
69 constexpr int kCoseMac0SemanticTag = 17;
70 
71 enum Label : int {
72     ALGORITHM = 1,
73     KEY_ID = 4,
74     IV = 5,
75     COSE_KEY = -1,
76 };
77 
78 enum CoseKeyAlgorithm : int {
79     AES_GCM_256 = 3,
80     HMAC_256 = 5,
81     ES256 = -7,  // ECDSA with SHA-256
82     EDDSA = -8,
83     ECDH_ES_HKDF_256 = -25,
84     ES384 = -35,  // ECDSA with SHA-384
85 };
86 
87 enum CoseKeyCurve : int { P256 = 1, P384 = 2, X25519 = 4, ED25519 = 6 };
88 enum CoseKeyType : int { OCTET_KEY_PAIR = 1, EC2 = 2, SYMMETRIC_KEY = 4 };
89 enum CoseKeyOps : int { SIGN = 1, VERIFY = 2, ENCRYPT = 3, DECRYPT = 4 };
90 
91 constexpr int kAesGcmNonceLength = 12;
92 constexpr int kAesGcmTagSize = 16;
93 constexpr int kAesGcmKeySize = 32;
94 constexpr int kAesGcmKeySizeBits = 256;
95 
96 template <typename T> class ErrMsgOr {
97   public:
ErrMsgOr(std::string errMsg)98     ErrMsgOr(std::string errMsg)  // NOLINT(google-explicit-constructor)
99         : errMsg_(std::move(errMsg)) {}
ErrMsgOr(const char * errMsg)100     ErrMsgOr(const char* errMsg)  // NOLINT(google-explicit-constructor)
101         : errMsg_(errMsg) {}
ErrMsgOr(std::string_view errMsg)102     ErrMsgOr(std::string_view errMsg)  // NOLINT(google-explicit-constructor)
103         : errMsg_(errMsg) {}
ErrMsgOr(T val)104     ErrMsgOr(T val)  // NOLINT(google-explicit-constructor)
105         : value_(std::move(val)) {}
106 
107     explicit operator bool() const { return value_.has_value(); }
108 
109     T* operator->() & {
110         assert(value_);
111         return &value_.value();
112     }
113     T& operator*() & {
114         assert(value_);
115         return value_.value();
116     };
117     T&& operator*() && {
118         assert(value_);
119         return std::move(value_).value();
120     };
121 
message()122     const std::string& message() { return errMsg_; }
moveMessage()123     std::string moveMessage() { return std::move(errMsg_); }
124 
moveValue()125     T moveValue() {
126         assert(value_);
127         return std::move(value_).value();
128     }
129 
130   private:
131     std::string errMsg_;
132     std::optional<T> value_;
133 };
134 
135 class CoseKey {
136   public:
CoseKey()137     CoseKey() {}
138     CoseKey(const CoseKey&) = delete;
139     CoseKey(CoseKey&&) = default;
140 
141     enum Label : int {
142         KEY_TYPE = 1,
143         KEY_ID = 2,
144         ALGORITHM = 3,
145         KEY_OPS = 4,
146         CURVE = -1,
147         PUBKEY_X = -2,
148         PUBKEY_Y = -3,
149         PRIVATE_KEY = -4,
150         TEST_KEY = -70000  // Application-defined
151     };
152 
parse(const bytevec & coseKey)153     static ErrMsgOr<CoseKey> parse(const bytevec& coseKey) {
154         auto [parsedKey, _, errMsg] = cppbor::parse(coseKey);
155         if (!parsedKey) return errMsg + " when parsing key";
156         if (!parsedKey->asMap()) return "CoseKey must be a map";
157         return CoseKey(static_cast<cppbor::Map*>(parsedKey.release()));
158     }
159 
parse(const bytevec & coseKey,CoseKeyType expectedKeyType,CoseKeyAlgorithm expectedAlgorithm,CoseKeyCurve expectedCurve)160     static ErrMsgOr<CoseKey> parse(const bytevec& coseKey, CoseKeyType expectedKeyType,
161                                    CoseKeyAlgorithm expectedAlgorithm, CoseKeyCurve expectedCurve) {
162         auto key = parse(coseKey);
163         if (!key) return key;
164 
165         if (!key->checkIntValue(CoseKey::KEY_TYPE, expectedKeyType) ||
166             !key->checkIntValue(CoseKey::ALGORITHM, expectedAlgorithm) ||
167             !key->checkIntValue(CoseKey::CURVE, expectedCurve)) {
168             return "Unexpected key type:";
169         }
170 
171         return key;
172     }
173 
parseEd25519(const bytevec & coseKey)174     static ErrMsgOr<CoseKey> parseEd25519(const bytevec& coseKey) {
175         auto key = parse(coseKey, OCTET_KEY_PAIR, EDDSA, ED25519);
176         if (!key) return key;
177 
178         auto& pubkey = key->getMap().get(PUBKEY_X);
179         if (!pubkey || !pubkey->asBstr() ||
180             pubkey->asBstr()->value().size() != ED25519_PUBLIC_KEY_LEN) {
181             return "Invalid Ed25519 public key";
182         }
183 
184         return key;
185     }
186 
parseX25519(const bytevec & coseKey,bool requireKid)187     static ErrMsgOr<CoseKey> parseX25519(const bytevec& coseKey, bool requireKid) {
188         auto key = parse(coseKey, OCTET_KEY_PAIR, ECDH_ES_HKDF_256, X25519);
189         if (!key) return key;
190 
191         auto& pubkey = key->getMap().get(PUBKEY_X);
192         if (!pubkey || !pubkey->asBstr() ||
193             pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) {
194             return "Invalid X25519 public key";
195         }
196 
197         auto& kid = key->getMap().get(KEY_ID);
198         if (requireKid && (!kid || !kid->asBstr())) {
199             return "Missing KID";
200         }
201 
202         return key;
203     }
204 
parseP256(const bytevec & coseKey)205     static ErrMsgOr<CoseKey> parseP256(const bytevec& coseKey) {
206         auto key = parse(coseKey, EC2, ES256, P256);
207         if (!key) return key;
208 
209         auto& pubkey_x = key->getMap().get(PUBKEY_X);
210         auto& pubkey_y = key->getMap().get(PUBKEY_Y);
211         if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() ||
212             pubkey_x->asBstr()->value().size() != 32 || pubkey_y->asBstr()->value().size() != 32) {
213             return "Invalid P256 public key";
214         }
215 
216         return key;
217     }
218 
parseP384(const bytevec & coseKey)219     static ErrMsgOr<CoseKey> parseP384(const bytevec& coseKey) {
220         auto key = parse(coseKey, EC2, ES384, P384);
221         if (!key) return key;
222 
223         auto& pubkey_x = key->getMap().get(PUBKEY_X);
224         auto& pubkey_y = key->getMap().get(PUBKEY_Y);
225         if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() ||
226             pubkey_x->asBstr()->value().size() != 48 || pubkey_y->asBstr()->value().size() != 48) {
227             return "Invalid P384 public key";
228         }
229 
230         return key;
231     }
232 
getEcPublicKey(const bytevec & pubX,const bytevec & pubY)233     static ErrMsgOr<bytevec> getEcPublicKey(const bytevec& pubX, const bytevec& pubY) {
234         if (pubX.empty() || pubY.empty()) {
235             return "Missing input parameters";
236         }
237         bytevec pubKey;
238         pubKey.insert(pubKey.begin(), pubX.begin(), pubX.end());
239         pubKey.insert(pubKey.end(), pubY.begin(), pubY.end());
240         return pubKey;
241     }
242 
getEcPublicKey()243     ErrMsgOr<bytevec> getEcPublicKey() {
244         auto pubX = getBstrValue(PUBKEY_X);
245         auto pubY = getBstrValue(PUBKEY_Y);
246         if (!pubX.has_value() || !pubY.has_value()) {
247             return "Error while getting EC public key from CoseKey.";
248         }
249         return getEcPublicKey(pubX.value(), pubY.value());
250     }
251 
getIntValue(Label label)252     std::optional<int> getIntValue(Label label) {
253         const auto& value = key_->get(label);
254         if (!value || !value->asInt()) return {};
255         return value->asInt()->value();
256     }
257 
getBstrValue(Label label)258     std::optional<bytevec> getBstrValue(Label label) {
259         const auto& value = key_->get(label);
260         if (!value || !value->asBstr()) return {};
261         return value->asBstr()->value();
262     }
263 
getMap()264     const cppbor::Map& getMap() const { return *key_; }
moveMap()265     cppbor::Map&& moveMap() { return std::move(*key_); }
266 
checkIntValue(Label label,int expectedValue)267     bool checkIntValue(Label label, int expectedValue) {
268         const auto& value = key_->get(label);
269         return value && value->asInt() && value->asInt()->value() == expectedValue;
270     }
271 
add(Label label,int value)272     void add(Label label, int value) { key_->add(label, value); }
add(Label label,bytevec value)273     void add(Label label, bytevec value) { key_->add(label, std::move(value)); }
274 
encode()275     bytevec encode() { return key_->canonicalize().encode(); }
276 
277   private:
CoseKey(cppbor::Map * parsedKey)278     explicit CoseKey(cppbor::Map* parsedKey) : key_(parsedKey) {}
279 
280     // This is the full parsed key structure.
281     std::unique_ptr<cppbor::Map> key_;
282 };
283 
284 // Utility function for generating an HMAC given a key and some input
285 // data. Returns std::nullopt on error
286 ErrMsgOr<HmacSha256> generateHmacSha256(const bytevec& key, const bytevec& data);
287 
288 ErrMsgOr<HmacSha256> generateCoseMac0Mac(HmacSha256Function macFunction, const bytevec& externalAad,
289                                          const bytevec& payload);
290 ErrMsgOr<cppbor::Array> constructCoseMac0(HmacSha256Function macFunction,
291                                           const bytevec& externalAad, const bytevec& payload);
292 ErrMsgOr<bytevec /* payload */> verifyAndParseCoseMac0(const cppbor::Item* macItem,
293                                                        const bytevec& macKey);
294 
295 ErrMsgOr<bytevec> createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams,
296                                            const bytevec& payload, const bytevec& aad);
297 ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, const bytevec& payload,
298                                            const bytevec& aad);
299 ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields,
300                                            const bytevec& payload, const bytevec& aad);
301 ErrMsgOr<cppbor::Array> constructECDSACoseSign1(const bytevec& key,
302                                                 cppbor::Map extraProtectedFields,
303                                                 const bytevec& payload, const bytevec& aad);
304 
305 /**
306  * Verify and parse a COSE_Sign1 message, returning the payload.
307  *
308  * @param coseSign1 is the COSE_Sign1 to verify and parse.
309  *
310  * @param signingCoseKey is a CBOR-encoded COSE_Key to use to verify the signature.  The bytevec may
311  *        be empty, in which case the function assumes that coseSign1's payload is the COSE_Key to
312  *        use, i.e. that coseSign1 is a self-signed "certificate".
313  */
314 ErrMsgOr<bytevec /* payload */> verifyAndParseCoseSign1(const cppbor::Array* coseSign1,
315                                                         const bytevec& signingCoseKey,
316                                                         const bytevec& aad);
317 
318 ErrMsgOr<bytevec> createCoseEncryptCiphertext(const bytevec& key, const bytevec& nonce,
319                                               const bytevec& protectedParams, const bytevec& aad);
320 ErrMsgOr<cppbor::Array> constructCoseEncrypt(const bytevec& key, const bytevec& nonce,
321                                              const bytevec& plaintextPayload, const bytevec& aad,
322                                              cppbor::Array recipients);
323 ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>>
324 getSenderPubKeyFromCoseEncrypt(const cppbor::Item* encryptItem);
325 inline ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>>
getSenderPubKeyFromCoseEncrypt(const std::unique_ptr<cppbor::Item> & encryptItem)326 getSenderPubKeyFromCoseEncrypt(const std::unique_ptr<cppbor::Item>& encryptItem) {
327     return getSenderPubKeyFromCoseEncrypt(encryptItem.get());
328 }
329 
330 ErrMsgOr<bytevec /* plaintextPayload */>
331 decryptCoseEncrypt(const bytevec& key, const cppbor::Item* encryptItem, const bytevec& aad);
332 
333 ErrMsgOr<bytevec> x25519_HKDF_DeriveKey(const bytevec& senderPubKey, const bytevec& senderPrivKey,
334                                         const bytevec& recipientPubKey, bool senderIsA);
335 ErrMsgOr<bytevec> ECDH_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA,
336                                       const bytevec& pubKeyB, bool senderIsA);
337 bytevec sha256(const bytevec& data);
338 bytevec sha384(const bytevec& data);
339 ErrMsgOr<bytevec /* ciphertextWithTag */> aesGcmEncrypt(const bytevec& key, const bytevec& nonce,
340                                                         const bytevec& aad,
341                                                         const bytevec& plaintext);
342 ErrMsgOr<bytevec /* plaintext */> aesGcmDecrypt(const bytevec& key, const bytevec& nonce,
343                                                 const bytevec& aad,
344                                                 const bytevec& ciphertextWithTag);
345 
346 }  // namespace cppcose
347