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