summaryrefslogtreecommitdiffstats
path: root/sm/t-minip12.c
diff options
context:
space:
mode:
Diffstat (limited to 'sm/t-minip12.c')
-rw-r--r--sm/t-minip12.c800
1 files changed, 800 insertions, 0 deletions
diff --git a/sm/t-minip12.c b/sm/t-minip12.c
new file mode 100644
index 0000000..c9adced
--- /dev/null
+++ b/sm/t-minip12.c
@@ -0,0 +1,800 @@
+/* t-minip12.c - Test driver for minip12.c
+ * Copyright (C) 2020, 2023 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/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "../common/util.h"
+#include "minip12.h"
+
+
+#define PGM "t-minip12"
+
+static int verbose;
+static int debug;
+static int any_error;
+
+static void die (const char *format, ...) GPGRT_ATTR_NR_PRINTF(1,2);
+static void err (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
+static void inf (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
+/* static void dbg (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); */
+static void printresult (const char *format, ...) GPGRT_ATTR_PRINTF(1,2);
+static char *my_xstrconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0);
+
+#define xstrconcat my_xstrconcat
+#define trim_spaces(a) my_trim_spaces ((a))
+#define my_isascii(c) (!((c) & 0x80))
+
+
+
+
+
+/* Print diagnostic message and exit with failure. */
+static void
+die (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ if (!*format || format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+
+ exit (1);
+}
+
+
+/* Print diagnostic message. */
+static void
+err (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ any_error = 1;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ if (!*format || format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+}
+
+
+/* Print an info message. */
+static void
+inf (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ if (verbose)
+ {
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ if (!*format || format[strlen(format)-1] != '\n')
+ putc ('\n', stderr);
+ }
+}
+
+
+/* Print a debug message. */
+/* static void */
+/* dbg (const char *format, ...) */
+/* { */
+/* va_list arg_ptr; */
+
+/* if (debug) */
+/* { */
+/* fprintf (stderr, "%s: DBG: ", PGM); */
+
+/* va_start (arg_ptr, format); */
+/* vfprintf (stderr, format, arg_ptr); */
+/* va_end (arg_ptr); */
+/* if (!*format || format[strlen(format)-1] != '\n') */
+/* putc ('\n', stderr); */
+/* } */
+/* } */
+
+
+/* Print a result line to stdout. */
+static void
+printresult (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ flockfile (stdout);
+#endif
+ va_start (arg_ptr, format);
+ vfprintf (stdout, format, arg_ptr);
+ if (*format && format[strlen(format)-1] != '\n')
+ putc ('\n', stdout);
+ va_end (arg_ptr);
+ fflush (stdout);
+#ifdef HAVE_FLOCKFILE
+ funlockfile (stdout);
+#endif
+}
+
+
+/* Helper for xstrconcat and strconcat. */
+static char *
+do_strconcat (int xmode, const char *s1, va_list arg_ptr)
+{
+ const char *argv[48];
+ size_t argc;
+ size_t needed;
+ char *buffer, *p;
+
+ argc = 0;
+ argv[argc++] = s1;
+ needed = strlen (s1);
+ while (((argv[argc] = va_arg (arg_ptr, const char *))))
+ {
+ needed += strlen (argv[argc]);
+ if (argc >= DIM (argv)-1)
+ die ("too may args for strconcat\n");
+ argc++;
+ }
+ needed++;
+ buffer = xmode? xmalloc (needed) : malloc (needed);
+ for (p = buffer, argc=0; argv[argc]; argc++)
+ p = stpcpy (p, argv[argc]);
+
+ return buffer;
+}
+
+
+/* Concatenate the string S1 with all the following strings up to a
+ NULL. Returns a malloced buffer with the new string or dies on error. */
+static char *
+my_xstrconcat (const char *s1, ...)
+{
+ va_list arg_ptr;
+ char *result;
+
+ if (!s1)
+ result = xstrdup ("");
+ else
+ {
+ va_start (arg_ptr, s1);
+ result = do_strconcat (1, s1, arg_ptr);
+ va_end (arg_ptr);
+ }
+ return result;
+}
+
+
+static char *
+my_trim_spaces (char *str )
+{
+ char *string, *p, *mark;
+
+ string = str;
+ for (p=string; *p && isspace (*(unsigned char *)p) ; p++)
+ ;
+ for (mark=NULL; (*string = *p); string++, p++ )
+ if (isspace (*(unsigned char *)p))
+ {
+ if (!mark)
+ mark = string;
+ }
+ else
+ mark = NULL;
+ if (mark)
+ *mark = '\0';
+
+ return str ;
+}
+
+
+/* Prepend FNAME with the srcdir environment variable's value and
+ * return an allocated filename. */
+static char *
+prepend_srcdir (const char *fname)
+{
+ static const char *srcdir;
+
+ if (!srcdir && !(srcdir = getenv ("srcdir")))
+ return xstrdup (fname);
+ else
+ return xstrconcat (srcdir, "/", fname, NULL);
+}
+
+
+/* (BUFFER,BUFLEN) and return a malloced hexstring. */
+static char *
+hash_buffer (const void *buffer, size_t buflen)
+{
+ unsigned char hash[20];
+ char *result;
+ int i;
+
+ gcry_md_hash_buffer (GCRY_MD_SHA1, hash, buffer, buflen);
+ result = xmalloc (41);
+ for (i=0; i < 20; i++)
+ snprintf (result + 2*i, 3, "%02x", hash[i]);
+ return result;
+}
+
+
+/* Read next line but skip over empty and comment lines. Caller must
+ xfree the result. */
+static char *
+read_textline (FILE *fp, int *lineno)
+{
+ char line[4096];
+ char *p;
+
+ do
+ {
+ if (!fgets (line, sizeof line, fp))
+ {
+ if (feof (fp))
+ return NULL;
+ die ("error reading input line: %s\n", strerror (errno));
+ }
+ ++*lineno;
+ p = strchr (line, '\n');
+ if (!p)
+ die ("input line %d not terminated or too long\n", *lineno);
+ *p = 0;
+ for (p--;p > line && my_isascii (*p) && isspace (*p); p--)
+ *p = 0;
+ }
+ while (!*line || *line == '#');
+ return xstrdup (line);
+}
+
+
+/* Copy the data after the tag to BUFFER. BUFFER will be allocated as
+ needed. */
+static void
+copy_data (char **buffer, const char *line, int lineno)
+{
+ const char *s;
+
+ xfree (*buffer);
+ *buffer = NULL;
+
+ s = strchr (line, ':');
+ if (!s)
+ {
+ err ("syntax error at input line %d", lineno);
+ return;
+ }
+ for (s++; my_isascii (*s) && isspace (*s); s++)
+ ;
+ *buffer = xstrdup (s);
+}
+
+
+static void
+hexdowncase (char *string)
+{
+ char *p;
+
+ if (string)
+ for (p=string; *p; p++)
+ if (my_isascii (*p))
+ *p = tolower (*p);
+}
+
+
+/* Return the value of the variable VARNAME from ~/.gnupg-autogen.rc
+ * or NULL if it does not exists or is empty. */
+static char *
+value_from_gnupg_autogen_rc (const char *varname)
+{
+ const char *home;
+ char *fname;
+ FILE *fp;
+ char *line = NULL;
+ char *p;
+ int lineno = 0;
+
+ if (!(home = getenv ("HOME")))
+ home = "";
+ fname = xstrconcat (home, "/.gnupg-autogen.rc", NULL);
+ fp = fopen (fname, "r");
+ if (!fp)
+ goto leave;
+
+ while ((line = read_textline (fp, &lineno)))
+ {
+ p = strchr (line, '=');
+ if (p)
+ {
+ *p++ = 0;
+ trim_spaces (line);
+ if (!strcmp (line, varname))
+ {
+ trim_spaces (p);
+ if (*p)
+ {
+ memmove (line, p, strlen (p)+1);
+ if (*line == '~' && line[1] == '/')
+ {
+ p = xstrconcat (home, line+1, NULL);
+ xfree (line);
+ line = p;
+ }
+ break; /* found. */
+ }
+ }
+ }
+ xfree (line);
+ }
+
+ leave:
+ if (fp)
+ fclose (fp);
+ xfree (fname);
+ return line;
+}
+
+
+static void
+cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
+{
+ (void)opaque;
+ (void)cert;
+
+ if (verbose)
+ log_info ("got a certificate of %zu bytes length\n", certlen);
+}
+
+
+/* Parse one PKCS#12 file. Returns zero on success. */
+static int
+one_file (const char *name, const char *pass)
+{
+ FILE *fp;
+ struct stat st;
+ unsigned char *buf;
+ size_t buflen;
+ gcry_mpi_t *result;
+ int badpass;
+ char *curve = NULL;
+
+ fp = fopen (name, "rb");
+ if (!fp)
+ {
+ fprintf (stderr, PGM": can't open '%s': %s\n", name, strerror (errno));
+ return 1;
+ }
+
+ if (fstat (fileno(fp), &st))
+ {
+ fprintf (stderr, PGM": can't stat '%s': %s\n", name, strerror (errno));
+ return 1;
+ }
+
+ buflen = st.st_size;
+ buf = xmalloc (buflen+1);
+ if (fread (buf, buflen, 1, fp) != 1)
+ {
+ fprintf (stderr, "error reading '%s': %s\n", name, strerror (errno));
+ return 1;
+ }
+ fclose (fp);
+
+ result = p12_parse (buf, buflen, pass, cert_cb, NULL, &badpass, &curve);
+ if (result)
+ {
+ int i, rc;
+ unsigned char *tmpbuf;
+
+ if (curve)
+ log_info ("curve: %s\n", curve);
+ for (i=0; result[i]; i++)
+ {
+ rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]);
+ if (rc)
+ log_error ("%d: [error printing number: %s]\n",
+ i, gpg_strerror (rc));
+ else
+ {
+ log_info ("%d: %s\n", i, tmpbuf);
+ gcry_free (tmpbuf);
+ }
+ }
+ }
+ if (badpass)
+ log_error ("Bad password given?\n");
+
+ xfree (buf);
+ return 0;
+}
+
+
+static void
+cert_collect_cb (void *opaque, const unsigned char *cert, size_t certlen)
+{
+ char **certstr = opaque;
+ char *hash, *save;
+
+ hash = hash_buffer (cert, certlen);
+ if (*certstr)
+ {
+ save = *certstr;
+ *certstr = xstrconcat (save, ",", hash, NULL);
+ xfree (save);
+ xfree (hash);
+ }
+ else
+ *certstr = hash;
+}
+
+
+static int
+run_one_test (const char *name, const char *desc, const char *pass,
+ const char *certexpected, const char *keyexpected)
+{
+ FILE *fp;
+ struct stat st;
+ unsigned char *buf;
+ size_t buflen;
+ gcry_mpi_t *result;
+ int badpass;
+ char *curve = NULL;
+ char *resulthash = NULL;
+ char *p;
+ char *certstr = NULL;
+ int ret;
+
+ inf ("testing '%s' (%s)", name , desc? desc:"");
+ fp = fopen (name, "rb");
+ if (!fp)
+ {
+ err ("can't open '%s': %s\n", name, strerror (errno));
+ printresult ("FAIL: %s - test file not found\n", name);
+ return 1;
+ }
+
+ if (fstat (fileno (fp), &st))
+ {
+ err ("can't stat '%s': %s\n", name, strerror (errno));
+ printresult ("FAIL: %s - error stating test file\n", name);
+ fclose (fp);
+ return 1;
+ }
+
+ buflen = st.st_size;
+ buf = xmalloc (buflen+1);
+ if (fread (buf, buflen, 1, fp) != 1)
+ {
+ err ("error reading '%s': %s\n", name, strerror (errno));
+ printresult ("FAIL: %s - error reading test file\n", name);
+ fclose (fp);
+ xfree (buf);
+ return 1;
+ }
+ fclose (fp);
+
+ result = p12_parse (buf, buflen, pass? pass:"", cert_collect_cb, &certstr,
+ &badpass, &curve);
+ if (result)
+ {
+ int i, rc;
+ char *tmpstring;
+ unsigned char *tmpbuf;
+ char numbuf[20];
+
+ if (curve)
+ {
+ if (verbose > 1)
+ inf ("curve: %s\n", curve);
+ tmpstring = xstrconcat ("curve:", curve, "\n", NULL);
+ }
+ else
+ tmpstring = xstrdup ("\n");
+ for (i=0; result[i]; i++)
+ {
+ rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]);
+ if (rc)
+ die ("result %d: [error printing number: %s]\n",
+ i, gpg_strerror (rc));
+ else
+ {
+ if (verbose > 1)
+ inf ("result %d: %s\n", i, tmpbuf);
+ snprintf (numbuf, sizeof numbuf, "%d:", i);
+ p = xstrconcat (tmpstring, numbuf, tmpbuf, "\n", NULL);
+ xfree (tmpstring);
+ tmpstring = p;
+ gcry_free (tmpbuf);
+ }
+ }
+
+ /* Hash only if we have at least one parameter; i.e. the curve
+ * alone is not sufficient. */
+ if (result[0])
+ resulthash = hash_buffer (tmpstring, strlen (tmpstring));
+ xfree (tmpstring);
+ }
+
+ if (verbose > 1)
+ {
+ inf ("cert(exp)=%s", certexpected);
+ inf ("cert(got)=%s", certstr? certstr:"[null]");
+ inf ("key(exp)=%s", keyexpected);
+ inf ("key(got)=%s", resulthash? resulthash:"[null]");
+ }
+
+ ret = 1;
+ if (!result)
+ printresult ("FAIL: %s - error from parser\n", name);
+ else if (certexpected && !certstr)
+ printresult ("FAIL: %s - expected certs but got none\n", name);
+ else if (!certexpected && certstr)
+ printresult ("FAIL: %s - no certs expected but got one\n", name);
+ else if (certexpected && certstr && strcmp (certexpected, certstr))
+ {
+ printresult ("FAIL: %s - certs not as expected\n", name);
+ inf ("cert(exp)=%s", certexpected);
+ inf ("cert(got)=%s", certstr? certstr:"[null]");
+ }
+ else if (keyexpected && !resulthash)
+ printresult ("FAIL: %s - expected key but got none\n", name);
+ else if (!keyexpected && resulthash)
+ printresult ("FAIL: %s - key not expected but got one\n", name);
+ else if (keyexpected && resulthash && strcmp (keyexpected, resulthash))
+ {
+ printresult ("FAIL: %s - keys not as expected\n", name);
+ inf ("key(exp)=%s", keyexpected);
+ inf ("key(got)=%s", resulthash? resulthash:"[null]");
+ }
+ else
+ {
+ printresult ("PASS: %s\n", name);
+ ret = 0;
+ }
+
+ if (result)
+ {
+ int i;
+ for (i=0; result[i]; i++)
+ gcry_mpi_release (result[i]);
+ gcry_free (result);
+ }
+ xfree (certstr);
+ xfree (resulthash);
+ xfree (curve);
+ xfree (buf);
+ return ret;
+}
+
+
+/* Run a regression test using the Info take from DESCFNAME. */
+static int
+run_tests_from_file (const char *descfname)
+{
+ FILE *fp;
+ char *descdir;
+ int lineno, ntests;
+ char *line;
+ char *name = NULL;
+ char *desc = NULL;
+ char *pass = NULL;
+ char *cert = NULL;
+ char *key = NULL;
+ int ret = 0;
+ char *p;
+
+ inf ("Running tests from '%s'", descfname);
+ descdir = xstrdup (descfname);
+ p = strrchr (descdir, '/');
+ if (p)
+ *p = 0;
+ else
+ {
+ xfree (descdir);
+ descdir = xstrdup (".");
+ }
+
+ fp = fopen (descfname, "r");
+ if (!fp)
+ die ("error opening '%s': %s\n", descfname, strerror (errno));
+
+ lineno = ntests = 0;
+ while ((line = read_textline (fp, &lineno)))
+ {
+ if (!strncmp (line, "Name:", 5))
+ {
+ if (name)
+ ret |= run_one_test (name, desc, pass, cert, key);
+ xfree (cert); cert = NULL;
+ xfree (desc); desc = NULL;
+ xfree (pass); pass = NULL;
+ xfree (key); key = NULL;
+ copy_data (&name, line, lineno);
+ if (name)
+ {
+ p = xstrconcat (descdir, "/", name, NULL);
+ xfree (name);
+ name = p;
+ }
+ }
+ else if (!strncmp (line, "Desc:", 5))
+ copy_data (&desc, line, lineno);
+ else if (!strncmp (line, "Pass:", 5))
+ copy_data (&pass, line, lineno);
+ else if (!strncmp (line, "Cert:", 5))
+ {
+ p = NULL;
+ copy_data (&p, line, lineno);
+ hexdowncase (p);
+ if (p && cert)
+ {
+ char *save = cert;
+ cert = xstrconcat (save, ",", p, NULL);
+ xfree (save);
+ xfree (p);
+ }
+ else
+ cert = p;
+ }
+ else if (!strncmp (line, "Key:", 4))
+ {
+ copy_data (&key, line, lineno);
+ hexdowncase (key);
+ }
+ else
+ inf ("%s:%d: unknown tag ignored", descfname, lineno);
+
+ xfree (line);
+ }
+ if (name)
+ ret |= run_one_test (name, desc, pass, cert, key);
+ xfree (name);
+ xfree (desc);
+ xfree (pass);
+ xfree (cert);
+ xfree (key);
+
+ fclose (fp);
+ xfree (descdir);
+ return ret;
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ char const *name = NULL;
+ char const *pass = NULL;
+ int ret;
+ int no_extra = 0;
+
+ if (argc)
+ { argc--; argv++; }
+ while (argc && last_argc != argc )
+ {
+ last_argc = argc;
+ if (!strcmp (*argv, "--"))
+ {
+ argc--; argv++;
+ break;
+ }
+ else if (!strcmp (*argv, "--help"))
+ {
+ fputs ("usage: " PGM " <pkcs12file> [<passphrase>]\n"
+ "Without <pkcs12file> a regression test is run\n"
+ "Options:\n"
+ " --no-extra do not run extra tests\n"
+ " --verbose print timings etc.\n"
+ " given twice shows more\n"
+ " --debug flyswatter\n"
+ , stdout);
+ exit (0);
+ }
+ else if (!strcmp (*argv, "--no-extra"))
+ {
+ no_extra = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--verbose"))
+ {
+ verbose++;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--debug"))
+ {
+ verbose += 2;
+ debug++;
+ argc--; argv++;
+ }
+ else if (!strncmp (*argv, "--", 2))
+ {
+ fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
+ exit (1);
+ }
+ }
+
+ if (!argc)
+ {
+ name = NULL;
+ pass = NULL;
+ }
+ else if (argc == 1)
+ {
+ name = argv[0];
+ pass = "";
+ }
+ else if (argc == 2)
+ {
+ name = argv[0];
+ pass = argv[1];
+ }
+ else
+ {
+ fprintf (stderr, "usage: " PGM " [<file> [<passphrase>]]\n");
+ exit (1);
+ }
+
+ gcry_control (GCRYCTL_DISABLE_SECMEM, NULL);
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL);
+
+ if (name)
+ {
+ p12_set_verbosity (verbose, debug);
+ ret = one_file (name, pass);
+ }
+ else
+ {
+ char *descfname, *p;
+
+ if (verbose > 1)
+ p12_set_verbosity (verbose > 1? (verbose - 1):0, debug);
+ descfname = prepend_srcdir ("../tests/samplekeys/Description-p12");
+ ret = run_tests_from_file (descfname);
+ xfree (descfname);
+
+ /* Check whether we have non-public regression test cases. */
+ p = no_extra? NULL:value_from_gnupg_autogen_rc ("GNUPG_EXTRA_TESTS_DIR");
+ if (p)
+ {
+ descfname = xstrconcat (p, "/pkcs12/Description", NULL);
+ xfree (p);
+ ret |= run_tests_from_file (descfname);
+ xfree (descfname);
+ }
+ }
+
+ return ret;
+}