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