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