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