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