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