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