Line data Source code
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 0 : die (const char *format, ...)
60 : {
61 : va_list arg_ptr ;
62 :
63 0 : fflush (stdout);
64 0 : fprintf (stderr, "%s: ", PGM);
65 0 : va_start( arg_ptr, format ) ;
66 0 : vfprintf (stderr, format, arg_ptr );
67 0 : va_end(arg_ptr);
68 0 : if (*format && format[strlen(format)-1] != '\n')
69 0 : putc ('\n', stderr);
70 0 : exit (1);
71 : }
72 :
73 : static void
74 0 : fail (const char *format, ...)
75 : {
76 : va_list arg_ptr;
77 :
78 0 : fflush (stdout);
79 0 : fprintf (stderr, "%s: ", PGM);
80 : /* if (wherestr) */
81 : /* fprintf (stderr, "%s: ", wherestr); */
82 0 : va_start (arg_ptr, format);
83 0 : vfprintf (stderr, format, arg_ptr);
84 0 : va_end (arg_ptr);
85 0 : if (*format && format[strlen(format)-1] != '\n')
86 0 : putc ('\n', stderr);
87 0 : error_count++;
88 0 : if (error_count >= 50)
89 0 : die ("stopped after 50 errors.");
90 0 : }
91 :
92 : static void
93 2 : show (const char *format, ...)
94 : {
95 : va_list arg_ptr;
96 :
97 2 : if (!verbose)
98 4 : return;
99 0 : fprintf (stderr, "%s: ", PGM);
100 0 : va_start (arg_ptr, format);
101 0 : vfprintf (stderr, format, arg_ptr);
102 0 : if (*format && format[strlen(format)-1] != '\n')
103 0 : putc ('\n', stderr);
104 0 : va_end (arg_ptr);
105 : }
106 :
107 :
108 : static void
109 5 : show_note (const char *format, ...)
110 : {
111 : va_list arg_ptr;
112 :
113 5 : if (!verbose && getenv ("srcdir"))
114 5 : fputs (" ", stderr); /* To align above "PASS: ". */
115 : else
116 0 : fprintf (stderr, "%s: ", PGM);
117 5 : va_start (arg_ptr, format);
118 5 : vfprintf (stderr, format, arg_ptr);
119 5 : if (*format && format[strlen(format)-1] != '\n')
120 0 : putc ('\n', stderr);
121 5 : va_end (arg_ptr);
122 5 : }
123 :
124 :
125 : static void
126 0 : show_sexp (const char *prefix, gcry_sexp_t a)
127 : {
128 : char *buf;
129 : size_t size;
130 :
131 0 : fprintf (stderr, "%s: ", PGM);
132 0 : if (prefix)
133 0 : fputs (prefix, stderr);
134 0 : size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
135 0 : buf = xmalloc (size);
136 :
137 0 : gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
138 0 : fprintf (stderr, "%.*s", (int)size, buf);
139 0 : gcry_free (buf);
140 0 : }
141 :
142 :
143 : /* Prepend FNAME with the srcdir environment variable's value and
144 : retrun an allocated filename. */
145 : char *
146 1 : prepend_srcdir (const char *fname)
147 : {
148 : static const char *srcdir;
149 : char *result;
150 :
151 1 : if (!srcdir && !(srcdir = getenv ("srcdir")))
152 0 : srcdir = ".";
153 :
154 1 : result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
155 1 : strcpy (result, srcdir);
156 1 : strcat (result, "/");
157 1 : strcat (result, fname);
158 1 : 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 6173 : read_textline (FILE *fp, int *lineno)
166 : {
167 : char line[4096];
168 : char *p;
169 :
170 : do
171 : {
172 6173 : if (!fgets (line, sizeof line, fp))
173 : {
174 1 : if (feof (fp))
175 1 : return NULL;
176 0 : die ("error reading input line: %s\n", strerror (errno));
177 : }
178 6172 : ++*lineno;
179 6172 : p = strchr (line, '\n');
180 6172 : if (!p)
181 0 : die ("input line %d not terminated or too long\n", *lineno);
182 6172 : *p = 0;
183 6172 : for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
184 0 : *p = 0;
185 : }
186 6172 : while (!*line || *line == '#');
187 : /* if (debug) */
188 : /* show ("read line: '%s'\n", line); */
189 5130 : 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 4104 : copy_data (char **buffer, const char *line, int lineno)
197 : {
198 : const char *s;
199 :
200 4104 : xfree (*buffer);
201 4104 : *buffer = NULL;
202 :
203 4104 : s = strchr (line, ':');
204 4104 : if (!s)
205 : {
206 0 : fail ("syntax error at input line %d", lineno);
207 4104 : return;
208 : }
209 4104 : for (s++; my_isascii (*s) && isspace (*s); s++)
210 : ;
211 4104 : *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 3078 : hex2buffer (const char *string, size_t *r_length)
222 : {
223 : const char *s;
224 : unsigned char *buffer;
225 : size_t length;
226 :
227 3078 : buffer = xmalloc (strlen(string)/2+1);
228 3078 : length = 0;
229 592552 : for (s=string; *s; s +=2 )
230 : {
231 589474 : if (!hexdigitp (s) || !hexdigitp (s+1))
232 0 : return NULL; /* Invalid hex digits. */
233 589474 : ((unsigned char*)buffer)[length++] = xtoi_2 (s);
234 : }
235 3078 : *r_length = length;
236 3078 : return buffer;
237 : }
238 :
239 :
240 : static void
241 1026 : hexdowncase (char *string)
242 : {
243 : char *p;
244 :
245 132354 : for (p=string; *p; p++)
246 131328 : if (my_isascii (*p))
247 131328 : *p = tolower (*p);
248 1026 : }
249 :
250 :
251 : static void
252 1026 : 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 1026 : void *buffer = NULL;
259 1026 : void *buffer2 = NULL;
260 : size_t buflen, buflen2;
261 : gcry_sexp_t s_tmp, s_tmp2;
262 1026 : gcry_sexp_t s_sk = NULL;
263 1026 : gcry_sexp_t s_pk = NULL;
264 1026 : gcry_sexp_t s_msg= NULL;
265 1026 : gcry_sexp_t s_sig= NULL;
266 1026 : unsigned char *sig_r = NULL;
267 1026 : unsigned char *sig_s = NULL;
268 1026 : char *sig_rs_string = NULL;
269 : size_t sig_r_len, sig_s_len;
270 :
271 1026 : if (verbose > 1)
272 0 : show ("Running test %d\n", testno);
273 :
274 1026 : if (!(buffer = hex2buffer (sk, &buflen)))
275 : {
276 0 : fail ("error building s-exp for test %d, %s: %s",
277 : testno, "sk", "invalid hex string");
278 0 : goto leave;
279 : }
280 1026 : if (!(buffer2 = hex2buffer (pk, &buflen2)))
281 : {
282 0 : fail ("error building s-exp for test %d, %s: %s",
283 : testno, "pk", "invalid hex string");
284 0 : goto leave;
285 : }
286 1026 : if (sign_with_pk)
287 0 : 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 1026 : 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 1026 : if (err)
305 : {
306 0 : fail ("error building s-exp for test %d, %s: %s",
307 : testno, "sk", gpg_strerror (err));
308 0 : goto leave;
309 : }
310 :
311 1026 : 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 0 : fail ("error building s-exp for test %d, %s: %s",
319 : testno, "pk", gpg_strerror (err));
320 0 : goto leave;
321 : }
322 :
323 1026 : xfree (buffer);
324 1026 : if (!(buffer = hex2buffer (msg, &buflen)))
325 : {
326 0 : fail ("error building s-exp for test %d, %s: %s",
327 : testno, "msg", "invalid hex string");
328 0 : goto leave;
329 : }
330 1026 : 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 0 : fail ("error building s-exp for test %d, %s: %s",
337 : testno, "msg", gpg_strerror (err));
338 0 : goto leave;
339 : }
340 :
341 1026 : if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk)))
342 0 : fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err));
343 1026 : if (debug)
344 0 : show_sexp ("sig=", s_sig);
345 :
346 1026 : s_tmp2 = NULL;
347 1026 : s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
348 1026 : if (s_tmp)
349 : {
350 1026 : s_tmp2 = s_tmp;
351 1026 : s_tmp = gcry_sexp_find_token (s_tmp2, "eddsa", 0);
352 1026 : if (s_tmp)
353 : {
354 1026 : gcry_sexp_release (s_tmp2);
355 1026 : s_tmp2 = s_tmp;
356 1026 : s_tmp = gcry_sexp_find_token (s_tmp2, "r", 0);
357 1026 : if (s_tmp)
358 : {
359 1026 : sig_r = gcry_sexp_nth_buffer (s_tmp, 1, &sig_r_len);
360 1026 : gcry_sexp_release (s_tmp);
361 : }
362 1026 : s_tmp = gcry_sexp_find_token (s_tmp2, "s", 0);
363 1026 : if (s_tmp)
364 : {
365 1026 : sig_s = gcry_sexp_nth_buffer (s_tmp, 1, &sig_s_len);
366 1026 : gcry_sexp_release (s_tmp);
367 : }
368 : }
369 : }
370 1026 : gcry_sexp_release (s_tmp2); s_tmp2 = NULL;
371 :
372 1026 : if (!sig_r || !sig_s)
373 0 : fail ("gcry_pk_sign failed for test %d: %s", testno, "r or s missing");
374 : else
375 : {
376 1026 : sig_rs_string = xmalloc (2*(sig_r_len + sig_s_len)+1);
377 1026 : p = sig_rs_string;
378 1026 : *p = 0;
379 33858 : for (i=0; i < sig_r_len; i++, p += 2)
380 32832 : snprintf (p, 3, "%02x", sig_r[i]);
381 33858 : for (i=0; i < sig_s_len; i++, p += 2)
382 32832 : snprintf (p, 3, "%02x", sig_s[i]);
383 1026 : if (strcmp (sig_rs_string, sig))
384 : {
385 0 : fail ("gcry_pk_sign failed for test %d: %s",
386 : testno, "wrong value returned");
387 0 : show (" expected: '%s'", sig);
388 0 : show (" got: '%s'", sig_rs_string);
389 : }
390 : }
391 :
392 1026 : if (!no_verify)
393 1026 : if ((err = gcry_pk_verify (s_sig, s_msg, s_pk)))
394 0 : fail ("gcry_pk_verify failed for test %d: %s",
395 : testno, gpg_strerror (err));
396 :
397 :
398 : leave:
399 1026 : gcry_sexp_release (s_sig);
400 1026 : gcry_sexp_release (s_sk);
401 1026 : gcry_sexp_release (s_pk);
402 1026 : gcry_sexp_release (s_msg);
403 1026 : xfree (buffer);
404 1026 : xfree (buffer2);
405 1026 : xfree (sig_r);
406 1026 : xfree (sig_s);
407 1026 : xfree (sig_rs_string);
408 1026 : }
409 :
410 :
411 : static void
412 1 : 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 1 : show ("Checking Ed25519.\n");
421 :
422 1 : fp = fopen (fname, "r");
423 1 : if (!fp)
424 0 : die ("error opening '%s': %s\n", fname, strerror (errno));
425 :
426 1 : testno = 0;
427 1 : sk = pk = msg = sig = NULL;
428 1 : lineno = ntests = 0;
429 5132 : while ((line = read_textline (fp, &lineno)))
430 : {
431 5130 : if (!strncmp (line, "TST:", 4))
432 1026 : testno = atoi (line+4);
433 4104 : else if (!strncmp (line, "SK:", 3))
434 1026 : copy_data (&sk, line, lineno);
435 3078 : else if (!strncmp (line, "PK:", 3))
436 1026 : copy_data (&pk, line, lineno);
437 2052 : else if (!strncmp (line, "MSG:", 4))
438 1026 : copy_data (&msg, line, lineno);
439 1026 : else if (!strncmp (line, "SIG:", 4))
440 1026 : copy_data (&sig, line, lineno);
441 : else
442 0 : fail ("unknown tag at input line %d", lineno);
443 :
444 5130 : xfree (line);
445 5130 : if (testno && sk && pk && msg && sig)
446 : {
447 1026 : hexdowncase (sig);
448 1026 : one_test (testno, sk, pk, msg, sig);
449 1026 : ntests++;
450 1026 : if (!(ntests % 256))
451 4 : show_note ("%d of %d tests done\n", ntests, N_TESTS);
452 1026 : xfree (pk); pk = NULL;
453 1026 : xfree (sk); sk = NULL;
454 1026 : xfree (msg); msg = NULL;
455 1026 : xfree (sig); sig = NULL;
456 : }
457 :
458 : }
459 1 : xfree (pk);
460 1 : xfree (sk);
461 1 : xfree (msg);
462 1 : xfree (sig);
463 :
464 1 : if (ntests != N_TESTS && !custom_data_file)
465 0 : fail ("did %d tests but expected %d", ntests, N_TESTS);
466 1 : else if ((ntests % 256))
467 1 : show_note ("%d tests done\n", ntests);
468 :
469 1 : fclose (fp);
470 1 : }
471 :
472 :
473 : int
474 1 : main (int argc, char **argv)
475 : {
476 1 : int last_argc = -1;
477 1 : char *fname = NULL;
478 :
479 1 : if (argc)
480 1 : { argc--; argv++; }
481 :
482 2 : while (argc && last_argc != argc )
483 : {
484 0 : last_argc = argc;
485 0 : if (!strcmp (*argv, "--"))
486 : {
487 0 : argc--; argv++;
488 0 : break;
489 : }
490 0 : else if (!strcmp (*argv, "--help"))
491 : {
492 0 : 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 0 : exit (0);
501 : }
502 0 : else if (!strcmp (*argv, "--verbose"))
503 : {
504 0 : verbose++;
505 0 : argc--; argv++;
506 : }
507 0 : else if (!strcmp (*argv, "--debug"))
508 : {
509 0 : verbose += 2;
510 0 : debug++;
511 0 : argc--; argv++;
512 : }
513 0 : else if (!strcmp (*argv, "--sign-with-pk"))
514 : {
515 0 : sign_with_pk = 1;
516 0 : argc--; argv++;
517 : }
518 0 : else if (!strcmp (*argv, "--no-verify"))
519 : {
520 0 : no_verify = 1;
521 0 : argc--; argv++;
522 : }
523 0 : else if (!strcmp (*argv, "--data"))
524 : {
525 0 : argc--; argv++;
526 0 : if (argc)
527 : {
528 0 : xfree (fname);
529 0 : fname = xstrdup (*argv);
530 0 : argc--; argv++;
531 : }
532 : }
533 0 : else if (!strncmp (*argv, "--", 2))
534 0 : die ("unknown option '%s'", *argv);
535 :
536 : }
537 :
538 1 : if (!fname)
539 1 : fname = prepend_srcdir ("t-ed25519.inp");
540 : else
541 0 : custom_data_file = 1;
542 :
543 1 : gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
544 1 : if (!gcry_check_version (GCRYPT_VERSION))
545 0 : die ("version mismatch\n");
546 1 : if (debug)
547 0 : gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0);
548 1 : gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
549 1 : gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
550 :
551 1 : start_timer ();
552 1 : check_ed25519 (fname);
553 1 : stop_timer ();
554 :
555 1 : xfree (fname);
556 :
557 1 : show ("All tests completed in %s. Errors: %d\n",
558 : elapsed_time (1), error_count);
559 1 : return !!error_count;
560 : }
|