Line data Source code
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
38 0 : print_mpi (const char *text, gcry_mpi_t a)
39 : {
40 : gcry_error_t err;
41 : char *buf;
42 0 : void *bufaddr = &buf;
43 :
44 0 : err = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a);
45 0 : if (err)
46 0 : fprintf (stderr, "%s: [error printing number: %s]\n",
47 : text, gpg_strerror (err));
48 : else
49 : {
50 0 : fprintf (stderr, "%s: %s\n", text, buf);
51 0 : gcry_free (buf);
52 : }
53 0 : }
54 :
55 :
56 : static void
57 1 : show_note (const char *format, ...)
58 : {
59 : va_list arg_ptr;
60 :
61 1 : if (!verbose && getenv ("srcdir"))
62 1 : fputs (" ", stderr); /* To align above "PASS: ". */
63 : else
64 0 : fprintf (stderr, "%s: ", PGM);
65 1 : va_start (arg_ptr, format);
66 1 : vfprintf (stderr, format, arg_ptr);
67 1 : if (*format && format[strlen(format)-1] != '\n')
68 0 : putc ('\n', stderr);
69 1 : va_end (arg_ptr);
70 1 : }
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 *
79 40 : hex2buffer (const char *string, size_t *r_length)
80 : {
81 : const char *s;
82 : unsigned char *buffer;
83 : size_t length;
84 :
85 40 : buffer = xmalloc (strlen(string)/2+1);
86 40 : length = 0;
87 1320 : for (s=string; *s; s +=2 )
88 : {
89 1280 : if (!hexdigitp (s) || !hexdigitp (s+1))
90 0 : return NULL; /* Invalid hex digits. */
91 1280 : ((unsigned char*)buffer)[length++] = xtoi_2 (s);
92 : }
93 40 : *r_length = length;
94 40 : return buffer;
95 : }
96 :
97 : static void
98 23 : reverse_buffer (unsigned char *buffer, unsigned int length)
99 : {
100 : unsigned int tmp, i;
101 :
102 391 : for (i=0; i < length/2; i++)
103 : {
104 368 : tmp = buffer[i];
105 368 : buffer[i] = buffer[length-1-i];
106 368 : buffer[length-1-i] = tmp;
107 : }
108 23 : }
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_decrypt with Curve25519 private key and let
119 : * it compute X25519.
120 : */
121 : static void
122 19 : test_cv (int testno, const char *k_str, const char *u_str,
123 : const char *result_str)
124 : {
125 : gpg_error_t err;
126 19 : void *buffer = NULL;
127 : size_t buflen;
128 19 : gcry_sexp_t s_pk = NULL;
129 19 : gcry_mpi_t mpi_k = NULL;
130 19 : gcry_sexp_t s_data = NULL;
131 19 : gcry_sexp_t s_result = NULL;
132 19 : gcry_sexp_t s_tmp = NULL;
133 19 : unsigned char *res = NULL;
134 : size_t res_len;
135 :
136 19 : if (verbose > 1)
137 0 : info ("Running test %d\n", testno);
138 :
139 19 : if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 32)
140 : {
141 0 : fail ("error building s-exp for test %d, %s: %s",
142 : testno, "k", "invalid hex string");
143 0 : goto leave;
144 : }
145 :
146 19 : reverse_buffer (buffer, buflen);
147 19 : if ((err = gcry_mpi_scan (&mpi_k, GCRYMPI_FMT_USG, buffer, buflen, NULL)))
148 : {
149 0 : fail ("error converting MPI for test %d: %s", testno, gpg_strerror (err));
150 0 : goto leave;
151 : }
152 :
153 19 : if ((err = gcry_sexp_build (&s_data, NULL, "%m", mpi_k)))
154 : {
155 0 : fail ("error building s-exp for test %d, %s: %s",
156 : testno, "data", gpg_strerror (err));
157 0 : goto leave;
158 : }
159 :
160 19 : xfree (buffer);
161 19 : if (!(buffer = hex2buffer (u_str, &buflen)) || buflen != 32)
162 : {
163 0 : fail ("error building s-exp for test %d, %s: %s",
164 : testno, "u", "invalid hex string");
165 0 : 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 19 : 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 0 : fail ("error building s-exp for test %d, %s: %s",
184 : testno, "pk", gpg_strerror (err));
185 0 : goto leave;
186 : }
187 :
188 19 : xfree (buffer);
189 19 : buffer = NULL;
190 :
191 19 : if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk)))
192 0 : fail ("gcry_pk_encrypt failed for test %d: %s", testno,
193 : gpg_strerror (err));
194 :
195 19 : s_tmp = gcry_sexp_find_token (s_result, "s", 0);
196 19 : if (!s_tmp || !(res = gcry_sexp_nth_buffer (s_tmp, 1, &res_len)))
197 0 : 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 19 : r0 = r = xmalloc (2*(res_len)+1);
205 19 : if (!r0)
206 : {
207 0 : fail ("memory allocation for test %d", testno);
208 0 : goto leave;
209 : }
210 :
211 627 : for (i=1; i < res_len; i++, r += 2)
212 608 : snprintf (r, 3, "%02x", res[i]);
213 19 : if (strcmp (result_str, r0))
214 : {
215 0 : fail ("gcry_pk_encrypt failed for test %d: %s",
216 : testno, "wrong value returned");
217 0 : info (" expected: '%s'", result_str);
218 0 : info (" got: '%s'", r0);
219 : }
220 19 : xfree (r0);
221 : }
222 :
223 : leave:
224 19 : xfree (res);
225 19 : gcry_mpi_release (mpi_k);
226 19 : gcry_sexp_release (s_tmp);
227 19 : gcry_sexp_release (s_result);
228 19 : gcry_sexp_release (s_data);
229 19 : gcry_sexp_release (s_pk);
230 19 : xfree (buffer);
231 19 : }
232 :
233 : /*
234 : * Test iterative X25519 computation through lower layer MPI routines.
235 : *
236 : * Input: K (as hex string), ITER, R (as hex string)
237 : *
238 : * where R is expected result of iterating X25519 by ITER times.
239 : *
240 : */
241 : static void
242 2 : test_it (int testno, const char *k_str, int iter, const char *result_str)
243 : {
244 : gcry_ctx_t ctx;
245 : gpg_error_t err;
246 2 : void *buffer = NULL;
247 : size_t buflen;
248 2 : gcry_mpi_t mpi_k = NULL;
249 2 : gcry_mpi_t mpi_x = NULL;
250 2 : gcry_mpi_point_t P = NULL;
251 : gcry_mpi_point_t Q;
252 : int i;
253 2 : gcry_mpi_t mpi_kk = NULL;
254 :
255 2 : if (verbose > 1)
256 0 : info ("Running test %d: iteration=%d\n", testno, iter);
257 :
258 2 : gcry_mpi_ec_new (&ctx, NULL, "Curve25519");
259 2 : Q = gcry_mpi_point_new (0);
260 :
261 2 : if (!(buffer = hex2buffer (k_str, &buflen)) || buflen != 32)
262 : {
263 0 : fail ("error scanning MPI for test %d, %s: %s",
264 : testno, "k", "invalid hex string");
265 0 : goto leave;
266 : }
267 2 : reverse_buffer (buffer, buflen);
268 2 : if ((err = gcry_mpi_scan (&mpi_x, GCRYMPI_FMT_USG, buffer, buflen, NULL)))
269 : {
270 0 : fail ("error scanning MPI for test %d, %s: %s",
271 : testno, "x", gpg_strerror (err));
272 0 : goto leave;
273 : }
274 :
275 2 : xfree (buffer);
276 2 : buffer = NULL;
277 :
278 2 : P = gcry_mpi_point_set (NULL, mpi_x, NULL, GCRYMPI_CONST_ONE);
279 :
280 2 : mpi_k = gcry_mpi_copy (mpi_x);
281 2 : if (debug)
282 0 : print_mpi ("k", mpi_k);
283 :
284 1003 : for (i = 0; i < iter; i++)
285 : {
286 : /*
287 : * Another variant of decodeScalar25519 thing.
288 : */
289 1001 : mpi_kk = gcry_mpi_set (mpi_kk, mpi_k);
290 1001 : gcry_mpi_set_bit (mpi_kk, 254);
291 1001 : gcry_mpi_clear_bit (mpi_kk, 255);
292 1001 : gcry_mpi_clear_bit (mpi_kk, 0);
293 1001 : gcry_mpi_clear_bit (mpi_kk, 1);
294 1001 : gcry_mpi_clear_bit (mpi_kk, 2);
295 :
296 1001 : gcry_mpi_ec_mul (Q, mpi_kk, P, ctx);
297 :
298 1001 : P = gcry_mpi_point_set (P, mpi_k, NULL, GCRYMPI_CONST_ONE);
299 1001 : gcry_mpi_ec_get_affine (mpi_k, NULL, Q, ctx);
300 :
301 1001 : if (debug)
302 0 : print_mpi ("k", mpi_k);
303 : }
304 :
305 : {
306 : unsigned char res[32];
307 : char *r, *r0;
308 :
309 2 : gcry_mpi_print (GCRYMPI_FMT_USG, res, 32, NULL, mpi_k);
310 2 : reverse_buffer (res, 32);
311 :
312 2 : r0 = r = xmalloc (65);
313 2 : if (!r0)
314 : {
315 0 : fail ("memory allocation for test %d", testno);
316 0 : goto leave;
317 : }
318 :
319 66 : for (i=0; i < 32; i++, r += 2)
320 64 : snprintf (r, 3, "%02x", res[i]);
321 :
322 2 : if (strcmp (result_str, r0))
323 : {
324 0 : fail ("curv25519 failed for test %d: %s",
325 : testno, "wrong value returned");
326 0 : info (" expected: '%s'", result_str);
327 0 : info (" got: '%s'", r0);
328 : }
329 2 : xfree (r0);
330 : }
331 :
332 : leave:
333 2 : gcry_mpi_release (mpi_kk);
334 2 : gcry_mpi_release (mpi_k);
335 2 : gcry_mpi_point_release (P);
336 2 : gcry_mpi_release (mpi_x);
337 2 : xfree (buffer);
338 2 : gcry_mpi_point_release (Q);
339 2 : gcry_ctx_release (ctx);
340 2 : }
341 :
342 : /*
343 : * X-coordinate of generator of the Curve25519.
344 : */
345 : #define G_X "0900000000000000000000000000000000000000000000000000000000000000"
346 :
347 : /*
348 : * Test Diffie-Hellman in RFC-7748.
349 : *
350 : * Note that it's not like the ECDH of OpenPGP, where we use
351 : * ephemeral public key.
352 : */
353 : static void
354 1 : test_dh (int testno, const char *a_priv_str, const char *a_pub_str,
355 : const char *b_priv_str, const char *b_pub_str,
356 : const char *result_str)
357 : {
358 : /* Test A for private key corresponds to public key. */
359 1 : test_cv (testno, a_priv_str, G_X, a_pub_str);
360 : /* Test B for private key corresponds to public key. */
361 1 : test_cv (testno, b_priv_str, G_X, b_pub_str);
362 : /* Test DH with A's private key and B's public key. */
363 1 : test_cv (testno, a_priv_str, b_pub_str, result_str);
364 : /* Test DH with B's private key and A's public key. */
365 1 : test_cv (testno, b_priv_str, a_pub_str, result_str);
366 1 : }
367 :
368 :
369 : static void
370 1 : check_cv25519 (void)
371 : {
372 : int ntests;
373 :
374 1 : info ("Checking Curve25519.\n");
375 :
376 1 : ntests = 0;
377 :
378 : /*
379 : * Values are cited from RFC-7748: 5.2. Test Vectors.
380 : * Following two tests are for the first type test.
381 : */
382 1 : test_cv (1,
383 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
384 : "e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
385 : "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552");
386 1 : ntests++;
387 1 : test_cv (2,
388 : "4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d",
389 : "e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493",
390 : "95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957");
391 1 : ntests++;
392 :
393 : /*
394 : * Additional test. Value is from second type test.
395 : */
396 1 : test_cv (3,
397 : G_X,
398 : G_X,
399 : "422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079");
400 1 : ntests++;
401 :
402 : /*
403 : * Following two tests are for the second type test,
404 : * with one iteration and 1,000 iterations. (1,000,000 iterations
405 : * takes too long.)
406 : */
407 1 : test_it (4,
408 : G_X,
409 : 1,
410 : "422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079");
411 1 : ntests++;
412 :
413 1 : test_it (5,
414 : G_X,
415 : 1000,
416 : "684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51");
417 1 : ntests++;
418 :
419 : /*
420 : * Last test is from: 6. Diffie-Hellman, 6.1. Curve25519
421 : */
422 1 : test_dh (6,
423 : /* Alice's private key, a */
424 : "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
425 : /* Alice's public key, X25519(a, 9) */
426 : "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
427 : /* Bob's private key, b */
428 : "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
429 : /* Bob's public key, X25519(b, 9) */
430 : "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
431 : /* Their shared secret, K */
432 : "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
433 1 : ntests++;
434 :
435 : /* Seven tests which results 0. */
436 1 : test_cv (7,
437 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
438 : "0000000000000000000000000000000000000000000000000000000000000000",
439 : "0000000000000000000000000000000000000000000000000000000000000000");
440 1 : ntests++;
441 :
442 1 : test_cv (8,
443 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
444 : "0100000000000000000000000000000000000000000000000000000000000000",
445 : "0000000000000000000000000000000000000000000000000000000000000000");
446 1 : ntests++;
447 :
448 1 : test_cv (9,
449 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
450 : "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800",
451 : "0000000000000000000000000000000000000000000000000000000000000000");
452 1 : ntests++;
453 :
454 1 : test_cv (10,
455 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
456 : "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157",
457 : "0000000000000000000000000000000000000000000000000000000000000000");
458 1 : ntests++;
459 :
460 1 : test_cv (11,
461 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
462 : "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
463 : "0000000000000000000000000000000000000000000000000000000000000000");
464 1 : ntests++;
465 :
466 1 : test_cv (12,
467 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
468 : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
469 : "0000000000000000000000000000000000000000000000000000000000000000");
470 1 : ntests++;
471 :
472 1 : test_cv (13,
473 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
474 : "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
475 : "0000000000000000000000000000000000000000000000000000000000000000");
476 1 : ntests++;
477 :
478 : /* Five tests which resulted 0 if decodeUCoordinate didn't change MSB. */
479 1 : test_cv (14,
480 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
481 : "cdeb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880",
482 : "7ce548bc4919008436244d2da7a9906528fe3a6d278047654bd32d8acde9707b");
483 1 : ntests++;
484 :
485 1 : test_cv (15,
486 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
487 : "4c9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7",
488 : "e17902e989a034acdf7248260e2c94cdaf2fe1e72aaac7024a128058b6189939");
489 1 : ntests++;
490 :
491 1 : test_cv (16,
492 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
493 : "d9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
494 : "ea6e6ddf0685c31e152d5818441ac9ac8db1a01f3d6cb5041b07443a901e7145");
495 1 : ntests++;
496 :
497 1 : test_cv (17,
498 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
499 : "daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
500 : "845ddce7b3a9b3ee01a2f1fd4282ad293310f7a232cbc5459fb35d94bccc9d05");
501 1 : ntests++;
502 :
503 1 : test_cv (18,
504 : "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
505 : "dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
506 : "6989e2cb1cea159acf121b0af6bf77493189c9bd32c2dac71669b540f9488247");
507 1 : ntests++;
508 :
509 1 : if (ntests != N_TESTS)
510 0 : fail ("did %d tests but expected %d", ntests, N_TESTS);
511 1 : else if ((ntests % 256))
512 1 : show_note ("%d tests done\n", ntests);
513 1 : }
514 :
515 :
516 : int
517 1 : main (int argc, char **argv)
518 : {
519 1 : int last_argc = -1;
520 :
521 1 : if (argc)
522 1 : { argc--; argv++; }
523 :
524 2 : while (argc && last_argc != argc )
525 : {
526 0 : last_argc = argc;
527 0 : if (!strcmp (*argv, "--"))
528 : {
529 0 : argc--; argv++;
530 0 : break;
531 : }
532 0 : else if (!strcmp (*argv, "--help"))
533 : {
534 0 : fputs ("usage: " PGM " [options]\n"
535 : "Options:\n"
536 : " --verbose print timings etc.\n"
537 : " --debug flyswatter\n",
538 : stdout);
539 0 : exit (0);
540 : }
541 0 : else if (!strcmp (*argv, "--verbose"))
542 : {
543 0 : verbose++;
544 0 : argc--; argv++;
545 : }
546 0 : else if (!strcmp (*argv, "--debug"))
547 : {
548 0 : verbose += 2;
549 0 : debug++;
550 0 : argc--; argv++;
551 : }
552 0 : else if (!strncmp (*argv, "--", 2))
553 0 : die ("unknown option '%s'", *argv);
554 : }
555 :
556 1 : xgcry_control (GCRYCTL_DISABLE_SECMEM, 0);
557 1 : if (!gcry_check_version (GCRYPT_VERSION))
558 0 : die ("version mismatch\n");
559 1 : if (debug)
560 0 : xgcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0);
561 1 : xgcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
562 1 : xgcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
563 :
564 1 : start_timer ();
565 1 : check_cv25519 ();
566 1 : stop_timer ();
567 :
568 1 : info ("All tests completed in %s. Errors: %d\n",
569 : elapsed_time (1), error_count);
570 1 : return !!error_count;
571 : }
|