* protect-tool.c (rsa_key_check): New. NEWPG-0-3-8
authorWerner Koch <wk@gnupg.org>
Tue, 25 Jun 2002 17:50:59 +0000 (17:50 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 25 Jun 2002 17:50:59 +0000 (17:50 +0000)
(import_p12_file): New.
(main): New command --p12-import.
* minip12.c, minip12.h: New.

agent/ChangeLog
agent/Makefile.am
agent/keyformat.txt
agent/minip12.c [new file with mode: 0644]
agent/minip12.h [new file with mode: 0644]
agent/protect-tool.c

index 7ab1ac6..3a37ec8 100644 (file)
@@ -1,8 +1,20 @@
+2002-06-25  Werner Koch  <wk@gnupg.org>
+
+       * protect-tool.c (rsa_key_check): New.
+       (import_p12_file): New.
+       (main): New command --p12-import.
+       * minip12.c, minip12.h: New.
+
+2002-06-24  Werner Koch  <wk@gnupg.org>
+
+       * protect-tool.c (read_file): New.
+       (read_key): Factored most code out to read_file.
+
 2002-06-17  Werner Koch  <wk@gnupg.org>
 
        * agent.h: Add a callback function to the pin_entry_info structure.
        * query.c (agent_askpin): Use the callback to check for a correct
-       PIN.  Removed the start_err_text argument becuase it is not
+       PIN.  Removed the start_err_text argument because it is not
        anymore needed; changed callers.
        * findkey.c (unprotect): Replace our own check loop by a callback.
        (try_unprotect_cb): New.
index 6680dcc..baeab5d 100644 (file)
@@ -47,8 +47,13 @@ gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a  \
 
 protect_tool_SOURCES = \
        protect-tool.c \
-       protect.c
+       protect.c \
+       minip12.c minip12.h
+
 
 protect_tool_LDADD = ../jnlib/libjnlib.a \
                     ../common/libcommon.a $(LIBGCRYPT_LIBS)
 
+
+
+
index bfb4ee4..6c0bd8b 100644 (file)
@@ -75,7 +75,7 @@ The only available protection mode for now is
 
   openpgp-s2k3-sha1-aes-cbc
 
-which describesan algorithm using using AES in CBC mode for
+which describes an algorithm using using AES in CBC mode for
 encryption, SHA-1 for integrity protection and the String to Key
 algorithm 3 from OpenPGP (rfc2440).
 
@@ -159,5 +159,5 @@ independent of any protocol, so that the same key can be ised with
 different protocols.  PKCS-15 calls this a subjectKeyHash; it can be
 calculate using Libgcrypt's gcry_pk_get_keygrip().
 
-[3] Even when canonical representation is required we will show the
+[3] Even when canonical representation are required we will show the
 S-expression here in a more readable representation.
diff --git a/agent/minip12.c b/agent/minip12.c
new file mode 100644 (file)
index 0000000..29531b2
--- /dev/null
@@ -0,0 +1,805 @@
+/* minip12.c - A minilam pkcs-12 implementation.
+ *     Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <gcrypt.h>
+
+#undef TEST 
+
+#ifdef TEST
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+#include "../jnlib/logging.h"
+#include "minip12.h"
+
+#ifndef DIM
+#define DIM(v)              (sizeof(v)/sizeof((v)[0]))
+#endif
+
+enum
+{
+  UNIVERSAL = 0,
+  APPLICATION = 1,
+  CONTEXT = 2,
+  PRIVATE = 3
+};
+
+
+enum
+{
+  TAG_NONE = 0,
+  TAG_BOOLEAN = 1,
+  TAG_INTEGER = 2,
+  TAG_BIT_STRING = 3,
+  TAG_OCTET_STRING = 4,
+  TAG_NULL = 5,
+  TAG_OBJECT_ID = 6,
+  TAG_OBJECT_DESCRIPTOR = 7,
+  TAG_EXTERNAL = 8,
+  TAG_REAL = 9,
+  TAG_ENUMERATED = 10,
+  TAG_EMBEDDED_PDV = 11,
+  TAG_UTF8_STRING = 12,
+  TAG_REALTIVE_OID = 13,
+  TAG_SEQUENCE = 16,
+  TAG_SET = 17,
+  TAG_NUMERIC_STRING = 18,
+  TAG_PRINTABLE_STRING = 19,
+  TAG_TELETEX_STRING = 20,
+  TAG_VIDEOTEX_STRING = 21,
+  TAG_IA5_STRING = 22,
+  TAG_UTC_TIME = 23,
+  TAG_GENERALIZED_TIME = 24,
+  TAG_GRAPHIC_STRING = 25,
+  TAG_VISIBLE_STRING = 26,
+  TAG_GENERAL_STRING = 27,
+  TAG_UNIVERSAL_STRING = 28,
+  TAG_CHARACTER_STRING = 29,
+  TAG_BMP_STRING = 30
+};
+
+
+static unsigned char const oid_data[9] = {
+  0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
+static unsigned char const oid_encryptedData[9] = {
+  0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 };
+static unsigned char const oid_pkcs_12_pkcs_8ShroudedKeyBag[11] = {
+  0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x0A, 0x01, 0x02 };
+static unsigned char const oid_pbeWithSHAAnd3_KeyTripleDES_CBC[10] = {
+  0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03 };
+
+static unsigned char const oid_rsaEncryption[9] = {
+  0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
+
+
+
+struct tag_info {
+  int class;
+  int is_constructed;
+  unsigned long tag;
+  unsigned long length;  /* length part of the TLV */
+  int nhdr;
+  int ndef;              /* It is an indefinite length */
+};
+
+
+/* Parse the buffer at the address BUFFER which is of SIZE and return
+   the tag and the length part from the TLV triplet.  Update BUFFER
+   and SIZE on success. */
+static int 
+parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
+{
+  int c;
+  unsigned long tag;
+  const unsigned char *buf = *buffer;
+  size_t length = *size;
+
+  ti->length = 0;
+  ti->ndef = 0;
+  ti->nhdr = 0;
+
+  /* Get the tag */
+  if (!length)
+    return -1; /* premature eof */
+  c = *buf++; length--;
+  ti->nhdr++;
+
+  ti->class = (c & 0xc0) >> 6;
+  ti->is_constructed = !!(c & 0x20);
+  tag = c & 0x1f;
+
+  if (tag == 0x1f)
+    {
+      tag = 0;
+      do
+        {
+          tag <<= 7;
+          if (!length)
+            return -1; /* premature eof */
+          c = *buf++; length--;
+          ti->nhdr++;
+          tag |= c & 0x7f;
+        }
+      while (c & 0x80);
+    }
+  ti->tag = tag;
+
+  /* Get the length */
+  if (!length)
+    return -1; /* prematureeof */
+  c = *buf++; length--;
+  ti->nhdr++;
+
+  if ( !(c & 0x80) )
+    ti->length = c;
+  else if (c == 0x80)
+    ti->ndef = 1;
+  else if (c == 0xff)
+    return -1; /* forbidden length value */
+  else
+    {
+      unsigned long len = 0;
+      int count = c & 0x7f;
+
+      for (; count; count--)
+        {
+          len <<= 8;
+          if (!length)
+            return -1; /* premature_eof */
+          c = *buf++; length--;
+          ti->nhdr++;
+          len |= c & 0xff;
+        }
+      ti->length = len;
+    }
+  
+  if (ti->class == UNIVERSAL && !ti->tag)
+    ti->length = 0;
+
+  if (ti->length > length)
+    return -1; /* data larger than buffer. */
+  
+  *buffer = buf;
+  *size = length;
+  return 0;
+}
+
+
+static int 
+string_to_key (int id, char *salt, int iter, const char *pw,
+               int req_keylen, unsigned char *keybuf)
+{
+  int rc, i, j;
+  GcryMDHd md;
+  GcryMPI num_b1 = NULL;
+  int pwlen;
+  unsigned char hash[20], buf_b[64], buf_i[128], *p;
+  size_t cur_keylen;
+  size_t n;
+
+  cur_keylen = 0;
+  pwlen = strlen (pw);
+  if (pwlen > 63/2)
+    {
+      log_error ("password too long\n");
+      return -1;
+    }
+
+  /* Store salt and password in BUF_I */
+  p = buf_i;
+  for(i=0; i < 64; i++)
+    *p++ = salt [i%8];
+  for(i=j=0; i < 64; i += 2)
+    {
+      *p++ = 0;
+      *p++ = pw[j];
+      if (++j > pwlen) /* Note, that we include the trailing zero */
+        j = 0;
+    }
+
+  for (;;)
+    {
+      md = gcry_md_open (GCRY_MD_SHA1, 0);
+      if (!md)
+        {
+          log_error ( "gcry_md_open failed: %s\n", gcry_strerror (-1));
+          return -1;
+        }
+      for(i=0; i < 64; i++)
+        gcry_md_putc (md, id);
+      gcry_md_write (md, buf_i, 128);
+      memcpy (hash, gcry_md_read (md, 0), 20);
+      gcry_md_close (md);
+      for (i=1; i < iter; i++)
+        gcry_md_hash_buffer (GCRY_MD_SHA1, hash, hash, 20);
+
+      for (i=0; i < 20 && cur_keylen < req_keylen; i++)
+        keybuf[cur_keylen++] = hash[i];
+      if (cur_keylen == req_keylen)
+        {
+          gcry_mpi_release (num_b1);
+          return 0; /* ready */
+        }
+      
+      /* need more bytes. */
+      for(i=0; i < 64; i++)
+        buf_b[i] = hash[i % 20];
+      n = 64;
+      rc = gcry_mpi_scan (&num_b1, GCRYMPI_FMT_USG, buf_b, &n);
+      if (rc)
+        {
+          log_error ( "gcry_mpi_scan failed: %s\n", gcry_strerror (rc));
+          return -1;
+        }
+      gcry_mpi_add_ui (num_b1, num_b1, 1);
+      for (i=0; i < 128; i += 64)
+        {
+          GcryMPI num_ij;
+
+          n = 64;
+          rc = gcry_mpi_scan (&num_ij, GCRYMPI_FMT_USG, buf_i + i, &n);
+          if (rc)
+            {
+              log_error ( "gcry_mpi_scan failed: %s\n",
+                       gcry_strerror (rc));
+              return -1;
+            }
+          gcry_mpi_add (num_ij, num_ij, num_b1);
+          gcry_mpi_clear_highbit (num_ij, 64*8);
+          n = 64;
+          rc = gcry_mpi_print (GCRYMPI_FMT_USG, buf_i + i, &n, num_ij);
+          if (rc)
+            {
+              log_error ( "gcry_mpi_print failed: %s\n",
+                       gcry_strerror (rc));
+              return -1;
+            }
+          gcry_mpi_release (num_ij);
+        }
+    }
+}
+
+
+static int 
+set_key_iv (GcryCipherHd chd, char *salt, int iter, const char *pw)
+{
+  unsigned char keybuf[24];
+  int rc;
+
+  if (string_to_key (1, salt, iter, pw, 24, keybuf))
+    return -1;
+  rc = gcry_cipher_setkey (chd, keybuf, 24);
+  if (rc)
+    {
+      log_error ( "gcry_cipher_setkey failed: %s\n", gcry_strerror (rc));
+      return -1;
+    }
+
+  if (string_to_key (2, salt, iter, pw, 8, keybuf))
+    return -1;
+  rc = gcry_cipher_setiv (chd, keybuf, 8);
+  if (rc)
+    {
+      log_error ("gcry_cipher_setiv failed: %s\n", gcry_strerror (rc));
+      return -1;
+    }
+  return 0;
+}
+
+
+static void
+decrypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
+               const char *pw)
+{
+  GcryCipherHd chd;
+  int rc;
+
+  chd = gcry_cipher_open (GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0);
+  if (!chd)
+    {
+      log_error ( "gcry_cipher_open failed: %s\n", gcry_strerror(-1));
+      return;
+    }
+  if (set_key_iv (chd, salt, iter, pw))
+    goto leave;
+
+  rc = gcry_cipher_decrypt (chd, buffer, length, NULL, 0);
+  if (rc)
+    {
+      log_error ( "gcry_cipher_decrypt failed: %s\n", gcry_strerror (rc));
+      goto leave;
+    }
+
+/*    { */
+/*      FILE *fp = fopen("inner.der", "wb"); */
+/*      fwrite (buffer, 1, length, fp); */
+/*      fclose (fp); */
+/*    } */
+
+ leave:
+  gcry_cipher_close (chd);
+}
+  
+
+
+
+static int
+parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
+                          int startoffset)
+{
+  struct tag_info ti;
+  const unsigned char *p = buffer;
+  size_t n = length;
+  const char *where;
+
+  where = "start";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class != CONTEXT || ti.tag)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.tag != TAG_SEQUENCE)
+    goto bailout;
+
+  where = "bag.encryptedData.version";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 0)
+    goto bailout;
+  p++; n--;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.tag != TAG_SEQUENCE)
+    goto bailout;
+
+  where = "bag.encryptedData.data";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data)
+      || memcmp (p, oid_data, DIM(oid_data)))
+    goto bailout;
+  p += DIM(oid_data);
+  n -= DIM(oid_data);
+
+  /* fixme: continue parsing */
+
+  return 0;
+ bailout:
+  log_error ("encrptedData error at \"%s\", offset %u\n",
+             where, (p - buffer)+startoffset);
+  return -1;
+}
+
+static GcryMPI *
+parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
+                const char *pw)
+{
+  int rc;
+  struct tag_info ti;
+  const unsigned char *p = buffer;
+  size_t n = length;
+  const char *where;
+  char salt[8];
+  unsigned int iter;
+  int len;
+  unsigned char *plain = NULL;
+  GcryMPI *result = NULL;
+  int result_count, i;
+
+  where = "start";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class != CONTEXT || ti.tag)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_OCTET_STRING)
+    goto bailout;
+
+  where = "data.outerseqs";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+
+  where = "data.objectidentifier";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_OBJECT_ID
+      || ti.length != DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)
+      || memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
+                 DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag)))
+    goto bailout;
+  p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag);
+  n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag);
+
+  where = "shrouded,outerseqs";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class != CONTEXT || ti.tag)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_OBJECT_ID
+      || ti.length != DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
+      || memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
+                 DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
+    goto bailout;
+  p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
+  n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
+
+  where = "3des-params";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_OCTET_STRING || ti.length != 8 )
+    goto bailout;
+  memcpy (salt, p, 8);
+  p += 8;
+  n -= 8;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
+    goto bailout;
+  for (iter=0; ti.length; ti.length--)
+    {
+      iter <<= 8;
+      iter |= (*p++) & 0xff; 
+      n--;
+    }
+  
+  where = "3des-ciphertext";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length )
+    goto bailout;
+  
+  log_info ("%lu bytes of 3DES encrypted text\n", ti.length);
+  
+  plain = gcry_malloc_secure (ti.length);
+  if (!plain)
+    {
+      log_error ("error allocating decryption buffer\n");
+      goto bailout;
+    }
+  memcpy (plain, p, ti.length);
+  decrypt_block (plain, ti.length, salt, iter, pw);
+  n = ti.length;
+  startoffset = 0;
+  buffer = p = plain;
+
+  where = "decrypted-text";
+  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
+      || ti.length != 1 || *p)
+    goto bailout;
+  p++; n--;
+  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  len = ti.length;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (len < ti.nhdr)
+    goto bailout;
+  len -= ti.nhdr;
+  if (ti.class || ti.tag != TAG_OBJECT_ID
+      || ti.length != DIM(oid_rsaEncryption)
+      || memcmp (p, oid_rsaEncryption,
+                 DIM(oid_rsaEncryption)))
+    goto bailout;
+  p += DIM (oid_rsaEncryption);
+  n -= DIM (oid_rsaEncryption);
+  if (len < ti.length)
+    goto bailout;
+  len -= ti.length;
+  if (n < len)
+    goto bailout;
+  p += len;
+  n -= len;
+  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  len = ti.length;
+
+  result = gcry_calloc (10, sizeof *result);
+  if (!result)
+    {
+      log_error ( "error allocating result array\n");
+      goto bailout;
+    }
+  result_count = 0;
+
+  where = "reading.key-parameters";
+  for (result_count=0; len && result_count < 9;)
+    {
+      int dummy_n;
+
+      if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER)
+        goto bailout;
+      if (len < ti.nhdr)
+        goto bailout;
+      len -= ti.nhdr;
+      if (len < ti.length)
+        goto bailout;
+      len -= ti.length;
+      dummy_n = ti.length;
+      if (!result_count && ti.length == 1 && !*p)
+        ; /* ignore the very first one if it is a 0 */
+      else 
+        {
+          rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p,
+                              &dummy_n);
+          if (rc)
+            {
+              log_error ("error parsing key parameter: %s\n",
+                         gcry_strerror (rc));
+              goto bailout;
+            }
+          result_count++;
+        }
+      p += ti.length;
+      n -= ti.length;
+    }
+  if (len)
+    goto bailout;
+
+  return result;
+
+ bailout:
+  gcry_free (plain);
+  if (result)
+    {
+      for (i=0; result[i]; i++)
+        gcry_mpi_release (result[i]);
+      gcry_free (result);
+    }
+  log_error ( "data error at \"%s\", offset %u\n",
+              where, (p - buffer) + startoffset);
+  return NULL;
+}
+
+
+/* Parse a PKCS12 object and return an array of MPI representing the
+   secret key parameters.  This is a very limited inplementation in
+   that it is only able to look for 3DES encoded enctyptedData and
+   tries to extract the first private key object it finds.  In case of
+   an error NULL is returned. */
+GcryMPI *
+p12_parse (const unsigned char *buffer, size_t length, const char *pw)
+{
+  struct tag_info ti;
+  const unsigned char *p = buffer;
+  size_t n = length;
+  const char *where;
+  int bagseqlength, len;
+
+  where = "pfx";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.tag != TAG_SEQUENCE)
+    goto bailout;
+
+  where = "pfxVersion";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3)
+    goto bailout;
+  p++; n--;
+  
+  where = "authSave";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data)
+      || memcmp (p, oid_data, DIM(oid_data)))
+    goto bailout;
+  p += DIM(oid_data);
+  n -= DIM(oid_data);
+
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class != CONTEXT || ti.tag)
+    goto bailout;
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class != UNIVERSAL || ti.tag != TAG_OCTET_STRING)
+    goto bailout;
+
+  where = "bags";
+  if (parse_tag (&p, &n, &ti))
+    goto bailout;
+  if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
+    goto bailout;
+  bagseqlength = ti.length;
+  while (bagseqlength)
+    {
+      log_error ( "at offset %u\n", (p - buffer));
+      where = "bag-sequence";
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+
+      if (bagseqlength < ti.nhdr)
+        goto bailout;
+      bagseqlength -= ti.nhdr;
+      if (bagseqlength < ti.length)
+        goto bailout;
+      bagseqlength -= ti.length;
+      len = ti.length;
+
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      len -= ti.nhdr;
+      if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData)
+          && !memcmp (p, oid_encryptedData, DIM(oid_encryptedData)))
+        {
+          p += DIM(oid_encryptedData);
+          n -= DIM(oid_encryptedData);
+          len -= DIM(oid_encryptedData);
+          where = "bag.encryptedData";
+          if (parse_bag_encrypted_data (p, n, (p - buffer)))
+            goto bailout;
+        }
+      else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data)
+          && !memcmp (p, oid_data, DIM(oid_data)))
+        {
+          p += DIM(oid_data);
+          n -= DIM(oid_data);
+          len -= DIM(oid_data);
+          return parse_bag_data (p, n, (p-buffer), pw);
+        }
+      else
+        log_info ( "unknown bag type - skipped\n");
+
+      if (len < 0 || len > n)
+        goto bailout;
+      p += len;
+      n -= len;
+    }
+  
+  return NULL;
+ bailout:
+  log_error ("error at \"%s\", offset %u\n", where, (p - buffer));
+  return NULL;
+}
+
+#if 0 /* unser construction. */
+/* Expect the RSA key parameters in KPARMS and a password in
+   PW. Create a PKCS structure from it and return it as well as the
+   length in R_LENGTH; return NULL in case of an error. */
+unsigned char * 
+p12_build (GcryMPI *kparms, const char *pw, size_t *r_length)
+{
+  int i;
+  unsigned char *result;
+  size_t resultlen;
+
+  for (i=0; kparms[i]; i++)
+    ;
+  if (i != 8)
+    {
+      log_error ("invalid paramters for p12_build\n");
+      return NULL;
+    }
+
+
+  *r_length = resultlen;
+  return result;
+}
+#endif
+
+
+#ifdef TEST
+int
+main (int argc, char **argv)
+{
+  FILE *fp;
+  struct stat st;
+  char *buf;
+  size_t buflen;
+  GcryMPI *result;
+
+  if (argc != 3)
+    {
+      fprintf (stderr, "usage: testp12 file passphrase\n");
+      return 1;
+    }
+
+  gcry_control (GCRYCTL_DISABLE_SECMEM, NULL);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL);
+
+  fp = fopen (argv[1], "rb");
+  if (!fp)
+    {
+      fprintf (stderr, "can't open `%s': %s\n", argv[1], strerror (errno));
+      return 1;
+    }
+  
+  if (fstat (fileno(fp), &st))
+    {
+      fprintf (stderr, "can't stat `%s': %s\n", argv[1], strerror (errno));
+      return 1;
+    }
+
+  buflen = st.st_size;
+  buf = malloc (buflen+1);
+  if (!buf || fread (buf, buflen, 1, fp) != 1)
+    {
+      fprintf (stderr, "error reading `%s': %s\n", argv[1], strerror (errno));
+      return 1;
+    }
+  fclose (fp);
+
+  result = p12_parse (buf, buflen, argv[2]);
+  if (result)
+    {
+      int i, rc;
+      char *buf;
+
+      for (i=0; result[i]; i++)
+        {
+          rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, (void**)&buf,
+                                NULL, result[i]);
+          if (rc)
+            printf ("%d: [error printing number: %s]\n",
+                    i, gcry_strerror (rc));
+          else
+            {
+              printf ("%d: %s\n", i, buf);
+              gcry_free (buf);
+            }
+        }
+    }
+
+  return 0;
+
+}
+#endif /* TEST */
diff --git a/agent/minip12.h b/agent/minip12.h
new file mode 100644 (file)
index 0000000..6858e81
--- /dev/null
@@ -0,0 +1,30 @@
+/* minip12.h - Global definitions for the minimal pkcs-12 implementation.
+ *     Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef MINIP12_H
+#define MINIP12_H
+
+#include <gcrypt.h>
+
+GcryMPI *p12_parse (const unsigned char *buffer, size_t length,
+                    const char *pw);
+
+
+#endif /*MINIP12_H*/
index 16277bb..f533425 100644 (file)
@@ -1,4 +1,4 @@
-/* protect-tool.c - A tool to text the secret key protection
+/* protect-tool.c - A tool to test the secret key protection
  *     Copyright (C) 2002 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
@@ -34,6 +34,7 @@
 
 #define JNLIB_NEED_LOG_LOGV
 #include "agent.h"
+#include "minip12.h"
 
 #define N_(a) a
 #define _(a) a
@@ -53,8 +54,20 @@ enum cmd_and_opt_values
   oShowShadowInfo,
   oShowKeygrip,
 
+  oP12Import,
+
 aTest };
 
+struct rsa_secret_key_s 
+  {
+    MPI n;         /* public modulus */
+    MPI e;         /* public exponent */
+    MPI d;         /* exponent */
+    MPI p;         /* prime  p. */
+    MPI q;         /* prime  q. */
+    MPI u;         /* inverse of p mod q. */
+  };
+
 
 static int opt_armor;
 static const char *passphrase = "abc";
@@ -65,13 +78,14 @@ static ARGPARSE_OPTS opts[] = {
 
   { oVerbose, "verbose",   0, "verbose" },
   { oArmor,   "armor",     0, "write output in advanced format" },
-  { oPassphrase, "passphrase", 2, "|STRING| Use passphrase STRING" },
+  { oPassphrase, "passphrase", 2, "|STRING|use passphrase STRING" },
   { oProtect, "protect",     256, "protect a private key"},
   { oUnprotect, "unprotect", 256, "unprotect a private key"},
   { oShadow,  "shadow", 256, "create a shadow entry for a priblic key"},
   { oShowShadowInfo,  "show-shadow-info", 256, "return the shadow info"},
-  { oShowKeygrip, "show-keygrip", 256, " show the \"keygrip\""},
+  { oShowKeygrip, "show-keygrip", 256, "show the \"keygrip\""},
 
+  { oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"},
   {0}
 };
 
@@ -135,6 +149,25 @@ my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
   log_logv (level, fmt, arg_ptr);
 }
 
+
+/*  static void */
+/*  print_mpi (const char *text, GcryMPI a) */
+/*  { */
+/*    char *buf; */
+/*    void *bufaddr = &buf; */
+/*    int rc; */
+
+/*    rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, bufaddr, NULL, a); */
+/*    if (rc) */
+/*      log_info ("%s: [error printing number: %s]\n", text, gcry_strerror (rc)); */
+/*    else */
+/*      { */
+/*        log_info ("%s: %s\n", text, buf); */
+/*        gcry_free (buf); */
+/*      } */
+/*  } */
+
+
 \f
 static unsigned char *
 make_canonical (const char *fname, const char *buf, size_t buflen)
@@ -185,14 +218,13 @@ make_advanced (const unsigned char *buf, size_t buflen)
 }
 
 
-static unsigned char *
-read_key (const char *fname)
+static char *
+read_file (const char *fname, size_t *r_length)
 {
   FILE *fp;
   struct stat st;
   char *buf;
   size_t buflen;
-  unsigned char *key;
   
   fp = fopen (fname, "rb");
   if (!fp)
@@ -219,6 +251,19 @@ read_key (const char *fname)
     }
   fclose (fp);
 
+  *r_length = buflen;
+  return buf;
+}
+
+
+static unsigned char *
+read_key (const char *fname)
+{
+  char *buf;
+  size_t buflen;
+  unsigned char *key;
+  
+  buf = read_file (fname, &buflen);
   key = make_canonical (fname, buf, buflen);
   xfree (buf);
   return key;
@@ -422,9 +467,211 @@ show_keygrip (const char *fname)
   putchar ('\n');
 }
 
+\f
+static int
+rsa_key_check (struct rsa_secret_key_s *skey)
+{
+  int err = 0;
+  MPI t = gcry_mpi_snew (0);
+  MPI t1 = gcry_mpi_snew (0);
+  MPI t2 = gcry_mpi_snew (0);
+  MPI phi = gcry_mpi_snew (0);
+
+  /* check that n == p * q */
+  gcry_mpi_mul (t, skey->p, skey->q);
+  if (gcry_mpi_cmp( t, skey->n) )
+    {
+      log_error ("RSA oops: n != p * q\n");
+      err++;
+    }
+
+  /* check that p is less than q */
+  if (gcry_mpi_cmp (skey->p, skey->q) > 0)
+    {
+      GcryMPI tmp;
+
+      log_info ("swapping secret primes\n");
+      tmp = gcry_mpi_copy (skey->p);
+      gcry_mpi_set (skey->p, skey->q);
+      gcry_mpi_set (skey->q, tmp);
+      gcry_mpi_release (tmp);
+      /* and must recompute u of course */
+      gcry_mpi_invm (skey->u, skey->p, skey->q);
+    }
+
+  /* check that e divides neither p-1 nor q-1 */
+  gcry_mpi_sub_ui (t, skey->p, 1 );
+  gcry_mpi_div (NULL, t, t, skey->e, 0);
+  if (!gcry_mpi_cmp_ui( t, 0) )
+    {
+      log_error ("RSA oops: e divides p-1\n");
+      err++;
+    }
+  gcry_mpi_sub_ui (t, skey->q, 1);
+  gcry_mpi_div (NULL, t, t, skey->e, 0);
+  if (!gcry_mpi_cmp_ui( t, 0))
+    {
+      log_info ( "RSA oops: e divides q-1\n" );
+      err++;
+    }
+
+  /* check that d is correct. */
+  gcry_mpi_sub_ui (t1, skey->p, 1);
+  gcry_mpi_sub_ui (t2, skey->q, 1);
+  gcry_mpi_mul (phi, t1, t2);
+  gcry_mpi_invm (t, skey->e, phi);
+  if (gcry_mpi_cmp (t, skey->d))
+    { /* no: try universal exponent. */
+      gcry_mpi_gcd (t, t1, t2);
+      gcry_mpi_div (t, NULL, phi, t, 0);
+      gcry_mpi_invm (t, skey->e, t);
+      if (gcry_mpi_cmp (t, skey->d))
+        {
+          log_error ("RSA oops: bad secret exponent\n");
+          err++;
+        }
+    }
+
+  /* check for correctness of u */
+  gcry_mpi_invm (t, skey->p, skey->q);
+  if (gcry_mpi_cmp (t, skey->u))
+    {
+      log_info ( "RSA oops: bad u parameter\n");
+      err++;
+    }
+
+  if (err)
+    log_info ("RSA secret key check failed\n");
+
+  gcry_mpi_release (t);
+  gcry_mpi_release (t1);
+  gcry_mpi_release (t2);
+  gcry_mpi_release (phi);
+
+  return err? -1:0;
+}
+
+
+static void
+import_p12_file (const char *fname)
+{
+  char *buf;
+  unsigned char *result;
+  size_t buflen, resultlen;
+  int i;
+  int rc;
+  GcryMPI *kparms;
+  struct rsa_secret_key_s sk;
+  GcrySexp s_key;
+  unsigned char *key;
+
+  /* fixme: we should release some stuff on error */
+  
+  buf = read_file (fname, &buflen);
+  if (!buf)
+    return;
+
+  kparms = p12_parse (buf, buflen, passphrase);
+  xfree (buf);
+  if (!kparms)
+    {
+      log_error ("error parsing or decrypting the PKCS-1 file\n");
+      return;
+    }
+  for (i=0; kparms[i]; i++)
+    ;
+  if (i != 8)
+    {
+      log_error ("invalid structure of private key\n");
+      return;
+    }
+
+
+/*    print_mpi ("   n", kparms[0]); */
+/*    print_mpi ("   e", kparms[1]); */
+/*    print_mpi ("   d", kparms[2]); */
+/*    print_mpi ("   p", kparms[3]); */
+/*    print_mpi ("   q", kparms[4]); */
+/*    print_mpi ("dmp1", kparms[5]); */
+/*    print_mpi ("dmq1", kparms[6]); */
+/*    print_mpi ("   u", kparms[7]); */
+
+  sk.n = kparms[0];
+  sk.e = kparms[1];
+  sk.d = kparms[2];
+  sk.q = kparms[3];
+  sk.p = kparms[4];
+  sk.u = kparms[7];
+  if (rsa_key_check (&sk))
+    return;
+/*    print_mpi ("   n", sk.n); */
+/*    print_mpi ("   e", sk.e); */
+/*    print_mpi ("   d", sk.d); */
+/*    print_mpi ("   p", sk.p); */
+/*    print_mpi ("   q", sk.q); */
+/*    print_mpi ("   u", sk.u); */
+
+  /* Create an S-expresion from the parameters. */
+  rc = gcry_sexp_build (&s_key, NULL,
+                        "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
+                        sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL);
+  for (i=0; i < 8; i++)
+    gcry_mpi_release (kparms[i]);
+  gcry_free (kparms);
+  if (rc)
+    {
+      log_error ("failed to created S-expression from key: %s\n",
+                 gcry_strerror (rc));
+      return;
+    }
 
+  /* Compute the keygrip. */
+  {
+    unsigned char grip[20];
+    if (!gcry_pk_get_keygrip (s_key, grip))
+      {
+        log_error ("can't calculate keygrip\n");
+        return;
+      }
+    log_info ("keygrip: ");
+    for (i=0; i < 20; i++)
+      log_printf ("%02X", grip[i]);
+    log_printf ("\n");
+  }
+
+  /* convert to canonical encoding */
+  buflen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_CANON, NULL, 0);
+  assert (buflen);
+  key = gcry_xmalloc_secure (buflen);
+  buflen = gcry_sexp_sprint (s_key, GCRYSEXP_FMT_CANON, key, buflen);
+  assert (buflen);
+  gcry_sexp_release (s_key);
 
 
+  rc = agent_protect (key, passphrase, &result, &resultlen);
+  xfree (key);
+  if (rc)
+    {
+      log_error ("protecting the key failed: %s\n", gnupg_strerror (rc));
+      return;
+    }
+  
+  if (opt_armor)
+    {
+      char *p = make_advanced (result, resultlen);
+      xfree (result);
+      if (!p)
+        return;
+      result = p;
+      resultlen = strlen (p);
+    }
+
+  fwrite (result, resultlen, 1, stdout);
+  xfree (result);
+}
+
+
+\f
 int
 main (int argc, char **argv )
 {
@@ -461,6 +708,7 @@ main (int argc, char **argv )
         case oShadow: cmd = oShadow; break;
         case oShowShadowInfo: cmd = oShowShadowInfo; break;
         case oShowKeygrip: cmd = oShowKeygrip; break;
+        case oP12Import: cmd = oP12Import; break;
 
         case oPassphrase: passphrase = pargs.r.ret_str; break;
 
@@ -483,6 +731,8 @@ main (int argc, char **argv )
     show_shadow_info (*argv);
   else if (cmd == oShowKeygrip)
     show_keygrip (*argv);
+  else if (cmd == oP12Import)
+    import_p12_file (*argv);
   else
     show_file (*argv);