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 <assert.h>
13 #include <attestation.h>
14 #include <attestation_defs_priv.h>
15 #include <attestation_priv.h>
16 #include <attestation_token.h>
17 #include <debug.h>
18 #include <fpu_helpers.h>
19 #include <measurement.h>
20 #include <qcbor/qcbor.h>
21 #include <t_cose/q_useful_buf.h>
22 #include <t_cose/t_cose_common.h>
23 #include <t_cose/t_cose_sign1_sign.h>
24 #include <utils_def.h>
25 
26 /*
27  * According to IANA hash algorithm registry:
28  *   - https://www.iana.org/assignments/hash-function-text-names/hash-function-text-names.xml
29  */
attest_get_hash_algo_text(enum hash_algo algo_id,struct q_useful_buf_c * algo_text)30 static void attest_get_hash_algo_text(enum hash_algo  algo_id,
31 				      struct q_useful_buf_c *algo_text)
32 {
33 	const char *sha256 = "sha-256";
34 	const char *sha512 = "sha-512";
35 
36 	switch (algo_id) {
37 	case HASH_ALGO_SHA256:
38 		*algo_text = UsefulBuf_FromSZ(sha256);
39 		break;
40 	case HASH_ALGO_SHA512:
41 		*algo_text = UsefulBuf_FromSZ(sha512);
42 		break;
43 	default:
44 		assert(false);
45 	}
46 }
47 
48 /*
49  * Outline of token creation. Much of this occurs inside
50  * t_cose_sign1_encode_parameters() and t_cose_sign1_encode_signature().
51  *
52  * - Create encoder context
53  * - Open the CBOR array that hold the COSE_Sign1
54  * - Write COSE Headers
55  *   - Protected Header
56  *      - Algorithm ID
57  *   - Unprotected Headers
58  *     - Key ID
59  * - Open payload bstr
60  *   - Write payload data, maybe lots of it
61  *   - Get bstr that is the encoded payload
62  * - Compute signature
63  *   - Create a separate encoder context for Sig_structure
64  *     - Encode CBOR context identifier
65  *     - Encode protected headers
66  *     - Encode two empty bstr
67  *     - Add one more empty bstr that is a "fake payload"
68  *     - Close off Sig_structure
69  *   - Hash all but "fake payload" of Sig_structure
70  *   - Get payload bstr ptr and length
71  *   - Continue hash of the real encoded payload
72  *   - Run ECDSA
73  * - Write signature into the CBOR output
74  * - Close CBOR array holding the COSE_Sign1
75  */
76 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)77 attest_token_encode_start(struct attest_token_encode_ctx *me,
78 			  uint32_t opt_flags,
79 			  int32_t key_select,
80 			  int32_t cose_alg_id,
81 			  const struct q_useful_buf *out_buf)
82 {
83 	enum t_cose_err_t cose_res;
84 	uint32_t t_cose_options = 0;
85 	struct t_cose_key attest_key;
86 	const void *signing_key;
87 	struct q_useful_buf_c attest_key_id = NULL_Q_USEFUL_BUF_C;
88 
89 	assert(me != NULL);
90 	assert(out_buf != NULL);
91 
92 	/* Remember some of the configuration values */
93 	me->opt_flags  = opt_flags;
94 	me->key_select = key_select;
95 
96 	t_cose_sign1_sign_init(&(me->signer_ctx),
97 			       t_cose_options,
98 			       cose_alg_id);
99 
100 	cose_res = t_cose_sign1_set_restart_context(&(me->signer_ctx),
101 						    &(me->signer_restart_ctx));
102 	if (cose_res != T_COSE_SUCCESS) {
103 		return ATTEST_TOKEN_ERR_COSE_ERROR;
104 	}
105 
106 	t_cose_sign1_set_crypto_context(&(me->signer_ctx),
107 					&(me->crypto_ctx));
108 
109 
110 	/*
111 	 * Get the reference to `mbedtls_ecp_keypair` and set it to t_cose.
112 	 */
113 	attest_get_realm_signing_key(&signing_key);
114 	attest_key.crypto_lib = T_COSE_CRYPTO_LIB_MBEDTLS;
115 	attest_key.k.key_ptr = (void *)signing_key;
116 	t_cose_sign1_set_signing_key(&(me->signer_ctx),
117 				     attest_key,
118 				     attest_key_id);
119 
120 	/* Spin up the CBOR encoder */
121 	QCBOREncode_Init(&(me->cbor_enc_ctx), *out_buf);
122 
123 	/*
124 	 * This will cause the cose headers to be encoded and written into
125 	 * out_buf using me->cbor_enc_ctx
126 	 */
127 	cose_res = t_cose_sign1_encode_parameters(&(me->signer_ctx),
128 						  &(me->cbor_enc_ctx));
129 
130 	if (cose_res != T_COSE_SUCCESS) {
131 		return ATTEST_TOKEN_ERR_COSE_ERROR;
132 	}
133 
134 	QCBOREncode_OpenMap(&(me->cbor_enc_ctx));
135 	return ATTEST_TOKEN_ERR_SUCCESS;
136 }
137 
138 enum attest_token_err_t
attest_realm_token_sign(struct attest_token_encode_ctx * me,struct q_useful_buf_c * completed_token)139 attest_realm_token_sign(struct attest_token_encode_ctx *me,
140 			struct q_useful_buf_c *completed_token)
141 {
142 	/* The completed and signed encoded cose_sign1 */
143 	struct q_useful_buf_c completed_token_ub;
144 	enum attest_token_err_t attest_res = ATTEST_TOKEN_ERR_SUCCESS;
145 	QCBORError qcbor_res;
146 	enum t_cose_err_t cose_res;
147 
148 	assert(me != NULL);
149 	assert(completed_token != NULL);
150 
151 	/* Finish up the COSE_Sign1. This is where the signing happens */
152 	FPU_ALLOW(
153 		cose_res = t_cose_sign1_encode_signature(&(me->signer_ctx),
154 							 &(me->cbor_enc_ctx)));
155 
156 	if (cose_res == T_COSE_ERR_SIG_IN_PROGRESS) {
157 		/* Token signing has not yet finished */
158 		return ATTEST_TOKEN_ERR_COSE_SIGN_IN_PROGRESS;
159 	}
160 
161 	if (cose_res != T_COSE_SUCCESS) {
162 		/* Main errors are invoking the hash or signature */
163 		return ATTEST_TOKEN_ERR_COSE_ERROR;
164 	}
165 
166 	/*
167 	 * Finally close off the CBOR formatting and get the pointer and length
168 	 * of the resulting COSE_Sign1
169 	 */
170 	qcbor_res = QCBOREncode_Finish(&(me->cbor_enc_ctx),
171 				       &completed_token_ub);
172 
173 	switch (qcbor_res) {
174 	case QCBOR_ERR_BUFFER_TOO_SMALL:
175 		attest_res = ATTEST_TOKEN_ERR_TOO_SMALL;
176 		break;
177 	case QCBOR_SUCCESS:
178 		*completed_token = completed_token_ub;
179 		break;
180 	default:
181 		/* likely from array not closed, too many closes ... */
182 		attest_res = ATTEST_TOKEN_ERR_CBOR_FORMATTING;
183 	}
184 
185 	return attest_res;
186 }
187 
attest_cca_token_create(struct q_useful_buf * attest_token_buf,const struct q_useful_buf_c * realm_token)188 size_t attest_cca_token_create(struct q_useful_buf         *attest_token_buf,
189 			       const struct q_useful_buf_c *realm_token)
190 {
191 	struct q_useful_buf_c   completed_token;
192 	QCBOREncodeContext      cbor_enc_ctx;
193 	QCBORError              qcbor_res;
194 	struct q_useful_buf_c  *rmm_platform_token;
195 
196 	__unused int            ret;
197 
198 	/* Get the platform token */
199 	ret = attest_get_platform_token(&rmm_platform_token);
200 	assert(ret == 0);
201 
202 	QCBOREncode_Init(&cbor_enc_ctx, *attest_token_buf);
203 
204 	QCBOREncode_AddTag(&cbor_enc_ctx, TAG_CCA_TOKEN);
205 
206 	QCBOREncode_OpenMap(&cbor_enc_ctx);
207 
208 	QCBOREncode_AddBytesToMapN(&cbor_enc_ctx,
209 				   CCA_PLAT_TOKEN,
210 				   *rmm_platform_token);
211 
212 	QCBOREncode_AddBytesToMapN(&cbor_enc_ctx,
213 				   CCA_REALM_DELEGATED_TOKEN,
214 				   *realm_token);
215 	QCBOREncode_CloseMap(&cbor_enc_ctx);
216 
217 	qcbor_res = QCBOREncode_Finish(&cbor_enc_ctx, &completed_token);
218 
219 	if (qcbor_res == QCBOR_ERR_BUFFER_TOO_SMALL) {
220 		ERROR("CCA output token buffer too small\n");
221 		return 0;
222 	} else if (qcbor_res != QCBOR_SUCCESS) {
223 		/* likely from array not closed, too many closes, ... */
224 		assert(false);
225 	} else {
226 		return completed_token.len;
227 	}
228 	return 0;
229 }
230 
231 /*
232  * Assemble the Realm Attestation Token in the buffer provided in
233  * realm_token_buf, except the signature.
234  *
235  * As per section A7.2.3.1 of RMM specfication, Realm Attestation token is
236  * composed of:
237  *	- Realm Challenge
238  *	- Realm Personalization Value
239  *	- Realm Hash Algorithm Id
240  *	- Realm Public Key
241  *	- Realm Public Key Hash Algorithm Id
242  *	- Realm Initial Measurement
243  *	- Realm Extensible Measurements
244  */
attest_realm_token_create(enum hash_algo algorithm,unsigned char measurements[][MAX_MEASUREMENT_SIZE],unsigned int num_measurements,struct q_useful_buf_c * rpv,struct token_sign_ctx * ctx,struct q_useful_buf * realm_token_buf)245 int attest_realm_token_create(enum hash_algo algorithm,
246 			     unsigned char measurements[][MAX_MEASUREMENT_SIZE],
247 			     unsigned int num_measurements,
248 			     struct q_useful_buf_c *rpv,
249 			     struct token_sign_ctx *ctx,
250 			     struct q_useful_buf *realm_token_buf)
251 {
252 	struct q_useful_buf_c buf;
253 	size_t measurement_size;
254 	enum attest_token_err_t token_ret;
255 	int ret;
256 
257 	/* Can only be called in the init state */
258 	assert(ctx->state == ATTEST_SIGN_NOT_STARTED);
259 
260 	assert(num_measurements == MEASUREMENT_SLOT_NR);
261 
262 	/*
263 	 * Get started creating the token. This sets up the CBOR and COSE
264 	 * contexts which causes the COSE headers to be constructed.
265 	 */
266 	token_ret = attest_token_encode_start(&(ctx->ctx),
267 					      0,     /* option_flags */
268 					      0,     /* key_select */
269 					      T_COSE_ALGORITHM_ES384,
270 					      realm_token_buf);
271 	if (token_ret != ATTEST_TOKEN_ERR_SUCCESS) {
272 		return token_ret;
273 	}
274 
275 	/* Add challenge value, which is the only input from the caller. */
276 	buf.ptr = ctx->challenge;
277 	buf.len = ATTEST_CHALLENGE_SIZE;
278 	QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
279 				   CCA_REALM_CHALLENGE,
280 				   buf);
281 
282 	QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
283 				   CCA_REALM_PERSONALIZATION_VALUE,
284 				   *rpv);
285 
286 	ret = attest_get_realm_public_key(&buf);
287 	if (ret != 0) {
288 		return ret;
289 	}
290 
291 	QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
292 				   CCA_REALM_PUB_KEY,
293 				   buf);
294 
295 	attest_get_hash_algo_text(algorithm, &buf);
296 	QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
297 				  CCA_REALM_HASH_ALGM_ID,
298 				  buf);
299 
300 	attest_get_hash_algo_text(attest_get_realm_public_key_hash_algo_id(),
301 				  &buf);
302 	QCBOREncode_AddTextToMapN(&(ctx->ctx.cbor_enc_ctx),
303 				  CCA_REALM_PUB_KEY_HASH_ALGO_ID,
304 				  buf);
305 
306 	measurement_size = measurement_get_size(algorithm);
307 	assert(measurement_size <= MAX_MEASUREMENT_SIZE);
308 
309 	/* RIM: 0, REM: 1..4 */
310 	buf.ptr = &measurements[RIM_MEASUREMENT_SLOT];
311 	buf.len = measurement_size;
312 	QCBOREncode_AddBytesToMapN(&(ctx->ctx.cbor_enc_ctx),
313 				   CCA_REALM_INITIAL_MEASUREMENT,
314 				   buf);
315 
316 	QCBOREncode_OpenArrayInMapN(&(ctx->ctx.cbor_enc_ctx),
317 				    CCA_REALM_EXTENSIBLE_MEASUREMENTS);
318 
319 	for (unsigned int i = 1U; i < num_measurements; ++i) {
320 		buf.ptr = &measurements[i];
321 		buf.len = measurement_size;
322 		QCBOREncode_AddBytes(&(ctx->ctx.cbor_enc_ctx), buf);
323 	}
324 
325 	QCBOREncode_CloseArray(&(ctx->ctx.cbor_enc_ctx));
326 	QCBOREncode_CloseMap(&(ctx->ctx.cbor_enc_ctx));
327 
328 	return ATTEST_TOKEN_ERR_SUCCESS;
329 }
330