1 // Copyright 2021 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 // This is a DiceGenerateCertificate implementation that generates a CWT-style
16 // CBOR certificate. The function DiceCoseEncodePublicKey depends on the
17 // signature algorithm type, and must be implemented elsewhere.
18
19 #include <stddef.h>
20 #include <stdint.h>
21 #include <string.h>
22
23 #include "dice/cbor_writer.h"
24 #include "dice/config/cose_key_config.h"
25 #include "dice/dice.h"
26 #include "dice/ops.h"
27 #include "dice/ops/trait/cose.h"
28 #include "dice/profile_name.h"
29 #include "dice/utils.h"
30
31 // Max size of COSE_Key encoding.
32 #define DICE_MAX_PUBLIC_KEY_SIZE (DICE_PUBLIC_KEY_BUFFER_SIZE + 32)
33 // Max size of the COSE_Sign1 protected attributes.
34 #define DICE_MAX_PROTECTED_ATTRIBUTES_SIZE 16
35
EncodeProtectedAttributes(void * context,DicePrincipal principal,size_t buffer_size,uint8_t * buffer,size_t * encoded_size)36 static DiceResult EncodeProtectedAttributes(void* context,
37 DicePrincipal principal,
38 size_t buffer_size, uint8_t* buffer,
39 size_t* encoded_size) {
40 // Constants per RFC 8152.
41 const int64_t kCoseHeaderAlgLabel = 1;
42
43 struct CborOut out;
44 CborOutInit(buffer, buffer_size, &out);
45 CborWriteMap(/*num_elements=*/1, &out);
46 // Add the algorithm.
47 DiceKeyParam key_param;
48 DiceResult result = DiceGetKeyParam(context, principal, &key_param);
49 if (result != kDiceResultOk) {
50 return result;
51 }
52 CborWriteInt(kCoseHeaderAlgLabel, &out);
53 CborWriteInt(key_param.cose_key_algorithm, &out);
54 *encoded_size = CborOutSize(&out);
55 if (CborOutOverflowed(&out)) {
56 return kDiceResultBufferTooSmall;
57 }
58 return kDiceResultOk;
59 }
60
EncodeCoseTbs(const uint8_t * protected_attributes,size_t protected_attributes_size,size_t payload_size,const uint8_t * aad,size_t aad_size,size_t buffer_size,uint8_t * buffer,uint8_t ** payload,size_t * encoded_size)61 static DiceResult EncodeCoseTbs(const uint8_t* protected_attributes,
62 size_t protected_attributes_size,
63 size_t payload_size, const uint8_t* aad,
64 size_t aad_size, size_t buffer_size,
65 uint8_t* buffer, uint8_t** payload,
66 size_t* encoded_size) {
67 struct CborOut out;
68 CborOutInit(buffer, buffer_size, &out);
69 // TBS is an array of four elements.
70 CborWriteArray(/*num_elements=*/4, &out);
71 // Context string field.
72 CborWriteTstr("Signature1", &out);
73 // Protected attributes from COSE_Sign1.
74 CborWriteBstr(protected_attributes_size, protected_attributes, &out);
75 // Additional authenticated data.
76 CborWriteBstr(aad_size, aad, &out);
77 // Space for the payload, to be filled in by the caller.
78 *payload = CborAllocBstr(payload_size, &out);
79 *encoded_size = CborOutSize(&out);
80 if (CborOutOverflowed(&out)) {
81 return kDiceResultBufferTooSmall;
82 }
83 return kDiceResultOk;
84 }
85
EncodeCoseSign1(void * context,const uint8_t * protected_attributes,size_t protected_attributes_size,const uint8_t * payload,size_t payload_size,bool move_payload,const uint8_t signature[DICE_SIGNATURE_BUFFER_SIZE],size_t buffer_size,uint8_t * buffer,size_t * encoded_size)86 static DiceResult EncodeCoseSign1(
87 void* context, const uint8_t* protected_attributes,
88 size_t protected_attributes_size, const uint8_t* payload,
89 size_t payload_size, bool move_payload,
90 const uint8_t signature[DICE_SIGNATURE_BUFFER_SIZE], size_t buffer_size,
91 uint8_t* buffer, size_t* encoded_size) {
92 struct CborOut out;
93 CborOutInit(buffer, buffer_size, &out);
94 // COSE_Sign1 is an array of four elements.
95 CborWriteArray(/*num_elements=*/4, &out);
96 // Protected attributes.
97 CborWriteBstr(protected_attributes_size, protected_attributes, &out);
98 // Empty map for unprotected attributes.
99 CborWriteMap(/*num_pairs=*/0, &out);
100 // Payload.
101 if (move_payload) {
102 // The payload is already present in the buffer, so we can move it into
103 // place.
104 uint8_t* payload_alloc = CborAllocBstr(payload_size, &out);
105 if (payload_alloc) {
106 // We're assuming what we've written above is small enough that it doesn't
107 // overwrite the payload. Check in case that stops being true.
108 if (payload < payload_alloc) {
109 return kDiceResultPlatformError;
110 }
111 memmove(payload_alloc, payload, payload_size);
112 }
113 } else {
114 CborWriteBstr(payload_size, payload, &out);
115 }
116 DiceKeyParam key_param;
117 DiceResult result =
118 DiceGetKeyParam(context, kDicePrincipalAuthority, &key_param);
119 if (result != kDiceResultOk) {
120 return result;
121 }
122 // Signature.
123 CborWriteBstr(/*num_elements=*/key_param.signature_size, signature, &out);
124 *encoded_size = CborOutSize(&out);
125 if (CborOutOverflowed(&out)) {
126 return kDiceResultBufferTooSmall;
127 }
128 return kDiceResultOk;
129 }
130
DiceCoseSignAndEncodeSign1(void * context,const uint8_t * payload,size_t payload_size,const uint8_t * aad,size_t aad_size,const uint8_t private_key[DICE_PRIVATE_KEY_BUFFER_SIZE],size_t buffer_size,uint8_t * buffer,size_t * encoded_size)131 DiceResult DiceCoseSignAndEncodeSign1(
132 void* context, const uint8_t* payload, size_t payload_size,
133 const uint8_t* aad, size_t aad_size,
134 const uint8_t private_key[DICE_PRIVATE_KEY_BUFFER_SIZE], size_t buffer_size,
135 uint8_t* buffer, size_t* encoded_size) {
136 DiceResult result;
137
138 *encoded_size = 0;
139
140 // The encoded protected attributes are used in the TBS and the final
141 // COSE_Sign1 structure.
142 uint8_t protected_attributes[DICE_MAX_PROTECTED_ATTRIBUTES_SIZE];
143 size_t protected_attributes_size = 0;
144 result = EncodeProtectedAttributes(
145 context, kDicePrincipalAuthority, sizeof(protected_attributes),
146 protected_attributes, &protected_attributes_size);
147 if (result != kDiceResultOk) {
148 return kDiceResultPlatformError;
149 }
150
151 // Construct a To-Be-Signed (TBS) structure based on the relevant fields of
152 // the COSE_Sign1.
153 uint8_t* payload_buffer;
154 result = EncodeCoseTbs(protected_attributes, protected_attributes_size,
155 payload_size, aad, aad_size, buffer_size, buffer,
156 &payload_buffer, encoded_size);
157 if (result != kDiceResultOk) {
158 // Check how big the buffer needs to be in total.
159 size_t final_encoded_size = 0;
160 EncodeCoseSign1(context, protected_attributes, protected_attributes_size,
161 payload, payload_size, /*move_payload=*/false,
162 /*signature=*/NULL, /*buffer_size=*/0, /*buffer=*/NULL,
163 &final_encoded_size);
164 if (*encoded_size < final_encoded_size) {
165 *encoded_size = final_encoded_size;
166 }
167 return result;
168 }
169 memcpy(payload_buffer, payload, payload_size);
170
171 // Sign the TBS with the authority key.
172 uint8_t signature[DICE_SIGNATURE_BUFFER_SIZE];
173 result = DiceSign(context, buffer, *encoded_size, private_key, signature);
174 if (result != kDiceResultOk) {
175 return result;
176 }
177
178 // The final certificate is an untagged COSE_Sign1 structure.
179 return EncodeCoseSign1(context, protected_attributes,
180 protected_attributes_size, payload, payload_size,
181 /*move_payload=*/false, signature, buffer_size, buffer,
182 encoded_size);
183 }
184
185 // Encodes a CBOR Web Token (CWT) with an issuer, subject, and additional
186 // fields.
EncodeCwt(void * context,const DiceInputValues * input_values,const char * authority_id_hex,const char * subject_id_hex,const uint8_t * encoded_public_key,size_t encoded_public_key_size,size_t buffer_size,uint8_t * buffer,size_t * encoded_size)187 static DiceResult EncodeCwt(void* context, const DiceInputValues* input_values,
188 const char* authority_id_hex,
189 const char* subject_id_hex,
190 const uint8_t* encoded_public_key,
191 size_t encoded_public_key_size, size_t buffer_size,
192 uint8_t* buffer, size_t* encoded_size) {
193 // Constants per RFC 8392.
194 const int64_t kCwtIssuerLabel = 1;
195 const int64_t kCwtSubjectLabel = 2;
196 // Constants per the Open Profile for DICE specification.
197 const int64_t kCodeHashLabel = -4670545;
198 const int64_t kCodeDescriptorLabel = -4670546;
199 const int64_t kConfigHashLabel = -4670547;
200 const int64_t kConfigDescriptorLabel = -4670548;
201 const int64_t kAuthorityHashLabel = -4670549;
202 const int64_t kAuthorityDescriptorLabel = -4670550;
203 const int64_t kModeLabel = -4670551;
204 const int64_t kSubjectPublicKeyLabel = -4670552;
205 const int64_t kKeyUsageLabel = -4670553;
206 const int64_t kProfileNameLabel = -4670554;
207 // Key usage constant per RFC 5280.
208 const uint8_t kKeyUsageCertSign = 32;
209
210 // Count the number of entries.
211 uint32_t map_pairs = 7;
212 if (input_values->code_descriptor_size > 0) {
213 map_pairs += 1;
214 }
215 if (input_values->config_type == kDiceConfigTypeDescriptor) {
216 map_pairs += 2;
217 } else {
218 map_pairs += 1;
219 }
220 if (input_values->authority_descriptor_size > 0) {
221 map_pairs += 1;
222 }
223
224 DiceKeyParam key_param;
225 DiceResult result =
226 DiceGetKeyParam(context, kDicePrincipalSubject, &key_param);
227 if (result != kDiceResultOk) {
228 return result;
229 }
230 if (DICE_PROFILE_NAME) {
231 map_pairs += 1;
232 }
233
234 struct CborOut out;
235 CborOutInit(buffer, buffer_size, &out);
236 CborWriteMap(map_pairs, &out);
237 // Add the issuer.
238 CborWriteInt(kCwtIssuerLabel, &out);
239 CborWriteTstr(authority_id_hex, &out);
240 // Add the subject.
241 CborWriteInt(kCwtSubjectLabel, &out);
242 CborWriteTstr(subject_id_hex, &out);
243 // Add the code hash.
244 CborWriteInt(kCodeHashLabel, &out);
245 CborWriteBstr(DICE_HASH_SIZE, input_values->code_hash, &out);
246 // Add the code descriptor, if provided.
247 if (input_values->code_descriptor_size > 0) {
248 CborWriteInt(kCodeDescriptorLabel, &out);
249 CborWriteBstr(input_values->code_descriptor_size,
250 input_values->code_descriptor, &out);
251 }
252 // Add the config inputs.
253 if (input_values->config_type == kDiceConfigTypeDescriptor) {
254 uint8_t config_descriptor_hash[DICE_HASH_SIZE];
255 // Skip hashing if we're not going to use the answer.
256 if (!CborOutOverflowed(&out)) {
257 result = DiceHash(context, input_values->config_descriptor,
258 input_values->config_descriptor_size,
259 config_descriptor_hash);
260 if (result != kDiceResultOk) {
261 return result;
262 }
263 }
264 // Add the config descriptor.
265 CborWriteInt(kConfigDescriptorLabel, &out);
266 CborWriteBstr(input_values->config_descriptor_size,
267 input_values->config_descriptor, &out);
268 // Add the Config hash.
269 CborWriteInt(kConfigHashLabel, &out);
270 CborWriteBstr(DICE_HASH_SIZE, config_descriptor_hash, &out);
271 } else if (input_values->config_type == kDiceConfigTypeInline) {
272 // Add the inline config.
273 CborWriteInt(kConfigDescriptorLabel, &out);
274 CborWriteBstr(DICE_INLINE_CONFIG_SIZE, input_values->config_value, &out);
275 }
276 // Add the authority inputs.
277 CborWriteInt(kAuthorityHashLabel, &out);
278 CborWriteBstr(DICE_HASH_SIZE, input_values->authority_hash, &out);
279 if (input_values->authority_descriptor_size > 0) {
280 CborWriteInt(kAuthorityDescriptorLabel, &out);
281 CborWriteBstr(input_values->authority_descriptor_size,
282 input_values->authority_descriptor, &out);
283 }
284 uint8_t mode_byte = input_values->mode;
285 uint8_t key_usage = kKeyUsageCertSign;
286 // Add the mode input.
287 CborWriteInt(kModeLabel, &out);
288 CborWriteBstr(/*data_sisze=*/1, &mode_byte, &out);
289 // Add the subject public key.
290 CborWriteInt(kSubjectPublicKeyLabel, &out);
291 CborWriteBstr(encoded_public_key_size, encoded_public_key, &out);
292 // Add the key usage.
293 CborWriteInt(kKeyUsageLabel, &out);
294 CborWriteBstr(/*data_size=*/1, &key_usage, &out);
295 // Add the profile name
296 if (DICE_PROFILE_NAME) {
297 CborWriteInt(kProfileNameLabel, &out);
298 CborWriteTstr(DICE_PROFILE_NAME, &out);
299 }
300 *encoded_size = CborOutSize(&out);
301 if (CborOutOverflowed(&out)) {
302 return kDiceResultBufferTooSmall;
303 }
304 return kDiceResultOk;
305 }
306
DiceGenerateCertificate(void * context,const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],const DiceInputValues * input_values,size_t certificate_buffer_size,uint8_t * certificate,size_t * certificate_actual_size)307 DiceResult DiceGenerateCertificate(
308 void* context,
309 const uint8_t subject_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
310 const uint8_t authority_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE],
311 const DiceInputValues* input_values, size_t certificate_buffer_size,
312 uint8_t* certificate, size_t* certificate_actual_size) {
313 DiceResult result = kDiceResultOk;
314
315 *certificate_actual_size = 0;
316 if (input_values->config_type != kDiceConfigTypeDescriptor &&
317 input_values->config_type != kDiceConfigTypeInline) {
318 return kDiceResultInvalidInput;
319 }
320
321 // Declare buffers which are cleared on 'goto out'.
322 uint8_t subject_private_key[DICE_PRIVATE_KEY_BUFFER_SIZE];
323 uint8_t authority_private_key[DICE_PRIVATE_KEY_BUFFER_SIZE];
324
325 // Derive keys and IDs from the private key seeds.
326 uint8_t subject_public_key[DICE_PUBLIC_KEY_BUFFER_SIZE];
327 result = DiceKeypairFromSeed(context, kDicePrincipalSubject,
328 subject_private_key_seed, subject_public_key,
329 subject_private_key);
330 if (result != kDiceResultOk) {
331 goto out;
332 }
333
334 DiceKeyParam subject_key_param;
335 DiceKeyParam authority_key_param;
336 result = DiceGetKeyParam(context, kDicePrincipalSubject, &subject_key_param);
337 if (result != kDiceResultOk) {
338 goto out;
339 }
340 result =
341 DiceGetKeyParam(context, kDicePrincipalAuthority, &authority_key_param);
342 if (result != kDiceResultOk) {
343 goto out;
344 }
345
346 uint8_t subject_id[DICE_ID_SIZE];
347 result =
348 DiceDeriveCdiCertificateId(context, subject_public_key,
349 subject_key_param.public_key_size, subject_id);
350 if (result != kDiceResultOk) {
351 goto out;
352 }
353 char subject_id_hex[41];
354 DiceHexEncode(subject_id, sizeof(subject_id), subject_id_hex,
355 sizeof(subject_id_hex));
356 subject_id_hex[sizeof(subject_id_hex) - 1] = '\0';
357
358 uint8_t authority_public_key[DICE_PUBLIC_KEY_BUFFER_SIZE];
359 result = DiceKeypairFromSeed(context, kDicePrincipalAuthority,
360 authority_private_key_seed, authority_public_key,
361 authority_private_key);
362 if (result != kDiceResultOk) {
363 goto out;
364 }
365
366 uint8_t authority_id[DICE_ID_SIZE];
367 result = DiceDeriveCdiCertificateId(context, authority_public_key,
368 authority_key_param.public_key_size,
369 authority_id);
370 if (result != kDiceResultOk) {
371 goto out;
372 }
373 char authority_id_hex[41];
374 DiceHexEncode(authority_id, sizeof(authority_id), authority_id_hex,
375 sizeof(authority_id_hex));
376 authority_id_hex[sizeof(authority_id_hex) - 1] = '\0';
377
378 // The public key encoded as a COSE_Key structure is embedded in the CWT.
379 uint8_t encoded_public_key[DICE_MAX_PUBLIC_KEY_SIZE];
380 size_t encoded_public_key_size = 0;
381 result = DiceCoseEncodePublicKey(
382 context, kDicePrincipalSubject, subject_public_key,
383 sizeof(encoded_public_key), encoded_public_key, &encoded_public_key_size);
384 if (result != kDiceResultOk) {
385 result = kDiceResultPlatformError;
386 goto out;
387 }
388
389 // The encoded protected attributes are used in the TBS and the final
390 // COSE_Sign1 structure.
391 uint8_t protected_attributes[DICE_MAX_PROTECTED_ATTRIBUTES_SIZE];
392 size_t protected_attributes_size = 0;
393 result = EncodeProtectedAttributes(
394 context, kDicePrincipalAuthority, sizeof(protected_attributes),
395 protected_attributes, &protected_attributes_size);
396 if (result != kDiceResultOk) {
397 result = kDiceResultPlatformError;
398 goto out;
399 }
400
401 // Find out how big the CWT will be.
402 size_t cwt_size;
403 EncodeCwt(context, input_values, authority_id_hex, subject_id_hex,
404 encoded_public_key, encoded_public_key_size, /*buffer_size=*/0,
405 /*buffer=*/NULL, &cwt_size);
406
407 // We need space to assemble the TBS. The size of the buffer needed depends on
408 // the size of the CWT, which is outside our control (e.g. it might have a
409 // very large config descriptor). So we use the certificate buffer as
410 // temporary storage; if we run out of space we will make sure the caller
411 // knows the size we actually need for this.
412 // Encode the TBS, leaving space for the final payload (the CWT).
413 uint8_t* cwt_ptr;
414 size_t tbs_size;
415 result =
416 EncodeCoseTbs(protected_attributes, protected_attributes_size, cwt_size,
417 /*aad=*/NULL, /*aad_size=*/0, certificate_buffer_size,
418 certificate, &cwt_ptr, &tbs_size);
419
420 if (result != kDiceResultOk) {
421 // There wasn't enough space to put together the TBS. The total buffer size
422 // we need is either the amount needed for the TBS, or the amount needed for
423 // encoded payload and signature.
424 size_t final_encoded_size = 0;
425 EncodeCoseSign1(context, protected_attributes, protected_attributes_size,
426 cwt_ptr, cwt_size, /*move_payload=*/false,
427 /*signature=*/NULL, /*buffer_size=*/0, /*buffer=*/NULL,
428 &final_encoded_size);
429 *certificate_actual_size =
430 final_encoded_size > tbs_size ? final_encoded_size : tbs_size;
431 result = kDiceResultBufferTooSmall;
432 goto out;
433 }
434
435 // Now we can encode the payload directly into the allocated BSTR in the TBS.
436 size_t final_cwt_size;
437 result = EncodeCwt(context, input_values, authority_id_hex, subject_id_hex,
438 encoded_public_key, encoded_public_key_size, cwt_size,
439 cwt_ptr, &final_cwt_size);
440 if (result == kDiceResultBufferTooSmall || final_cwt_size != cwt_size) {
441 result = kDiceResultPlatformError;
442 }
443 if (result != kDiceResultOk) {
444 goto out;
445 }
446
447 // Sign the now-complete TBS.
448 uint8_t signature[DICE_SIGNATURE_BUFFER_SIZE];
449 result = DiceSign(context, certificate, tbs_size, authority_private_key,
450 signature);
451 if (result != kDiceResultOk) {
452 goto out;
453 }
454
455 // And now we can produce the complete CoseSign1, including the signature, and
456 // moving the payload into place as we do it.
457 result = EncodeCoseSign1(
458 context, protected_attributes, protected_attributes_size, cwt_ptr,
459 cwt_size, /*move_payload=*/true, signature, certificate_buffer_size,
460 certificate, certificate_actual_size);
461
462 out:
463 DiceClearMemory(context, sizeof(subject_private_key), subject_private_key);
464 DiceClearMemory(context, sizeof(authority_private_key),
465 authority_private_key);
466
467 return result;
468 }
469
DiceCoseEncodePublicKey(void * context,DicePrincipal principal,const uint8_t public_key[DICE_PUBLIC_KEY_BUFFER_SIZE],size_t buffer_size,uint8_t * buffer,size_t * encoded_size)470 DiceResult DiceCoseEncodePublicKey(
471 void* context, DicePrincipal principal,
472 const uint8_t public_key[DICE_PUBLIC_KEY_BUFFER_SIZE], size_t buffer_size,
473 uint8_t* buffer, size_t* encoded_size) {
474 DiceKeyParam key_param;
475 DiceResult result = DiceGetKeyParam(context, principal, &key_param);
476 if (result != kDiceResultOk) {
477 return result;
478 }
479 struct CborOut out;
480 CborOutInit(buffer, buffer_size, &out);
481 if (key_param.cose_key_type == kCoseKeyKtyOkp) {
482 CborWriteMap(/*num_pairs=*/5, &out);
483 } else if (key_param.cose_key_type == kCoseKeyKtyEc2) {
484 CborWriteMap(/*num_pairs=*/6, &out);
485 } else {
486 return kDiceResultInvalidInput;
487 }
488 // Add the key type.
489 CborWriteInt(kCoseKeyKtyLabel, &out);
490 CborWriteInt(key_param.cose_key_type, &out);
491 // Add the algorithm.
492 CborWriteInt(kCoseKeyAlgLabel, &out);
493 CborWriteInt(key_param.cose_key_algorithm, &out);
494 // Add the KeyOps.
495 CborWriteInt(kCoseKeyOpsLabel, &out);
496 CborWriteArray(/*num_elements=*/1, &out);
497 CborWriteInt(kCoseKeyOpsVerify, &out);
498 // Add the curve.
499 CborWriteInt(kCoseKeyCrvLabel, &out);
500 CborWriteInt(key_param.cose_key_curve, &out);
501
502 // Add the public key.
503 if (key_param.cose_key_type == kCoseKeyKtyOkp) {
504 CborWriteInt(kCoseKeyXLabel, &out);
505 CborWriteBstr(key_param.public_key_size, public_key, &out);
506 } else if (key_param.cose_key_type == kCoseKeyKtyEc2) {
507 // Add the subject public key x and y coordinates
508 int xy_param_size = key_param.public_key_size / 2;
509 CborWriteInt(kCoseKeyXLabel, &out);
510 CborWriteBstr(xy_param_size, &public_key[0], &out);
511 CborWriteInt(kCoseKeyYLabel, &out);
512 CborWriteBstr(xy_param_size, &public_key[xy_param_size], &out);
513 }
514
515 *encoded_size = CborOutSize(&out);
516 if (CborOutOverflowed(&out)) {
517 return kDiceResultBufferTooSmall;
518 }
519 return kDiceResultOk;
520 }
521