ecc: Change algorithm for Ed25519 x recovery.
[libgcrypt.git] / tests / t-ed25519.c
1 /* t-ed25519.c - Check the Ed25519 crypto
2  * Copyright (C) 2013 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 "../src/gcrypt-int.h"
31
32 #include "stopwatch.h"
33
34 #define PGM "t-ed25519"
35 #define N_TESTS 1025
36
37 #define my_isascii(c) (!((c) & 0x80))
38 #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
39 #define hexdigitp(a) (digitp (a)                     \
40                       || (*(a) >= 'A' && *(a) <= 'F')  \
41                       || (*(a) >= 'a' && *(a) <= 'f'))
42 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
43                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
44 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
45 #define xmalloc(a)    gcry_xmalloc ((a))
46 #define xcalloc(a,b)  gcry_xcalloc ((a),(b))
47 #define xstrdup(a)    gcry_xstrdup ((a))
48 #define xfree(a)      gcry_free ((a))
49 #define pass()        do { ; } while (0)
50
51 static int verbose;
52 static int debug;
53 static int error_count;
54 static int sign_with_pk;
55 static int no_verify;
56 static int custom_data_file;
57
58 static void
59 die (const char *format, ...)
60 {
61   va_list arg_ptr ;
62
63   fflush (stdout);
64   fprintf (stderr, "%s: ", PGM);
65   va_start( arg_ptr, format ) ;
66   vfprintf (stderr, format, arg_ptr );
67   va_end(arg_ptr);
68   if (*format && format[strlen(format)-1] != '\n')
69     putc ('\n', stderr);
70   exit (1);
71 }
72
73 static void
74 fail (const char *format, ...)
75 {
76   va_list arg_ptr;
77
78   fflush (stdout);
79   fprintf (stderr, "%s: ", PGM);
80   /* if (wherestr) */
81   /*   fprintf (stderr, "%s: ", wherestr); */
82   va_start (arg_ptr, format);
83   vfprintf (stderr, format, arg_ptr);
84   va_end (arg_ptr);
85   if (*format && format[strlen(format)-1] != '\n')
86     putc ('\n', stderr);
87   error_count++;
88   if (error_count >= 50)
89     die ("stopped after 50 errors.");
90 }
91
92 static void
93 show (const char *format, ...)
94 {
95   va_list arg_ptr;
96
97   if (!verbose)
98     return;
99   fprintf (stderr, "%s: ", PGM);
100   va_start (arg_ptr, format);
101   vfprintf (stderr, format, arg_ptr);
102   if (*format && format[strlen(format)-1] != '\n')
103     putc ('\n', stderr);
104   va_end (arg_ptr);
105 }
106
107
108 static void
109 show_note (const char *format, ...)
110 {
111   va_list arg_ptr;
112
113   if (!verbose && getenv ("srcdir"))
114     fputs ("      ", stderr);  /* To align above "PASS: ".  */
115   else
116     fprintf (stderr, "%s: ", PGM);
117   va_start (arg_ptr, format);
118   vfprintf (stderr, format, arg_ptr);
119   if (*format && format[strlen(format)-1] != '\n')
120     putc ('\n', stderr);
121   va_end (arg_ptr);
122 }
123
124
125 static void
126 show_sexp (const char *prefix, gcry_sexp_t a)
127 {
128   char *buf;
129   size_t size;
130
131   fprintf (stderr, "%s: ", PGM);
132   if (prefix)
133     fputs (prefix, stderr);
134   size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
135   buf = xmalloc (size);
136
137   gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
138   fprintf (stderr, "%.*s", (int)size, buf);
139   gcry_free (buf);
140 }
141
142
143 /* Prepend FNAME with the srcdir environment variable's value and
144    retrun an allocated filename. */
145 char *
146 prepend_srcdir (const char *fname)
147 {
148   static const char *srcdir;
149   char *result;
150
151   if (!srcdir && !(srcdir = getenv ("srcdir")))
152     srcdir = ".";
153
154   result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
155   strcpy (result, srcdir);
156   strcat (result, "/");
157   strcat (result, fname);
158   return result;
159 }
160
161
162 /* Read next line but skip over empty and comment lines.  Caller must
163    xfree the result.  */
164 static char *
165 read_textline (FILE *fp, int *lineno)
166 {
167   char line[4096];
168   char *p;
169
170   do
171     {
172       if (!fgets (line, sizeof line, fp))
173         {
174           if (feof (fp))
175             return NULL;
176           die ("error reading input line: %s\n", strerror (errno));
177         }
178       ++*lineno;
179       p = strchr (line, '\n');
180       if (!p)
181         die ("input line %d not terminated or too long\n", *lineno);
182       *p = 0;
183       for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
184         *p = 0;
185     }
186   while (!*line || *line == '#');
187   /* if (debug) */
188   /*   show ("read line: '%s'\n", line); */
189   return xstrdup (line);
190 }
191
192
193 /* Copy the data after the tag to BUFFER.  BUFFER will be allocated as
194    needed.  */
195 static void
196 copy_data (char **buffer, const char *line, int lineno)
197 {
198   const char *s;
199
200   xfree (*buffer);
201   *buffer = NULL;
202
203   s = strchr (line, ':');
204   if (!s)
205     {
206       fail ("syntax error at input line %d", lineno);
207       return;
208     }
209   for (s++; my_isascii (*s) && isspace (*s); s++)
210     ;
211   *buffer = xstrdup (s);
212 }
213
214
215 /* Convert STRING consisting of hex characters into its binary
216    representation and return it as an allocated buffer. The valid
217    length of the buffer is returned at R_LENGTH.  The string is
218    delimited by end of string.  The function returns NULL on
219    error.  */
220 static void *
221 hex2buffer (const char *string, size_t *r_length)
222 {
223   const char *s;
224   unsigned char *buffer;
225   size_t length;
226
227   buffer = xmalloc (strlen(string)/2+1);
228   length = 0;
229   for (s=string; *s; s +=2 )
230     {
231       if (!hexdigitp (s) || !hexdigitp (s+1))
232         return NULL;           /* Invalid hex digits. */
233       ((unsigned char*)buffer)[length++] = xtoi_2 (s);
234     }
235   *r_length = length;
236   return buffer;
237 }
238
239
240 static void
241 hexdowncase (char *string)
242 {
243   char *p;
244
245   for (p=string; *p; p++)
246     if (my_isascii (*p))
247       *p = tolower (*p);
248 }
249
250
251 static void
252 one_test (int testno, const char *sk, const char *pk,
253           const char *msg, const char *sig)
254 {
255   gpg_error_t err;
256   int i;
257   char *p;
258   void *buffer = NULL;
259   void *buffer2 = NULL;
260   size_t buflen, buflen2;
261   gcry_sexp_t s_tmp, s_tmp2;
262   gcry_sexp_t s_sk = NULL;
263   gcry_sexp_t s_pk = NULL;
264   gcry_sexp_t s_msg= NULL;
265   gcry_sexp_t s_sig= NULL;
266   unsigned char *sig_r = NULL;
267   unsigned char *sig_s = NULL;
268   char *sig_rs_string = NULL;
269   size_t sig_r_len, sig_s_len;
270
271   if (verbose > 1)
272     show ("Running test %d\n", testno);
273
274   if (!(buffer = hex2buffer (sk, &buflen)))
275     {
276       fail ("error building s-exp for test %d, %s: %s",
277             testno, "sk", "invalid hex string");
278       goto leave;
279     }
280   if (!(buffer2 = hex2buffer (pk, &buflen2)))
281     {
282       fail ("error building s-exp for test %d, %s: %s",
283             testno, "pk", "invalid hex string");
284       goto leave;
285     }
286   if (sign_with_pk)
287     err = gcry_sexp_build (&s_sk, NULL,
288                            "(private-key"
289                            " (ecc"
290                            "  (curve \"Ed25519\")"
291                            "  (q %b)"
292                            "  (d %b)))",
293                            (int)buflen2, buffer2,
294                            (int)buflen, buffer);
295   else
296     err = gcry_sexp_build (&s_sk, NULL,
297                            "(private-key"
298                            " (ecc"
299                            "  (curve \"Ed25519\")"
300                            "  (d %b)))",
301                            (int)buflen, buffer);
302   if (err)
303     {
304       fail ("error building s-exp for test %d, %s: %s",
305             testno, "sk", gpg_strerror (err));
306       goto leave;
307     }
308
309   if ((err = gcry_sexp_build (&s_pk, NULL,
310                               "(public-key"
311                               " (ecc"
312                               "  (curve \"Ed25519\")"
313                               "  (q %b)))",  (int)buflen2, buffer2)))
314     {
315       fail ("error building s-exp for test %d, %s: %s",
316             testno, "pk", gpg_strerror (err));
317       goto leave;
318     }
319
320   xfree (buffer);
321   if (!(buffer = hex2buffer (msg, &buflen)))
322     {
323       fail ("error building s-exp for test %d, %s: %s",
324             testno, "msg", "invalid hex string");
325       goto leave;
326     }
327   if ((err = gcry_sexp_build (&s_msg, NULL,
328                               "(data"
329                               " (flags eddsa)"
330                               " (hash-algo sha512)"
331                               " (value %b))",  (int)buflen, buffer)))
332     {
333       fail ("error building s-exp for test %d, %s: %s",
334             testno, "msg", gpg_strerror (err));
335       goto leave;
336     }
337
338   if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk)))
339     fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err));
340   if (debug)
341     show_sexp ("sig=", s_sig);
342
343   s_tmp2 = NULL;
344   s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
345   if (s_tmp)
346     {
347       s_tmp2 = s_tmp;
348       s_tmp = gcry_sexp_find_token (s_tmp2, "eddsa", 0);
349       if (s_tmp)
350         {
351           gcry_sexp_release (s_tmp2);
352           s_tmp2 = s_tmp;
353           s_tmp = gcry_sexp_find_token (s_tmp2, "r", 0);
354           if (s_tmp)
355             {
356               sig_r = gcry_sexp_nth_buffer (s_tmp, 1, &sig_r_len);
357               gcry_sexp_release (s_tmp);
358             }
359           s_tmp = gcry_sexp_find_token (s_tmp2, "s", 0);
360           if (s_tmp)
361             {
362               sig_s = gcry_sexp_nth_buffer (s_tmp, 1, &sig_s_len);
363               gcry_sexp_release (s_tmp);
364             }
365         }
366     }
367   gcry_sexp_release (s_tmp2); s_tmp2 = NULL;
368
369   if (!sig_r || !sig_s)
370     fail ("gcry_pk_sign failed for test %d: %s", testno, "r or s missing");
371   else
372     {
373       sig_rs_string = xmalloc (2*(sig_r_len + sig_s_len)+1);
374       p = sig_rs_string;
375       *p = 0;
376       for (i=0; i < sig_r_len; i++, p += 2)
377         snprintf (p, 3, "%02x", sig_r[i]);
378       for (i=0; i < sig_s_len; i++, p += 2)
379         snprintf (p, 3, "%02x", sig_s[i]);
380       if (strcmp (sig_rs_string, sig))
381         {
382           fail ("gcry_pk_sign failed for test %d: %s",
383                 testno, "wrong value returned");
384           show ("  expected: '%s'", sig);
385           show ("       got: '%s'", sig_rs_string);
386         }
387     }
388
389   if (!no_verify)
390     if ((err = gcry_pk_verify (s_sig, s_msg, s_pk)))
391       fail ("gcry_pk_verify failed for test %d: %s",
392             testno, gpg_strerror (err));
393
394
395  leave:
396   gcry_sexp_release (s_sig);
397   gcry_sexp_release (s_sk);
398   gcry_sexp_release (s_pk);
399   gcry_sexp_release (s_msg);
400   xfree (buffer);
401   xfree (buffer2);
402   xfree (sig_r);
403   xfree (sig_s);
404   xfree (sig_rs_string);
405 }
406
407
408 static void
409 check_ed25519 (const char *fname)
410 {
411   FILE *fp;
412   int lineno, ntests;
413   char *line;
414   int testno;
415   char *sk, *pk, *msg, *sig;
416
417   show ("Checking Ed25519.\n");
418
419   fp = fopen (fname, "r");
420   if (!fp)
421     die ("error opening '%s': %s\n", fname, strerror (errno));
422
423   testno = 0;
424   sk = pk = msg = sig = NULL;
425   lineno = ntests = 0;
426   while ((line = read_textline (fp, &lineno)))
427     {
428       if (!strncmp (line, "TST:", 4))
429         testno = atoi (line+4);
430       else if (!strncmp (line, "SK:", 3))
431         copy_data (&sk, line, lineno);
432       else if (!strncmp (line, "PK:", 3))
433         copy_data (&pk, line, lineno);
434       else if (!strncmp (line, "MSG:", 4))
435         copy_data (&msg, line, lineno);
436       else if (!strncmp (line, "SIG:", 4))
437         copy_data (&sig, line, lineno);
438       else
439         fail ("unknown tag at input line %d", lineno);
440
441       xfree (line);
442       if (testno && sk && pk && msg && sig)
443         {
444           hexdowncase (sig);
445           one_test (testno, sk, pk, msg, sig);
446           ntests++;
447           if (!(ntests % 256))
448             show_note ("%d of %d tests done\n", ntests, N_TESTS);
449           xfree (pk);  pk = NULL;
450           xfree (sk);  sk = NULL;
451           xfree (msg); msg = NULL;
452           xfree (sig); sig = NULL;
453         }
454
455     }
456   xfree (pk);
457   xfree (sk);
458   xfree (msg);
459   xfree (sig);
460
461   if (ntests != N_TESTS && !custom_data_file)
462     fail ("did %d tests but expected %d", ntests, N_TESTS);
463   else if ((ntests % 256))
464     show_note ("%d tests done\n", ntests);
465
466   fclose (fp);
467 }
468
469
470 int
471 main (int argc, char **argv)
472 {
473   int last_argc = -1;
474   char *fname = NULL;
475
476   if (argc)
477     { argc--; argv++; }
478
479   while (argc && last_argc != argc )
480     {
481       last_argc = argc;
482       if (!strcmp (*argv, "--"))
483         {
484           argc--; argv++;
485           break;
486         }
487       else if (!strcmp (*argv, "--help"))
488         {
489           fputs ("usage: " PGM " [options]\n"
490                  "Options:\n"
491                  "  --verbose       print timings etc.\n"
492                  "  --debug         flyswatter\n"
493                  "  --sign-with-pk  also use the public key for signing\n"
494                  "  --no-verify     skip the verify test\n"
495                  "  --data FNAME    take test data from file FNAME\n",
496                  stdout);
497           exit (0);
498         }
499       else if (!strcmp (*argv, "--verbose"))
500         {
501           verbose++;
502           argc--; argv++;
503         }
504       else if (!strcmp (*argv, "--debug"))
505         {
506           verbose += 2;
507           debug++;
508           argc--; argv++;
509         }
510       else if (!strcmp (*argv, "--sign-with-pk"))
511         {
512           sign_with_pk = 1;
513           argc--; argv++;
514         }
515       else if (!strcmp (*argv, "--no-verify"))
516         {
517           no_verify = 1;
518           argc--; argv++;
519         }
520       else if (!strcmp (*argv, "--data"))
521         {
522           argc--; argv++;
523           if (argc)
524             {
525               xfree (fname);
526               fname = xstrdup (*argv);
527               argc--; argv++;
528             }
529         }
530       else if (!strncmp (*argv, "--", 2))
531         die ("unknown option '%s'", *argv);
532
533     }
534
535   if (!fname)
536     fname = prepend_srcdir ("t-ed25519.inp");
537   else
538     custom_data_file = 1;
539
540   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
541   if (!gcry_check_version (GCRYPT_VERSION))
542     die ("version mismatch\n");
543   if (debug)
544     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0);
545   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
546   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
547
548   start_timer ();
549   check_ed25519 (fname);
550   stop_timer ();
551
552   xfree (fname);
553
554   show ("All tests completed in %s.  Errors: %d\n",
555         elapsed_time (), error_count);
556   return !!error_count;
557 }