1 /*
2 * t_cose_util.c
3 *
4 * Copyright 2019, Laurence Lundblade
5 * Copyright (c) 2020-2023, Arm Limited. All rights reserved.
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 * See BSD-3-Clause license in README.md
10 */
11
12 #include "t_cose_util.h"
13 #include "qcbor/qcbor.h"
14 #include "t_cose_standard_constants.h"
15 #include "t_cose_common.h"
16 #include "t_cose_crypto.h"
17
18
19 /**
20 * \file t_cose_util.c
21 *
22 * \brief Implementation of t_cose utility functions.
23 *
24 * These are some functions common to signing and verification,
25 * primarily the to-be-signed bytes hashing.
26 */
27
28
29 /*
30 * Public function. See t_cose_util.h
31 */
hash_alg_id_from_sig_alg_id(int32_t cose_algorithm_id)32 int32_t hash_alg_id_from_sig_alg_id(int32_t cose_algorithm_id)
33 {
34 /* If other hashes, particularly those that output bigger hashes
35 * are added here, various other parts of this code have to be
36 * changed to have larger buffers, in particular
37 * \ref T_COSE_CRYPTO_MAX_HASH_SIZE.
38 */
39 /* ? : operator precedence is correct here. This makes smaller
40 * code than a switch statement and is easier to read.
41 */
42 return cose_algorithm_id == COSE_ALGORITHM_ES256 ? COSE_ALGORITHM_SHA_256 :
43 #ifndef T_COSE_DISABLE_ES384
44 cose_algorithm_id == COSE_ALGORITHM_ES384 ? COSE_ALGORITHM_SHA_384 :
45 #endif
46 #ifndef T_COSE_DISABLE_ES512
47 cose_algorithm_id == COSE_ALGORITHM_ES512 ? COSE_ALGORITHM_SHA_512 :
48 #endif
49 T_COSE_INVALID_ALGORITHM_ID;
50 }
51
52
53 #ifndef T_COSE_DISABLE_MAC0
create_tbm(UsefulBuf tbm_first_part_buf,struct q_useful_buf_c protected_headers,struct q_useful_buf_c * tbm_first_part,enum t_cose_tbm_payload_mode_t payload_mode,struct q_useful_buf_c payload)54 enum t_cose_err_t create_tbm(UsefulBuf tbm_first_part_buf,
55 struct q_useful_buf_c protected_headers,
56 struct q_useful_buf_c *tbm_first_part,
57 enum t_cose_tbm_payload_mode_t payload_mode,
58 struct q_useful_buf_c payload)
59 {
60 QCBOREncodeContext cbor_encode_ctx;
61 QCBORError qcbor_result;
62 size_t bytes_to_omit;
63
64 /* This builds the CBOR-format to-be-maced bytes */
65 QCBOREncode_Init(&cbor_encode_ctx, tbm_first_part_buf);
66 QCBOREncode_OpenArray(&cbor_encode_ctx);
67 /* context */
68 QCBOREncode_AddSZString(&cbor_encode_ctx, COSE_MAC_CONTEXT_STRING_MAC0);
69 /* body_protected */
70 QCBOREncode_AddBytes(&cbor_encode_ctx, protected_headers);
71
72 /* external_aad. There is none so an empty bstr */
73 QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
74
75 /* The short fake payload. */
76 if(payload_mode == T_COSE_TBM_PAYLOAD_IS_BSTR_WRAPPED) {
77 /* Fake payload is just an empty bstr. It is here only
78 * to make the array count right. It must be omitted
79 * in the actual MAC below
80 */
81 bytes_to_omit = 1;
82 QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
83 } else {
84 /* Fake payload is the type and length of the wrapping
85 * bstr. It gets MACed with the first part, so no
86 * bytes to omit.
87 */
88 bytes_to_omit = 0;
89 QCBOREncode_AddBytesLenOnly(&cbor_encode_ctx, payload);
90 }
91
92 /* Close of the array */
93 QCBOREncode_CloseArray(&cbor_encode_ctx);
94
95 /* get the encoded results, except for payload */
96 qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, tbm_first_part);
97 if(qcbor_result) {
98 /* Mainly means that the protected_headers were too big
99 * (which should never happen)
100 */
101 return T_COSE_ERR_SIG_STRUCT;
102 }
103
104 tbm_first_part->len -= bytes_to_omit;
105
106 return T_COSE_SUCCESS;
107 }
108 #endif /* !T_COSE_DISABLE_MAC0 */
109
110
111 #ifndef T_COSE_DISABLE_SIGN1
112 /*
113 * Format of to-be-signed bytes used by create_tbs_hash(). This is
114 * defined in COSE (RFC 8152) section 4.4. It is the input to the
115 * hash.
116 *
117 * Sig_structure = [
118 * context : "Signature" / "Signature1" / "CounterSignature",
119 * body_protected : empty_or_serialized_map,
120 * ? sign_protected : empty_or_serialized_map,
121 * external_aad : bstr,
122 * payload : bstr
123 * ]
124 *
125 * body_protected refers to the protected parameters from the
126 * main COSE_Sign1 structure. This is a little hard to
127 * to understand in the spec.
128 *
129 * sign_protected is not used with COSE_Sign1 since there is no signer
130 * chunk.
131 *
132 * external_aad allows external data to be covered by the hash, but is
133 * not supported by this implementation.
134 */
135
136
137 /**
138 * This is the size of the first part of the CBOR encoded TBS
139 * bytes. It is around 30 bytes. See create_tbs_hash().
140 */
141 #define T_COSE_SIZE_OF_TBS \
142 1 + /* For opening the array */ \
143 sizeof(COSE_SIG_CONTEXT_STRING_SIGNATURE1) + /* "Signature1" */ \
144 2 + /* Overhead for encoding string */ \
145 T_COSE_SIGN1_MAX_SIZE_PROTECTED_PARAMETERS + /* entire protected params */ \
146 1 + /* Empty bstr for absent external_aad */ \
147 9 /* The max CBOR length encoding for start of payload */
148
149
150 /*
151 * Public function. See t_cose_util.h
152 */
create_tbs_hash(int32_t cose_algorithm_id,struct q_useful_buf_c protected_parameters,enum t_cose_tbs_hash_mode_t payload_mode,struct q_useful_buf_c payload,struct q_useful_buf buffer_for_hash,struct q_useful_buf_c * hash)153 enum t_cose_err_t create_tbs_hash(int32_t cose_algorithm_id,
154 struct q_useful_buf_c protected_parameters,
155 enum t_cose_tbs_hash_mode_t payload_mode,
156 struct q_useful_buf_c payload,
157 struct q_useful_buf buffer_for_hash,
158 struct q_useful_buf_c *hash)
159 {
160 /* approximate stack use on 32-bit machine:
161 * 210 bytes for all but hash context
162 * 8 to 224 of hash context depending on hash implementation
163 * 220 to 434 bytes total
164 */
165 enum t_cose_err_t return_value;
166 QCBOREncodeContext cbor_encode_ctx;
167 UsefulBuf_MAKE_STACK_UB( buffer_for_TBS_first_part, T_COSE_SIZE_OF_TBS);
168 struct q_useful_buf_c tbs_first_part;
169 QCBORError qcbor_result;
170 struct t_cose_crypto_hash hash_ctx;
171 int32_t hash_alg_id;
172 size_t bytes_to_omit;
173
174 /* This builds the CBOR-format to-be-signed bytes */
175 QCBOREncode_Init(&cbor_encode_ctx, buffer_for_TBS_first_part);
176 QCBOREncode_OpenArray(&cbor_encode_ctx);
177
178 /* context */
179 QCBOREncode_AddSZString(&cbor_encode_ctx, COSE_SIG_CONTEXT_STRING_SIGNATURE1);
180 /* body_protected */
181 QCBOREncode_AddBytes(&cbor_encode_ctx, protected_parameters);
182
183 /* sign_protected is not used for COSE_Sign1 */
184
185 /* external_aad. There is none so an empty bstr */
186 QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
187
188 /* The short fake payload */
189 if(payload_mode == T_COSE_TBS_PAYLOAD_IS_BSTR_WRAPPED) {
190 /* Fake payload is just an empty bstr. It is here only
191 * to make the array count right. It must be ommitted
192 * in the actual hashing below.
193 */
194 bytes_to_omit = 1;
195 QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
196 } else {
197 /* Fake payload is the type and length of the wrapping
198 * bstr. It gets hashed with the first part, so no bytes to
199 * omit.
200 */
201 bytes_to_omit = 0;
202 QCBOREncode_AddBytesLenOnly(&cbor_encode_ctx, payload);
203 }
204 /* Cleverness only works because the payload is last in the array */
205
206 /* Close off the array */
207 QCBOREncode_CloseArray(&cbor_encode_ctx);
208
209 /* get the encoded results, except for payload */
210 qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part);
211 if(qcbor_result) {
212 /* Mainly means that the protected_parameters were too big
213 * (which should never happen) */
214 return_value = T_COSE_ERR_SIG_STRUCT;
215 goto Done;
216 }
217
218 /* Start the hashing */
219 hash_alg_id = hash_alg_id_from_sig_alg_id(cose_algorithm_id);
220 /* Don't check hash_alg_id for failure. t_cose_crypto_hash_start()
221 * will handle error properly. It was also checked earlier.
222 */
223 return_value = t_cose_crypto_hash_start(&hash_ctx, hash_alg_id);
224 if(return_value) {
225 goto Done;
226 }
227
228 /* This structure is hashed in two parts. The first part is
229 * the CBOR-formatted array with protected parameters and such.
230 * The last part is the actual bytes of the payload. Doing it
231 * this way avoids having to allocate a big buffer to hold
232 * these two parts together. It avoids having two copies of
233 * the payload in the implementaiton as the payload as formatted
234 * in the output buffer can be what is hashed. They payload
235 * is the largest memory use, so this saves a lot.
236 *
237 * This is further complicated because the the payload does have
238 * to be wrapped in a bstr. It is done one way when signing and
239 * another when verifying.
240 */
241
242 /* This is the hashing of the first part, all the CBOR except the
243 * payload.
244 */
245 t_cose_crypto_hash_update(&hash_ctx,
246 q_useful_buf_head(tbs_first_part,
247 tbs_first_part.len - bytes_to_omit));
248
249 /* Hash the payload, the second part. This may or may not have the
250 * bstr wrapping. If not, it was hashed above.
251 */
252 t_cose_crypto_hash_update(&hash_ctx, payload);
253
254 /* Finish the hash and set up to return it */
255 return_value = t_cose_crypto_hash_finish(&hash_ctx,
256 buffer_for_hash,
257 hash);
258 Done:
259 return return_value;
260 }
261 #endif /* !T_COSE_DISABLE_SIGN1 */
262
263
264 #ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
265 /* This is a random hard coded kid (key ID) that is used to indicate
266 * short-circuit signing. It is OK to hard code this as the
267 * probability of collision with this ID is very low and the same as
268 * for collision between any two key IDs of any sort.
269 */
270
271 static const uint8_t defined_short_circuit_kid[] = {
272 0xef, 0x95, 0x4b, 0x4b, 0xd9, 0xbd, 0xf6, 0x70,
273 0xd0, 0x33, 0x60, 0x82, 0xf5, 0xef, 0x15, 0x2a,
274 0xf8, 0xf3, 0x5b, 0x6a, 0x6c, 0x00, 0xef, 0xa6,
275 0xa9, 0xa7, 0x1f, 0x49, 0x51, 0x7e, 0x18, 0xc6};
276
277 static struct q_useful_buf_c short_circuit_kid;
278
279 /*
280 * Public function. See t_cose_util.h
281 */
get_short_circuit_kid(void)282 struct q_useful_buf_c get_short_circuit_kid(void)
283 {
284 short_circuit_kid.len = sizeof(defined_short_circuit_kid);
285 short_circuit_kid.ptr = defined_short_circuit_kid;
286
287 return short_circuit_kid;
288 }
289 #endif
290