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