xref: /aosp_15_r20/external/open-dice/src/cbor_cert_op.c (revision 60b67249c2e226f42f35cc6cfe66c6048e0bae6b)
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