Line data Source code
1 : /* t-name-value.c - Module test for name-value.c
2 : * Copyright (C) 2016 g10 Code GmbH
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * GnuPG is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * GnuPG 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 General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include <config.h>
21 : #include <stdio.h>
22 : #include <stdlib.h>
23 : #include <errno.h>
24 : #include <assert.h>
25 : #include <unistd.h>
26 : #include <sys/stat.h>
27 :
28 : #include "util.h"
29 : #include "name-value.h"
30 :
31 : static int verbose;
32 : static int private_key_mode;
33 :
34 :
35 : static nvc_t
36 4 : my_nvc_new (void)
37 : {
38 4 : if (private_key_mode)
39 2 : return nvc_new_private_key ();
40 : else
41 2 : return nvc_new ();
42 : }
43 :
44 :
45 : void
46 6 : test_getting_values (nvc_t pk)
47 : {
48 : nve_t e;
49 :
50 6 : e = nvc_lookup (pk, "Comment:");
51 6 : assert (e);
52 :
53 : /* Names are case-insensitive. */
54 6 : e = nvc_lookup (pk, "comment:");
55 6 : assert (e);
56 6 : e = nvc_lookup (pk, "COMMENT:");
57 6 : assert (e);
58 :
59 6 : e = nvc_lookup (pk, "SomeOtherName:");
60 6 : assert (e);
61 6 : }
62 :
63 :
64 : void
65 2 : test_key_extraction (nvc_t pk)
66 : {
67 : gpg_error_t err;
68 : gcry_sexp_t key;
69 :
70 2 : if (private_key_mode)
71 : {
72 1 : err = nvc_get_private_key (pk, &key);
73 1 : assert (err == 0);
74 1 : assert (key);
75 :
76 1 : if (verbose)
77 0 : gcry_sexp_dump (key);
78 :
79 1 : gcry_sexp_release (key);
80 : }
81 : else
82 : {
83 1 : err = nvc_get_private_key (pk, &key);
84 1 : assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY);
85 : }
86 2 : }
87 :
88 :
89 : void
90 2 : test_iteration (nvc_t pk)
91 : {
92 : int i;
93 : nve_t e;
94 :
95 2 : i = 0;
96 10 : for (e = nvc_first (pk); e; e = nve_next (e))
97 8 : i++;
98 2 : assert (i == 4);
99 :
100 2 : i = 0;
101 10 : for (e = nvc_lookup (pk, "Comment:");
102 : e;
103 6 : e = nve_next_value (e, "Comment:"))
104 6 : i++;
105 2 : assert (i == 3);
106 2 : }
107 :
108 :
109 : void
110 2 : test_whitespace (nvc_t pk)
111 : {
112 : nve_t e;
113 :
114 2 : e = nvc_lookup (pk, "One:");
115 2 : assert (e);
116 2 : assert (strcmp (nve_value (e), "WithoutWhitespace") == 0);
117 :
118 2 : e = nvc_lookup (pk, "Two:");
119 2 : assert (e);
120 2 : assert (strcmp (nve_value (e), "With Whitespace") == 0);
121 :
122 2 : e = nvc_lookup (pk, "Three:");
123 2 : assert (e);
124 2 : assert (strcmp (nve_value (e),
125 : "Blank lines in continuations encode newlines.\n"
126 : "Next paragraph.") == 0);
127 2 : }
128 :
129 :
130 : struct
131 : {
132 : char *value;
133 : void (*test_func) (nvc_t);
134 : } tests[] =
135 : {
136 : {
137 : "# This is a comment followed by an empty line\n"
138 : "\n",
139 : NULL,
140 : },
141 : {
142 : "# This is a comment followed by two empty lines, Windows style\r\n"
143 : "\r\n"
144 : "\r\n",
145 : NULL,
146 : },
147 : {
148 : "# Some name,value pairs\n"
149 : "Comment: Some comment.\n"
150 : "SomeOtherName: Some value.\n",
151 : test_getting_values,
152 : },
153 : {
154 : " # Whitespace is preserved as much as possible\r\n"
155 : "Comment:Some comment.\n"
156 : "SomeOtherName: Some value. \n",
157 : test_getting_values,
158 : },
159 : {
160 : "# Values may be continued in the next line as indicated by leading\n"
161 : "# space\n"
162 : "Comment: Some rather long\n"
163 : " comment that is continued in the next line.\n"
164 : "\n"
165 : " Blank lines with or without whitespace are allowed within\n"
166 : " continuations to allow paragraphs.\n"
167 : "SomeOtherName: Some value.\n",
168 : test_getting_values,
169 : },
170 : {
171 : "# Names may be given multiple times forming an array of values\n"
172 : "Comment: Some comment, element 0.\n"
173 : "Comment: Some comment, element 1.\n"
174 : "Comment: Some comment, element 2.\n"
175 : "SomeOtherName: Some value.\n",
176 : test_iteration,
177 : },
178 : {
179 : "# One whitespace at the beginning of a continuation is swallowed.\n"
180 : "One: Without\n"
181 : " Whitespace\n"
182 : "Two: With\n"
183 : " Whitespace\n"
184 : "Three: Blank lines in continuations encode newlines.\n"
185 : "\n"
186 : " Next paragraph.\n",
187 : test_whitespace,
188 : },
189 : {
190 : "Description: Key to sign all GnuPG released tarballs.\n"
191 : " The key is actually stored on a smart card.\n"
192 : "Use-for-ssh: yes\n"
193 : "OpenSSH-cert: long base64 encoded string wrapped so that this\n"
194 : " key file can be easily edited with a standard editor.\n"
195 : "Key: (shadowed-private-key\n"
196 : " (rsa\n"
197 : " (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n"
198 : " 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n"
199 : " 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n"
200 : " 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n"
201 : " 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n"
202 : " 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n"
203 : " F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n"
204 : " 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n"
205 : " E186A02BA2497FDC5D1221#)\n"
206 : " (e #00010001#)\n"
207 : " (shadowed t1-v1\n"
208 : " (#D2760001240102000005000011730000# OPENPGP.1)\n"
209 : " )))\n",
210 : test_key_extraction,
211 : },
212 : };
213 :
214 :
215 : static char *
216 43 : nvc_to_string (nvc_t pk)
217 : {
218 : gpg_error_t err;
219 : char *buf;
220 : size_t len;
221 : estream_t sink;
222 :
223 43 : sink = es_fopenmem (0, "rw");
224 43 : assert (sink);
225 :
226 43 : err = nvc_write (pk, sink);
227 43 : assert (err == 0);
228 :
229 43 : len = es_ftell (sink);
230 43 : buf = xmalloc (len+1);
231 43 : assert (buf);
232 :
233 43 : es_fseek (sink, 0, SEEK_SET);
234 43 : es_read (sink, buf, len, NULL);
235 43 : buf[len] = 0;
236 :
237 43 : es_fclose (sink);
238 43 : return buf;
239 : }
240 :
241 :
242 16 : void dummy_free (void *p) { (void) p; }
243 0 : void *dummy_realloc (void *p, size_t s) { (void) s; return p; }
244 :
245 : void
246 2 : run_tests (void)
247 : {
248 : gpg_error_t err;
249 : nvc_t pk;
250 :
251 : int i;
252 18 : for (i = 0; i < DIM (tests); i++)
253 : {
254 : estream_t source;
255 : char *buf;
256 : size_t len;
257 :
258 16 : len = strlen (tests[i].value);
259 16 : source = es_mopen (tests[i].value, len, len,
260 : 0, dummy_realloc, dummy_free, "r");
261 16 : assert (source);
262 :
263 16 : if (private_key_mode)
264 8 : err = nvc_parse_private_key (&pk, NULL, source);
265 : else
266 8 : err = nvc_parse (&pk, NULL, source);
267 16 : assert (err == 0);
268 16 : assert (pk);
269 :
270 16 : if (verbose)
271 : {
272 0 : err = nvc_write (pk, es_stderr);
273 0 : assert (err == 0);
274 : }
275 :
276 16 : buf = nvc_to_string (pk);
277 16 : assert (memcmp (tests[i].value, buf, len) == 0);
278 :
279 16 : es_fclose (source);
280 16 : xfree (buf);
281 :
282 16 : if (tests[i].test_func)
283 12 : tests[i].test_func (pk);
284 :
285 16 : nvc_release (pk);
286 : }
287 2 : }
288 :
289 :
290 : void
291 2 : run_modification_tests (void)
292 : {
293 : gpg_error_t err;
294 : nvc_t pk;
295 : gcry_sexp_t key;
296 : char *buf;
297 :
298 2 : pk = my_nvc_new ();
299 2 : assert (pk);
300 :
301 2 : nvc_set (pk, "Foo:", "Bar");
302 2 : buf = nvc_to_string (pk);
303 2 : assert (strcmp (buf, "Foo: Bar\n") == 0);
304 2 : xfree (buf);
305 :
306 2 : nvc_set (pk, "Foo:", "Baz");
307 2 : buf = nvc_to_string (pk);
308 2 : assert (strcmp (buf, "Foo: Baz\n") == 0);
309 2 : xfree (buf);
310 :
311 2 : nvc_set (pk, "Bar:", "Bazzel");
312 2 : buf = nvc_to_string (pk);
313 2 : assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0);
314 2 : xfree (buf);
315 :
316 2 : nvc_add (pk, "Foo:", "Bar");
317 2 : buf = nvc_to_string (pk);
318 2 : assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0);
319 2 : xfree (buf);
320 :
321 2 : nvc_add (pk, "DontExistYet:", "Bar");
322 2 : buf = nvc_to_string (pk);
323 2 : assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\nDontExistYet: Bar\n")
324 : == 0);
325 2 : xfree (buf);
326 :
327 2 : nvc_delete (pk, nvc_lookup (pk, "DontExistYet:"));
328 2 : buf = nvc_to_string (pk);
329 2 : assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0);
330 2 : xfree (buf);
331 :
332 2 : nvc_delete (pk, nve_next_value (nvc_lookup (pk, "Foo:"), "Foo:"));
333 2 : buf = nvc_to_string (pk);
334 2 : assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0);
335 2 : xfree (buf);
336 :
337 2 : nvc_delete (pk, nvc_lookup (pk, "Foo:"));
338 2 : buf = nvc_to_string (pk);
339 2 : assert (strcmp (buf, "Bar: Bazzel\n") == 0);
340 2 : xfree (buf);
341 :
342 2 : nvc_delete (pk, nvc_first (pk));
343 2 : buf = nvc_to_string (pk);
344 2 : assert (strcmp (buf, "") == 0);
345 2 : xfree (buf);
346 :
347 2 : nvc_set (pk, "Foo:", "A really long value spanning across multiple lines"
348 : " that has to be wrapped at a convenient space.");
349 2 : buf = nvc_to_string (pk);
350 2 : assert (strcmp (buf, "Foo: A really long value spanning across multiple"
351 : " lines that has to be\n wrapped at a convenient space.\n")
352 : == 0);
353 2 : xfree (buf);
354 :
355 2 : nvc_set (pk, "Foo:", "XA really long value spanning across multiple lines"
356 : " that has to be wrapped at a convenient space.");
357 2 : buf = nvc_to_string (pk);
358 2 : assert (strcmp (buf, "Foo: XA really long value spanning across multiple"
359 : " lines that has to\n be wrapped at a convenient space.\n")
360 : == 0);
361 2 : xfree (buf);
362 :
363 2 : nvc_set (pk, "Foo:", "XXXXA really long value spanning across multiple lines"
364 : " that has to be wrapped at a convenient space.");
365 2 : buf = nvc_to_string (pk);
366 2 : assert (strcmp (buf, "Foo: XXXXA really long value spanning across multiple"
367 : " lines that has\n to be wrapped at a convenient space.\n")
368 : == 0);
369 2 : xfree (buf);
370 :
371 2 : nvc_set (pk, "Foo:", "Areallylongvaluespanningacrossmultiplelines"
372 : "thathastobewrappedataconvenientspacethatisnotthere.");
373 2 : buf = nvc_to_string (pk);
374 2 : assert (strcmp (buf, "Foo: Areallylongvaluespanningacrossmultiplelinesthat"
375 : "hastobewrappedataco\n nvenientspacethatisnotthere.\n")
376 : == 0);
377 2 : xfree (buf);
378 2 : nvc_release (pk);
379 :
380 2 : pk = my_nvc_new ();
381 2 : assert (pk);
382 :
383 2 : err = gcry_sexp_build (&key, NULL, "(hello world)");
384 2 : assert (err == 0);
385 2 : assert (key);
386 :
387 2 : if (private_key_mode)
388 : {
389 1 : err = nvc_set_private_key (pk, key);
390 1 : assert (err == 0);
391 :
392 1 : buf = nvc_to_string (pk);
393 1 : assert (strcmp (buf, "Key: (hello world)\n") == 0);
394 1 : xfree (buf);
395 : }
396 : else
397 : {
398 1 : err = nvc_set_private_key (pk, key);
399 1 : assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY);
400 : }
401 2 : gcry_sexp_release (key);
402 2 : nvc_release (pk);
403 2 : }
404 :
405 :
406 : void
407 0 : convert (const char *fname)
408 : {
409 : gpg_error_t err;
410 : estream_t source;
411 : gcry_sexp_t key;
412 : char *buf;
413 : size_t buflen;
414 : struct stat st;
415 : nvc_t pk;
416 :
417 0 : source = es_fopen (fname, "rb");
418 0 : if (source == NULL)
419 0 : goto leave;
420 :
421 0 : if (fstat (es_fileno (source), &st))
422 0 : goto leave;
423 :
424 0 : buflen = st.st_size;
425 0 : buf = xtrymalloc (buflen+1);
426 0 : assert (buf);
427 :
428 0 : if (es_fread (buf, buflen, 1, source) != 1)
429 0 : goto leave;
430 :
431 0 : err = gcry_sexp_sscan (&key, NULL, buf, buflen);
432 0 : if (err)
433 : {
434 0 : fprintf (stderr, "malformed s-expression in %s\n", fname);
435 0 : exit (1);
436 : }
437 :
438 0 : pk = my_nvc_new ();
439 0 : assert (pk);
440 :
441 0 : err = nvc_set_private_key (pk, key);
442 0 : assert (err == 0);
443 :
444 0 : err = nvc_write (pk, es_stdout);
445 0 : assert (err == 0);
446 :
447 0 : return;
448 :
449 : leave:
450 0 : perror (fname);
451 0 : exit (1);
452 : }
453 :
454 :
455 : void
456 0 : parse (const char *fname)
457 : {
458 : gpg_error_t err;
459 : estream_t source;
460 : char *buf;
461 : nvc_t pk_a, pk_b;
462 : nve_t e;
463 : int line;
464 :
465 0 : source = es_fopen (fname, "rb");
466 0 : if (source == NULL)
467 : {
468 0 : perror (fname);
469 0 : exit (1);
470 : }
471 :
472 0 : if (private_key_mode)
473 0 : err = nvc_parse_private_key (&pk_a, &line, source);
474 : else
475 0 : err = nvc_parse (&pk_a, &line, source);
476 0 : if (err)
477 : {
478 0 : fprintf (stderr, "failed to parse %s line %d: %s\n",
479 : fname, line, gpg_strerror (err));
480 0 : exit (1);
481 : }
482 :
483 0 : buf = nvc_to_string (pk_a);
484 0 : xfree (buf);
485 :
486 0 : pk_b = my_nvc_new ();
487 0 : assert (pk_b);
488 :
489 0 : for (e = nvc_first (pk_a); e; e = nve_next (e))
490 : {
491 0 : gcry_sexp_t key = NULL;
492 :
493 0 : if (private_key_mode && !strcasecmp (nve_name (e), "Key:"))
494 : {
495 0 : err = nvc_get_private_key (pk_a, &key);
496 0 : if (err)
497 0 : key = NULL;
498 : }
499 :
500 0 : if (key)
501 : {
502 0 : err = nvc_set_private_key (pk_b, key);
503 0 : assert (err == 0);
504 : }
505 : else
506 : {
507 0 : err = nvc_add (pk_b, nve_name (e), nve_value (e));
508 0 : assert (err == 0);
509 : }
510 : }
511 :
512 0 : buf = nvc_to_string (pk_b);
513 0 : if (verbose)
514 0 : fprintf (stdout, "%s", buf);
515 0 : xfree (buf);
516 0 : }
517 :
518 :
519 : void
520 0 : print_usage (void)
521 : {
522 0 : fprintf (stderr,
523 : "usage: t-private-keys [--verbose]"
524 : " [--convert <private-key-file>"
525 : " || --parse-key <extended-private-key-file>"
526 : " || --parse <file> ]\n");
527 0 : exit (2);
528 : }
529 :
530 :
531 : int
532 1 : main (int argc, char **argv)
533 : {
534 1 : enum { TEST, CONVERT, PARSE, PARSEKEY } command = TEST;
535 :
536 1 : if (argc)
537 1 : { argc--; argv++; }
538 1 : if (argc && !strcmp (argv[0], "--verbose"))
539 : {
540 0 : verbose = 1;
541 0 : argc--; argv++;
542 : }
543 :
544 1 : if (argc && !strcmp (argv[0], "--convert"))
545 : {
546 0 : command = CONVERT;
547 0 : argc--; argv++;
548 0 : if (argc != 1)
549 0 : print_usage ();
550 : }
551 :
552 1 : if (argc && !strcmp (argv[0], "--parse-key"))
553 : {
554 0 : command = PARSEKEY;
555 0 : argc--; argv++;
556 0 : if (argc != 1)
557 0 : print_usage ();
558 : }
559 :
560 1 : if (argc && !strcmp (argv[0], "--parse"))
561 : {
562 0 : command = PARSE;
563 0 : argc--; argv++;
564 0 : if (argc != 1)
565 0 : print_usage ();
566 : }
567 :
568 1 : switch (command)
569 : {
570 : case TEST:
571 1 : run_tests ();
572 1 : run_modification_tests ();
573 1 : private_key_mode = 1;
574 1 : run_tests ();
575 1 : run_modification_tests ();
576 1 : break;
577 :
578 : case CONVERT:
579 0 : convert (*argv);
580 0 : break;
581 :
582 : case PARSEKEY:
583 0 : private_key_mode = 1;
584 0 : parse (*argv);
585 0 : break;
586 :
587 : case PARSE:
588 0 : parse (*argv);
589 0 : break;
590 : }
591 :
592 1 : return 0;
593 : }
|