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