1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * SPDX-FileCopyrightText: Copyright Laurence Lundblade.
4 * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
5 */
6
7 /*
8 * This file is derived from:
9 * trusted-firmware-m/secure_fw/partitions/initial_attestation/attest_token_encode.c
10 */
11
12 #include <arch.h>
13 #include <arch_features.h>
14 #include <assert.h>
15 #include <attestation_defs_priv.h>
16 #include <attestation_priv.h>
17 #include <attestation_token.h>
18 #include <debug.h>
19 #include <qcbor/qcbor.h>
20 #include <t_cose/q_useful_buf.h>
21 #include <t_cose/t_cose_common.h>
22 #include <t_cose/t_cose_sign1_sign.h>
23 #include <utils_def.h>
24
25 /*
26 * According to IANA hash algorithm registry:
27 * - https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml
28 */
attest_get_hash_algo_text(enum hash_algo algorithm,struct q_useful_buf_c * algo_text)29 static void attest_get_hash_algo_text(enum hash_algo algorithm,
30 struct q_useful_buf_c *algo_text)
31 {
32 const char *sha256 = "sha-256";
33 const char *sha512 = "sha-512";
34
35 switch (algorithm) {
36 case HASH_SHA_256:
37 *algo_text = UsefulBuf_FromSZ(sha256);
38 break;
39 case HASH_SHA_512:
40 *algo_text = UsefulBuf_FromSZ(sha512);
41 break;
42 default:
43 assert(false);
44 }
45 }
46
47 /*
48 * Outline of token creation. Much of this occurs inside
49 * t_cose_sign1_encode_parameters() and t_cose_sign1_encode_signature().
50 *
51 * - Create encoder context
52 * - Open the CBOR array that hold the COSE_Sign1
53 * - Write COSE Headers
54 * - Protected Header
55 * - Algorithm ID
56 * - Unprotected Headers
57 * - Key ID
58 * - Open payload bstr
59 * - Write payload data, maybe lots of it
60 * - Get bstr that is the encoded payload
61 * - Compute signature
62 * - Create a separate encoder context for Sig_structure
63 * - Encode CBOR context identifier
64 * - Encode protected headers
65 * - Encode two empty bstr
66 * - Add one more empty bstr that is a "fake payload"
67 * - Close off Sig_structure
68 * - Hash all but "fake payload" of Sig_structure
69 * - Get payload bstr ptr and length
70 * - Continue hash of the real encoded payload
71 * - Run ECDSA
72 * - Write signature into the CBOR output
73 * - Close CBOR array holding the COSE_Sign1
74 */
75 static enum attest_token_err_t
attest_token_encode_start(struct attest_token_encode_ctx * me,uint32_t opt_flags,int32_t key_select,int32_t cose_alg_id,const struct q_useful_buf * out_buf)76 attest_token_encode_start(struct attest_token_encode_ctx *me,
77 uint32_t opt_flags,
78 int32_t key_select,
79 int32_t cose_alg_id,
80 const struct q_useful_buf *out_buf)
81 {
82 enum t_cose_err_t cose_res;
83 struct t_cose_key attest_key __unused;
84 psa_key_handle_t key_handle __unused;
85
86 assert(me != NULL);
87
88 /* Remember some of the configuration values */
89 me->opt_flags = opt_flags;
90 me->key_select = key_select;
91
92 t_cose_signature_sign_restart_init(&me->restartable_signer_ctx, cose_alg_id);
93 t_cose_signature_sign_restart_set_crypto_context(
94 &me->restartable_signer_ctx, &(me->crypto_ctx));
95 t_cose_sign_sign_init(&me->sign_ctx, T_COSE_OPT_MESSAGE_TYPE_SIGN1);
96 t_cose_sign_add_signer(&me->sign_ctx,
97 t_cose_signature_sign_from_restart(&me->restartable_signer_ctx));
98
99 #if !ATTEST_EL3_TOKEN_SIGN
100 /*
101 * Get the reference to `mbedtls_ecp_keypair` and set it to t_cose.
102 */
103 if (attest_get_realm_signing_key(&key_handle) != 0) {
104 return ATTEST_TOKEN_ERR_SIGNING_KEY;
105 }
106
107 attest_key.key.handle = key_handle;
108
109 t_cose_signature_sign_restart_set_signing_key(&me->restartable_signer_ctx, attest_key);
110 #endif
111
112 /* Spin up the CBOR encoder */
113 QCBOREncode_Init(&(me->cbor_enc_ctx), *out_buf);
114
115 /*
116 * This will cause the cose headers to be encoded and written into
117 * out_buf using me->cbor_enc_ctx
118 */
119 cose_res = t_cose_sign_encode_start(&me->sign_ctx, &me->cbor_enc_ctx);
120
121 if (cose_res != T_COSE_SUCCESS) {
122 return ATTEST_TOKEN_ERR_COSE_ERROR;
123 }
124
125 return ATTEST_TOKEN_ERR_SUCCESS;
126 }
127
128 enum attest_token_err_t
attest_app_realm_token_sign(struct token_sign_cntxt * me,size_t * completed_token_len)129 attest_app_realm_token_sign(struct token_sign_cntxt *me,
130 size_t *completed_token_len)
131 {
132 /* The completed and signed encoded cose_sign1 */
133 struct q_useful_buf_c completed_token_ub;
134 enum attest_token_err_t attest_res = ATTEST_TOKEN_ERR_SUCCESS;
135 QCBORError qcbor_res;
136 enum t_cose_err_t cose_res;
137
138 assert(me != NULL);
139 assert(completed_token_len != NULL);
140
141 if (me->state != ATTEST_TOKEN_SIGN) {
142 return ATTEST_TOKEN_ERR_INVALID_STATE;
143 }
144
145 /* Enable Data Independent Timing feature */
146 write_dit(DIT_BIT);
147
148 /* Finish up the COSE_Sign1. This is where the signing happens */
149 cose_res = t_cose_sign_encode_finish(&me->ctx.sign_ctx,
150 NULL_Q_USEFUL_BUF_C,
151 me->ctx.signed_payload,
152 &me->ctx.cbor_enc_ctx);
153
154 /* Disable Data Independent Timing feature */
155 write_dit(0x0);
156
157 if (cose_res == T_COSE_ERR_SIG_IN_PROGRESS) {
158 /* Token signing has not yet finished */
159 return ATTEST_TOKEN_ERR_COSE_SIGN_IN_PROGRESS;
160 }
161
162 if (cose_res != T_COSE_SUCCESS) {
163 /* Main errors are invoking the hash or signature */
164 return ATTEST_TOKEN_ERR_COSE_ERROR;
165 }
166
167 /*
168 * Finally close off the CBOR formatting and get the pointer and length
169 * of the resulting COSE_Sign1
170 */
171 qcbor_res = QCBOREncode_Finish(&me->ctx.cbor_enc_ctx,
172 &completed_token_ub);
173
174 switch (qcbor_res) {
175 case QCBOR_ERR_BUFFER_TOO_SMALL:
176 attest_res = ATTEST_TOKEN_ERR_TOO_SMALL;
177 break;
178 case QCBOR_SUCCESS:
179 /* coverity[uninit_use:SUPPRESS] */
180 *completed_token_len = completed_token_ub.len;
181 /* This was the last signing cycle. Transition to next state */
182 me->state = ATTEST_TOKEN_CREATE;
183 break;
184 default:
185 /* likely from array not closed, too many closes ... */
186 attest_res = ATTEST_TOKEN_ERR_CBOR_FORMATTING;
187 }
188
189 return attest_res;
190 }
191
192 enum attest_token_err_t
attest_app_cca_token_create(struct token_sign_cntxt * me,void * attest_token_buf,size_t attest_token_buf_size,const void * realm_token_buf,size_t realm_token_len,size_t * cca_token_len)193 attest_app_cca_token_create(struct token_sign_cntxt *me,
194 void *attest_token_buf,
195 size_t attest_token_buf_size,
196 const void *realm_token_buf,
197 size_t realm_token_len,
198 size_t *cca_token_len)
199 {
200 struct q_useful_buf_c completed_token;
201 QCBOREncodeContext cbor_enc_ctx;
202 QCBORError qcbor_res;
203 struct q_useful_buf_c platform_token;
204 struct q_useful_buf attest_token_ub = {attest_token_buf, attest_token_buf_size};
205 struct q_useful_buf_c realm_token_ub = {realm_token_buf, realm_token_len};
206 int ret;
207
208 if (me->state != ATTEST_TOKEN_CREATE) {
209 return ATTEST_TOKEN_ERR_INVALID_STATE;
210 }
211
212 /* Get the platform token */
213 ret = attest_get_platform_token(&platform_token.ptr,
214 &platform_token.len);
215 if (ret != 0) {
216 ERROR("Platform token is not ready for retrieval\n");
217 return ATTEST_TOKEN_ERR_CCA_TOKEN_CREATE;
218 }
219
220 QCBOREncode_Init(&cbor_enc_ctx, attest_token_ub);
221
222 QCBOREncode_AddTag(&cbor_enc_ctx, TAG_CCA_TOKEN);
223
224 QCBOREncode_OpenMap(&cbor_enc_ctx);
225
226 QCBOREncode_AddBytesToMapN(&cbor_enc_ctx,
227 CCA_PLAT_TOKEN,
228 platform_token);
229
230 QCBOREncode_AddBytesToMapN(&cbor_enc_ctx,
231 CCA_REALM_DELEGATED_TOKEN,
232 realm_token_ub);
233 QCBOREncode_CloseMap(&cbor_enc_ctx);
234
235 qcbor_res = QCBOREncode_Finish(&cbor_enc_ctx, &completed_token);
236
237 if (qcbor_res == QCBOR_ERR_BUFFER_TOO_SMALL) {
238 ERROR("CCA output token buffer too small\n");
239 return ATTEST_TOKEN_ERR_CCA_TOKEN_CREATE;
240 } else if (qcbor_res != QCBOR_SUCCESS) {
241 /* likely from array not closed, too many closes, ... */
242 ERROR("CBOR Encode finish failed with error 0x%x\n", qcbor_res);
243 return ATTEST_TOKEN_ERR_CCA_TOKEN_CREATE;
244 }
245
246 /* Transition back to NOT_STARTED */
247 me->state = ATTEST_TOKEN_NOT_STARTED;
248
249 /* coverity[uninit_use:SUPPRESS] */
250 *cca_token_len = completed_token.len;
251 return ATTEST_TOKEN_ERR_SUCCESS;
252 }
253
254 /*
255 * Assemble the Realm Attestation Token in the buffer provided in
256 * realm_token_buf, except the signature.
257 *
258 * As per section A7.2.3.1 of RMM specification, Realm Attestation token is
259 * composed of:
260 * - Realm Challenge
261 * - Realm Personalization Value
262 * - Realm Hash Algorithm Id
263 * - Realm Public Key
264 * - Realm Public Key Hash Algorithm Id
265 * - Realm Initial Measurement
266 * - Realm Extensible Measurements
267 */
attest_app_realm_token_create(enum hash_algo algorithm,unsigned char measurements[][MAX_MEASUREMENT_SIZE],unsigned int num_measurements,const void * rpv_buf,size_t rpv_len,const void * challenge_buf,size_t challenge_len,struct token_sign_cntxt * ctx,void * realm_token_buf,size_t realm_token_buf_size)268 int attest_app_realm_token_create(enum hash_algo algorithm,
269 unsigned char measurements[][MAX_MEASUREMENT_SIZE],
270 unsigned int num_measurements,
271 const void *rpv_buf,
272 size_t rpv_len,
273 const void *challenge_buf,
274 size_t challenge_len,
275 struct token_sign_cntxt *ctx,
276 void *realm_token_buf,
277 size_t realm_token_buf_size)
278 {
279 struct q_useful_buf_c buf;
280 size_t measurement_size;
281 enum attest_token_err_t token_ret = ATTEST_TOKEN_ERR_INVALID_STATE;
282 struct q_useful_buf realm_token_ub = {realm_token_buf, realm_token_buf_size};
283 struct q_useful_buf_c rpv_ub = {rpv_buf, rpv_len};
284 int ret;
285
286 /* Can only be called in the init state */
287 if (ctx->state != ATTEST_TOKEN_INIT) {
288 return (int)token_ret;
289 }
290
291 assert(num_measurements == MEASUREMENT_SLOT_NR);
292
293 /*
294 * Get started creating the token. This sets up the CBOR and COSE
295 * contexts which causes the COSE headers to be constructed.
296 */
297 token_ret = attest_token_encode_start(&(ctx->ctx),
298 0, /* option_flags */
299 0, /* key_select */
300 T_COSE_ALGORITHM_ES384,
301 &realm_token_ub);
302 if (token_ret != ATTEST_TOKEN_ERR_SUCCESS) {
303 return (int)token_ret;
304 }
305
306 QCBOREncode_BstrWrap(&(ctx->ctx.cbor_enc_ctx));
307 QCBOREncode_OpenMap(&(ctx->ctx.cbor_enc_ctx));
308
309 /* Add challenge value, which is the only input from the caller. */
310 buf.ptr = challenge_buf;
311 buf.len = challenge_len;
312 QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
313 CCA_REALM_CHALLENGE,
314 buf);
315
316 QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
317 CCA_REALM_PERSONALIZATION_VALUE,
318 rpv_ub);
319
320 ret = attest_get_realm_public_key(&buf);
321 if (ret != 0) {
322 return ret;
323 }
324
325 QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
326 CCA_REALM_PUB_KEY,
327 buf);
328
329 attest_get_hash_algo_text(algorithm, &buf);
330 QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
331 CCA_REALM_HASH_ALGM_ID,
332 buf);
333
334 attest_get_hash_algo_text(attest_get_realm_public_key_hash_algo_id(),
335 &buf);
336 QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
337 CCA_REALM_PUB_KEY_HASH_ALGO_ID,
338 buf);
339
340 QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
341 CCA_REALM_PROFILE,
342 UsefulBuf_FromSZ(CCA_REALM_PROFILE_STR));
343
344 measurement_size = measurement_get_size(algorithm);
345 assert(measurement_size <= MAX_MEASUREMENT_SIZE);
346
347 /* RIM: 0, REM: 1..4 */
348 buf.ptr = &measurements[RIM_MEASUREMENT_SLOT];
349 buf.len = measurement_size;
350 QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
351 CCA_REALM_INITIAL_MEASUREMENT,
352 buf);
353
354 QCBOREncode_OpenArrayInMapN(&(ctx->ctx.cbor_enc_ctx),
355 CCA_REALM_EXTENSIBLE_MEASUREMENTS);
356
357 for (unsigned int i = 1U; i < num_measurements; ++i) {
358 buf.ptr = &measurements[i];
359 buf.len = measurement_size;
360 QCBOREncode_AddBytes(&(ctx->ctx.cbor_enc_ctx), buf);
361 }
362
363 QCBOREncode_CloseArray(&(ctx->ctx.cbor_enc_ctx));
364 QCBOREncode_CloseMap(&(ctx->ctx.cbor_enc_ctx));
365 QCBOREncode_CloseBstrWrap2(&(ctx->ctx.cbor_enc_ctx), false,
366 &(ctx->ctx.signed_payload));
367
368 /* Transition to ATTEST_CCA_TOKEN_SIGN state */
369 ctx->state = ATTEST_TOKEN_SIGN;
370 return 0;
371 }
372
373 /* This function will only succeed if attestation_init() has succeeded. */
attest_token_ctx_init(struct token_sign_cntxt * token_ctx,uintptr_t cookie)374 enum attest_token_err_t attest_token_ctx_init(struct token_sign_cntxt *token_ctx,
375 uintptr_t cookie)
376 {
377 (void)cookie;
378
379 if (token_ctx->state != ATTEST_TOKEN_INIT) {
380 /* Clear context for signing an attestation token */
381 (void)memset(token_ctx, 0, sizeof(struct token_sign_cntxt));
382
383 #if ATTEST_EL3_TOKEN_SIGN
384 t_cose_crypto_el3_ctx_init(&token_ctx->ctx.crypto_ctx, cookie);
385 #endif
386
387 token_ctx->state = ATTEST_TOKEN_INIT;
388 }
389
390 return ATTEST_TOKEN_ERR_SUCCESS;
391 }
392