tests: Add option to time the S2K function.
[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 1026
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                            "  (flags eddsa)"
292                            "  (q %b)"
293                            "  (d %b)))",
294                            (int)buflen2, buffer2,
295                            (int)buflen, buffer);
296   else
297     err = gcry_sexp_build (&s_sk, NULL,
298                            "(private-key"
299                            " (ecc"
300                            "  (curve \"Ed25519\")"
301                            "  (flags eddsa)"
302                            "  (d %b)))",
303                            (int)buflen, buffer);
304   if (err)
305     {
306       fail ("error building s-exp for test %d, %s: %s",
307             testno, "sk", gpg_strerror (err));
308       goto leave;
309     }
310
311   if ((err = gcry_sexp_build (&s_pk, NULL,
312                               "(public-key"
313                               " (ecc"
314                               "  (curve \"Ed25519\")"
315                               "  (flags eddsa)"
316                               "  (q %b)))",  (int)buflen2, buffer2)))
317     {
318       fail ("error building s-exp for test %d, %s: %s",
319             testno, "pk", gpg_strerror (err));
320       goto leave;
321     }
322
323   xfree (buffer);
324   if (!(buffer = hex2buffer (msg, &buflen)))
325     {
326       fail ("error building s-exp for test %d, %s: %s",
327             testno, "msg", "invalid hex string");
328       goto leave;
329     }
330   if ((err = gcry_sexp_build (&s_msg, NULL,
331                               "(data"
332                               " (flags eddsa)"
333                               " (hash-algo sha512)"
334                               " (value %b))",  (int)buflen, buffer)))
335     {
336       fail ("error building s-exp for test %d, %s: %s",
337             testno, "msg", gpg_strerror (err));
338       goto leave;
339     }
340
341   if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk)))
342     fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err));
343   if (debug)
344     show_sexp ("sig=", s_sig);
345
346   s_tmp2 = NULL;
347   s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
348   if (s_tmp)
349     {
350       s_tmp2 = s_tmp;
351       s_tmp = gcry_sexp_find_token (s_tmp2, "eddsa", 0);
352       if (s_tmp)
353         {
354           gcry_sexp_release (s_tmp2);
355           s_tmp2 = s_tmp;
356           s_tmp = gcry_sexp_find_token (s_tmp2, "r", 0);
357           if (s_tmp)
358             {
359               sig_r = gcry_sexp_nth_buffer (s_tmp, 1, &sig_r_len);
360               gcry_sexp_release (s_tmp);
361             }
362           s_tmp = gcry_sexp_find_token (s_tmp2, "s", 0);
363           if (s_tmp)
364             {
365               sig_s = gcry_sexp_nth_buffer (s_tmp, 1, &sig_s_len);
366               gcry_sexp_release (s_tmp);
367             }
368         }
369     }
370   gcry_sexp_release (s_tmp2); s_tmp2 = NULL;
371
372   if (!sig_r || !sig_s)
373     fail ("gcry_pk_sign failed for test %d: %s", testno, "r or s missing");
374   else
375     {
376       sig_rs_string = xmalloc (2*(sig_r_len + sig_s_len)+1);
377       p = sig_rs_string;
378       *p = 0;
379       for (i=0; i < sig_r_len; i++, p += 2)
380         snprintf (p, 3, "%02x", sig_r[i]);
381       for (i=0; i < sig_s_len; i++, p += 2)
382         snprintf (p, 3, "%02x", sig_s[i]);
383       if (strcmp (sig_rs_string, sig))
384         {
385           fail ("gcry_pk_sign failed for test %d: %s",
386                 testno, "wrong value returned");
387           show ("  expected: '%s'", sig);
388           show ("       got: '%s'", sig_rs_string);
389         }
390     }
391
392   if (!no_verify)
393     if ((err = gcry_pk_verify (s_sig, s_msg, s_pk)))
394       fail ("gcry_pk_verify failed for test %d: %s",
395             testno, gpg_strerror (err));
396
397
398  leave:
399   gcry_sexp_release (s_sig);
400   gcry_sexp_release (s_sk);
401   gcry_sexp_release (s_pk);
402   gcry_sexp_release (s_msg);
403   xfree (buffer);
404   xfree (buffer2);
405   xfree (sig_r);
406   xfree (sig_s);
407   xfree (sig_rs_string);
408 }
409
410
411 static void
412 check_ed25519 (const char *fname)
413 {
414   FILE *fp;
415   int lineno, ntests;
416   char *line;
417   int testno;
418   char *sk, *pk, *msg, *sig;
419
420   show ("Checking Ed25519.\n");
421
422   fp = fopen (fname, "r");
423   if (!fp)
424     die ("error opening '%s': %s\n", fname, strerror (errno));
425
426   testno = 0;
427   sk = pk = msg = sig = NULL;
428   lineno = ntests = 0;
429   while ((line = read_textline (fp, &lineno)))
430     {
431       if (!strncmp (line, "TST:", 4))
432         testno = atoi (line+4);
433       else if (!strncmp (line, "SK:", 3))
434         copy_data (&sk, line, lineno);
435       else if (!strncmp (line, "PK:", 3))
436         copy_data (&pk, line, lineno);
437       else if (!strncmp (line, "MSG:", 4))
438         copy_data (&msg, line, lineno);
439       else if (!strncmp (line, "SIG:", 4))
440         copy_data (&sig, line, lineno);
441       else
442         fail ("unknown tag at input line %d", lineno);
443
444       xfree (line);
445       if (testno && sk && pk && msg && sig)
446         {
447           hexdowncase (sig);
448           one_test (testno, sk, pk, msg, sig);
449           ntests++;
450           if (!(ntests % 256))
451             show_note ("%d of %d tests done\n", ntests, N_TESTS);
452           xfree (pk);  pk = NULL;
453           xfree (sk);  sk = NULL;
454           xfree (msg); msg = NULL;
455           xfree (sig); sig = NULL;
456         }
457
458     }
459   xfree (pk);
460   xfree (sk);
461   xfree (msg);
462   xfree (sig);
463
464   if (ntests != N_TESTS && !custom_data_file)
465     fail ("did %d tests but expected %d", ntests, N_TESTS);
466   else if ((ntests % 256))
467     show_note ("%d tests done\n", ntests);
468
469   fclose (fp);
470 }
471
472
473 int
474 main (int argc, char **argv)
475 {
476   int last_argc = -1;
477   char *fname = NULL;
478
479   if (argc)
480     { argc--; argv++; }
481
482   while (argc && last_argc != argc )
483     {
484       last_argc = argc;
485       if (!strcmp (*argv, "--"))
486         {
487           argc--; argv++;
488           break;
489         }
490       else if (!strcmp (*argv, "--help"))
491         {
492           fputs ("usage: " PGM " [options]\n"
493                  "Options:\n"
494                  "  --verbose       print timings etc.\n"
495                  "  --debug         flyswatter\n"
496                  "  --sign-with-pk  also use the public key for signing\n"
497                  "  --no-verify     skip the verify test\n"
498                  "  --data FNAME    take test data from file FNAME\n",
499                  stdout);
500           exit (0);
501         }
502       else if (!strcmp (*argv, "--verbose"))
503         {
504           verbose++;
505           argc--; argv++;
506         }
507       else if (!strcmp (*argv, "--debug"))
508         {
509           verbose += 2;
510           debug++;
511           argc--; argv++;
512         }
513       else if (!strcmp (*argv, "--sign-with-pk"))
514         {
515           sign_with_pk = 1;
516           argc--; argv++;
517         }
518       else if (!strcmp (*argv, "--no-verify"))
519         {
520           no_verify = 1;
521           argc--; argv++;
522         }
523       else if (!strcmp (*argv, "--data"))
524         {
525           argc--; argv++;
526           if (argc)
527             {
528               xfree (fname);
529               fname = xstrdup (*argv);
530               argc--; argv++;
531             }
532         }
533       else if (!strncmp (*argv, "--", 2))
534         die ("unknown option '%s'", *argv);
535
536     }
537
538   if (!fname)
539     fname = prepend_srcdir ("t-ed25519.inp");
540   else
541     custom_data_file = 1;
542
543   gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
544   if (!gcry_check_version (GCRYPT_VERSION))
545     die ("version mismatch\n");
546   if (debug)
547     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0);
548   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
549   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
550
551   start_timer ();
552   check_ed25519 (fname);
553   stop_timer ();
554
555   xfree (fname);
556
557   show ("All tests completed in %s.  Errors: %d\n",
558         elapsed_time (1), error_count);
559   return !!error_count;
560 }