• 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 #include <keymaster/cppcose/cppcose.h>
18 
19 #include <iostream>
20 #include <stdio.h>
21 
22 #include <cppbor.h>
23 #include <cppbor_parse.h>
24 
25 #include <openssl/err.h>
26 
27 namespace cppcose {
28 
29 namespace {
30 
aesGcmInitAndProcessAad(const bytevec & key,const bytevec & nonce,const bytevec & aad,bool encrypt)31 ErrMsgOr<bssl::UniquePtr<EVP_CIPHER_CTX>> aesGcmInitAndProcessAad(const bytevec& key,
32                                                                   const bytevec& nonce,
33                                                                   const bytevec& aad,
34                                                                   bool encrypt) {
35     if (key.size() != kAesGcmKeySize) return "Invalid key size";
36 
37     bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
38     if (!ctx) return "Failed to allocate cipher context";
39 
40     if (!EVP_CipherInit_ex(ctx.get(), EVP_aes_256_gcm(), nullptr /* engine */, key.data(),
41                            nonce.data(), encrypt ? 1 : 0)) {
42         return "Failed to initialize cipher";
43     }
44 
45     int outlen;
46     if (!aad.empty() && !EVP_CipherUpdate(ctx.get(), nullptr /* out; null means AAD */, &outlen,
47                                           aad.data(), aad.size())) {
48         return "Failed to process AAD";
49     }
50 
51     return std::move(ctx);
52 }
53 
54 }  // namespace
55 
generateHmacSha256(const bytevec & key,const bytevec & data)56 ErrMsgOr<HmacSha256> generateHmacSha256(const bytevec& key, const bytevec& data) {
57     HmacSha256 digest;
58     unsigned int outLen;
59     uint8_t* out = HMAC(EVP_sha256(),              //
60                         key.data(), key.size(),    //
61                         data.data(), data.size(),  //
62                         digest.data(), &outLen);
63 
64     if (out == nullptr || outLen != digest.size()) {
65         return "Error generating HMAC";
66     }
67     return digest;
68 }
69 
generateCoseMac0Mac(HmacSha256Function macFunction,const bytevec & externalAad,const bytevec & payload)70 ErrMsgOr<HmacSha256> generateCoseMac0Mac(HmacSha256Function macFunction, const bytevec& externalAad,
71                                          const bytevec& payload) {
72     auto macStructure = cppbor::Array()
73                             .add("MAC0")
74                             .add(cppbor::Map().add(ALGORITHM, HMAC_256).canonicalize().encode())
75                             .add(externalAad)
76                             .add(payload)
77                             .encode();
78 
79     auto macTag = macFunction(macStructure);
80     if (!macTag) {
81         return "Error computing public key MAC";
82     }
83 
84     return *macTag;
85 }
86 
constructCoseMac0(HmacSha256Function macFunction,const bytevec & externalAad,const bytevec & payload)87 ErrMsgOr<cppbor::Array> constructCoseMac0(HmacSha256Function macFunction,
88                                           const bytevec& externalAad, const bytevec& payload) {
89     auto tag = generateCoseMac0Mac(macFunction, externalAad, payload);
90     if (!tag) return tag.moveMessage();
91 
92     return cppbor::Array()
93         .add(cppbor::Map().add(ALGORITHM, HMAC_256).canonicalize().encode())
94         .add(cppbor::Map() /* unprotected */)
95         .add(payload)
96         .add(std::pair(tag->begin(), tag->end()));
97 }
98 
verifyAndParseCoseMac0(const cppbor::Item * macItem,const bytevec & macKey)99 ErrMsgOr<bytevec /* payload */> verifyAndParseCoseMac0(const cppbor::Item* macItem,
100                                                        const bytevec& macKey) {
101     auto mac = macItem ? macItem->asArray() : nullptr;
102     if (!mac || mac->size() != kCoseMac0EntryCount) {
103         return "Invalid COSE_Mac0";
104     }
105 
106     auto protectedParms = mac->get(kCoseMac0ProtectedParams)->asBstr();
107     auto unprotectedParms = mac->get(kCoseMac0UnprotectedParams)->asMap();
108     auto payload = mac->get(kCoseMac0Payload)->asBstr();
109     auto tag = mac->get(kCoseMac0Tag)->asBstr();
110     if (!protectedParms || !unprotectedParms || !payload || !tag) {
111         return "Invalid COSE_Mac0 contents";
112     }
113 
114     auto [protectedMap, _, errMsg] = cppbor::parse(protectedParms);
115     if (!protectedMap || !protectedMap->asMap()) {
116         return "Invalid Mac0 protected: " + errMsg;
117     }
118     auto& algo = protectedMap->asMap()->get(ALGORITHM);
119     if (!algo || !algo->asInt() || algo->asInt()->value() != HMAC_256) {
120         return "Unsupported Mac0 algorithm";
121     }
122 
123     auto macFunction = [&macKey](const bytevec& input) {
124         return generateHmacSha256(macKey, input);
125     };
126     auto macTag = generateCoseMac0Mac(macFunction, {} /* external_aad */, payload->value());
127     if (!macTag) return macTag.moveMessage();
128 
129     if (macTag->size() != tag->value().size() ||
130         CRYPTO_memcmp(macTag->data(), tag->value().data(), macTag->size()) != 0) {
131         return "MAC tag mismatch";
132     }
133 
134     return payload->value();
135 }
136 
createCoseSign1Signature(const bytevec & key,const bytevec & protectedParams,const bytevec & payload,const bytevec & aad)137 ErrMsgOr<bytevec> createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams,
138                                            const bytevec& payload, const bytevec& aad) {
139     bytevec signatureInput = cppbor::Array()
140                                  .add("Signature1")  //
141                                  .add(protectedParams)
142                                  .add(aad)
143                                  .add(payload)
144                                  .encode();
145 
146     if (key.size() != ED25519_PRIVATE_KEY_LEN) return "Invalid signing key";
147     bytevec signature(ED25519_SIGNATURE_LEN);
148     if (!ED25519_sign(signature.data(), signatureInput.data(), signatureInput.size(), key.data())) {
149         return "Signing failed";
150     }
151 
152     return signature;
153 }
154 
constructCoseSign1(const bytevec & key,cppbor::Map protectedParams,const bytevec & payload,const bytevec & aad)155 ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, cppbor::Map protectedParams,
156                                            const bytevec& payload, const bytevec& aad) {
157     bytevec protParms = protectedParams.add(ALGORITHM, EDDSA).canonicalize().encode();
158     auto signature = createCoseSign1Signature(key, protParms, payload, aad);
159     if (!signature) return signature.moveMessage();
160 
161     return cppbor::Array()
162         .add(std::move(protParms))
163         .add(cppbor::Map() /* unprotected parameters */)
164         .add(std::move(payload))
165         .add(std::move(*signature));
166 }
167 
constructCoseSign1(const bytevec & key,const bytevec & payload,const bytevec & aad)168 ErrMsgOr<cppbor::Array> constructCoseSign1(const bytevec& key, const bytevec& payload,
169                                            const bytevec& aad) {
170     return constructCoseSign1(key, {} /* protectedParams */, payload, aad);
171 }
172 
verifyAndParseCoseSign1(const cppbor::Array * coseSign1,const bytevec & signingCoseKey,const bytevec & aad)173 ErrMsgOr<bytevec> verifyAndParseCoseSign1(const cppbor::Array* coseSign1,
174                                           const bytevec& signingCoseKey, const bytevec& aad) {
175     if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) {
176         return "Invalid COSE_Sign1";
177     }
178 
179     const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
180     const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap();
181     const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr();
182 
183     if (!protectedParams || !unprotectedParams || !payload) {
184         return "Missing input parameters";
185     }
186 
187     auto [parsedProtParams, _, errMsg] = cppbor::parse(protectedParams);
188     if (!parsedProtParams) {
189         return errMsg + " when parsing protected params.";
190     }
191     if (!parsedProtParams->asMap()) {
192         return "Protected params must be a map";
193     }
194 
195     auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM);
196     if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) {
197         return "Unsupported signature algorithm";
198     }
199 
200     const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr();
201     if (!signature || signature->value().empty()) {
202         return "Missing signature input";
203     }
204 
205     bool selfSigned = signingCoseKey.empty();
206     auto key = CoseKey::parseEd25519(selfSigned ? payload->value() : signingCoseKey);
207     if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty()) {
208         return "Bad signing key: " + key.moveMessage();
209     }
210 
211     bytevec signatureInput =
212         cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode();
213 
214     if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
215                         key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
216         return "Signature verification failed";
217     }
218 
219     return payload->value();
220 }
221 
createCoseEncryptCiphertext(const bytevec & key,const bytevec & nonce,const bytevec & protectedParams,const bytevec & plaintextPayload,const bytevec & aad)222 ErrMsgOr<bytevec> createCoseEncryptCiphertext(const bytevec& key, const bytevec& nonce,
223                                               const bytevec& protectedParams,
224                                               const bytevec& plaintextPayload, const bytevec& aad) {
225     auto ciphertext = aesGcmEncrypt(key, nonce,
226                                     cppbor::Array()            // Enc strucure as AAD
227                                         .add("Encrypt")        // Context
228                                         .add(protectedParams)  // Protected
229                                         .add(aad)              // External AAD
230                                         .encode(),
231                                     plaintextPayload);
232 
233     if (!ciphertext) return ciphertext.moveMessage();
234     return ciphertext.moveValue();
235 }
236 
constructCoseEncrypt(const bytevec & key,const bytevec & nonce,const bytevec & plaintextPayload,const bytevec & aad,cppbor::Array recipients)237 ErrMsgOr<cppbor::Array> constructCoseEncrypt(const bytevec& key, const bytevec& nonce,
238                                              const bytevec& plaintextPayload, const bytevec& aad,
239                                              cppbor::Array recipients) {
240     auto encryptProtectedHeader = cppbor::Map()  //
241                                       .add(ALGORITHM, AES_GCM_256)
242                                       .canonicalize()
243                                       .encode();
244 
245     auto ciphertext =
246         createCoseEncryptCiphertext(key, nonce, encryptProtectedHeader, plaintextPayload, aad);
247     if (!ciphertext) return ciphertext.moveMessage();
248 
249     return cppbor::Array()
250         .add(encryptProtectedHeader)                       // Protected
251         .add(cppbor::Map().add(IV, nonce).canonicalize())  // Unprotected
252         .add(*ciphertext)                                  // Payload
253         .add(std::move(recipients));
254 }
255 
256 ErrMsgOr<std::pair<bytevec /* pubkey */, bytevec /* key ID */>>
getSenderPubKeyFromCoseEncrypt(const cppbor::Item * coseEncrypt)257 getSenderPubKeyFromCoseEncrypt(const cppbor::Item* coseEncrypt) {
258     if (!coseEncrypt || !coseEncrypt->asArray() ||
259         coseEncrypt->asArray()->size() != kCoseEncryptEntryCount) {
260         return "Invalid COSE_Encrypt";
261     }
262 
263     auto& recipients = coseEncrypt->asArray()->get(kCoseEncryptRecipients);
264     if (!recipients || !recipients->asArray() || recipients->asArray()->size() != 1) {
265         return "Invalid recipients list";
266     }
267 
268     auto& recipient = recipients->asArray()->get(0);
269     if (!recipient || !recipient->asArray() || recipient->asArray()->size() != 3) {
270         return "Invalid COSE_recipient";
271     }
272 
273     auto& ciphertext = recipient->asArray()->get(2);
274     if (!ciphertext->asSimple() || !ciphertext->asSimple()->asNull()) {
275         return "Unexpected value in recipients ciphertext field " +
276                cppbor::prettyPrint(ciphertext.get());
277     }
278 
279     auto& protParms = recipient->asArray()->get(0);
280     if (!protParms || !protParms->asBstr()) return "Invalid protected params";
281     auto [parsedProtParms, _, errMsg] = cppbor::parse(protParms->asBstr());
282     if (!parsedProtParms) return "Failed to parse protected params: " + errMsg;
283     if (!parsedProtParms->asMap()) return "Invalid protected params";
284 
285     auto& algorithm = parsedProtParms->asMap()->get(ALGORITHM);
286     if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != ECDH_ES_HKDF_256) {
287         return "Invalid algorithm";
288     }
289 
290     auto& unprotParms = recipient->asArray()->get(1);
291     if (!unprotParms || !unprotParms->asMap()) return "Invalid unprotected params";
292 
293     auto& senderCoseKey = unprotParms->asMap()->get(COSE_KEY);
294     if (!senderCoseKey || !senderCoseKey->asMap()) return "Invalid sender COSE_Key";
295 
296     auto& keyType = senderCoseKey->asMap()->get(CoseKey::KEY_TYPE);
297     if (!keyType || !keyType->asInt() || keyType->asInt()->value() != OCTET_KEY_PAIR) {
298         return "Invalid key type";
299     }
300 
301     auto& curve = senderCoseKey->asMap()->get(CoseKey::CURVE);
302     if (!curve || !curve->asInt() || curve->asInt()->value() != X25519) {
303         return "Unsupported curve";
304     }
305 
306     auto& pubkey = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X);
307     if (!pubkey || !pubkey->asBstr() ||
308         pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) {
309         return "Invalid X25519 public key";
310     }
311 
312     auto& key_id = unprotParms->asMap()->get(KEY_ID);
313     if (key_id && key_id->asBstr()) {
314         return std::make_pair(pubkey->asBstr()->value(), key_id->asBstr()->value());
315     }
316 
317     // If no key ID, just return an empty vector.
318     return std::make_pair(pubkey->asBstr()->value(), bytevec{});
319 }
320 
decryptCoseEncrypt(const bytevec & key,const cppbor::Item * coseEncrypt,const bytevec & external_aad)321 ErrMsgOr<bytevec> decryptCoseEncrypt(const bytevec& key, const cppbor::Item* coseEncrypt,
322                                      const bytevec& external_aad) {
323     if (!coseEncrypt || !coseEncrypt->asArray() ||
324         coseEncrypt->asArray()->size() != kCoseEncryptEntryCount) {
325         return "Invalid COSE_Encrypt";
326     }
327 
328     auto& protParms = coseEncrypt->asArray()->get(kCoseEncryptProtectedParams);
329     auto& unprotParms = coseEncrypt->asArray()->get(kCoseEncryptUnprotectedParams);
330     auto& ciphertext = coseEncrypt->asArray()->get(kCoseEncryptPayload);
331     auto& recipients = coseEncrypt->asArray()->get(kCoseEncryptRecipients);
332 
333     if (!protParms || !protParms->asBstr() || !unprotParms || !ciphertext || !recipients) {
334         return "Invalid COSE_Encrypt";
335     }
336 
337     auto [parsedProtParams, _, errMsg] = cppbor::parse(protParms->asBstr()->value());
338     if (!parsedProtParams) {
339         return errMsg + " when parsing protected params.";
340     }
341     if (!parsedProtParams->asMap()) {
342         return "Protected params must be a map";
343     }
344 
345     auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM);
346     if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != AES_GCM_256) {
347         return "Unsupported encryption algorithm";
348     }
349 
350     if (!unprotParms->asMap() || unprotParms->asMap()->size() != 1) {
351         return "Invalid unprotected params";
352     }
353 
354     auto& nonce = unprotParms->asMap()->get(IV);
355     if (!nonce || !nonce->asBstr() || nonce->asBstr()->value().size() != kAesGcmNonceLength) {
356         return "Invalid nonce";
357     }
358 
359     if (!ciphertext->asBstr()) return "Invalid ciphertext";
360 
361     auto aad = cppbor::Array()                         // Enc strucure as AAD
362                    .add("Encrypt")                     // Context
363                    .add(protParms->asBstr()->value())  // Protected
364                    .add(external_aad)                  // External AAD
365                    .encode();
366 
367     return aesGcmDecrypt(key, nonce->asBstr()->value(), aad, ciphertext->asBstr()->value());
368 }
369 
x25519_HKDF_DeriveKey(const bytevec & pubKeyA,const bytevec & privKeyA,const bytevec & pubKeyB,bool senderIsA)370 ErrMsgOr<bytevec> x25519_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA,
371                                         const bytevec& pubKeyB, bool senderIsA) {
372     if (privKeyA.empty() || pubKeyA.empty() || pubKeyB.empty()) {
373         return "Missing input key parameters";
374     }
375 
376     bytevec rawSharedKey(X25519_SHARED_KEY_LEN);
377     if (!::X25519(rawSharedKey.data(), privKeyA.data(), pubKeyB.data())) {
378         return "ECDH operation failed";
379     }
380 
381     bytevec kdfContext = cppbor::Array()
382                              .add(AES_GCM_256)
383                              .add(cppbor::Array()  // Sender Info
384                                       .add(cppbor::Bstr("client"))
385                                       .add(bytevec{} /* nonce */)
386                                       .add(senderIsA ? pubKeyA : pubKeyB))
387                              .add(cppbor::Array()  // Recipient Info
388                                       .add(cppbor::Bstr("server"))
389                                       .add(bytevec{} /* nonce */)
390                                       .add(senderIsA ? pubKeyB : pubKeyA))
391                              .add(cppbor::Array()               // SuppPubInfo
392                                       .add(kAesGcmKeySizeBits)  // output key length
393                                       .add(bytevec{}))          // protected
394                              .encode();
395 
396     bytevec retval(SHA256_DIGEST_LENGTH);
397     bytevec salt{};
398     if (!HKDF(retval.data(), retval.size(),              //
399               EVP_sha256(),                              //
400               rawSharedKey.data(), rawSharedKey.size(),  //
401               salt.data(), salt.size(),                  //
402               kdfContext.data(), kdfContext.size())) {
403         return "ECDH HKDF failed";
404     }
405 
406     return retval;
407 }
408 
aesGcmEncrypt(const bytevec & key,const bytevec & nonce,const bytevec & aad,const bytevec & plaintext)409 ErrMsgOr<bytevec> aesGcmEncrypt(const bytevec& key, const bytevec& nonce, const bytevec& aad,
410                                 const bytevec& plaintext) {
411     auto ctx = aesGcmInitAndProcessAad(key, nonce, aad, true /* encrypt */);
412     if (!ctx) return ctx.moveMessage();
413 
414     bytevec ciphertext(plaintext.size() + kAesGcmTagSize);
415     int outlen;
416     if (!EVP_CipherUpdate(ctx->get(), ciphertext.data(), &outlen, plaintext.data(),
417                           plaintext.size())) {
418         return "Failed to encrypt plaintext";
419     }
420     assert(plaintext.size() == static_cast<uint64_t>(outlen));
421 
422     if (!EVP_CipherFinal_ex(ctx->get(), ciphertext.data() + outlen, &outlen)) {
423         return "Failed to finalize encryption";
424     }
425     assert(outlen == 0);
426 
427     if (!EVP_CIPHER_CTX_ctrl(ctx->get(), EVP_CTRL_GCM_GET_TAG, kAesGcmTagSize,
428                              ciphertext.data() + plaintext.size())) {
429         return "Failed to retrieve tag";
430     }
431 
432     return ciphertext;
433 }
434 
aesGcmDecrypt(const bytevec & key,const bytevec & nonce,const bytevec & aad,const bytevec & ciphertextWithTag)435 ErrMsgOr<bytevec> aesGcmDecrypt(const bytevec& key, const bytevec& nonce, const bytevec& aad,
436                                 const bytevec& ciphertextWithTag) {
437     auto ctx = aesGcmInitAndProcessAad(key, nonce, aad, false /* encrypt */);
438     if (!ctx) return ctx.moveMessage();
439 
440     if (ciphertextWithTag.size() < kAesGcmTagSize) return "Missing tag";
441 
442     bytevec plaintext(ciphertextWithTag.size() - kAesGcmTagSize);
443     int outlen;
444     if (!EVP_CipherUpdate(ctx->get(), plaintext.data(), &outlen, ciphertextWithTag.data(),
445                           ciphertextWithTag.size() - kAesGcmTagSize)) {
446         return "Failed to decrypt plaintext";
447     }
448     assert(plaintext.size() == static_cast<uint64_t>(outlen));
449 
450     bytevec tag(ciphertextWithTag.end() - kAesGcmTagSize, ciphertextWithTag.end());
451     if (!EVP_CIPHER_CTX_ctrl(ctx->get(), EVP_CTRL_GCM_SET_TAG, kAesGcmTagSize, tag.data())) {
452         return "Failed to set tag: " + std::to_string(ERR_peek_last_error());
453     }
454 
455     if (!EVP_CipherFinal_ex(ctx->get(), nullptr, &outlen)) {
456         return "Failed to finalize encryption";
457     }
458     assert(outlen == 0);
459 
460     return plaintext;
461 }
462 
463 }  // namespace cppcose
464