* protect-tool.c (rsa_key_check): New.

(import_p12_file): New.
(main): New command --p12-import.
* minip12.c, minip12.h: New.
This commit is contained in:
Werner Koch 2002-06-25 17:50:59 +00:00
parent 5c5a3f689a
commit c65009a2c5
6 changed files with 1112 additions and 10 deletions

View 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.

View 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)

View 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.

805
agent/minip12.c Normal file
View File

@ -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 */

30
agent/minip12.h Normal file
View File

@ -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*/

View 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); */
/* } */
/* } */
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');
}
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);
}
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);