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