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
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)195 DiceResult BccHandoverMainFlow(void* context, const uint8_t* bcc_handover,
196 size_t bcc_handover_size,
197 const DiceInputValues* input_values,
198 size_t buffer_size, uint8_t* buffer,
199 size_t* actual_size) {
200 static const int64_t kCdiAttestLabel = 1;
201 static const int64_t kCdiSealLabel = 2;
202 static const int64_t kBccLabel = 3;
203
204 DiceResult result;
205 const uint8_t* current_cdi_attest;
206 const uint8_t* current_cdi_seal;
207 const uint8_t* bcc;
208
209 // Extract details from the handover data.
210 //
211 // BccHandover = {
212 // 1 : bstr .size 32, ; CDI_Attest
213 // 2 : bstr .size 32, ; CDI_Seal
214 // ? 3 : Bcc, ; Certificate chain
215 // }
216 struct CborIn in;
217 int64_t label;
218 size_t item_size;
219 CborInInit(bcc_handover, bcc_handover_size, &in);
220 if (CborReadMap(&in, &item_size) != CBOR_READ_RESULT_OK || item_size < 2 ||
221 // Read the attestation CDI.
222 CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
223 label != kCdiAttestLabel ||
224 CborReadBstr(&in, &item_size, ¤t_cdi_attest) !=
225 CBOR_READ_RESULT_OK ||
226 item_size != DICE_CDI_SIZE ||
227 // Read the sealing CDI.
228 CborReadInt(&in, &label) != CBOR_READ_RESULT_OK ||
229 label != kCdiSealLabel ||
230 CborReadBstr(&in, &item_size, ¤t_cdi_seal) != CBOR_READ_RESULT_OK ||
231 item_size != DICE_CDI_SIZE) {
232 return kDiceResultInvalidInput;
233 }
234
235 size_t bcc_size = 0;
236 // Calculate the BCC size, if the BCC is present in the BccHandover.
237 if (CborReadInt(&in, &label) == CBOR_READ_RESULT_OK) {
238 if (label != kBccLabel) {
239 return kDiceResultInvalidInput;
240 }
241 size_t bcc_start = CborInOffset(&in);
242 bcc = bcc_handover + bcc_start;
243 if (CborReadSkip(&in) != CBOR_READ_RESULT_OK) {
244 return kDiceResultInvalidInput;
245 }
246 bcc_size = CborInOffset(&in) - bcc_start;
247 }
248
249 // Write the new handover data.
250 struct CborOut out;
251 CborOutInit(buffer, buffer_size, &out);
252 CborWriteMap(/*num_pairs=*/3, &out);
253 CborWriteInt(kCdiAttestLabel, &out);
254 uint8_t* next_cdi_attest = CborAllocBstr(DICE_CDI_SIZE, &out);
255 CborWriteInt(kCdiSealLabel, &out);
256 uint8_t* next_cdi_seal = CborAllocBstr(DICE_CDI_SIZE, &out);
257 CborWriteInt(kBccLabel, &out);
258
259 if (CborOutOverflowed(&out) || !next_cdi_attest || !next_cdi_seal) {
260 return kDiceResultBufferTooSmall;
261 }
262
263 if (bcc_size != 0) {
264 // If BCC is present in the bcc_handover, append the next certificate to the
265 // existing BCC.
266 result = BccMainFlow(context, current_cdi_attest, current_cdi_seal, bcc,
267 bcc_size, input_values, buffer_size - CborOutSize(&out),
268 buffer + CborOutSize(&out), &bcc_size, next_cdi_attest,
269 next_cdi_seal);
270 } else {
271 // If BCC is not present in the bcc_handover, construct BCC from the public key
272 // derived from the current CDI attest and the next CDI certificate.
273 result = BccMainFlowWithNewBcc(
274 context, current_cdi_attest, current_cdi_seal, input_values,
275 buffer_size - CborOutSize(&out), buffer + CborOutSize(&out), &bcc_size,
276 next_cdi_attest, next_cdi_seal);
277 }
278 if (result != kDiceResultOk) {
279 return result;
280 }
281 *actual_size = CborOutSize(&out) + bcc_size;
282 return kDiceResultOk;
283 }
284