• 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 #include "dice/android/bcc.h"
16 
17 #include <string.h>
18 
19 #include "dice/cbor_reader.h"
20 #include "dice/cbor_writer.h"
21 #include "dice/ops/trait/cose.h"
22 #include "dice/dice.h"
23 #include "dice/ops.h"
24 
25 // Completely gratuitous bit twiddling.
PopulationCount(uint32_t n)26 static size_t PopulationCount(uint32_t n) {
27   n = n - ((n >> 1) & 0x55555555);
28   n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
29   return (((n + (n >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
30 }
31 
BccFormatConfigDescriptor(const BccConfigValues * input_values,size_t buffer_size,uint8_t * buffer,size_t * actual_size)32 DiceResult BccFormatConfigDescriptor(const BccConfigValues* input_values,
33                                      size_t buffer_size, uint8_t* buffer,
34                                      size_t* actual_size) {
35   static const int64_t kComponentNameLabel = -70002;
36   static const int64_t kComponentVersionLabel = -70003;
37   static const int64_t kResettableLabel = -70004;
38 
39   // BccConfigDescriptor = {
40   //   ? -70002 : tstr,     ; Component name
41   //   ? -70003 : int,      ; Component version
42   //   ? -70004 : null,     ; Resettable
43   // }
44   struct CborOut out;
45   CborOutInit(buffer, buffer_size, &out);
46   CborWriteMap(PopulationCount(input_values->inputs), &out);
47   if (input_values->inputs & BCC_INPUT_COMPONENT_NAME &&
48       input_values->component_name) {
49     CborWriteInt(kComponentNameLabel, &out);
50     CborWriteTstr(input_values->component_name, &out);
51   }
52   if (input_values->inputs & BCC_INPUT_COMPONENT_VERSION) {
53     CborWriteInt(kComponentVersionLabel, &out);
54     CborWriteUint(input_values->component_version, &out);
55   }
56   if (input_values->inputs & BCC_INPUT_RESETTABLE) {
57     CborWriteInt(kResettableLabel, &out);
58     CborWriteNull(&out);
59   }
60   if (CborOutOverflowed(&out)) {
61     return kDiceResultBufferTooSmall;
62   }
63   *actual_size = CborOutSize(&out);
64   return kDiceResultOk;
65 }
66 
BccMainFlow(void * context,const uint8_t current_cdi_attest[DICE_CDI_SIZE],const uint8_t current_cdi_seal[DICE_CDI_SIZE],const uint8_t * bcc,size_t bcc_size,const DiceInputValues * input_values,size_t buffer_size,uint8_t * buffer,size_t * actual_size,uint8_t next_cdi_attest[DICE_CDI_SIZE],uint8_t next_cdi_seal[DICE_CDI_SIZE])67 DiceResult BccMainFlow(void* context,
68                        const uint8_t current_cdi_attest[DICE_CDI_SIZE],
69                        const uint8_t current_cdi_seal[DICE_CDI_SIZE],
70                        const uint8_t* bcc, size_t bcc_size,
71                        const DiceInputValues* input_values, size_t buffer_size,
72                        uint8_t* buffer, size_t* actual_size,
73                        uint8_t next_cdi_attest[DICE_CDI_SIZE],
74                        uint8_t next_cdi_seal[DICE_CDI_SIZE]) {
75   DiceResult result;
76   enum CborReadResult res;
77   struct CborIn in;
78   size_t bcc_item_count;
79 
80   // The BCC has a more detailed internal structure, but those details aren't
81   // relevant to the work of this function.
82   //
83   // Bcc = [
84   //   COSE_Key,         ; Root public key
85   //   + COSE_Sign1,     ; Bcc entries
86   // ]
87   CborInInit(bcc, bcc_size, &in);
88   res = CborReadArray(&in, &bcc_item_count);
89   if (res != CBOR_READ_RESULT_OK) {
90     return kDiceResultInvalidInput;
91   }
92 
93   if (bcc_item_count < 2 || bcc_item_count == SIZE_MAX) {
94     // There should at least be the public key and one entry.
95     return kDiceResultInvalidInput;
96   }
97 
98   // Measure the existing BCC entries.
99   size_t bcc_items_offset = CborInOffset(&in);
100   for (size_t bcc_pos = 0; bcc_pos < bcc_item_count; ++bcc_pos) {
101     res = CborReadSkip(&in);
102     if (res != CBOR_READ_RESULT_OK) {
103       return kDiceResultInvalidInput;
104     }
105   }
106   size_t bcc_items_size = CborInOffset(&in) - bcc_items_offset;
107 
108   // Copy to the new buffer, with space in the BCC for one more entry.
109   struct CborOut out;
110   CborOutInit(buffer, buffer_size, &out);
111   CborWriteArray(bcc_item_count + 1, &out);
112   if (CborOutOverflowed(&out) ||
113       bcc_items_size > buffer_size - CborOutSize(&out)) {
114     return kDiceResultBufferTooSmall;
115   }
116   memcpy(buffer + CborOutSize(&out), bcc + bcc_items_offset, bcc_items_size);
117 
118   size_t certificate_size;
119   result =
120       DiceMainFlow(context, current_cdi_attest, current_cdi_seal, input_values,
121                    buffer_size - (CborOutSize(&out) + bcc_items_size),
122                    buffer + CborOutSize(&out) + bcc_items_size,
123                    &certificate_size, next_cdi_attest, next_cdi_seal);
124   if (result != kDiceResultOk) {
125     return result;
126   }
127 
128   *actual_size = CborOutSize(&out) + bcc_items_size + certificate_size;
129   return kDiceResultOk;
130 }
131 
BccMainFlowWithNewBcc(void * context,const uint8_t current_cdi_attest[DICE_CDI_SIZE],const uint8_t current_cdi_seal[DICE_CDI_SIZE],const DiceInputValues * input_values,size_t buffer_size,uint8_t * buffer,size_t * bcc_size,uint8_t next_cdi_attest[DICE_CDI_SIZE],uint8_t next_cdi_seal[DICE_CDI_SIZE])132 static DiceResult BccMainFlowWithNewBcc(
133     void* context, const uint8_t current_cdi_attest[DICE_CDI_SIZE],
134     const uint8_t current_cdi_seal[DICE_CDI_SIZE],
135     const DiceInputValues* input_values, size_t buffer_size, uint8_t* buffer,
136     size_t* bcc_size, uint8_t next_cdi_attest[DICE_CDI_SIZE],
137     uint8_t next_cdi_seal[DICE_CDI_SIZE]) {
138   uint8_t current_cdi_private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE];
139   uint8_t attestation_public_key[DICE_PUBLIC_KEY_SIZE];
140   uint8_t attestation_private_key[DICE_PRIVATE_KEY_SIZE];
141   // Derive an asymmetric private key seed from the current attestation CDI
142   // value.
143   DiceResult result = DiceDeriveCdiPrivateKeySeed(context, current_cdi_attest,
144                                                   current_cdi_private_key_seed);
145   if (result != kDiceResultOk) {
146     goto out;
147   }
148   // Derive attestation key pair.
149   result = DiceKeypairFromSeed(context, current_cdi_private_key_seed,
150                                attestation_public_key, attestation_private_key);
151   if (result != kDiceResultOk) {
152     goto out;
153   }
154 
155   // Consruct the BCC from the attestation public key and the next CDI
156   // certificate.
157   struct CborOut out;
158   CborOutInit(buffer, buffer_size, &out);
159   CborWriteArray(2, &out);
160   if (CborOutOverflowed(&out)) {
161     result = kDiceResultBufferTooSmall;
162     goto out;
163   }
164   size_t encoded_size_used = CborOutSize(&out);
165   buffer += encoded_size_used;
166   buffer_size -= encoded_size_used;
167 
168   size_t encoded_pub_key_size = 0;
169   result = DiceCoseEncodePublicKey(context, attestation_public_key, buffer_size,
170                                    buffer, &encoded_pub_key_size);
171   if (result != kDiceResultOk) {
172     goto out;
173   }
174 
175   buffer += encoded_pub_key_size;
176   buffer_size -= encoded_pub_key_size;
177 
178   result = DiceMainFlow(context, current_cdi_attest, current_cdi_seal,
179                         input_values, buffer_size, buffer, bcc_size,
180                         next_cdi_attest, next_cdi_seal);
181   if (result != kDiceResultOk) {
182     return result;
183   }
184   *bcc_size += encoded_size_used + encoded_pub_key_size;
185 
186 out:
187   DiceClearMemory(context, sizeof(current_cdi_private_key_seed),
188                   current_cdi_private_key_seed);
189   DiceClearMemory(context, sizeof(attestation_private_key),
190                   attestation_private_key);
191 
192   return result;
193 }
194 
195 static const int64_t kCdiAttestLabel = 1;
196 static const int64_t kCdiSealLabel = 2;
197 static const int64_t kBccLabel = 3;
198 
BccHandoverMainFlow(void * context,const uint8_t * bcc_handover,size_t bcc_handover_size,const DiceInputValues * input_values,size_t buffer_size,uint8_t * buffer,size_t * actual_size)199 DiceResult BccHandoverMainFlow(void* context, const uint8_t* bcc_handover,
200                                size_t bcc_handover_size,
201                                const DiceInputValues* input_values,
202                                size_t buffer_size, uint8_t* buffer,
203                                size_t* actual_size) {
204   DiceResult result;
205   const uint8_t* current_cdi_attest;
206   const uint8_t* current_cdi_seal;
207   const uint8_t* bcc;
208   size_t bcc_size;
209 
210   result =
211       BccHandoverParse(bcc_handover, bcc_handover_size, &current_cdi_attest,
212                        &current_cdi_seal, &bcc, &bcc_size);
213   if (result != kDiceResultOk) {
214     return kDiceResultInvalidInput;
215   }
216 
217   // Write the new handover data.
218   struct CborOut out;
219   CborOutInit(buffer, buffer_size, &out);
220   CborWriteMap(/*num_pairs=*/3, &out);
221   CborWriteInt(kCdiAttestLabel, &out);
222   uint8_t* next_cdi_attest = CborAllocBstr(DICE_CDI_SIZE, &out);
223   CborWriteInt(kCdiSealLabel, &out);
224   uint8_t* next_cdi_seal = CborAllocBstr(DICE_CDI_SIZE, &out);
225   CborWriteInt(kBccLabel, &out);
226 
227   if (CborOutOverflowed(&out) || !next_cdi_attest || !next_cdi_seal) {
228     return kDiceResultBufferTooSmall;
229   }
230 
231   if (bcc_size != 0) {
232     // If BCC is present in the bcc_handover, append the next certificate to the
233     // existing BCC.
234     result = BccMainFlow(context, current_cdi_attest, current_cdi_seal, bcc,
235                          bcc_size, input_values, buffer_size - CborOutSize(&out),
236                          buffer + CborOutSize(&out), &bcc_size, next_cdi_attest,
237                          next_cdi_seal);
238   } else {
239     // If BCC is not present in the bcc_handover, construct BCC from the public key
240     // derived from the current CDI attest and the next CDI certificate.
241     result = BccMainFlowWithNewBcc(
242         context, current_cdi_attest, current_cdi_seal, input_values,
243         buffer_size - CborOutSize(&out), buffer + CborOutSize(&out), &bcc_size,
244         next_cdi_attest, next_cdi_seal);
245   }
246   if (result != kDiceResultOk) {
247       return result;
248   }
249   *actual_size = CborOutSize(&out) + bcc_size;
250   return kDiceResultOk;
251 }
252 
BccHandoverParse(const uint8_t * bcc_handover,size_t bcc_handover_size,const uint8_t ** cdi_attest,const uint8_t ** cdi_seal,const uint8_t ** bcc,size_t * bcc_size)253 DiceResult BccHandoverParse(const uint8_t* bcc_handover,
254                             size_t bcc_handover_size,
255                             const uint8_t** cdi_attest,
256                             const uint8_t** cdi_seal, const uint8_t** bcc,
257                             size_t* bcc_size) {
258   // Extract details from the handover data.
259   //
260   // BccHandover = {
261   //   1 : bstr .size 32,     ; CDI_Attest
262   //   2 : bstr .size 32,     ; CDI_Seal
263   //   ? 3 : Bcc,             ; Certificate chain
264   // }
265   struct CborIn in;
266   int64_t label;
267   size_t num_pairs;
268   size_t item_size;
269   CborInInit(bcc_handover, bcc_handover_size, &in);
270   if (CborReadMap(&in, &num_pairs) != CBOR_READ_RESULT_OK || num_pairs < 2 ||
271       // Read the attestation CDI.
272       CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
273       label != kCdiAttestLabel ||
274       CborReadBstr(&in, &item_size, cdi_attest) != CBOR_READ_RESULT_OK ||
275       item_size != DICE_CDI_SIZE ||
276       // Read the sealing CDI.
277       CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
278       label != kCdiSealLabel ||
279       CborReadBstr(&in, &item_size, cdi_seal) != CBOR_READ_RESULT_OK ||
280       item_size != DICE_CDI_SIZE) {
281     return kDiceResultInvalidInput;
282   }
283 
284   *bcc = NULL;
285   *bcc_size = 0;
286   if (num_pairs >= 3 && CborReadInt(&in, &label) == CBOR_READ_RESULT_OK) {
287     if (label == kBccLabel) {
288       // Calculate the BCC size, if the BCC is present in the BccHandover.
289       size_t bcc_start = CborInOffset(&in);
290       if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
291         return kDiceResultInvalidInput;
292       }
293       *bcc = bcc_handover + bcc_start;
294       *bcc_size = CborInOffset(&in) - bcc_start;
295     }
296   }
297 
298   return kDiceResultOk;
299 }
300