• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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