1 /*
2  *  t_cose_sign_verify_test.c
3  *
4  * Copyright 2019-2020, Laurence Lundblade
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 "t_cose_sign1_verify.h"
13 #include "q_useful_buf.h"
14 #include "t_cose_make_test_pub_key.h"
15 
16 #include "t_cose_crypto.h" /* Just for t_cose_crypto_sig_size() */
17 
18 
19 /*
20  * Public function, see t_cose_sign_verify_test.h
21  */
sign_verify_basic_test_alg(int32_t cose_alg)22 int_fast32_t sign_verify_basic_test_alg(int32_t cose_alg)
23 {
24     struct t_cose_sign1_sign_ctx   sign_ctx;
25     int32_t                        return_value;
26     Q_USEFUL_BUF_MAKE_STACK_UB(    signed_cose_buffer, 300);
27     struct q_useful_buf_c          signed_cose;
28     struct t_cose_key              key_pair;
29     struct q_useful_buf_c          payload;
30     struct t_cose_sign1_verify_ctx verify_ctx;
31 
32     /* -- Get started with context initialization, selecting the alg -- */
33     t_cose_sign1_sign_init(&sign_ctx, 0, cose_alg);
34 
35     /* Make an ECDSA key pair that will be used for both signing and
36      * verification.
37      */
38     return_value = make_ecdsa_key_pair(cose_alg, &key_pair);
39     if(return_value) {
40         return 1000 + return_value;
41     }
42     t_cose_sign1_set_signing_key(&sign_ctx, key_pair, NULL_Q_USEFUL_BUF_C);
43 
44     return_value = t_cose_sign1_sign(&sign_ctx,
45                       Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
46                       signed_cose_buffer,
47                       &signed_cose);
48     if(return_value) {
49         return_value += 2000;
50         goto Done;
51     }
52 
53     /* Verification */
54     t_cose_sign1_verify_init(&verify_ctx, 0);
55 
56     t_cose_sign1_set_verification_key(&verify_ctx, key_pair);
57 
58     return_value = t_cose_sign1_verify(&verify_ctx,
59                                        signed_cose,         /* COSE to verify */
60                                        &payload,  /* Payload from signed_cose */
61                                        NULL);      /* Don't return parameters */
62     if(return_value) {
63         return_value += 5000;
64         goto Done;
65     }
66 
67 
68     /* compare payload output to the one expected */
69     if(q_useful_buf_compare(payload, Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"))) {
70         return_value = 6000;
71         goto Done;
72     }
73 
74 Done:
75     /* Many crypto libraries allocate memory, slots, etc for keys */
76     free_ecdsa_key_pair(key_pair);
77 
78     return return_value;
79 }
80 
81 
82 /*
83  * Public function, see t_cose_sign_verify_test.h
84  */
sign_verify_basic_test()85 int_fast32_t sign_verify_basic_test()
86 {
87     int_fast32_t return_value;
88 
89    return_value  = sign_verify_basic_test_alg(T_COSE_ALGORITHM_ES256);
90    if(return_value) {
91         return 20000 + return_value;
92    }
93 
94 #ifndef T_COSE_DISABLE_ES384
95     return_value  = sign_verify_basic_test_alg(T_COSE_ALGORITHM_ES384);
96     if(return_value) {
97         return 30000 + return_value;
98     }
99 #endif
100 
101 #ifndef T_COSE_DISABLE_ES512
102     return_value  = sign_verify_basic_test_alg(T_COSE_ALGORITHM_ES512);
103     if(return_value) {
104         return 50000 + return_value;
105     }
106 #endif
107 
108     return 0;
109 
110 }
111 
112 
113 /*
114  * Public function, see t_cose_sign_verify_test.h
115  */
sign_verify_sig_fail_test()116 int_fast32_t sign_verify_sig_fail_test()
117 {
118     struct t_cose_sign1_sign_ctx   sign_ctx;
119     QCBOREncodeContext             cbor_encode;
120     int32_t                        return_value;
121     Q_USEFUL_BUF_MAKE_STACK_UB(    signed_cose_buffer, 300);
122     struct q_useful_buf_c          signed_cose;
123     struct t_cose_key              key_pair;
124     struct q_useful_buf_c          payload;
125     QCBORError                     cbor_error;
126     struct t_cose_sign1_verify_ctx verify_ctx;
127     size_t                         tamper_offset;
128 
129 
130     /* Make an ECDSA key pair that will be used for both signing and
131      * verification.
132      */
133     return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES256, &key_pair);
134     if(return_value) {
135         return 1000 + return_value;
136     }
137 
138     QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
139 
140     t_cose_sign1_sign_init(&sign_ctx, 0, T_COSE_ALGORITHM_ES256);
141     t_cose_sign1_set_signing_key(&sign_ctx, key_pair, NULL_Q_USEFUL_BUF_C);
142 
143     return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
144     if(return_value) {
145         return_value += 2000;
146         goto Done;
147     }
148 
149     QCBOREncode_AddSZString(&cbor_encode, "payload");
150 
151 
152     return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
153     if(return_value) {
154         return_value += 3000;
155         goto Done;
156     }
157 
158     cbor_error = QCBOREncode_Finish(&cbor_encode, &signed_cose);
159     if(cbor_error) {
160         return_value = 4000 + cbor_error;
161         goto Done;
162     }
163 
164     /* tamper with the pay load to see that the signature verification fails */
165     tamper_offset = q_useful_buf_find_bytes(signed_cose, Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"));
166     if(tamper_offset == SIZE_MAX) {
167         return_value = 99;
168         goto Done;
169     }
170     ((char *)signed_cose.ptr)[tamper_offset] = 'h';
171 
172 
173     t_cose_sign1_verify_init(&verify_ctx, 0);
174 
175     t_cose_sign1_set_verification_key(&verify_ctx, key_pair);
176 
177     return_value = t_cose_sign1_verify(&verify_ctx,
178                                        signed_cose,         /* COSE to verify */
179                                        &payload,  /* Payload from signed_cose */
180                                        NULL);      /* Don't return parameters */
181 
182     if(return_value != T_COSE_ERR_SIG_VERIFY) {
183         return_value += 5000;
184     }
185 
186     return_value = 0; /* This was supposed to fail, so it needs to be reset */
187 
188 Done:
189     free_ecdsa_key_pair(key_pair);
190 
191     return return_value;
192 }
193 
194 
195 /*
196  * Public function, see t_cose_sign_verify_test.h
197  */
sign_verify_make_cwt_test()198 int_fast32_t sign_verify_make_cwt_test()
199 {
200     struct t_cose_sign1_sign_ctx   sign_ctx;
201     QCBOREncodeContext             cbor_encode;
202     int32_t                        return_value;
203     Q_USEFUL_BUF_MAKE_STACK_UB(    signed_cose_buffer, 300);
204     struct q_useful_buf_c          signed_cose;
205     struct t_cose_key              key_pair;
206     struct q_useful_buf_c          payload;
207     QCBORError                     cbor_error;
208     struct t_cose_sign1_verify_ctx verify_ctx;
209     struct q_useful_buf_c          expected_rfc8392_first_part;
210     struct q_useful_buf_c          expected_payload;
211     struct q_useful_buf_c          actual_rfc8392_first_part;
212 
213     /* -- initialize for signing --
214      *  No special options selected
215      */
216     t_cose_sign1_sign_init(&sign_ctx, 0, T_COSE_ALGORITHM_ES256);
217 
218 
219     /* -- Key and kid --
220      * The ECDSA key pair made is both for signing and verification.
221      * The kid comes from RFC 8932
222      */
223     return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES256, &key_pair);
224     if(return_value) {
225         return 1000 + return_value;
226     }
227     t_cose_sign1_set_signing_key(&sign_ctx,
228                                   key_pair,
229                                   Q_USEFUL_BUF_FROM_SZ_LITERAL("AsymmetricECDSA256"));
230 
231 
232     /* -- Encoding context and output of parameters -- */
233     QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
234     return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
235     if(return_value) {
236         return_value += 2000;
237         goto Done;
238     }
239 
240 
241     /* -- The payload as from RFC 8932 -- */
242     QCBOREncode_OpenMap(&cbor_encode);
243     QCBOREncode_AddSZStringToMapN(&cbor_encode, 1, "coap://as.example.com");
244     QCBOREncode_AddSZStringToMapN(&cbor_encode, 2, "erikw");
245     QCBOREncode_AddSZStringToMapN(&cbor_encode, 3, "coap://light.example.com");
246     QCBOREncode_AddInt64ToMapN(&cbor_encode, 4, 1444064944);
247     QCBOREncode_AddInt64ToMapN(&cbor_encode, 5, 1443944944);
248     QCBOREncode_AddInt64ToMapN(&cbor_encode, 6, 1443944944);
249     const uint8_t xx[] = {0x0b, 0x71};
250     QCBOREncode_AddBytesToMapN(&cbor_encode, 7,
251                                Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(xx));
252     QCBOREncode_CloseMap(&cbor_encode);
253 
254 
255     /* -- Finish up the COSE_Sign1. This is where the signing happens -- */
256     return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
257     if(return_value) {
258         return_value += 3000;
259         goto Done;
260     }
261 
262     /* Finally close off the CBOR formatting and get the pointer and length
263      * of the resulting COSE_Sign1
264      */
265     cbor_error = QCBOREncode_Finish(&cbor_encode, &signed_cose);
266     if(cbor_error) {
267         return_value = cbor_error + 4000;
268         goto Done;
269     }
270     /* --- Done making COSE Sign1 object  --- */
271 
272 
273     /* Compare to expected from CWT RFC */
274     /* The first part, the intro and protected parameters must be the same */
275     const uint8_t rfc8392_first_part_bytes[] = {
276         0xd2, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa1, 0x04, 0x52, 0x41, 0x73, 0x79,
277         0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x45, 0x43, 0x44, 0x53, 0x41,
278         0x32, 0x35, 0x36, 0x58, 0x50, 0xa7, 0x01, 0x75, 0x63, 0x6f, 0x61, 0x70,
279         0x3a, 0x2f, 0x2f, 0x61, 0x73, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
280         0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x02, 0x65, 0x65, 0x72, 0x69, 0x6b, 0x77,
281         0x03, 0x78, 0x18, 0x63, 0x6f, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x69,
282         0x67, 0x68, 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
283         0x63, 0x6f, 0x6d, 0x04, 0x1a, 0x56, 0x12, 0xae, 0xb0, 0x05, 0x1a, 0x56,
284         0x10, 0xd9, 0xf0, 0x06, 0x1a, 0x56, 0x10, 0xd9, 0xf0, 0x07, 0x42, 0x0b,
285         0x71};
286     expected_rfc8392_first_part = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(rfc8392_first_part_bytes);
287     actual_rfc8392_first_part = q_useful_buf_head(signed_cose, sizeof(rfc8392_first_part_bytes));
288     if(q_useful_buf_compare(actual_rfc8392_first_part, expected_rfc8392_first_part)) {
289         return_value = -1;
290         goto Done;
291     }
292 
293     /* --- Start verifying the COSE Sign1 object  --- */
294     /* Run the signature verification */
295     t_cose_sign1_verify_init(&verify_ctx, 0);
296 
297     t_cose_sign1_set_verification_key(&verify_ctx, key_pair);
298 
299     return_value =  t_cose_sign1_verify(&verify_ctx,
300                                         signed_cose, /* COSE to verify */
301                                        &payload, /* Payload from signed_cose */
302                                         NULL);  /* Don't return parameters */
303 
304     if(return_value) {
305         return_value += 5000;
306         goto Done;
307     }
308 
309     /* Format the expected payload CBOR fragment */
310 
311     /* Skip the key id, because this has the short-circuit key id */
312     const size_t kid_encoded_len =
313       1 +
314       1 +
315       1 +
316       strlen("AsymmetricECDSA256"); // length of short-circuit key id
317 
318 
319     /* compare payload output to the one expected */
320     expected_payload = q_useful_buf_tail(expected_rfc8392_first_part, kid_encoded_len + 8);
321     if(q_useful_buf_compare(payload, expected_payload)) {
322         return_value = 6000;
323     }
324     /* --- Done verifying the COSE Sign1 object  --- */
325 
326 Done:
327     /* Many crypto libraries allocate memory, slots, etc for keys */
328     free_ecdsa_key_pair(key_pair);
329 
330     return return_value;
331 }
332 
333 
334 /*
335  * Public function, see t_cose_sign_verify_test.h
336  */
size_test(int32_t cose_algorithm_id,struct q_useful_buf_c kid,struct t_cose_key key_pair)337 static int size_test(int32_t               cose_algorithm_id,
338                      struct q_useful_buf_c kid,
339                      struct t_cose_key     key_pair)
340 {
341     struct t_cose_sign1_sign_ctx   sign_ctx;
342     QCBOREncodeContext             cbor_encode;
343     enum t_cose_err_t              return_value;
344     struct q_useful_buf            nil_buf;
345     size_t                         calculated_size;
346     QCBORError                     cbor_error;
347     struct q_useful_buf_c          actual_signed_cose;
348     Q_USEFUL_BUF_MAKE_STACK_UB(    signed_cose_buffer, 300);
349     struct q_useful_buf_c          payload;
350     size_t                         sig_size;
351 
352     /* ---- Common Set up ---- */
353     payload = Q_USEFUL_BUF_FROM_SZ_LITERAL("payload");
354     return_value = t_cose_crypto_sig_size(cose_algorithm_id, key_pair, &sig_size);
355 
356     /* ---- First calculate the size ----- */
357     nil_buf = (struct q_useful_buf) {NULL, INT32_MAX};
358     QCBOREncode_Init(&cbor_encode, nil_buf);
359 
360     t_cose_sign1_sign_init(&sign_ctx,  0,  cose_algorithm_id);
361     t_cose_sign1_set_signing_key(&sign_ctx, key_pair, kid);
362 
363     return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
364     if(return_value) {
365         return 2000 + return_value;
366     }
367 
368     QCBOREncode_AddEncoded(&cbor_encode, payload);
369 
370     return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
371     if(return_value) {
372         return 3000 + return_value;
373     }
374 
375     cbor_error = QCBOREncode_FinishGetSize(&cbor_encode, &calculated_size);
376     if(cbor_error) {
377         return 4000 + cbor_error;
378     }
379 
380     /* ---- General sanity check ---- */
381     size_t expected_min = sig_size + payload.len + kid.len;
382 
383     if(calculated_size < expected_min || calculated_size > expected_min + 30) {
384         return -1;
385     }
386 
387 
388 
389     /* ---- Now make a real COSE_Sign1 and compare the size ---- */
390     QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
391 
392     t_cose_sign1_sign_init(&sign_ctx,  0,  cose_algorithm_id);
393     t_cose_sign1_set_signing_key(&sign_ctx, key_pair, kid);
394 
395     return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
396     if(return_value) {
397         return 2000 + return_value;
398     }
399 
400     QCBOREncode_AddEncoded(&cbor_encode, payload);
401 
402     return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
403     if(return_value) {
404         return 3000 + return_value;
405     }
406 
407     cbor_error = QCBOREncode_Finish(&cbor_encode, &actual_signed_cose);
408     if(actual_signed_cose.len != calculated_size) {
409         return -2;
410     }
411 
412     /* ---- Again with one-call API to make COSE_Sign1 ---- */\
413     t_cose_sign1_sign_init(&sign_ctx, 0, cose_algorithm_id);
414     t_cose_sign1_set_signing_key(&sign_ctx, key_pair, kid);
415     return_value = t_cose_sign1_sign(&sign_ctx,
416                                      payload,
417                                      signed_cose_buffer,
418                                      &actual_signed_cose);
419     if(return_value) {
420         return 7000 + return_value;
421     }
422 
423     if(actual_signed_cose.len != calculated_size) {
424         return -3;
425     }
426 
427     return 0;
428 }
429 
430 
431 /*
432  * Public function, see t_cose_sign_verify_test.h
433  */
sign_verify_get_size_test()434 int_fast32_t sign_verify_get_size_test()
435 {
436     enum t_cose_err_t   return_value;
437     struct t_cose_key   key_pair;
438     int32_t             result;
439 
440     return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES256, &key_pair);
441     if(return_value) {
442         return 1000 + return_value;
443     }
444 
445     result = size_test(T_COSE_ALGORITHM_ES256, NULL_Q_USEFUL_BUF_C, key_pair);
446     free_ecdsa_key_pair(key_pair);
447     if(result) {
448         return 2000 + result;
449     }
450 
451 
452 #ifndef T_COSE_DISABLE_ES384
453 
454     return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES384, &key_pair);
455     if(return_value) {
456         return 3000 + return_value;
457     }
458 
459     result = size_test(T_COSE_ALGORITHM_ES384, NULL_Q_USEFUL_BUF_C, key_pair);
460     free_ecdsa_key_pair(key_pair);
461     if(result) {
462         return 4000 + result;
463     }
464 
465 #endif /* T_COSE_DISABLE_ES384 */
466 
467 
468 #ifndef T_COSE_DISABLE_ES512
469 
470     return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES512, &key_pair);
471     if(return_value) {
472         return 5000 + return_value;
473     }
474 
475     result = size_test(T_COSE_ALGORITHM_ES512, NULL_Q_USEFUL_BUF_C, key_pair);
476     if(result) {
477         free_ecdsa_key_pair(key_pair);
478         return 6000 + result;
479     }
480 
481     result = size_test(T_COSE_ALGORITHM_ES512,
482                        Q_USEFUL_BUF_FROM_SZ_LITERAL("greasy kid stuff"),
483                        key_pair);
484     free_ecdsa_key_pair(key_pair);
485     if(result) {
486         return 7000 + result;
487     }
488 
489 #endif /* T_COSE_DISABLE_ES512 */
490 
491 
492     return 0;
493 }
494