• 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 
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