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