1 /*
2  * Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
3  * Copyright (c) 2020-2023, Arm Limited. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * See BSD-3-Clause license in README.md
8  */
9 
10 #include "qcbor/qcbor.h"
11 #include "t_cose_crypto.h"
12 #include "t_cose_mac0_verify.h"
13 #include "t_cose_parameters.h"
14 #include "t_cose_util.h"
15 
16 #ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
17 /**
18  *  \brief Verify a short-circuit tag
19  *
20  * \param[in] cose_alg_id  Algorithm ID. This is used only to make
21  *                         the short-circuit signature the same size as the
22  *                         real tag would be for the particular algorithm.
23  * \param[in] header       The Header of COSE_Mac0.
24  * \param[in] payload      The payload of COSE_Mac0
25  * \param[in] tag          Pointer and length of tag to be verified
26  *
27  * \return This returns one of the error codes defined by \ref
28  *         t_cose_err_t.
29  *
30  * See short_circuit_tag() in t_cose_mac0_sign.c for description of
31  * the short-circuit tag.
32  */
33 static inline enum t_cose_err_t
short_circuit_verify(int32_t cose_alg_id,struct q_useful_buf_c header,struct q_useful_buf_c payload,struct q_useful_buf_c tag_to_verify)34 short_circuit_verify(int32_t               cose_alg_id,
35                      struct q_useful_buf_c header,
36                      struct q_useful_buf_c payload,
37                      struct q_useful_buf_c tag_to_verify)
38 {
39     /* approximate stack use on 32-bit machine: local use: 16 bytes */
40     enum t_cose_err_t         return_value;
41     struct t_cose_crypto_hash hash_ctx;
42     Q_USEFUL_BUF_MAKE_STACK_UB(tag_buffer, T_COSE_CRYPTO_HMAC_TAG_MAX_SIZE);
43     struct q_useful_buf_c     tag;
44     int32_t                   hash_alg_id;
45 
46     hash_alg_id = t_cose_hmac_to_hash_alg_id(cose_alg_id);
47     if (hash_alg_id == INT32_MAX) {
48         return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
49         goto Done;
50     }
51 
52     return_value = t_cose_crypto_hash_start(&hash_ctx, hash_alg_id);
53     if (return_value != T_COSE_SUCCESS) {
54         goto Done;
55     }
56 
57     /* Hash the Header */
58     t_cose_crypto_hash_update(&hash_ctx, q_useful_buf_head(header, header.len));
59 
60     /* Hash the payload */
61     t_cose_crypto_hash_update(&hash_ctx, payload);
62 
63     return_value = t_cose_crypto_hash_finish(&hash_ctx, tag_buffer, &tag);
64     if (return_value != T_COSE_SUCCESS) {
65         goto Done;
66     }
67 
68     if (q_useful_buf_compare(tag_to_verify, tag)) {
69         return_value = T_COSE_ERR_SIG_VERIFY;
70     } else {
71         return_value = T_COSE_SUCCESS;
72     }
73 
74 Done:
75     return return_value;
76 }
77 #endif /* T_COSE_DISABLE_SHORT_CIRCUIT_SIGN */
78 
79 /**
80  * \file t_cose_mac0_verify.c
81  *
82  * \brief This verifies t_cose Mac authentication structure without a recipient
83  *        structure.
84  *        Only HMAC is supported so far.
85  */
86 
87 /*
88  * Public function. See t_cose_mac0.h
89  */
t_cose_mac0_verify(struct t_cose_mac0_verify_ctx * context,struct q_useful_buf_c cose_mac0,struct q_useful_buf_c * payload,struct t_cose_parameters * parameters)90 enum t_cose_err_t t_cose_mac0_verify(struct t_cose_mac0_verify_ctx *context,
91                                      struct q_useful_buf_c          cose_mac0,
92                                      struct q_useful_buf_c         *payload,
93                                      struct t_cose_parameters      *parameters)
94 {
95     QCBORDecodeContext            decode_context;
96     QCBORItem                     item;
97     struct q_useful_buf_c         protected_parameters;
98     struct t_cose_parameters      parsed_protected_parameters;
99     struct t_cose_parameters      unprotected_parameters;
100     struct t_cose_label_list      critical_labels;
101     struct t_cose_label_list      unknown_labels;
102     enum t_cose_err_t             return_value;
103     struct q_useful_buf_c         tag;
104     struct q_useful_buf_c         tbm_first_part;
105     /* Buffer for the ToBeMaced */
106     Q_USEFUL_BUF_MAKE_STACK_UB(   tbm_first_part_buf,
107                                   T_COSE_SIZE_OF_TBM);
108     struct t_cose_crypto_hmac     hmac_ctx;
109 
110     *payload = NULL_Q_USEFUL_BUF_C;
111 
112     QCBORDecode_Init(&decode_context, cose_mac0, QCBOR_DECODE_MODE_NORMAL);
113     /* Calls to QCBORDecode_GetNext() rely on item.uDataType != QCBOR_TYPE_ARRAY
114      * to detect decoding errors rather than checking the return code.
115      */
116 
117     /* --  The array of four -- */
118     (void)QCBORDecode_GetNext(&decode_context, &item);
119     if(item.uDataType != QCBOR_TYPE_ARRAY) {
120         return_value = T_COSE_ERR_MAC0_FORMAT;
121         goto Done;
122     }
123 
124     if((context->option_flags & T_COSE_OPT_TAG_REQUIRED) &&
125        !QCBORDecode_IsTagged(&decode_context, &item, CBOR_TAG_COSE_MAC0)) {
126         return_value = T_COSE_ERR_INCORRECTLY_TAGGED;
127         goto Done;
128     }
129 
130     /* -- Clear list where unknown labels are accumulated -- */
131     clear_label_list(&unknown_labels);
132 
133     /* --  Get the protected header parameters -- */
134     (void)QCBORDecode_GetNext(&decode_context, &item);
135     if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
136         return_value = T_COSE_ERR_MAC0_FORMAT;
137         goto Done;
138     }
139 
140     protected_parameters = item.val.string;
141 
142     return_value = parse_protected_header_parameters(protected_parameters,
143                                                    &parsed_protected_parameters,
144                                                    &critical_labels,
145                                                    &unknown_labels);
146     if(return_value != T_COSE_SUCCESS) {
147         goto Done;
148     }
149 
150     /* --  Get the unprotected parameters -- */
151     return_value = parse_unprotected_header_parameters(&decode_context,
152                                                        &unprotected_parameters,
153                                                        &unknown_labels);
154     if(return_value != T_COSE_SUCCESS) {
155         goto Done;
156     }
157     if((context->option_flags & T_COSE_OPT_REQUIRE_KID) &&
158         q_useful_buf_c_is_null(unprotected_parameters.kid)) {
159         return_value = T_COSE_ERR_NO_KID;
160         goto Done;
161     }
162 
163     /* -- Check critical parameter labels -- */
164     return_value = check_critical_labels(&critical_labels, &unknown_labels);
165     if(return_value != T_COSE_SUCCESS) {
166         goto Done;
167     }
168 
169     /* -- Check for duplicate parameters and copy to returned parameters -- */
170     return_value = check_and_copy_parameters(&parsed_protected_parameters,
171                                              &unprotected_parameters,
172                                              parameters);
173     if(return_value != T_COSE_SUCCESS) {
174         goto Done;
175     }
176 
177     /* -- Get the payload -- */
178     QCBORDecode_GetNext(&decode_context, &item);
179     if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
180         return_value = T_COSE_ERR_MAC0_FORMAT;
181         goto Done;
182     }
183     *payload = item.val.string;
184 
185     /* -- Get the tag -- */
186     QCBORDecode_GetNext(&decode_context, &item);
187     if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
188         return_value = T_COSE_ERR_MAC0_FORMAT;
189         goto Done;
190     }
191     tag = item.val.string;
192 
193     /* -- Finish up the CBOR decode -- */
194     /* This check make sure the array only had the expected four
195      * items. Works for definite and indefinite length arrays. Also
196      * make sure there were no extra bytes.
197      */
198     if(QCBORDecode_Finish(&decode_context) != QCBOR_SUCCESS) {
199         return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
200         goto Done;
201     }
202 
203     /* -- Skip tag verification if such is requested --*/
204     if(context->option_flags & T_COSE_OPT_DECODE_ONLY) {
205         return_value = T_COSE_SUCCESS;
206         goto Done;
207     }
208 
209     /* -- Compute the ToBeMaced -- */
210     return_value = create_tbm(tbm_first_part_buf,
211                               protected_parameters,
212                               &tbm_first_part,
213                               T_COSE_TBM_BARE_PAYLOAD,
214                               *payload);
215     if(return_value) {
216         goto Done;
217     }
218 
219     if (context->option_flags & T_COSE_OPT_ALLOW_SHORT_CIRCUIT) {
220 #ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
221         /* Short-circuit tag. Hash is used to generated tag instead of HMAC */
222         return_value = short_circuit_verify(
223                                   parsed_protected_parameters.cose_algorithm_id,
224                                   tbm_first_part,
225                                   *payload,
226                                   tag);
227 #else
228         return_value = T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED;
229 #endif
230         goto Done;
231 
232     }
233     /*
234      * Start the HMAC verification.
235      * Calculate the tag of the first part of ToBeMaced and the wrapped
236      * payload, to save a bigger buffer containing the entire ToBeMaced.
237      */
238     return_value = t_cose_crypto_hmac_verify_setup(&hmac_ctx,
239                                   parsed_protected_parameters.cose_algorithm_id,
240                                   context->verification_key);
241     if(return_value) {
242         goto Done;
243     }
244 
245     /* Compute the tag of the first part. */
246     return_value = t_cose_crypto_hmac_update(&hmac_ctx,
247                                          q_useful_buf_head(tbm_first_part,
248                                                            tbm_first_part.len));
249     if(return_value) {
250         goto Done;
251     }
252 
253     return_value = t_cose_crypto_hmac_update(&hmac_ctx, *payload);
254     if(return_value) {
255         goto Done;
256     }
257 
258     return_value = t_cose_crypto_hmac_verify_finish(&hmac_ctx, tag);
259 
260 Done:
261     return return_value;
262 }
263