1 /*
2 * t_cose_sign1_sign.c
3 *
4 * Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 *
8 * See BSD-3-Clause license in README.md
9 */
10
11 #include "t_cose_sign1_sign.h"
12 #include "qcbor/qcbor.h"
13 #include "t_cose_standard_constants.h"
14 #include "t_cose_crypto.h"
15 #include "t_cose_util.h"
16
17
18 /**
19 * \file t_cose_sign1_sign.c
20 *
21 * \brief This implements t_cose signing
22 *
23 * Stack usage to sign is dependent on the signing alg and key size
24 * and type of hash implementation. t_cose_sign1_finish() is the main
25 * user of stack It is 384 for \ref COSE_ALGORITHM_ES256 and 778 for
26 * \ref COSE_ALGORITHM_ES512.
27 */
28
29
30 /*
31 * Cross-check to make sure public definition of algorithm
32 * IDs matches the internal ones.
33 */
34 #if T_COSE_ALGORITHM_ES256 != COSE_ALGORITHM_ES256
35 #error COSE algorithm identifier definitions are in error
36 #endif
37
38 #if T_COSE_ALGORITHM_ES384 != COSE_ALGORITHM_ES384
39 #error COSE algorithm identifier definitions are in error
40 #endif
41
42 #if T_COSE_ALGORITHM_ES512 != COSE_ALGORITHM_ES512
43 #error COSE algorithm identifier definitions are in error
44 #endif
45
46
47 #ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
48 /**
49 * \brief Create a short-circuit signature
50 *
51 * \param[in] cose_algorithm_id Algorithm ID. This is used only to make
52 * the short-circuit signature the same size
53 * as the real signature would be for the
54 * particular algorithm.
55 * \param[in] hash_to_sign The bytes to sign. Typically, a hash of
56 * a payload.
57 * \param[in] signature_buffer Pointer and length of buffer into which
58 * the resulting signature is put.
59 * \param[in] signature Pointer and length of the signature
60 * returned.
61 *
62 * \return This returns one of the error codes defined by \ref t_cose_err_t.
63 *
64 * This creates the short-circuit signature that is a concatenation of
65 * hashes up to the expected size of the signature. This is a test
66 * mode only has it has no security value. This is retained in
67 * commercial production code as a useful test or demo that can run
68 * even if key material is not set up or accessible.
69 */
70 static inline enum t_cose_err_t
short_circuit_sign(int32_t cose_algorithm_id,struct q_useful_buf_c hash_to_sign,struct q_useful_buf signature_buffer,struct q_useful_buf_c * signature)71 short_circuit_sign(int32_t cose_algorithm_id,
72 struct q_useful_buf_c hash_to_sign,
73 struct q_useful_buf signature_buffer,
74 struct q_useful_buf_c *signature)
75 {
76 /* approximate stack use on 32-bit machine: local use: 16 bytes
77 */
78 enum t_cose_err_t return_value;
79 size_t array_indx;
80 size_t amount_to_copy;
81 size_t sig_size;
82
83 sig_size = cose_algorithm_id == COSE_ALGORITHM_ES256 ? T_COSE_EC_P256_SIG_SIZE :
84 cose_algorithm_id == COSE_ALGORITHM_ES384 ? T_COSE_EC_P384_SIG_SIZE :
85 cose_algorithm_id == COSE_ALGORITHM_ES512 ? T_COSE_EC_P512_SIG_SIZE :
86 0;
87
88 /* Check the signature length against buffer size */
89 if(sig_size == 0) {
90 return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
91 goto Done;
92 }
93
94 if(sig_size > signature_buffer.len) {
95 /* Buffer too small for this signature type */
96 return_value = T_COSE_ERR_SIG_BUFFER_SIZE;
97 goto Done;
98 }
99
100 /* Loop concatening copies of the hash to fill out to signature size */
101 for(array_indx = 0; array_indx < sig_size; array_indx += hash_to_sign.len) {
102 amount_to_copy = sig_size - array_indx;
103 if(amount_to_copy > hash_to_sign.len) {
104 amount_to_copy = hash_to_sign.len;
105 }
106 memcpy((uint8_t *)signature_buffer.ptr + array_indx,
107 hash_to_sign.ptr,
108 amount_to_copy);
109 }
110 signature->ptr = signature_buffer.ptr;
111 signature->len = sig_size;
112 return_value = T_COSE_SUCCESS;
113
114 Done:
115 return return_value;
116 }
117 #endif /* T_COSE_DISABLE_SHORT_CIRCUIT_SIGN */
118
119
120 /**
121 * \brief Makes the protected header parameters for COSE.
122 *
123 * \param[in] cose_algorithm_id The COSE algorithm ID to put in the
124 * header parameters.
125 * \param[in] buffer_for_parameters Pointer and length of buffer into which
126 * the resulting encoded protected
127 * parameters is put. See return value.
128 *
129 * \return The pointer and length of the encoded protected
130 * parameters is returned, or \c NULL_Q_USEFUL_BUF_C if this fails.
131 * This will have the same pointer as \c buffer_for_parameters,
132 * but the pointer is conts and the length is that of the valid
133 * data, not of the size of the buffer.
134 *
135 * The protected parameters are returned in fully encoded CBOR format as
136 * they are added to the \c COSE_Sign1 message as a binary string. This is
137 * different from the unprotected parameters which are not handled this
138 * way.
139 *
140 * This returns \c NULL_Q_USEFUL_BUF_C if buffer_for_parameters was too
141 * small. See also definition of \c T_COSE_SIGN1_MAX_SIZE_PROTECTED_PARAMETERS
142 */
143 static inline struct q_useful_buf_c
encode_protected_parameters(int32_t cose_algorithm_id,struct q_useful_buf buffer_for_parameters)144 encode_protected_parameters(int32_t cose_algorithm_id,
145 struct q_useful_buf buffer_for_parameters)
146 {
147 /* approximate stack use on 32-bit machine:
148 * CBOR encode context 148
149 * local use: 20
150 * total: 168
151 */
152 struct q_useful_buf_c protected_parameters;
153 QCBORError qcbor_result;
154 QCBOREncodeContext cbor_encode_ctx;
155 struct q_useful_buf_c return_value;
156
157 QCBOREncode_Init(&cbor_encode_ctx, buffer_for_parameters);
158 QCBOREncode_OpenMap(&cbor_encode_ctx);
159 QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx,
160 COSE_HEADER_PARAM_ALG,
161 cose_algorithm_id);
162 QCBOREncode_CloseMap(&cbor_encode_ctx);
163 qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &protected_parameters);
164
165 if(qcbor_result == QCBOR_SUCCESS) {
166 return_value = protected_parameters;
167 } else {
168 return_value = NULL_Q_USEFUL_BUF_C;
169 }
170
171 return return_value;
172 }
173
174
175 /**
176 * \brief Add the unprotected parameters to a CBOR encoding context
177 *
178 * \param[in] me The t_cose signing context.
179 * \param[in] kid The key ID.
180 * \param[in] cbor_encode_ctx CBOR encoding context to output to
181 *
182 * No error is returned. If an error occurred it will be returned when
183 * \c QCBOR_Finish() is called on \c cbor_encode_ctx.
184 *
185 * The unprotected parameters added by this are the kid and content type.
186 */
187 static inline enum t_cose_err_t
add_unprotected_parameters(const struct t_cose_sign1_sign_ctx * me,const struct q_useful_buf_c kid,QCBOREncodeContext * cbor_encode_ctx)188 add_unprotected_parameters(const struct t_cose_sign1_sign_ctx *me,
189 const struct q_useful_buf_c kid,
190 QCBOREncodeContext *cbor_encode_ctx)
191 {
192 QCBOREncode_OpenMap(cbor_encode_ctx);
193
194 if(!q_useful_buf_c_is_null_or_empty(kid)) {
195 QCBOREncode_AddBytesToMapN(cbor_encode_ctx,
196 COSE_HEADER_PARAM_KID,
197 kid);
198 }
199
200 #ifndef T_COSE_DISABLE_CONTENT_TYPE
201 if(me->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE &&
202 me->content_type_tstr != NULL) {
203 /* Both the string and int content types are not allowed */
204 return T_COSE_ERR_DUPLICATE_PARAMETER;
205 }
206
207
208 if(me->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE) {
209 QCBOREncode_AddUInt64ToMapN(cbor_encode_ctx,
210 COSE_HEADER_PARAM_CONTENT_TYPE,
211 me->content_type_uint);
212 }
213
214 if(me->content_type_tstr != NULL) {
215 QCBOREncode_AddSZStringToMapN(cbor_encode_ctx,
216 COSE_HEADER_PARAM_CONTENT_TYPE,
217 me->content_type_tstr);
218 }
219 #else
220 (void)me; /* avoid unused parameter warning */
221 #endif
222
223 QCBOREncode_CloseMap(cbor_encode_ctx);
224
225 return T_COSE_SUCCESS;
226 }
227
228
229 /*
230 * Public function. See t_cose_sign1_sign.h
231 */
232 enum t_cose_err_t
t_cose_sign1_encode_parameters(struct t_cose_sign1_sign_ctx * me,QCBOREncodeContext * cbor_encode_ctx)233 t_cose_sign1_encode_parameters(struct t_cose_sign1_sign_ctx *me,
234 QCBOREncodeContext *cbor_encode_ctx)
235 {
236 /* approximate stack use on 32-bit machine:
237 * 48 bytes local use
238 * 168 call to make_protected
239 * 216 total
240 */
241 enum t_cose_err_t return_value;
242 struct q_useful_buf buffer_for_protected_parameters;
243 struct q_useful_buf_c kid;
244 int32_t hash_alg_id;
245
246 /* Check the cose_algorithm_id now by getting the hash alg as an
247 * early error check even though it is not used until later.
248 */
249 hash_alg_id = hash_alg_id_from_sig_alg_id(me->cose_algorithm_id);
250 if(hash_alg_id == T_COSE_INVALID_ALGORITHM_ID) {
251 return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
252 }
253
254 /* Add the CBOR tag indicating COSE_Sign1 */
255 if(!(me->option_flags & T_COSE_OPT_OMIT_CBOR_TAG)) {
256 QCBOREncode_AddTag(cbor_encode_ctx, CBOR_TAG_COSE_SIGN1);
257 }
258
259 /* Get started with the tagged array that holds the four parts of
260 * a cose single signed message */
261 QCBOREncode_OpenArray(cbor_encode_ctx);
262
263 /* The protected parameters, which are added as a wrapped bstr */
264 buffer_for_protected_parameters = Q_USEFUL_BUF_FROM_BYTE_ARRAY(me->protected_parameters_buffer);
265 me->protected_parameters = encode_protected_parameters(me->cose_algorithm_id, buffer_for_protected_parameters);
266 if(q_useful_buf_c_is_null(me->protected_parameters)) {
267 /* The sizing of storage for protected parameters is
268 * off (should never happen in tested, released code) */
269 return_value = T_COSE_ERR_MAKING_PROTECTED;
270 goto Done;
271 }
272 /* The use of _AddBytes here achieves the bstr wrapping */
273 QCBOREncode_AddBytes(cbor_encode_ctx, me->protected_parameters);
274
275 /* The Unprotected parameters */
276 /* Get the kid because it goes into the parameters that are about
277 * to be made. */
278 kid = me->kid;
279
280 if(me->option_flags & T_COSE_OPT_SHORT_CIRCUIT_SIG) {
281 #ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
282 if(q_useful_buf_c_is_null_or_empty(kid)) {
283 /* No kid passed in, Use the short-circuit kid */
284 kid = get_short_circuit_kid();
285 }
286 #else
287 return_value = T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED;
288 goto Done;
289 #endif
290 }
291
292 return_value = add_unprotected_parameters(me, kid, cbor_encode_ctx);
293 if(return_value != T_COSE_SUCCESS) {
294 goto Done;
295 }
296
297 QCBOREncode_BstrWrap(cbor_encode_ctx);
298
299 /* Any failures in CBOR encoding will be caught in finish when the
300 * CBOR encoding is closed off. No need to track here as the CBOR
301 * encoder tracks it internally.
302 */
303
304 Done:
305 return return_value;
306 }
307
308
309 /*
310 * Public function. See t_cose_sign1_sign.h
311 */
312 enum t_cose_err_t
t_cose_sign1_encode_signature(struct t_cose_sign1_sign_ctx * me,QCBOREncodeContext * cbor_encode_ctx)313 t_cose_sign1_encode_signature(struct t_cose_sign1_sign_ctx *me,
314 QCBOREncodeContext *cbor_encode_ctx)
315 {
316 /* approximate stack use on 32-bit machine:
317 * 32 bytes local use
318 * 220 to 434 for calls depending on hash implementation
319 * 32 to 64 bytes depending on hash alg (SHA256, 384 or 512)
320 * 64 to 260 depending on EC alg
321 * 348 to 778 total depending on hash and EC alg
322 * Also add stack use by EC and hash functions
323 */
324 enum t_cose_err_t return_value;
325 QCBORError cbor_err;
326 /* pointer and length of the completed tbs hash */
327 struct q_useful_buf_c tbs_hash;
328 /* Pointer and length of the completed signature */
329 struct q_useful_buf_c signature;
330 /* Buffer for the actual signature */
331 Q_USEFUL_BUF_MAKE_STACK_UB( buffer_for_signature, T_COSE_MAX_SIG_SIZE);
332 /* Buffer for the tbs hash. */
333 Q_USEFUL_BUF_MAKE_STACK_UB( buffer_for_tbs_hash, T_COSE_CRYPTO_MAX_HASH_SIZE);
334 struct q_useful_buf_c signed_payload;
335
336 QCBOREncode_CloseBstrWrap(cbor_encode_ctx, &signed_payload);
337
338 /* Check that there are no CBOR encoding errors before proceeding
339 * with hashing and signing. This is not actually necessary as the
340 * errors will be caught correctly later, but it does make it a
341 * bit easier for the caller to debug problems.
342 */
343 cbor_err = QCBOREncode_GetErrorState(cbor_encode_ctx);
344 if(cbor_err == QCBOR_ERR_BUFFER_TOO_SMALL) {
345 return_value = T_COSE_ERR_TOO_SMALL;
346 goto Done;
347 } else if(cbor_err != QCBOR_SUCCESS) {
348 return_value = T_COSE_ERR_CBOR_FORMATTING;
349 goto Done;
350 }
351
352 if (QCBOREncode_IsBufferNULL(cbor_encode_ctx)) {
353 /* Just calculating sizes. All that is needed is the signature
354 * size.
355 */
356 signature.ptr = NULL;
357 return_value = t_cose_crypto_sig_size(me->cose_algorithm_id,
358 me->signing_key,
359 &signature.len);
360 } else {
361
362 /* Create the hash of the to-be-signed bytes. Inputs to the
363 * hash are the protected parameters, the payload that is
364 * getting signed, the cose signature alg from which the hash
365 * alg is determined. The cose_algorithm_id was checked in
366 * t_cose_sign1_init() so it doesn't need to be checked here.
367 */
368 return_value = create_tbs_hash(me->cose_algorithm_id,
369 me->protected_parameters,
370 T_COSE_TBS_PAYLOAD_IS_BSTR_WRAPPED,
371 signed_payload,
372 buffer_for_tbs_hash,
373 &tbs_hash);
374 if(return_value) {
375 goto Done;
376 }
377
378 /* Compute the signature using public key crypto. The key and
379 * algorithm ID are passed in to know how and what to sign
380 * with. The hash of the TBS bytes is what is signed. A buffer
381 * in which to place the signature is passed in and the
382 * signature is returned.
383 *
384 * Short-circuit signing is invoked if requested. It does no
385 * public key operation and requires no key. It is just a test
386 * mode that works even if no public key algorithm is
387 * integrated.
388 */
389 if(!(me->option_flags & T_COSE_OPT_SHORT_CIRCUIT_SIG)) {
390 /* Normal, non-short-circuit signing */
391 return_value = t_cose_crypto_pub_key_sign(me->cose_algorithm_id,
392 me->signing_key,
393 tbs_hash,
394 buffer_for_signature,
395 &signature);
396 } else {
397 #ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
398 /* Short-circuit signing */
399 return_value = short_circuit_sign(me->cose_algorithm_id,
400 tbs_hash,
401 buffer_for_signature,
402 &signature);
403 #endif
404 }
405
406 if(return_value) {
407 goto Done;
408 }
409 }
410
411 /* Add signature to CBOR and close out the array */
412 QCBOREncode_AddBytes(cbor_encode_ctx, signature);
413 QCBOREncode_CloseArray(cbor_encode_ctx);
414
415 /* The layer above this must check for and handle CBOR encoding
416 * errors CBOR encoding errors. Some are detected at the start of
417 * this function, but they cannot all be deteced there.
418 */
419 Done:
420 return return_value;
421 }
422
423
424 /*
425 * Public function. See t_cose_sign1_sign.h
426 */
427 enum t_cose_err_t
t_cose_sign1_sign(struct t_cose_sign1_sign_ctx * me,struct q_useful_buf_c payload,struct q_useful_buf out_buf,struct q_useful_buf_c * result)428 t_cose_sign1_sign(struct t_cose_sign1_sign_ctx *me,
429 struct q_useful_buf_c payload,
430 struct q_useful_buf out_buf,
431 struct q_useful_buf_c *result)
432 {
433 QCBOREncodeContext encode_context;
434 enum t_cose_err_t return_value;
435
436 /* -- Initialize CBOR encoder context with output buffer -- */
437 QCBOREncode_Init(&encode_context, out_buf);
438
439 /* -- Output the header parameters into the encoder context -- */
440 return_value = t_cose_sign1_encode_parameters(me, &encode_context);
441 if(return_value != T_COSE_SUCCESS) {
442 goto Done;
443 }
444
445 /* -- Output the payload into the encoder context -- */
446 /* Payload may or may not actually be CBOR format here. This
447 * function does the job just fine because it just adds bytes to
448 * the encoded output without anything extra.
449 */
450 QCBOREncode_AddEncoded(&encode_context, payload);
451
452 /* -- Sign and put signature in the encoder context -- */
453 return_value = t_cose_sign1_encode_signature(me, &encode_context);
454 if(return_value) {
455 goto Done;
456 }
457
458 /* -- Close off and get the resulting encoded CBOR -- */
459 if(QCBOREncode_Finish(&encode_context, result)) {
460 return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
461 goto Done;
462 }
463
464 Done:
465 return return_value;
466 }
467