1 /* t-x448.c - Check the X488 computation
2 * Copyright (C) 2019 g10 Code GmbH
3 *
4 * This file is part of Libgcrypt.
5 *
6 * Libgcrypt is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 of
9 * the License, or (at your option) any later version.
10 *
11 * Libgcrypt is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 * SPDX-License-Identifier: LGPL-2.1+
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30
31 #include "stopwatch.h"
32
33 #define PGM "t-x448"
34 #include "t-common.h"
35 #define N_TESTS 9
36
37
38 static void
print_mpi(const char * text,gcry_mpi_t a)39 print_mpi (const char *text, gcry_mpi_t a)
40 {
41 gcry_error_t err;
42 char *buf;
43 void *bufaddr = &buf;
44
45 err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a);
46 if (err)
47 fprintf (stderr, "%s: [error printing number: %s]\n",
48 text, gpg_strerror (err));
49 else
50 {
51 fprintf (stderr, "%s: %s\n", text, buf);
52 gcry_free (buf);
53 }
54 }
55
56
57 static void
show_note(const char * format,...)58 show_note (const char *format, ...)
59 {
60 va_list arg_ptr;
61
62 if (!verbose && getenv ("srcdir"))
63 fputs (" ", stderr); /* To align above "PASS: ". */
64 else
65 fprintf (stderr, "%s: ", PGM);
66 va_start (arg_ptr, format);
67 vfprintf (stderr, format, arg_ptr);
68 if (*format && format[strlen(format)-1] != '\n')
69 putc ('\n', stderr);
70 va_end (arg_ptr);
71 }
72
73
74 /* Convert STRING consisting of hex characters into its binary
75 representation and return it as an allocated buffer. The valid
76 length of the buffer is returned at R_LENGTH. The string is
77 delimited by end of string. The function returns NULL on
78 error. */
79 static void *
hex2buffer(const char * string,size_t * r_length)80 hex2buffer (const char *string, size_t *r_length)
81 {
82 const char *s;
83 unsigned char *buffer;
84 size_t length;
85
86 buffer = xmalloc (strlen(string)/2+1);
87 length = 0;
88 for (s=string; *s; s +=2 )
89 {
90 if (!hexdigitp (s) || !hexdigitp (s+1))
91 return NULL; /* Invalid hex digits. */
92 ((unsigned char*)buffer)[length++] = xtoi_2 (s);
93 }
94 *r_length = length;
95 return buffer;
96 }
97
98 static void
reverse_buffer(unsigned char * buffer,unsigned int length)99 reverse_buffer (unsigned char *buffer, unsigned int length)
100 {
101 unsigned int tmp, i;
102
103 for (i=0; i < length/2; i++)
104 {
105 tmp = buffer[i];
106 buffer[i] = buffer[length-1-i];
107 buffer[length-1-i] = tmp;
108 }
109 }
110
111
112 /*
113 * Test X448 functionality through higher layer crypto routines.
114 *
115 * Input: K (as hex string), U (as hex string), R (as hex string)
116 *
117 * where R is expected result of X448 (K, U).
118 *
119 */
120 static void
test_cv_hl(int testno,const char * k_str,const char * u_str,const char * result_str)121 test_cv_hl (int testno, const char *k_str, const char *u_str,
122 const char *result_str)
123 {
124 gpg_error_t err;
125 void *buffer = NULL;
126 size_t buflen;
127 gcry_sexp_t s_pk = NULL;
128 gcry_mpi_t mpi_k = NULL;
129 gcry_sexp_t s_data = NULL;
130 gcry_sexp_t s_result = NULL;
131 gcry_sexp_t s_tmp = NULL;
132 unsigned char *res = NULL;
133 size_t res_len;
134
135 if (verbose > 1)
136 info ("Running test %d\n", testno);
137
138 if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56)
139 {
140 fail ("error building s-exp for test %d, %s: %s",
141 testno, "k", "invalid hex string");
142 goto leave;
143 }
144
145 mpi_k = gcry_mpi_set_opaque (NULL, buffer, buflen*8);
146 if ((err = gcry_sexp_build (&s_data, NULL, "%m", mpi_k)))
147 {
148 fail ("error building s-exp for test %d, %s: %s",
149 testno, "data", gpg_strerror (err));
150 goto leave;
151 }
152
153 if (!(buffer = hex2buffer (u_str, &buflen)) || buflen != 56)
154 {
155 fail ("error building s-exp for test %d, %s: %s",
156 testno, "u", "invalid hex string");
157 goto leave;
158 }
159
160 /*
161 * The procedure of decodeUCoordinate will be done internally
162 * by _gcry_ecc_mont_decodepoint. So, we just put the little-endian
163 * binary to build S-exp.
164 *
165 * We could add the prefix 0x40, but libgcrypt also supports
166 * format with no prefix. So, it is OK not to put the prefix.
167 */
168 if ((err = gcry_sexp_build (&s_pk, NULL,
169 "(public-key"
170 " (ecc"
171 " (curve \"X448\")"
172 " (q%b)))", (int)buflen, buffer)))
173 {
174 fail ("error building s-exp for test %d, %s: %s",
175 testno, "pk", gpg_strerror (err));
176 goto leave;
177 }
178
179 xfree (buffer);
180 buffer = NULL;
181
182 if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk)))
183 fail ("gcry_pk_encrypt failed for test %d: %s", testno,
184 gpg_strerror (err));
185
186 s_tmp = gcry_sexp_find_token (s_result, "s", 0);
187 if (!s_tmp || !(res = gcry_sexp_nth_buffer (s_tmp, 1, &res_len)))
188 fail ("gcry_pk_encrypt failed for test %d: %s", testno, "missing value");
189 else
190 {
191 char *r, *r0;
192 int i;
193
194 r0 = r = xmalloc (2*(res_len)+1);
195 if (!r0)
196 {
197 fail ("memory allocation for test %d", testno);
198 goto leave;
199 }
200
201 for (i=0; i < res_len; i++, r += 2)
202 snprintf (r, 3, "%02x", res[i]);
203 if (strcmp (result_str, r0))
204 {
205 fail ("gcry_pk_encrypt failed for test %d: %s",
206 testno, "wrong value returned");
207 info (" expected: '%s'", result_str);
208 info (" got: '%s'", r0);
209 }
210 xfree (r0);
211 }
212
213 leave:
214 xfree (res);
215 gcry_mpi_release (mpi_k);
216 gcry_sexp_release (s_tmp);
217 gcry_sexp_release (s_result);
218 gcry_sexp_release (s_data);
219 gcry_sexp_release (s_pk);
220 xfree (buffer);
221 }
222
223 /*
224 * Test X448 functionality through the API for X448.
225 *
226 * Input: K (as hex string), U (as hex string), R (as hex string)
227 *
228 * where R is expected result of X448 (K, U).
229 *
230 */
231 static void
test_cv_x448(int testno,const char * k_str,const char * u_str,const char * result_str)232 test_cv_x448 (int testno, const char *k_str, const char *u_str,
233 const char *result_str)
234 {
235 gpg_error_t err;
236 void *scalar;
237 void *point = NULL;
238 size_t buflen;
239 unsigned char result[56];
240 char result_hex[113];
241 int i;
242
243 if (verbose > 1)
244 info ("Running test %d\n", testno);
245
246 if (!(scalar = hex2buffer (k_str, &buflen)) || buflen != 56)
247 {
248 fail ("error building s-exp for test %d, %s: %s",
249 testno, "k", "invalid hex string");
250 goto leave;
251 }
252
253 if (!(point = hex2buffer (u_str, &buflen)) || buflen != 56)
254 {
255 fail ("error building s-exp for test %d, %s: %s",
256 testno, "u", "invalid hex string");
257 goto leave;
258 }
259
260 if ((err = gcry_ecc_mul_point (GCRY_ECC_CURVE448, result, scalar, point)))
261 fail ("gcry_ecc_mul_point failed for test %d: %s", testno,
262 gpg_strerror (err));
263
264 for (i=0; i < 56; i++)
265 snprintf (&result_hex[i*2], 3, "%02x", result[i]);
266
267 if (strcmp (result_str, result_hex))
268 {
269 fail ("gcry_ecc_mul_point failed for test %d: %s",
270 testno, "wrong value returned");
271 info (" expected: '%s'", result_str);
272 info (" got: '%s'", result_hex);
273 }
274
275 leave:
276 xfree (scalar);
277 xfree (point);
278 }
279
280 static void
test_cv(int testno,const char * k_str,const char * u_str,const char * result_str)281 test_cv (int testno, const char *k_str, const char *u_str,
282 const char *result_str)
283 {
284 test_cv_hl (testno, k_str, u_str, result_str);
285 test_cv_x448 (testno, k_str, u_str, result_str);
286 }
287
288 /*
289 * Test iterative X448 computation through lower layer MPI routines.
290 *
291 * Input: K (as hex string), ITER, R (as hex string)
292 *
293 * where R is expected result of iterating X448 by ITER times.
294 *
295 */
296 static void
test_it(int testno,const char * k_str,int iter,const char * result_str)297 test_it (int testno, const char *k_str, int iter, const char *result_str)
298 {
299 gcry_ctx_t ctx;
300 gpg_error_t err;
301 void *buffer = NULL;
302 size_t buflen;
303 gcry_mpi_t mpi_k = NULL;
304 gcry_mpi_t mpi_x = NULL;
305 gcry_mpi_point_t P = NULL;
306 gcry_mpi_point_t Q;
307 int i;
308 gcry_mpi_t mpi_kk = NULL;
309
310 if (verbose > 1)
311 info ("Running test %d: iteration=%d\n", testno, iter);
312
313 gcry_mpi_ec_new (&ctx, NULL, "X448");
314 Q = gcry_mpi_point_new (0);
315
316 if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 56)
317 {
318 fail ("error scanning MPI for test %d, %s: %s",
319 testno, "k", "invalid hex string");
320 goto leave;
321 }
322 reverse_buffer (buffer, buflen);
323 if ((err = gcry_mpi_scan (&mpi_x, GCRYMPI_FMT_USG, buffer, buflen, NULL)))
324 {
325 fail ("error scanning MPI for test %d, %s: %s",
326 testno, "x", gpg_strerror (err));
327 goto leave;
328 }
329
330 xfree (buffer);
331 buffer = NULL;
332
333 P = gcry_mpi_point_set (NULL, mpi_x, NULL, GCRYMPI_CONST_ONE);
334
335 mpi_k = gcry_mpi_copy (mpi_x);
336 if (debug)
337 print_mpi ("k", mpi_k);
338
339 for (i = 0; i < iter; i++)
340 {
341 /*
342 * Another variant of decodeScalar448 thing.
343 */
344 mpi_kk = gcry_mpi_set (mpi_kk, mpi_k);
345 gcry_mpi_set_bit (mpi_kk, 447);
346 gcry_mpi_clear_bit (mpi_kk, 0);
347 gcry_mpi_clear_bit (mpi_kk, 1);
348
349 gcry_mpi_ec_mul (Q, mpi_kk, P, ctx);
350
351 P = gcry_mpi_point_set (P, mpi_k, NULL, GCRYMPI_CONST_ONE);
352 gcry_mpi_ec_get_affine (mpi_k, NULL, Q, ctx);
353
354 if (debug)
355 print_mpi ("k", mpi_k);
356 }
357
358 {
359 unsigned char res[56];
360 char *r, *r0;
361
362 gcry_mpi_print (GCRYMPI_FMT_USG, res, 56, NULL, mpi_k);
363 reverse_buffer (res, 56);
364
365 r0 = r = xmalloc (113);
366 if (!r0)
367 {
368 fail ("memory allocation for test %d", testno);
369 goto leave;
370 }
371
372 for (i=0; i < 56; i++, r += 2)
373 snprintf (r, 3, "%02x", res[i]);
374
375 if (strcmp (result_str, r0))
376 {
377 fail ("X448 failed for test %d: %s",
378 testno, "wrong value returned");
379 info (" expected: '%s'", result_str);
380 info (" got: '%s'", r0);
381 }
382 xfree (r0);
383 }
384
385 leave:
386 gcry_mpi_release (mpi_kk);
387 gcry_mpi_release (mpi_k);
388 gcry_mpi_point_release (P);
389 gcry_mpi_release (mpi_x);
390 xfree (buffer);
391 gcry_mpi_point_release (Q);
392 gcry_ctx_release (ctx);
393 }
394
395 /*
396 * X-coordinate of generator of the X448.
397 */
398 #define G_X ("0500000000000000000000000000000000000000000000000000000000000000" \
399 "000000000000000000000000000000000000000000000000")
400
401 /*
402 * Test Diffie-Hellman in RFC-7748.
403 *
404 * Note that it's not like the ECDH of OpenPGP, where we use
405 * ephemeral public key.
406 */
407 static void
test_dh(int testno,const char * a_priv_str,const char * a_pub_str,const char * b_priv_str,const char * b_pub_str,const char * result_str)408 test_dh (int testno, const char *a_priv_str, const char *a_pub_str,
409 const char *b_priv_str, const char *b_pub_str,
410 const char *result_str)
411 {
412 /* Test A for private key corresponds to public key. */
413 test_cv (testno, a_priv_str, G_X, a_pub_str);
414 /* Test B for private key corresponds to public key. */
415 test_cv (testno, b_priv_str, G_X, b_pub_str);
416 /* Test DH with A's private key and B's public key. */
417 test_cv (testno, a_priv_str, b_pub_str, result_str);
418 /* Test DH with B's private key and A's public key. */
419 test_cv (testno, b_priv_str, a_pub_str, result_str);
420 }
421
422
423 static void
check_x448(void)424 check_x448 (void)
425 {
426 int ntests;
427
428 info ("Checking X448.\n");
429
430 ntests = 0;
431
432 /*
433 * Values are cited from RFC-7748: 5.2. Test Vectors.
434 * Following two tests are for the first type test.
435 */
436 test_cv (1,
437 "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
438 "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
439 "06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9"
440 "814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086",
441 "ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239f"
442 "e14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f");
443 ntests++;
444 test_cv (2,
445 "203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c5"
446 "38345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f",
447 "0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b"
448 "165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db",
449 "884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7"
450 "ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d");
451 ntests++;
452
453 /*
454 * Additional test. Value is from second type test.
455 */
456 test_cv (3,
457 G_X,
458 G_X,
459 "3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a"
460 "4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113");
461 ntests++;
462
463 /*
464 * Following two tests are for the second type test,
465 * with one iteration and 1,000 iterations. (1,000,000 iterations
466 * takes too long.)
467 */
468 test_it (4,
469 G_X,
470 1,
471 "3f482c8a9f19b01e6c46ee9711d9dc14fd4bf67af30765c2ae2b846a"
472 "4d23a8cd0db897086239492caf350b51f833868b9bc2b3bca9cf4113");
473 ntests++;
474
475 test_it (5,
476 G_X,
477 1000,
478 "aa3b4749d55b9daf1e5b00288826c467274ce3ebbdd5c17b975e09d4"
479 "af6c67cf10d087202db88286e2b79fceea3ec353ef54faa26e219f38");
480 ntests++;
481
482 /*
483 * Last test is from: 6. Diffie-Hellman, 6.2. Curve448
484 */
485 test_dh (6,
486 /* Alice's private key, a */
487 "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d"
488 "d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b",
489 /* Alice's public key, X448(a, 5) */
490 "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c"
491 "22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0",
492 /* Bob's private key, b */
493 "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d"
494 "6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d",
495 /* Bob's public key, X448(b, 5) */
496 "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430"
497 "27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609",
498 /* Their shared secret, K */
499 "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282b"
500 "b60c0b56fd2464c335543936521c24403085d59a449a5037514a879d");
501 ntests++;
502
503 /* three tests which results 0. */
504 test_cv (7,
505 "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
506 "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
507 "00000000000000000000000000000000000000000000000000000000"
508 "00000000000000000000000000000000000000000000000000000000",
509 "00000000000000000000000000000000000000000000000000000000"
510 "00000000000000000000000000000000000000000000000000000000");
511 ntests++;
512
513 test_cv (8,
514 "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
515 "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
516 "01000000000000000000000000000000000000000000000000000000"
517 "00000000000000000000000000000000000000000000000000000000",
518 "00000000000000000000000000000000000000000000000000000000"
519 "00000000000000000000000000000000000000000000000000000000");
520 ntests++;
521
522 test_cv (9,
523 "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121"
524 "700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3",
525 "feffffffffffffffffffffffffffffffffffffffffffffffffffffff"
526 "feffffffffffffffffffffffffffffffffffffffffffffffffffffff",
527 "00000000000000000000000000000000000000000000000000000000"
528 "00000000000000000000000000000000000000000000000000000000");
529 ntests++;
530
531 if (ntests != N_TESTS)
532 fail ("did %d tests but expected %d", ntests, N_TESTS);
533 else if ((ntests % 256))
534 show_note ("%d tests done\n", ntests);
535 }
536
537
538 int
main(int argc,char ** argv)539 main (int argc, char **argv)
540 {
541 int last_argc = -1;
542
543 if (argc)
544 { argc--; argv++; }
545
546 while (argc && last_argc != argc )
547 {
548 last_argc = argc;
549 if (!strcmp (*argv, "--"))
550 {
551 argc--; argv++;
552 break;
553 }
554 else if (!strcmp (*argv, "--help"))
555 {
556 fputs ("usage: " PGM " [options]\n"
557 "Options:\n"
558 " --verbose print timings etc.\n"
559 " --debug flyswatter\n",
560 stdout);
561 exit (0);
562 }
563 else if (!strcmp (*argv, "--verbose"))
564 {
565 verbose++;
566 argc--; argv++;
567 }
568 else if (!strcmp (*argv, "--debug"))
569 {
570 verbose += 2;
571 debug++;
572 argc--; argv++;
573 }
574 else if (!strncmp (*argv, "--", 2))
575 die ("unknown option '%s'", *argv);
576 }
577
578 xgcry_control ((GCRYCTL_DISABLE_SECMEM, 0));
579 if (!gcry_check_version (GCRYPT_VERSION))
580 die ("version mismatch\n");
581 if (debug)
582 xgcry_control ((GCRYCTL_SET_DEBUG_FLAGS, 1u , 0));
583 xgcry_control ((GCRYCTL_ENABLE_QUICK_RANDOM, 0));
584 xgcry_control ((GCRYCTL_INITIALIZATION_FINISHED, 0));
585
586 start_timer ();
587 check_x448 ();
588 stop_timer ();
589
590 info ("All tests completed in %s. Errors: %d\n",
591 elapsed_time (1), error_count);
592 return !!error_count;
593 }
594