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