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