blob: d66d599848cbf2550cde9a4394ddd61d322f56f2 [file] [log] [blame]
Maulik Patel2358bbb2023-07-21 10:56:56 +01001/*
2 * Copyright (c) 2023, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8#include <assert.h>
9#include "dpe_certificate.h"
10#include "dpe_context_mngr.h"
11#include "dpe_crypto_config.h"
12#include "dpe_crypto_interface.h"
13#include "qcbor/qcbor_encode.h"
14#include "t_cose_common.h"
15#include "t_cose_sign1_sign.h"
16
17#define ID_HEX_SIZE (2 * DICE_ID_SIZE) /* Size of CDI encoded to ascii hex */
18
19struct dpe_cert_encode_ctx {
20 QCBOREncodeContext cbor_enc_ctx;
21 struct t_cose_sign1_sign_ctx signer_ctx;
22};
23
24static void convert_to_ascii_hex(const uint8_t *in,
25 size_t in_size,
26 char *out,
27 size_t out_size)
28{
29 const char hex_table[] = "0123456789abcdef";
30 size_t in_pos = 0;
31 size_t out_pos = 0;
32
33 for (in_pos = 0; in_pos < in_size && out_pos < out_size; in_pos++) {
34 out[out_pos++] = hex_table[(in[in_pos] & 0xF0 >> 4)];
35 out[out_pos++] = hex_table[in[in_pos] & 0x0F];
36 }
37}
38
39static dpe_error_t t_cose_err_to_dpe_err(enum t_cose_err_t err)
40{
41 switch(err) {
42
43 case T_COSE_SUCCESS:
44 return DPE_NO_ERROR;
45
46 case T_COSE_ERR_TOO_SMALL:
47 return DPE_INSUFFICIENT_MEMORY;
48
49 default:
50 /* A lot of the errors are not mapped because they are
51 * primarily internal errors that should never happen. They
52 * end up here.
53 */
54 return DPE_INTERNAL_ERROR;
55 }
56}
57
58static dpe_error_t certificate_encode_start(struct dpe_cert_encode_ctx *me,
59 const UsefulBuf out_buf,
60 psa_key_handle_t private_key)
61{
62 enum t_cose_err_t t_cose_err;
63 struct t_cose_key attest_key;
64 UsefulBufC attest_key_id = {NULL, 0};
65
66 /* DPE Certificate is untagged COSE_Sign1 message */
67 t_cose_sign1_sign_init(&(me->signer_ctx), T_COSE_OPT_OMIT_CBOR_TAG, DPE_T_COSE_ALG);
68
69 attest_key.crypto_lib = T_COSE_CRYPTO_LIB_PSA;
70 attest_key.k.key_handle = private_key;
71
72 t_cose_sign1_set_signing_key(&(me->signer_ctx),
73 attest_key,
74 attest_key_id);
75
76 /* Spin up the CBOR encoder */
77 QCBOREncode_Init(&(me->cbor_enc_ctx), out_buf);
78
79 /* This will cause the cose headers to be encoded and written into
80 * out_buf using me->cbor_enc_ctx
81 */
82 t_cose_err = t_cose_sign1_encode_parameters(&(me->signer_ctx),
83 &(me->cbor_enc_ctx));
84 if (t_cose_err) {
85 return t_cose_err_to_dpe_err(t_cose_err);
86 }
87
88 QCBOREncode_OpenMap(&(me->cbor_enc_ctx));
89
90 return DPE_NO_ERROR;
91}
92
93static void add_key_usage_claim(struct dpe_cert_encode_ctx *me)
94{
95 uint8_t key_usage = DPE_CERT_KEY_USAGE_CERT_SIGN;
96
97 /* Encode key usage as byte string */
98 QCBOREncode_AddBytesToMapN(&me->cbor_enc_ctx,
99 DPE_CERT_LABEL_KEY_USAGE,
100 (UsefulBufC){ &key_usage,
101 sizeof(key_usage) });
102}
103
104static void add_subject_claim(struct dpe_cert_encode_ctx *me,
105 struct layer_context_t *layer_ctx)
106{
107 char cdi_id_hex[ID_HEX_SIZE];
108
109 convert_to_ascii_hex(&layer_ctx->data.cdi_id[0],
110 sizeof(layer_ctx->data.cdi_id),
111 &cdi_id_hex[0],
112 sizeof(cdi_id_hex));
113 /* Encode subject as text string */
114 QCBOREncode_AddTextToMapN(&me->cbor_enc_ctx,
115 DPE_CERT_LABEL_SUBJECT,
116 (UsefulBufC){ &cdi_id_hex[0],
117 sizeof(cdi_id_hex) });
118}
119
120static void add_issuer_claim(struct dpe_cert_encode_ctx *me,
121 const struct layer_context_t *parent_layer_ctx)
122{
123
124 char cdi_id_hex[ID_HEX_SIZE];
125
126 convert_to_ascii_hex(&parent_layer_ctx->data.cdi_id[0],
127 sizeof(parent_layer_ctx->data.cdi_id),
128 &cdi_id_hex[0],
129 sizeof(cdi_id_hex));
130
131 /* Encode issuer as text string */
132 QCBOREncode_AddTextToMapN(&me->cbor_enc_ctx,
133 DPE_CERT_LABEL_ISSUER,
134 (UsefulBufC){ &cdi_id_hex[0],
135 sizeof(cdi_id_hex) });
136}
137
138static void add_public_key_claim(struct dpe_cert_encode_ctx *me,
139 const struct layer_context_t *layer_ctx)
140{
141 /* As per RFC8152 */
142 const int64_t cose_key_type_value = DPE_T_COSE_KEY_TYPE_VAL;
143 const int64_t cose_key_ops_value = DPE_T_COSE_KEY_OPS_VAL;
144 const int64_t cose_key_ec2_curve_value = DPE_T_COSE_KEY_EC2_CURVE_VAL;
145 const int64_t cose_key_alg_value = DPE_T_COSE_KEY_ALG_VAL;
146 size_t pub_key_size = layer_ctx->data.attest_pub_key_len;
147 UsefulBufC wrapped;
148
149 /* Cose key is encoded as a map wrapped into a byte string */
150 QCBOREncode_BstrWrapInMapN(&me->cbor_enc_ctx, DPE_CERT_LABEL_SUBJECT_PUBLIC_KEY);
151 QCBOREncode_OpenMap(&me->cbor_enc_ctx);
152
153 /* Add the key type as int */
154 QCBOREncode_AddInt64ToMapN(&me->cbor_enc_ctx,
155 DPE_CERT_LABEL_COSE_KEY_TYPE,
156 cose_key_type_value);
157
158 /* Add the algorithm as int */
159 QCBOREncode_AddInt64ToMapN(&me->cbor_enc_ctx,
160 DPE_CERT_LABEL_COSE_KEY_ALG,
161 cose_key_alg_value);
162
163 /* Add the key operation as [+ (tstr/int)] */
164 QCBOREncode_OpenArrayInMapN(&me->cbor_enc_ctx, DPE_CERT_LABEL_COSE_KEY_OPS);
165 QCBOREncode_AddInt64(&me->cbor_enc_ctx,
166 cose_key_ops_value);
167 QCBOREncode_CloseArray(&me->cbor_enc_ctx);
168
169 /* Add the curve */
170 QCBOREncode_AddInt64ToMapN(&me->cbor_enc_ctx,
171 DPE_CERT_LABEL_COSE_KEY_EC2_CURVE,
172 cose_key_ec2_curve_value);
173
174 /* Add the subject public key x and y coordinates */
175 QCBOREncode_AddBytesToMapN(&me->cbor_enc_ctx,
176 DPE_CERT_LABEL_COSE_KEY_EC2_X,
177 (UsefulBufC){ &layer_ctx->data.attest_pub_key[0],
178 pub_key_size/2 });
179
180 QCBOREncode_AddBytesToMapN(&me->cbor_enc_ctx,
181 DPE_CERT_LABEL_COSE_KEY_EC2_Y,
182 (UsefulBufC){ &layer_ctx->data.attest_pub_key[pub_key_size/2],
183 pub_key_size/2 });
184
185 QCBOREncode_CloseMap(&me->cbor_enc_ctx);
186 QCBOREncode_CloseBstrWrap2(&me->cbor_enc_ctx, true, &wrapped);
187
188 assert(wrapped.len <= DICE_MAX_ENCODED_PUBLIC_KEY_SIZE);
189}
190
191static dpe_error_t certificate_encode_finish(struct dpe_cert_encode_ctx *me,
192 UsefulBufC *completed_cert)
193{
194 QCBORError qcbor_result;
195 enum t_cose_err_t cose_return_value;
196
197 QCBOREncode_CloseMap(&(me->cbor_enc_ctx));
198
199 /* -- Finish up the COSE_Sign1. This is where the signing happens -- */
200 cose_return_value = t_cose_sign1_encode_signature(&(me->signer_ctx),
201 &(me->cbor_enc_ctx));
202 if (cose_return_value) {
203 /* Main errors are invoking the hash or signature */
204 return t_cose_err_to_dpe_err(cose_return_value);
205 }
206
207 /* Finally close off the CBOR formatting and get the pointer and length
208 * of the resulting COSE_Sign1
209 */
210 qcbor_result = QCBOREncode_Finish(&(me->cbor_enc_ctx), completed_cert);
211 if (qcbor_result == QCBOR_ERR_BUFFER_TOO_SMALL) {
212 return DPE_INSUFFICIENT_MEMORY;
213
214 } else if (qcbor_result != QCBOR_SUCCESS) {
215 /* likely from array not closed, too many closes, ... */
216 return DPE_INTERNAL_ERROR;
217
218 } else {
219 return DPE_NO_ERROR;
220 }
221}
222
223static void encode_sw_component_measurements(QCBOREncodeContext *encode_ctx,
224 struct component_context_t *component_ctx)
225{
226 QCBOREncode_OpenMap(encode_ctx);
227
228 /* Encode measurement value as byte string */
229 QCBOREncode_AddBytesToMapN(encode_ctx,
230 DPE_CERT_LABEL_CODE_HASH,
231 (UsefulBufC){ &component_ctx->data.measurement_value,
232 DICE_HASH_SIZE });
233
234 /* Encode measurement descriptor version as byte string */
235 QCBOREncode_AddBytesToMapN(encode_ctx,
236 DPE_CERT_LABEL_CODE_DESCRIPTOR,
237 (UsefulBufC){ &component_ctx->data.measurement_descriptor,
238 component_ctx->data.measurement_descriptor_size });
239
240 /* Encode signer ID Hash as byte string */
241 QCBOREncode_AddBytesToMapN(encode_ctx,
242 DPE_CERT_LABEL_AUTHORITY_HASH,
243 (UsefulBufC){ &component_ctx->data.signer_id,
244 DICE_HASH_SIZE });
245
246 /* Encode signer ID descriptor as byte string */
247 QCBOREncode_AddBytesToMapN(encode_ctx,
248 DPE_CERT_LABEL_AUTHORITY_DESCRIPTOR,
249 (UsefulBufC){ &component_ctx->data.signer_id_descriptor,
250 component_ctx->data.signer_id_descriptor_size });
251
252 if (component_ctx->data.config_descriptor_size > 0) {
253 /* Encode config descriptor as byte string */
254 QCBOREncode_AddBytesToMapN(encode_ctx,
255 DPE_CERT_LABEL_CONFIGURATION_DESCRIPTOR,
256 (UsefulBufC){ &component_ctx->data.config_descriptor,
257 component_ctx->data.config_descriptor_size });
258 /* Encode config value as byte string */
259 QCBOREncode_AddBytesToMapN(encode_ctx,
260 DPE_CERT_LABEL_CONFIGURATION_HASH,
261 (UsefulBufC){ &component_ctx->data.config_value,
262 DICE_INLINE_CONFIG_SIZE });
263 } else {
264 /* Encode config value as byte string */
265 QCBOREncode_AddBytesToMapN(encode_ctx,
266 DPE_CERT_LABEL_CONFIGURATION_DESCRIPTOR,
267 (UsefulBufC){ &component_ctx->data.config_value,
268 DICE_INLINE_CONFIG_SIZE });
269 }
270
271 /* Encode mode value as byte string */
272 QCBOREncode_AddBytesToMapN(encode_ctx,
273 DPE_CERT_LABEL_MODE,
274 (UsefulBufC){ &component_ctx->data.mode,
275 sizeof(DiceMode) });
276
277 QCBOREncode_CloseMap(encode_ctx);
278}
279
280static void encode_layer_sw_components_array(uint16_t layer_idx,
281 struct dpe_cert_encode_ctx *me)
282{
283 int i, cnt;
284 struct component_context_t *component_ctx;
285
286 for (i = 0, cnt = 0; i < MAX_NUM_OF_COMPONENTS; i++) {
287 component_ctx = get_component_if_linked_to_layer(layer_idx, i);
288 if (component_ctx != NULL) {
289 /* This component belongs to current layer */
290 cnt++;
291
292 if (cnt == 1) {
293 /* Open array which stores SW components claims. */
294 QCBOREncode_OpenArrayInMapN(&me->cbor_enc_ctx,
295 DPE_CERT_LABEL_SW_COMPONENTS);
296 }
297 encode_sw_component_measurements(&me->cbor_enc_ctx, component_ctx);
298 }
299 }
300
301 if (cnt != 0) {
302 /* Close array which stores SW components claims. */
303 QCBOREncode_CloseArray(&me->cbor_enc_ctx);
304 }
305}
306
307
308dpe_error_t encode_layer_certificate(uint16_t layer_idx,
309 struct layer_context_t *layer_ctx,
310 const struct layer_context_t *parent_layer_ctx)
311{
312 dpe_error_t err;
313 struct dpe_cert_encode_ctx dpe_cert_ctx;
314 UsefulBuf cert;
315 UsefulBufC completed_cert;
316
317 psa_key_id_t attest_key_id = parent_layer_ctx->data.attest_key_id;
318
319 /* Get started creating the certificate/token. This sets up the CBOR and
320 * COSE contexts which causes the COSE headers to be constructed.
321 */
322 cert.ptr = &layer_ctx->data.cert_buf[0];
323 cert.len = sizeof(layer_ctx->data.cert_buf);
324
325 err = certificate_encode_start(&dpe_cert_ctx,
326 cert,
327 attest_key_id);
328 if (err != DPE_NO_ERROR) {
329 return err;
330 }
331
332 /* Add all the required claims */
333 /* Add issuer/authority claim */
334 add_issuer_claim(&dpe_cert_ctx, parent_layer_ctx);
335
336 /* Add subject claim */
337 add_subject_claim(&dpe_cert_ctx, layer_ctx);
338
339 /* Encode all firmware measurements for the components linked to this layer */
340 //TODO:
341 /* It is not yet defined in the open-dice profile how to represent
342 * multiple SW components in a single certificate; In current implementation,
343 * an array is created for all the components' measurements and within the
344 * array, there are multiple maps, one for each SW component
345 */
346 encode_layer_sw_components_array(layer_idx, &dpe_cert_ctx);
347
348 /* Add public key claim */
349 add_public_key_claim(&dpe_cert_ctx, layer_ctx);
350
351 /* Add key usage claim */
352 add_key_usage_claim(&dpe_cert_ctx);
353
354 /* Finish up creating the token. This is where the actual signature
355 * is generated. This finishes up the CBOR encoding too.
356 */
357 err = certificate_encode_finish(&dpe_cert_ctx, &completed_cert);
358 if (err != DPE_NO_ERROR) {
359 return err;
360 }
361
362 /* Update the final size of the token/certificate */
363 layer_ctx->data.cert_buf_len = completed_cert.len;
364
365 return err;
366}
367
368dpe_error_t store_layer_certificate(struct layer_context_t *layer_ctx)
369{
370 //TODO:
371 (void)layer_ctx;
372 return DPE_NO_ERROR;
373}