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 "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 5 : show_note (const char *format, ...)
43 : {
44 : va_list arg_ptr;
45 :
46 5 : if (!verbose && getenv ("srcdir"))
47 5 : fputs (" ", stderr); /* To align above "PASS: ". */
48 : else
49 0 : fprintf (stderr, "%s: ", PGM);
50 5 : va_start (arg_ptr, format);
51 5 : vfprintf (stderr, format, arg_ptr);
52 5 : if (*format && format[strlen(format)-1] != '\n')
53 0 : putc ('\n', stderr);
54 5 : va_end (arg_ptr);
55 5 : }
56 :
57 :
58 : static void
59 0 : show_sexp (const char *prefix, gcry_sexp_t a)
60 : {
61 : char *buf;
62 : size_t size;
63 :
64 0 : fprintf (stderr, "%s: ", PGM);
65 0 : if (prefix)
66 0 : fputs (prefix, stderr);
67 0 : size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0);
68 0 : buf = xmalloc (size);
69 :
70 0 : gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size);
71 0 : fprintf (stderr, "%.*s", (int)size, buf);
72 0 : gcry_free (buf);
73 0 : }
74 :
75 :
76 : /* Prepend FNAME with the srcdir environment variable's value and
77 : retrun an allocated filename. */
78 : char *
79 1 : prepend_srcdir (const char *fname)
80 : {
81 : static const char *srcdir;
82 : char *result;
83 :
84 1 : if (!srcdir && !(srcdir = getenv ("srcdir")))
85 0 : srcdir = ".";
86 :
87 1 : result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
88 1 : strcpy (result, srcdir);
89 1 : strcat (result, "/");
90 1 : strcat (result, fname);
91 1 : 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 6173 : read_textline (FILE *fp, int *lineno)
99 : {
100 : char line[4096];
101 : char *p;
102 :
103 : do
104 : {
105 6173 : if (!fgets (line, sizeof line, fp))
106 : {
107 1 : if (feof (fp))
108 1 : return NULL;
109 0 : die ("error reading input line: %s\n", strerror (errno));
110 : }
111 6172 : ++*lineno;
112 6172 : p = strchr (line, '\n');
113 6172 : if (!p)
114 0 : die ("input line %d not terminated or too long\n", *lineno);
115 6172 : *p = 0;
116 6172 : for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
117 0 : *p = 0;
118 : }
119 6172 : while (!*line || *line == '#');
120 : /* if (debug) */
121 : /* info ("read line: '%s'\n", line); */
122 5130 : 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 4104 : copy_data (char **buffer, const char *line, int lineno)
130 : {
131 : const char *s;
132 :
133 4104 : xfree (*buffer);
134 4104 : *buffer = NULL;
135 :
136 4104 : s = strchr (line, ':');
137 4104 : if (!s)
138 : {
139 0 : fail ("syntax error at input line %d", lineno);
140 0 : return;
141 : }
142 4104 : for (s++; my_isascii (*s) && isspace (*s); s++)
143 : ;
144 4104 : *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 3078 : hex2buffer (const char *string, size_t *r_length)
155 : {
156 : const char *s;
157 : unsigned char *buffer;
158 : size_t length;
159 :
160 3078 : buffer = xmalloc (strlen(string)/2+1);
161 3078 : length = 0;
162 592552 : for (s=string; *s; s +=2 )
163 : {
164 589474 : if (!hexdigitp (s) || !hexdigitp (s+1))
165 0 : return NULL; /* Invalid hex digits. */
166 589474 : ((unsigned char*)buffer)[length++] = xtoi_2 (s);
167 : }
168 3078 : *r_length = length;
169 3078 : return buffer;
170 : }
171 :
172 :
173 : static void
174 1026 : hexdowncase (char *string)
175 : {
176 : char *p;
177 :
178 132354 : for (p=string; *p; p++)
179 131328 : if (my_isascii (*p))
180 131328 : *p = tolower (*p);
181 1026 : }
182 :
183 :
184 : static void
185 1026 : 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 1026 : void *buffer = NULL;
192 1026 : void *buffer2 = NULL;
193 : size_t buflen, buflen2;
194 : gcry_sexp_t s_tmp, s_tmp2;
195 1026 : gcry_sexp_t s_sk = NULL;
196 1026 : gcry_sexp_t s_pk = NULL;
197 1026 : gcry_sexp_t s_msg= NULL;
198 1026 : gcry_sexp_t s_sig= NULL;
199 1026 : unsigned char *sig_r = NULL;
200 1026 : unsigned char *sig_s = NULL;
201 1026 : char *sig_rs_string = NULL;
202 : size_t sig_r_len, sig_s_len;
203 :
204 1026 : if (verbose > 1)
205 0 : info ("Running test %d\n", testno);
206 :
207 1026 : if (!(buffer = hex2buffer (sk, &buflen)))
208 : {
209 0 : fail ("error building s-exp for test %d, %s: %s",
210 : testno, "sk", "invalid hex string");
211 0 : goto leave;
212 : }
213 1026 : if (!(buffer2 = hex2buffer (pk, &buflen2)))
214 : {
215 0 : fail ("error building s-exp for test %d, %s: %s",
216 : testno, "pk", "invalid hex string");
217 0 : goto leave;
218 : }
219 1026 : if (sign_with_pk)
220 0 : 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 1026 : 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 1026 : if (err)
238 : {
239 0 : fail ("error building s-exp for test %d, %s: %s",
240 : testno, "sk", gpg_strerror (err));
241 0 : goto leave;
242 : }
243 :
244 1026 : 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 0 : fail ("error building s-exp for test %d, %s: %s",
252 : testno, "pk", gpg_strerror (err));
253 0 : goto leave;
254 : }
255 :
256 1026 : xfree (buffer);
257 1026 : if (!(buffer = hex2buffer (msg, &buflen)))
258 : {
259 0 : fail ("error building s-exp for test %d, %s: %s",
260 : testno, "msg", "invalid hex string");
261 0 : goto leave;
262 : }
263 1026 : 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 0 : fail ("error building s-exp for test %d, %s: %s",
270 : testno, "msg", gpg_strerror (err));
271 0 : goto leave;
272 : }
273 :
274 1026 : if ((err = gcry_pk_sign (&s_sig, s_msg, s_sk)))
275 0 : fail ("gcry_pk_sign failed for test %d: %s", testno, gpg_strerror (err));
276 1026 : if (debug)
277 0 : show_sexp ("sig=", s_sig);
278 :
279 1026 : s_tmp2 = NULL;
280 1026 : s_tmp = gcry_sexp_find_token (s_sig, "sig-val", 0);
281 1026 : if (s_tmp)
282 : {
283 1026 : s_tmp2 = s_tmp;
284 1026 : s_tmp = gcry_sexp_find_token (s_tmp2, "eddsa", 0);
285 1026 : if (s_tmp)
286 : {
287 1026 : gcry_sexp_release (s_tmp2);
288 1026 : s_tmp2 = s_tmp;
289 1026 : s_tmp = gcry_sexp_find_token (s_tmp2, "r", 0);
290 1026 : if (s_tmp)
291 : {
292 1026 : sig_r = gcry_sexp_nth_buffer (s_tmp, 1, &sig_r_len);
293 1026 : gcry_sexp_release (s_tmp);
294 : }
295 1026 : s_tmp = gcry_sexp_find_token (s_tmp2, "s", 0);
296 1026 : if (s_tmp)
297 : {
298 1026 : sig_s = gcry_sexp_nth_buffer (s_tmp, 1, &sig_s_len);
299 1026 : gcry_sexp_release (s_tmp);
300 : }
301 : }
302 : }
303 1026 : gcry_sexp_release (s_tmp2); s_tmp2 = NULL;
304 :
305 1026 : if (!sig_r || !sig_s)
306 0 : fail ("gcry_pk_sign failed for test %d: %s", testno, "r or s missing");
307 : else
308 : {
309 1026 : sig_rs_string = xmalloc (2*(sig_r_len + sig_s_len)+1);
310 1026 : p = sig_rs_string;
311 1026 : *p = 0;
312 33858 : for (i=0; i < sig_r_len; i++, p += 2)
313 32832 : snprintf (p, 3, "%02x", sig_r[i]);
314 33858 : for (i=0; i < sig_s_len; i++, p += 2)
315 32832 : snprintf (p, 3, "%02x", sig_s[i]);
316 1026 : if (strcmp (sig_rs_string, sig))
317 : {
318 0 : fail ("gcry_pk_sign failed for test %d: %s",
319 : testno, "wrong value returned");
320 0 : info (" expected: '%s'", sig);
321 0 : info (" got: '%s'", sig_rs_string);
322 : }
323 : }
324 :
325 1026 : if (!no_verify)
326 1026 : if ((err = gcry_pk_verify (s_sig, s_msg, s_pk)))
327 0 : fail ("gcry_pk_verify failed for test %d: %s",
328 : testno, gpg_strerror (err));
329 :
330 :
331 : leave:
332 1026 : gcry_sexp_release (s_sig);
333 1026 : gcry_sexp_release (s_sk);
334 1026 : gcry_sexp_release (s_pk);
335 1026 : gcry_sexp_release (s_msg);
336 1026 : xfree (buffer);
337 1026 : xfree (buffer2);
338 1026 : xfree (sig_r);
339 1026 : xfree (sig_s);
340 1026 : xfree (sig_rs_string);
341 1026 : }
342 :
343 :
344 : static void
345 1 : 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 1 : info ("Checking Ed25519.\n");
354 :
355 1 : fp = fopen (fname, "r");
356 1 : if (!fp)
357 0 : die ("error opening '%s': %s\n", fname, strerror (errno));
358 :
359 1 : testno = 0;
360 1 : sk = pk = msg = sig = NULL;
361 1 : lineno = ntests = 0;
362 5132 : while ((line = read_textline (fp, &lineno)))
363 : {
364 5130 : if (!strncmp (line, "TST:", 4))
365 1026 : testno = atoi (line+4);
366 4104 : else if (!strncmp (line, "SK:", 3))
367 1026 : copy_data (&sk, line, lineno);
368 3078 : else if (!strncmp (line, "PK:", 3))
369 1026 : copy_data (&pk, line, lineno);
370 2052 : else if (!strncmp (line, "MSG:", 4))
371 1026 : copy_data (&msg, line, lineno);
372 1026 : else if (!strncmp (line, "SIG:", 4))
373 1026 : copy_data (&sig, line, lineno);
374 : else
375 0 : fail ("unknown tag at input line %d", lineno);
376 :
377 5130 : xfree (line);
378 5130 : if (testno && sk && pk && msg && sig)
379 : {
380 1026 : hexdowncase (sig);
381 1026 : one_test (testno, sk, pk, msg, sig);
382 1026 : ntests++;
383 1026 : if (!(ntests % 256))
384 4 : show_note ("%d of %d tests done\n", ntests, N_TESTS);
385 1026 : xfree (pk); pk = NULL;
386 1026 : xfree (sk); sk = NULL;
387 1026 : xfree (msg); msg = NULL;
388 1026 : xfree (sig); sig = NULL;
389 : }
390 :
391 : }
392 1 : xfree (pk);
393 1 : xfree (sk);
394 1 : xfree (msg);
395 1 : xfree (sig);
396 :
397 1 : if (ntests != N_TESTS && !custom_data_file)
398 0 : fail ("did %d tests but expected %d", ntests, N_TESTS);
399 1 : else if ((ntests % 256))
400 1 : show_note ("%d tests done\n", ntests);
401 :
402 1 : fclose (fp);
403 1 : }
404 :
405 :
406 : int
407 1 : main (int argc, char **argv)
408 : {
409 1 : int last_argc = -1;
410 1 : char *fname = NULL;
411 :
412 1 : if (argc)
413 1 : { argc--; argv++; }
414 :
415 2 : while (argc && last_argc != argc )
416 : {
417 0 : last_argc = argc;
418 0 : if (!strcmp (*argv, "--"))
419 : {
420 0 : argc--; argv++;
421 0 : break;
422 : }
423 0 : else if (!strcmp (*argv, "--help"))
424 : {
425 0 : 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 0 : exit (0);
434 : }
435 0 : else if (!strcmp (*argv, "--verbose"))
436 : {
437 0 : verbose++;
438 0 : argc--; argv++;
439 : }
440 0 : else if (!strcmp (*argv, "--debug"))
441 : {
442 0 : verbose += 2;
443 0 : debug++;
444 0 : argc--; argv++;
445 : }
446 0 : else if (!strcmp (*argv, "--sign-with-pk"))
447 : {
448 0 : sign_with_pk = 1;
449 0 : argc--; argv++;
450 : }
451 0 : else if (!strcmp (*argv, "--no-verify"))
452 : {
453 0 : no_verify = 1;
454 0 : argc--; argv++;
455 : }
456 0 : else if (!strcmp (*argv, "--data"))
457 : {
458 0 : argc--; argv++;
459 0 : if (argc)
460 : {
461 0 : xfree (fname);
462 0 : fname = xstrdup (*argv);
463 0 : argc--; argv++;
464 : }
465 : }
466 0 : else if (!strncmp (*argv, "--", 2))
467 0 : die ("unknown option '%s'", *argv);
468 :
469 : }
470 :
471 1 : if (!fname)
472 1 : fname = prepend_srcdir ("t-ed25519.inp");
473 : else
474 0 : custom_data_file = 1;
475 :
476 1 : xgcry_control (GCRYCTL_DISABLE_SECMEM, 0);
477 1 : if (!gcry_check_version (GCRYPT_VERSION))
478 0 : die ("version mismatch\n");
479 1 : if (debug)
480 0 : xgcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0);
481 1 : xgcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
482 1 : xgcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
483 :
484 : /* Ed25519 isn't supported in fips mode */
485 1 : if (gcry_fips_mode_active())
486 0 : return 77;
487 :
488 1 : start_timer ();
489 1 : check_ed25519 (fname);
490 1 : stop_timer ();
491 :
492 1 : xfree (fname);
493 :
494 1 : info ("All tests completed in %s. Errors: %d\n",
495 : elapsed_time (1), error_count);
496 1 : return !!error_count;
497 : }
|