summaryrefslogtreecommitdiffstats
path: root/common/t-name-value.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--common/t-name-value.c593
1 files changed, 593 insertions, 0 deletions
diff --git a/common/t-name-value.c b/common/t-name-value.c
new file mode 100644
index 0000000..57f685f
--- /dev/null
+++ b/common/t-name-value.c
@@ -0,0 +1,593 @@
+/* t-name-value.c - Module test for name-value.c
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "util.h"
+#include "name-value.h"
+
+static int verbose;
+static int private_key_mode;
+
+
+static nvc_t
+my_nvc_new (void)
+{
+ if (private_key_mode)
+ return nvc_new_private_key ();
+ else
+ return nvc_new ();
+}
+
+
+void
+test_getting_values (nvc_t pk)
+{
+ nve_t e;
+
+ e = nvc_lookup (pk, "Comment:");
+ assert (e);
+
+ /* Names are case-insensitive. */
+ e = nvc_lookup (pk, "comment:");
+ assert (e);
+ e = nvc_lookup (pk, "COMMENT:");
+ assert (e);
+
+ e = nvc_lookup (pk, "SomeOtherName:");
+ assert (e);
+}
+
+
+void
+test_key_extraction (nvc_t pk)
+{
+ gpg_error_t err;
+ gcry_sexp_t key;
+
+ if (private_key_mode)
+ {
+ err = nvc_get_private_key (pk, &key);
+ assert (err == 0);
+ assert (key);
+
+ if (verbose)
+ gcry_sexp_dump (key);
+
+ gcry_sexp_release (key);
+ }
+ else
+ {
+ err = nvc_get_private_key (pk, &key);
+ assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY);
+ }
+}
+
+
+void
+test_iteration (nvc_t pk)
+{
+ int i;
+ nve_t e;
+
+ i = 0;
+ for (e = nvc_first (pk); e; e = nve_next (e))
+ i++;
+ assert (i == 4);
+
+ i = 0;
+ for (e = nvc_lookup (pk, "Comment:");
+ e;
+ e = nve_next_value (e, "Comment:"))
+ i++;
+ assert (i == 3);
+}
+
+
+void
+test_whitespace (nvc_t pk)
+{
+ nve_t e;
+
+ e = nvc_lookup (pk, "One:");
+ assert (e);
+ assert (strcmp (nve_value (e), "WithoutWhitespace") == 0);
+
+ e = nvc_lookup (pk, "Two:");
+ assert (e);
+ assert (strcmp (nve_value (e), "With Whitespace") == 0);
+
+ e = nvc_lookup (pk, "Three:");
+ assert (e);
+ assert (strcmp (nve_value (e),
+ "Blank lines in continuations encode newlines.\n"
+ "Next paragraph.") == 0);
+}
+
+
+struct
+{
+ char *value;
+ void (*test_func) (nvc_t);
+} tests[] =
+ {
+ {
+ "# This is a comment followed by an empty line\n"
+ "\n",
+ NULL,
+ },
+ {
+ "# This is a comment followed by two empty lines, Windows style\r\n"
+ "\r\n"
+ "\r\n",
+ NULL,
+ },
+ {
+ "# Some name,value pairs\n"
+ "Comment: Some comment.\n"
+ "SomeOtherName: Some value.\n",
+ test_getting_values,
+ },
+ {
+ " # Whitespace is preserved as much as possible\r\n"
+ "Comment:Some comment.\n"
+ "SomeOtherName: Some value. \n",
+ test_getting_values,
+ },
+ {
+ "# Values may be continued in the next line as indicated by leading\n"
+ "# space\n"
+ "Comment: Some rather long\n"
+ " comment that is continued in the next line.\n"
+ "\n"
+ " Blank lines with or without whitespace are allowed within\n"
+ " continuations to allow paragraphs.\n"
+ "SomeOtherName: Some value.\n",
+ test_getting_values,
+ },
+ {
+ "# Names may be given multiple times forming an array of values\n"
+ "Comment: Some comment, element 0.\n"
+ "Comment: Some comment, element 1.\n"
+ "Comment: Some comment, element 2.\n"
+ "SomeOtherName: Some value.\n",
+ test_iteration,
+ },
+ {
+ "# One whitespace at the beginning of a continuation is swallowed.\n"
+ "One: Without\n"
+ " Whitespace\n"
+ "Two: With\n"
+ " Whitespace\n"
+ "Three: Blank lines in continuations encode newlines.\n"
+ "\n"
+ " Next paragraph.\n",
+ test_whitespace,
+ },
+ {
+ "Description: Key to sign all GnuPG released tarballs.\n"
+ " The key is actually stored on a smart card.\n"
+ "Use-for-ssh: yes\n"
+ "OpenSSH-cert: long base64 encoded string wrapped so that this\n"
+ " key file can be easily edited with a standard editor.\n"
+ "Key: (shadowed-private-key\n"
+ " (rsa\n"
+ " (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n"
+ " 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n"
+ " 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n"
+ " 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n"
+ " 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n"
+ " 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n"
+ " F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n"
+ " 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n"
+ " E186A02BA2497FDC5D1221#)\n"
+ " (e #00010001#)\n"
+ " (shadowed t1-v1\n"
+ " (#D2760001240102000005000011730000# OPENPGP.1)\n"
+ " )))\n",
+ test_key_extraction,
+ },
+ };
+
+
+static char *
+nvc_to_string (nvc_t pk)
+{
+ gpg_error_t err;
+ char *buf;
+ size_t len;
+ estream_t sink;
+
+ sink = es_fopenmem (0, "rw");
+ assert (sink);
+
+ err = nvc_write (pk, sink);
+ assert (err == 0);
+
+ len = es_ftell (sink);
+ buf = xmalloc (len+1);
+ assert (buf);
+
+ es_fseek (sink, 0, SEEK_SET);
+ es_read (sink, buf, len, NULL);
+ buf[len] = 0;
+
+ es_fclose (sink);
+ return buf;
+}
+
+
+void dummy_free (void *p) { (void) p; }
+void *dummy_realloc (void *p, size_t s) { (void) s; return p; }
+
+void
+run_tests (void)
+{
+ gpg_error_t err;
+ nvc_t pk;
+
+ int i;
+ for (i = 0; i < DIM (tests); i++)
+ {
+ estream_t source;
+ char *buf;
+ size_t len;
+
+ len = strlen (tests[i].value);
+ source = es_mopen (tests[i].value, len, len,
+ 0, dummy_realloc, dummy_free, "r");
+ assert (source);
+
+ if (private_key_mode)
+ err = nvc_parse_private_key (&pk, NULL, source);
+ else
+ err = nvc_parse (&pk, NULL, source);
+ assert (err == 0);
+ assert (pk);
+
+ if (verbose)
+ {
+ err = nvc_write (pk, es_stderr);
+ assert (err == 0);
+ }
+
+ buf = nvc_to_string (pk);
+ assert (memcmp (tests[i].value, buf, len) == 0);
+
+ es_fclose (source);
+ xfree (buf);
+
+ if (tests[i].test_func)
+ tests[i].test_func (pk);
+
+ nvc_release (pk);
+ }
+}
+
+
+void
+run_modification_tests (void)
+{
+ gpg_error_t err;
+ nvc_t pk;
+ gcry_sexp_t key;
+ char *buf;
+
+ pk = my_nvc_new ();
+ assert (pk);
+
+ nvc_set (pk, "Foo:", "Bar");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: Bar\n") == 0);
+ xfree (buf);
+
+ nvc_set (pk, "Foo:", "Baz");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: Baz\n") == 0);
+ xfree (buf);
+
+ nvc_set (pk, "Bar:", "Bazzel");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0);
+ xfree (buf);
+
+ nvc_add (pk, "Foo:", "Bar");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0);
+ xfree (buf);
+
+ nvc_add (pk, "DontExistYet:", "Bar");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\nDontExistYet: Bar\n")
+ == 0);
+ xfree (buf);
+
+ nvc_delete (pk, nvc_lookup (pk, "DontExistYet:"));
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0);
+ xfree (buf);
+
+ nvc_delete (pk, nve_next_value (nvc_lookup (pk, "Foo:"), "Foo:"));
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0);
+ xfree (buf);
+
+ nvc_delete (pk, nvc_lookup (pk, "Foo:"));
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Bar: Bazzel\n") == 0);
+ xfree (buf);
+
+ nvc_delete (pk, nvc_first (pk));
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "") == 0);
+ xfree (buf);
+
+ nvc_set (pk, "Foo:", "A really long value spanning across multiple lines"
+ " that has to be wrapped at a convenient space.");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: A really long value spanning across multiple"
+ " lines that has to be\n wrapped at a convenient space.\n")
+ == 0);
+ xfree (buf);
+
+ nvc_set (pk, "Foo:", "XA really long value spanning across multiple lines"
+ " that has to be wrapped at a convenient space.");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: XA really long value spanning across multiple"
+ " lines that has to\n be wrapped at a convenient space.\n")
+ == 0);
+ xfree (buf);
+
+ nvc_set (pk, "Foo:", "XXXXA really long value spanning across multiple lines"
+ " that has to be wrapped at a convenient space.");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: XXXXA really long value spanning across multiple"
+ " lines that has\n to be wrapped at a convenient space.\n")
+ == 0);
+ xfree (buf);
+
+ nvc_set (pk, "Foo:", "Areallylongvaluespanningacrossmultiplelines"
+ "thathastobewrappedataconvenientspacethatisnotthere.");
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Foo: Areallylongvaluespanningacrossmultiplelinesthat"
+ "hastobewrappedataco\n nvenientspacethatisnotthere.\n")
+ == 0);
+ xfree (buf);
+ nvc_release (pk);
+
+ pk = my_nvc_new ();
+ assert (pk);
+
+ err = gcry_sexp_build (&key, NULL, "(hello world)");
+ assert (err == 0);
+ assert (key);
+
+ if (private_key_mode)
+ {
+ err = nvc_set_private_key (pk, key);
+ assert (err == 0);
+
+ buf = nvc_to_string (pk);
+ assert (strcmp (buf, "Key: (hello world)\n") == 0);
+ xfree (buf);
+ }
+ else
+ {
+ err = nvc_set_private_key (pk, key);
+ assert (gpg_err_code (err) == GPG_ERR_MISSING_KEY);
+ }
+ gcry_sexp_release (key);
+ nvc_release (pk);
+}
+
+
+void
+convert (const char *fname)
+{
+ gpg_error_t err;
+ estream_t source;
+ gcry_sexp_t key;
+ char *buf;
+ size_t buflen;
+ struct stat st;
+ nvc_t pk;
+
+ source = es_fopen (fname, "rb");
+ if (source == NULL)
+ goto leave;
+
+ if (fstat (es_fileno (source), &st))
+ goto leave;
+
+ buflen = st.st_size;
+ buf = xtrymalloc (buflen+1);
+ assert (buf);
+
+ if (es_fread (buf, buflen, 1, source) != 1)
+ goto leave;
+
+ err = gcry_sexp_sscan (&key, NULL, buf, buflen);
+ if (err)
+ {
+ fprintf (stderr, "malformed s-expression in %s\n", fname);
+ exit (1);
+ }
+
+ pk = my_nvc_new ();
+ assert (pk);
+
+ err = nvc_set_private_key (pk, key);
+ assert (err == 0);
+
+ err = nvc_write (pk, es_stdout);
+ assert (err == 0);
+
+ return;
+
+ leave:
+ perror (fname);
+ exit (1);
+}
+
+
+void
+parse (const char *fname)
+{
+ gpg_error_t err;
+ estream_t source;
+ char *buf;
+ nvc_t pk_a, pk_b;
+ nve_t e;
+ int line;
+
+ source = es_fopen (fname, "rb");
+ if (source == NULL)
+ {
+ perror (fname);
+ exit (1);
+ }
+
+ if (private_key_mode)
+ err = nvc_parse_private_key (&pk_a, &line, source);
+ else
+ err = nvc_parse (&pk_a, &line, source);
+ if (err)
+ {
+ fprintf (stderr, "failed to parse %s line %d: %s\n",
+ fname, line, gpg_strerror (err));
+ exit (1);
+ }
+
+ buf = nvc_to_string (pk_a);
+ xfree (buf);
+
+ pk_b = my_nvc_new ();
+ assert (pk_b);
+
+ for (e = nvc_first (pk_a); e; e = nve_next (e))
+ {
+ gcry_sexp_t key = NULL;
+
+ if (private_key_mode && !strcasecmp (nve_name (e), "Key:"))
+ {
+ err = nvc_get_private_key (pk_a, &key);
+ if (err)
+ key = NULL;
+ }
+
+ if (key)
+ {
+ err = nvc_set_private_key (pk_b, key);
+ assert (err == 0);
+ }
+ else
+ {
+ err = nvc_add (pk_b, nve_name (e), nve_value (e));
+ assert (err == 0);
+ }
+ }
+
+ buf = nvc_to_string (pk_b);
+ if (verbose)
+ fprintf (stdout, "%s", buf);
+ xfree (buf);
+}
+
+
+void
+print_usage (void)
+{
+ fprintf (stderr,
+ "usage: t-private-keys [--verbose]"
+ " [--convert <private-key-file>"
+ " || --parse-key <extended-private-key-file>"
+ " || --parse <file> ]\n");
+ exit (2);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ enum { TEST, CONVERT, PARSE, PARSEKEY } command = TEST;
+
+ if (argc)
+ { argc--; argv++; }
+ if (argc && !strcmp (argv[0], "--verbose"))
+ {
+ verbose = 1;
+ argc--; argv++;
+ }
+
+ if (argc && !strcmp (argv[0], "--convert"))
+ {
+ command = CONVERT;
+ argc--; argv++;
+ if (argc != 1)
+ print_usage ();
+ }
+
+ if (argc && !strcmp (argv[0], "--parse-key"))
+ {
+ command = PARSEKEY;
+ argc--; argv++;
+ if (argc != 1)
+ print_usage ();
+ }
+
+ if (argc && !strcmp (argv[0], "--parse"))
+ {
+ command = PARSE;
+ argc--; argv++;
+ if (argc != 1)
+ print_usage ();
+ }
+
+ switch (command)
+ {
+ case TEST:
+ run_tests ();
+ run_modification_tests ();
+ private_key_mode = 1;
+ run_tests ();
+ run_modification_tests ();
+ break;
+
+ case CONVERT:
+ convert (*argv);
+ break;
+
+ case PARSEKEY:
+ private_key_mode = 1;
+ parse (*argv);
+ break;
+
+ case PARSE:
+ parse (*argv);
+ break;
+ }
+
+ return 0;
+}