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