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