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