mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
* kbxutil.c: New command --import-openpgp.
(main): Updated libgcrypt initialization stuff. (my_gcry_logger): New. (read_file): New. Taken from ../agent/protect-tool. (dump_fpr, dump_openpgp_key, import_openpgp): New. * keybox-openpgp.c: New.
This commit is contained in:
parent
d851bdf579
commit
2a09a35ad0
@ -1,3 +1,13 @@
|
||||
2004-08-24 Werner Koch <wk@g10code.de>
|
||||
|
||||
* kbxutil.c: New command --import-openpgp.
|
||||
(main): Updated libgcrypt initialization stuff.
|
||||
(my_gcry_logger): New.
|
||||
(read_file): New. Taken from ../agent/protect-tool.
|
||||
(dump_fpr, dump_openpgp_key, import_openpgp): New.
|
||||
|
||||
* keybox-openpgp.c: New.
|
||||
|
||||
2004-06-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* keybox-dump.c (_keybox_dump_file): New arg STATS_ONLY.
|
||||
|
@ -37,6 +37,7 @@ common_sources = \
|
||||
keybox-file.c \
|
||||
keybox-search.c \
|
||||
keybox-update.c \
|
||||
keybox-openpgp.c \
|
||||
keybox-dump.c
|
||||
|
||||
|
||||
|
269
kbx/kbxutil.c
269
kbx/kbxutil.c
@ -1,5 +1,5 @@
|
||||
/* kbxutil.c - The Keybox utility
|
||||
* Copyright (C) 2000, 2001 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2000, 2001, 2004 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
@ -24,11 +24,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
#include "../jnlib/logging.h"
|
||||
#include "../jnlib/argparse.h"
|
||||
#include "../jnlib/stringhelp.h"
|
||||
#include "../jnlib/utf8conv.h"
|
||||
#include "../common/i18n.h"
|
||||
#include "keybox-defs.h"
|
||||
|
||||
@ -48,6 +52,7 @@ enum cmd_and_opt_values {
|
||||
aFindByKid,
|
||||
aFindByUid,
|
||||
aStats,
|
||||
aImportOpenPGP,
|
||||
|
||||
oDebug,
|
||||
oDebugAll,
|
||||
@ -66,6 +71,7 @@ static ARGPARSE_OPTS opts[] = {
|
||||
/* { aFindByKid, "find-by-kid", 0, "|KID| find key using it's keyid" }, */
|
||||
/* { aFindByUid, "find-by-uid", 0, "|NAME| find key by user name" }, */
|
||||
{ aStats, "stats", 0, "show key statistics" },
|
||||
{ aImportOpenPGP, "import-openpgp", 0, "import OpenPGP keyblocks"},
|
||||
|
||||
{ 301, NULL, 0, N_("@\nOptions:\n ") },
|
||||
|
||||
@ -135,6 +141,26 @@ i18n_init(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Used by gcry for logging */
|
||||
static void
|
||||
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
||||
{
|
||||
/* Map the log levels. */
|
||||
switch (level)
|
||||
{
|
||||
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
|
||||
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
|
||||
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
|
||||
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
|
||||
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
|
||||
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
|
||||
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
|
||||
default: level = JNLIB_LOG_ERROR; break;
|
||||
}
|
||||
log_logv (level, fmt, arg_ptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* static void */
|
||||
/* wrong_args( const char *text ) */
|
||||
@ -215,6 +241,181 @@ format_keyid ( const char *s, u32 *kid )
|
||||
}
|
||||
#endif
|
||||
|
||||
static char *
|
||||
read_file (const char *fname, size_t *r_length)
|
||||
{
|
||||
FILE *fp;
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
|
||||
if (!strcmp (fname, "-"))
|
||||
{
|
||||
size_t nread, bufsize = 0;
|
||||
|
||||
fp = stdin;
|
||||
buf = NULL;
|
||||
buflen = 0;
|
||||
#define NCHUNK 8192
|
||||
do
|
||||
{
|
||||
bufsize += NCHUNK;
|
||||
if (!buf)
|
||||
buf = xtrymalloc (bufsize);
|
||||
else
|
||||
buf = xtryrealloc (buf, bufsize);
|
||||
if (!buf)
|
||||
log_fatal ("can't allocate buffer: %s\n", strerror (errno));
|
||||
|
||||
nread = fread (buf+buflen, 1, NCHUNK, fp);
|
||||
if (nread < NCHUNK && ferror (fp))
|
||||
{
|
||||
log_error ("error reading `[stdin]': %s\n", strerror (errno));
|
||||
xfree (buf);
|
||||
return NULL;
|
||||
}
|
||||
buflen += nread;
|
||||
}
|
||||
while (nread == NCHUNK);
|
||||
#undef NCHUNK
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
fp = fopen (fname, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
log_error ("can't open `%s': %s\n", fname, strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fstat (fileno(fp), &st))
|
||||
{
|
||||
log_error ("can't stat `%s': %s\n", fname, strerror (errno));
|
||||
fclose (fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buflen = st.st_size;
|
||||
buf = xtrymalloc (buflen+1);
|
||||
if (!buf)
|
||||
log_fatal ("can't allocate buffer: %s\n", strerror (errno));
|
||||
if (fread (buf, buflen, 1, fp) != 1)
|
||||
{
|
||||
log_error ("error reading `%s': %s\n", fname, strerror (errno));
|
||||
fclose (fp);
|
||||
xfree (buf);
|
||||
return NULL;
|
||||
}
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
*r_length = buflen;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_fpr (const unsigned char *buffer, size_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < len; i++, buffer++)
|
||||
{
|
||||
if (len == 20)
|
||||
{
|
||||
if (i == 10)
|
||||
putchar (' ');
|
||||
printf (" %02X%02X", buffer[0], buffer[1]);
|
||||
i++; buffer++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i && !(i % 8))
|
||||
putchar (' ');
|
||||
printf (" %02X", buffer[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
|
||||
{
|
||||
printf ("pub %02X%02X%02X%02X",
|
||||
info->primary.keyid[4], info->primary.keyid[5],
|
||||
info->primary.keyid[6], info->primary.keyid[7] );
|
||||
dump_fpr (info->primary.fpr, info->primary.fprlen);
|
||||
putchar ('\n');
|
||||
if (info->nsubkeys)
|
||||
{
|
||||
struct _keybox_openpgp_key_info *k;
|
||||
|
||||
k = &info->subkeys;
|
||||
do
|
||||
{
|
||||
printf ("sub %02X%02X%02X%02X",
|
||||
k->keyid[4], k->keyid[5],
|
||||
k->keyid[6], k->keyid[7] );
|
||||
dump_fpr (k->fpr, k->fprlen);
|
||||
putchar ('\n');
|
||||
k = k->next;
|
||||
}
|
||||
while (k);
|
||||
}
|
||||
if (info->nuids)
|
||||
{
|
||||
struct _keybox_openpgp_uid_info *u;
|
||||
|
||||
u = &info->uids;
|
||||
do
|
||||
{
|
||||
printf ("uid\t\t%.*s\n", u->len, image + u->off);
|
||||
u = u->next;
|
||||
}
|
||||
while (u);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
import_openpgp (const char *filename)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char *buffer;
|
||||
size_t buflen, nparsed;
|
||||
unsigned char *p;
|
||||
struct _keybox_openpgp_info info;
|
||||
|
||||
buffer = read_file (filename, &buflen);
|
||||
if (!buffer)
|
||||
return;
|
||||
p = buffer;
|
||||
for (;;)
|
||||
{
|
||||
err = _keybox_parse_openpgp (p, buflen, &nparsed, &info);
|
||||
assert (nparsed <= buflen);
|
||||
if (err)
|
||||
{
|
||||
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
|
||||
break;
|
||||
log_info ("%s: failed to parse OpenPGP keyblock: %s\n",
|
||||
filename, gpg_strerror (err));
|
||||
}
|
||||
else
|
||||
{
|
||||
dump_openpgp_key (&info, p);
|
||||
_keybox_destroy_openpgp_info (&info);
|
||||
}
|
||||
p += nparsed;
|
||||
buflen -= nparsed;
|
||||
}
|
||||
xfree (buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int
|
||||
main( int argc, char **argv )
|
||||
@ -223,19 +424,22 @@ main( int argc, char **argv )
|
||||
enum cmd_and_opt_values cmd = 0;
|
||||
|
||||
set_strusage( my_strusage );
|
||||
/*log_set_name("kbxutil"); fixme */
|
||||
#if 0
|
||||
/* check that the libraries are suitable. Do it here because
|
||||
* the option parse may need services of the library */
|
||||
if ( !gcry_check_version ( "1.1.4" ) )
|
||||
gcry_control (GCRYCTL_DISABLE_SECMEM);
|
||||
log_set_prefix ("kbxutil", 1);
|
||||
set_native_charset (NULL);
|
||||
i18n_init ();
|
||||
|
||||
/* Check that the libraries are suitable. Do it here because
|
||||
the option parsing may need services of the library. */
|
||||
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
|
||||
{
|
||||
log_fatal(_("libgcrypt is too old (need %s, have %s)\n"),
|
||||
"1.1.4", gcry_check_version(NULL) );
|
||||
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
|
||||
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
|
||||
}
|
||||
#endif
|
||||
|
||||
gcry_set_log_handler (my_gcry_logger, NULL);
|
||||
|
||||
/*create_dotlock(NULL); register locking cleanup */
|
||||
i18n_init();
|
||||
|
||||
/* We need to use the gcry malloc function because jnlib does use them */
|
||||
keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
|
||||
@ -264,9 +468,10 @@ main( int argc, char **argv )
|
||||
case aFindByKid:
|
||||
case aFindByUid:
|
||||
case aStats:
|
||||
case aImportOpenPGP:
|
||||
cmd = pargs.r_opt;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
pargs.err = 2;
|
||||
break;
|
||||
@ -276,24 +481,34 @@ main( int argc, char **argv )
|
||||
myexit(2);
|
||||
|
||||
if (!cmd)
|
||||
{ /* default is to list a KBX file */
|
||||
if (!argc)
|
||||
_keybox_dump_file (NULL, 0, stdout);
|
||||
else
|
||||
{
|
||||
for (; argc; argc--, argv++)
|
||||
_keybox_dump_file (*argv, 0, stdout);
|
||||
}
|
||||
}
|
||||
{ /* Default is to list a KBX file */
|
||||
if (!argc)
|
||||
_keybox_dump_file (NULL, 0, stdout);
|
||||
else
|
||||
{
|
||||
for (; argc; argc--, argv++)
|
||||
_keybox_dump_file (*argv, 0, stdout);
|
||||
}
|
||||
}
|
||||
else if (cmd == aStats )
|
||||
{
|
||||
if (!argc)
|
||||
_keybox_dump_file (NULL, 1, stdout);
|
||||
else
|
||||
{
|
||||
for (; argc; argc--, argv++)
|
||||
_keybox_dump_file (*argv, 1, stdout);
|
||||
}
|
||||
if (!argc)
|
||||
_keybox_dump_file (NULL, 1, stdout);
|
||||
else
|
||||
{
|
||||
for (; argc; argc--, argv++)
|
||||
_keybox_dump_file (*argv, 1, stdout);
|
||||
}
|
||||
}
|
||||
else if (cmd == aImportOpenPGP)
|
||||
{
|
||||
if (!argc)
|
||||
import_openpgp ("-");
|
||||
else
|
||||
{
|
||||
for (; argc; argc--, argv++)
|
||||
import_openpgp (*argv);
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
else if ( cmd == aFindByFpr )
|
||||
|
@ -82,6 +82,40 @@ struct keybox_handle {
|
||||
};
|
||||
|
||||
|
||||
/* Openpgp helper structures. */
|
||||
struct _keybox_openpgp_key_info
|
||||
{
|
||||
struct _keybox_openpgp_key_info *next;
|
||||
unsigned char keyid[8];
|
||||
int fprlen; /* Either 16 or 20 */
|
||||
unsigned char fpr[20];
|
||||
};
|
||||
|
||||
struct _keybox_openpgp_uid_info
|
||||
{
|
||||
struct _keybox_openpgp_uid_info *next;
|
||||
size_t off;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct _keybox_openpgp_info
|
||||
{
|
||||
int is_secret; /* True if this is a secret key. */
|
||||
unsigned int nsubkeys;/* Total number of subkeys. */
|
||||
unsigned int nuids; /* Total number of user IDs in the keyblock. */
|
||||
unsigned int nsigs; /* Total number of signatures in the keyblock. */
|
||||
|
||||
/* Note, we use 2 structs here to better cope with the most common
|
||||
use of having one primary and one subkey - this allows us to
|
||||
statically allocate this structure and only malloc stuff for more
|
||||
than one subkey. */
|
||||
struct _keybox_openpgp_key_info primary;
|
||||
struct _keybox_openpgp_key_info subkeys;
|
||||
struct _keybox_openpgp_uid_info uids;
|
||||
};
|
||||
typedef struct _keybox_openpgp_info *keybox_openpgp_info_t;
|
||||
|
||||
|
||||
/* Don't know whether this is needed: */
|
||||
/* static struct { */
|
||||
/* const char *homedir; */
|
||||
@ -108,6 +142,13 @@ const char *_keybox_get_blob_image (KEYBOXBLOB blob, size_t *n);
|
||||
off_t _keybox_get_blob_fileoffset (KEYBOXBLOB blob);
|
||||
void _keybox_update_header_blob (KEYBOXBLOB blob);
|
||||
|
||||
/*-- keybox-openpgp.c --*/
|
||||
gpg_error_t _keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
|
||||
size_t *nparsed,
|
||||
keybox_openpgp_info_t info);
|
||||
void _keybox_destroy_openpgp_info (keybox_openpgp_info_t info);
|
||||
|
||||
|
||||
/*-- keybox-file.c --*/
|
||||
int _keybox_read_blob (KEYBOXBLOB *r_blob, FILE *fp);
|
||||
int _keybox_read_blob2 (KEYBOXBLOB *r_blob, FILE *fp, int *skipped_deleted);
|
||||
|
516
kbx/keybox-openpgp.c
Normal file
516
kbx/keybox-openpgp.c
Normal file
@ -0,0 +1,516 @@
|
||||
/* keybox-openpgp.c - OpenPGP key parsing
|
||||
* Copyright (C) 2001, 2003 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
|
||||
*/
|
||||
|
||||
/* This is a simple OpenPGP parser suitable for all OpenPGP key
|
||||
material. It just provides the functionality required to build and
|
||||
parse an KBX OpenPGP key blob. Thus it is not a complete parser.
|
||||
However it is self-contained and optimized for fast in-memory
|
||||
parsing. Note that we don't support old ElGamal v3 keys
|
||||
anymore. */
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "keybox-defs.h"
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
|
||||
enum packet_types
|
||||
{
|
||||
PKT_NONE =0,
|
||||
PKT_PUBKEY_ENC =1, /* public key encrypted packet */
|
||||
PKT_SIGNATURE =2, /* secret key encrypted packet */
|
||||
PKT_SYMKEY_ENC =3, /* session key packet (OpenPGP)*/
|
||||
PKT_ONEPASS_SIG =4, /* one pass sig packet (OpenPGP)*/
|
||||
PKT_SECRET_KEY =5, /* secret key */
|
||||
PKT_PUBLIC_KEY =6, /* public key */
|
||||
PKT_SECRET_SUBKEY =7, /* secret subkey (OpenPGP) */
|
||||
PKT_COMPRESSED =8, /* compressed data packet */
|
||||
PKT_ENCRYPTED =9, /* conventional encrypted data */
|
||||
PKT_MARKER =10, /* marker packet (OpenPGP) */
|
||||
PKT_PLAINTEXT =11, /* plaintext data with filename and mode */
|
||||
PKT_RING_TRUST =12, /* keyring trust packet */
|
||||
PKT_USER_ID =13, /* user id packet */
|
||||
PKT_PUBLIC_SUBKEY =14, /* public subkey (OpenPGP) */
|
||||
PKT_OLD_COMMENT =16, /* comment packet from an OpenPGP draft */
|
||||
PKT_ATTRIBUTE =17, /* PGP's attribute packet */
|
||||
PKT_ENCRYPTED_MDC =18, /* integrity protected encrypted data */
|
||||
PKT_MDC =19, /* manipulation detection code packet */
|
||||
PKT_COMMENT =61, /* new comment packet (private) */
|
||||
PKT_GPG_CONTROL =63 /* internal control packet */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Assume a valid OpenPGP packet at the address pointed to by BUFBTR
|
||||
which is of amaximum length as stored at BUFLEN. Return the header
|
||||
information of that packet and advance the pointer stored at BUFPTR
|
||||
to the next packet; also adjust the length stored at BUFLEN to
|
||||
match the remaining bytes. If there are no more packets, store NULL
|
||||
at BUFPTR. Return an non-zero error code on failure or the
|
||||
follwing data on success:
|
||||
|
||||
R_DATAPKT = Pointer to the begin of the packet data.
|
||||
R_DATALEN = Length of this data. This has already been checked to fit
|
||||
into the buffer.
|
||||
R_PKTTYPE = The packet type.
|
||||
R_NTOTAL = The total number of bytes of this packet
|
||||
|
||||
Note that these values are only updated on success.
|
||||
*/
|
||||
static gpg_error_t
|
||||
next_packet (unsigned char const **bufptr, size_t *buflen,
|
||||
unsigned char const **r_data, size_t *r_datalen, int *r_pkttype,
|
||||
size_t *r_ntotal)
|
||||
{
|
||||
const unsigned char *buf = *bufptr;
|
||||
size_t len = *buflen;
|
||||
int c, ctb, pkttype;
|
||||
unsigned long pktlen;
|
||||
|
||||
if (!len)
|
||||
return gpg_error (GPG_ERR_NO_DATA);
|
||||
|
||||
ctb = *buf++; len--;
|
||||
if ( !(ctb & 0x80) )
|
||||
return gpg_error (GPG_ERR_INV_PACKET); /* Invalid CTB. */
|
||||
|
||||
pktlen = 0;
|
||||
if ((ctb & 0x40)) /* New style (OpenPGP) CTB. */
|
||||
{
|
||||
pkttype = (ctb & 0x3f);
|
||||
if (!len)
|
||||
return gpg_error (GPG_ERR_INV_PACKET); /* No 1st length byte. */
|
||||
c = *buf++; len--;
|
||||
if (pkttype == PKT_COMPRESSED)
|
||||
return gpg_error (GPG_ERR_UNEXPECTED); /* ... packet in a keyblock. */
|
||||
if ( c < 192 )
|
||||
pktlen = c;
|
||||
else if ( c < 224 )
|
||||
{
|
||||
pktlen = (c - 192) * 256;
|
||||
if (!len)
|
||||
return gpg_error (GPG_ERR_INV_PACKET); /* No 2nd length byte. */
|
||||
c = *buf++; len--;
|
||||
pktlen += c + 192;
|
||||
}
|
||||
else if (c == 255)
|
||||
{
|
||||
if (len <4 )
|
||||
return gpg_error (GPG_ERR_INV_PACKET); /* No length bytes. */
|
||||
pktlen = (*buf++) << 24;
|
||||
pktlen |= (*buf++) << 16;
|
||||
pktlen |= (*buf++) << 8;
|
||||
pktlen |= (*buf++);
|
||||
len -= 4;
|
||||
}
|
||||
else /* Partial length encoding is not allowed for key packets. */
|
||||
return gpg_error (GPG_ERR_UNEXPECTED);
|
||||
}
|
||||
else /* Old style CTB. */
|
||||
{
|
||||
int lenbytes;
|
||||
|
||||
pktlen = 0;
|
||||
pkttype = (ctb>>2)&0xf;
|
||||
lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
|
||||
if (!lenbytes) /* Not allowed in key packets. */
|
||||
return gpg_error (GPG_ERR_UNEXPECTED);
|
||||
if (len < lenbytes)
|
||||
return gpg_error (GPG_ERR_INV_PACKET); /* Not enough length bytes. */
|
||||
for (; lenbytes; lenbytes--)
|
||||
{
|
||||
pktlen <<= 8;
|
||||
pktlen |= *buf++; len--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do some basic sanity check. */
|
||||
switch (pkttype)
|
||||
{
|
||||
case PKT_SIGNATURE:
|
||||
case PKT_SECRET_KEY:
|
||||
case PKT_PUBLIC_KEY:
|
||||
case PKT_SECRET_SUBKEY:
|
||||
case PKT_MARKER:
|
||||
case PKT_RING_TRUST:
|
||||
case PKT_USER_ID:
|
||||
case PKT_PUBLIC_SUBKEY:
|
||||
case PKT_OLD_COMMENT:
|
||||
case PKT_ATTRIBUTE:
|
||||
case PKT_COMMENT:
|
||||
case PKT_GPG_CONTROL:
|
||||
break; /* Okay these are allowed packets. */
|
||||
default:
|
||||
return gpg_error (GPG_ERR_UNEXPECTED);
|
||||
}
|
||||
|
||||
if (pktlen == 0xffffffff)
|
||||
return gpg_error (GPG_ERR_INV_PACKET);
|
||||
|
||||
if (pktlen > len)
|
||||
return gpg_error (GPG_ERR_INV_PACKET); /* Packet length header too long. */
|
||||
|
||||
*r_data = buf;
|
||||
*r_datalen = pktlen;
|
||||
*r_pkttype = pkttype;
|
||||
*r_ntotal = (buf - *bufptr) + pktlen;
|
||||
|
||||
*bufptr = buf + pktlen;
|
||||
*buflen = len - pktlen;
|
||||
if (!*buflen)
|
||||
*bufptr = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Parse a key packet and store the ionformation in KI. */
|
||||
static gpg_error_t
|
||||
parse_key (const unsigned char *data, size_t datalen,
|
||||
struct _keybox_openpgp_key_info *ki)
|
||||
{
|
||||
gpg_error_t err;
|
||||
const unsigned char *data_start = data;
|
||||
int i, version, algorithm;
|
||||
size_t n;
|
||||
unsigned long timestamp, expiredate;
|
||||
int npkey;
|
||||
unsigned char hashbuffer[768];
|
||||
const unsigned char *mpi_n = NULL;
|
||||
size_t mpi_n_len = 0, mpi_e_len = 0;
|
||||
gcry_md_hd_t md;
|
||||
|
||||
if (datalen < 5)
|
||||
return gpg_error (GPG_ERR_INV_PACKET);
|
||||
version = *data++; datalen--;
|
||||
if (version < 2 || version > 4 )
|
||||
return gpg_error (GPG_ERR_INV_PACKET); /* Invalid version. */
|
||||
|
||||
timestamp = ((data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3]));
|
||||
data +=4; datalen -=4;
|
||||
|
||||
if (version < 4)
|
||||
{
|
||||
unsigned short ndays;
|
||||
|
||||
if (datalen < 2)
|
||||
return gpg_error (GPG_ERR_INV_PACKET);
|
||||
ndays = ((data[0]<<8)|(data[1]));
|
||||
data +=2; datalen -= 2;
|
||||
if (ndays)
|
||||
expiredate = ndays? (timestamp + ndays * 86400L) : 0;
|
||||
}
|
||||
else
|
||||
expiredate = 0; /* This is stored in the self-signature. */
|
||||
|
||||
if (!datalen)
|
||||
return gpg_error (GPG_ERR_INV_PACKET);
|
||||
algorithm = *data++; datalen--;
|
||||
|
||||
switch (algorithm)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3: /* RSA */
|
||||
npkey = 2;
|
||||
break;
|
||||
case 16:
|
||||
case 20: /* Elgamal */
|
||||
npkey = 3;
|
||||
break;
|
||||
case 17: /* DSA */
|
||||
npkey = 4;
|
||||
break;
|
||||
default: /* Unknown algorithm. */
|
||||
return gpg_error (GPG_ERR_UNKNOWN_ALGORITHM);
|
||||
}
|
||||
|
||||
for (i=0; i < npkey; i++ )
|
||||
{
|
||||
unsigned int nbits, nbytes;
|
||||
|
||||
if (datalen < 2)
|
||||
return gpg_error (GPG_ERR_INV_PACKET);
|
||||
nbits = ((data[0]<<8)|(data[1]));
|
||||
data += 2; datalen -=2;
|
||||
nbytes = (nbits+7) / 8;
|
||||
if (datalen < nbytes)
|
||||
return gpg_error (GPG_ERR_INV_PACKET);
|
||||
/* For use by v3 fingerprint calculation we need to know the RSA
|
||||
modulus and exponent. */
|
||||
if (i==0)
|
||||
{
|
||||
mpi_n = data;
|
||||
mpi_n_len = nbytes;
|
||||
}
|
||||
else if (i==1)
|
||||
mpi_e_len = nbytes;
|
||||
|
||||
data += nbytes; datalen -= nbytes;
|
||||
}
|
||||
n = data - data_start;
|
||||
|
||||
if (version < 4)
|
||||
{
|
||||
/* We do not support any other algorithm than RSA in v3
|
||||
packets. */
|
||||
if (algorithm < 1 || algorithm > 3)
|
||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||
|
||||
err = gcry_md_open (&md, GCRY_MD_MD5, 0);
|
||||
if (err)
|
||||
return err; /* Oops */
|
||||
gcry_md_write (md, mpi_n, mpi_n_len);
|
||||
gcry_md_write (md, mpi_n+mpi_n_len+2, mpi_e_len);
|
||||
memcpy (ki->fpr, gcry_md_read (md, 0), 16);
|
||||
gcry_md_close (md);
|
||||
ki->fprlen = 16;
|
||||
|
||||
if (mpi_n_len < 8)
|
||||
{
|
||||
/* Moduli less than 64 bit are out of the specs scope. Zero
|
||||
them out becuase this is what gpg does too. */
|
||||
memset (ki->keyid, 0, 8);
|
||||
}
|
||||
else
|
||||
memcpy (ki->keyid, mpi_n + mpi_n_len - 8, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Its a pitty that we need to prefix the buffer with the tag
|
||||
and a length header: We can't simply psss it to the fast
|
||||
hashing fucntion for that reason. It might be a good idea to
|
||||
have a scatter-gather enabled hash function. What we do here
|
||||
is to use a static buffer if this one is large enough and
|
||||
only use the regular hash fucntions if this buffer is not
|
||||
large enough. */
|
||||
if ( 3 + n < sizeof hashbuffer )
|
||||
{
|
||||
hashbuffer[0] = 0x99; /* CTB */
|
||||
hashbuffer[1] = (n >> 8); /* 2 byte length header. */
|
||||
hashbuffer[2] = n;
|
||||
memcpy (hashbuffer + 3, data_start, n);
|
||||
gcry_md_hash_buffer (GCRY_MD_SHA1, ki->fpr, hashbuffer, 3 + n);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = gcry_md_open (&md, GCRY_MD_SHA1, 0);
|
||||
if (err)
|
||||
return err; /* Oops */
|
||||
gcry_md_putc (md, 0x99 ); /* CTB */
|
||||
gcry_md_putc (md, (n >> 8) ); /* 2 byte length header. */
|
||||
gcry_md_putc (md, n );
|
||||
gcry_md_write (md, data_start, n);
|
||||
memcpy (ki->fpr, gcry_md_read (md, 0), 20);
|
||||
gcry_md_close (md);
|
||||
}
|
||||
ki->fprlen = 20;
|
||||
memcpy (ki->keyid, ki->fpr+12, 8);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* The caller must pass the address of an INFO structure which will
|
||||
get filled on success with information pertaining to the OpenPGP
|
||||
keyblock IMAGE of length IMAGELEN. Note that a caller does only
|
||||
need to release this INFO structure when the function returns
|
||||
success. If NPARSED is not NULL the actual number of bytes parsed
|
||||
will be stored at this address. */
|
||||
gpg_error_t
|
||||
_keybox_parse_openpgp (const unsigned char *image, size_t imagelen,
|
||||
size_t *nparsed,
|
||||
keybox_openpgp_info_t info)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
const unsigned char *image_start, *data;
|
||||
size_t n, datalen;
|
||||
int pkttype;
|
||||
int first = 1;
|
||||
struct _keybox_openpgp_key_info *k, **ktail = NULL;
|
||||
struct _keybox_openpgp_uid_info *u, **utail = NULL;
|
||||
|
||||
memset (info, 0, sizeof *info);
|
||||
if (nparsed)
|
||||
*nparsed = 0;
|
||||
|
||||
image_start = image;
|
||||
while (image)
|
||||
{
|
||||
err = next_packet (&image, &imagelen, &data, &datalen, &pkttype, &n);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
if (first)
|
||||
{
|
||||
if (pkttype == PKT_PUBLIC_KEY)
|
||||
;
|
||||
else if (pkttype == PKT_SECRET_KEY)
|
||||
info->is_secret = 1;
|
||||
else
|
||||
{
|
||||
err = gpg_error (GPG_ERR_UNEXPECTED);
|
||||
break;
|
||||
}
|
||||
first = 0;
|
||||
}
|
||||
else if (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY)
|
||||
break; /* Next keyblock encountered - ready. */
|
||||
|
||||
if (nparsed)
|
||||
*nparsed += n;
|
||||
|
||||
if (pkttype == PKT_SIGNATURE)
|
||||
{
|
||||
/* For now we only count the total number of signatures. */
|
||||
info->nsigs++;
|
||||
}
|
||||
else if (pkttype == PKT_USER_ID)
|
||||
{
|
||||
info->nuids++;
|
||||
if (info->nuids == 1)
|
||||
{
|
||||
info->uids.off = data - image_start;
|
||||
info->uids.len = datalen;
|
||||
utail = &info->uids.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
u = xtrycalloc (1, sizeof *u);
|
||||
if (!u)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
break;
|
||||
}
|
||||
u->off = data - image_start;
|
||||
u->len = datalen;
|
||||
*utail = u;
|
||||
utail = &u->next;
|
||||
}
|
||||
}
|
||||
else if (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY)
|
||||
{
|
||||
err = parse_key (data, datalen, &info->primary);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
else if( pkttype == PKT_PUBLIC_SUBKEY && datalen && *data == '#' )
|
||||
{
|
||||
/* Early versions of GnuPG used old PGP comment packets;
|
||||
* luckily all those comments are prefixed by a hash
|
||||
* sign - ignore these packets. */
|
||||
}
|
||||
else if (pkttype == PKT_PUBLIC_SUBKEY || pkttype == PKT_SECRET_SUBKEY)
|
||||
{
|
||||
info->nsubkeys++;
|
||||
if (info->nsubkeys == 1)
|
||||
{
|
||||
err = parse_key (data, datalen, &info->subkeys);
|
||||
if (err)
|
||||
{
|
||||
info->nsubkeys--;
|
||||
if (gpg_err_code (err) != GPG_ERR_UNKNOWN_ALGORITHM)
|
||||
break;
|
||||
/* We ignore subkeys with unknown algorithms. */
|
||||
}
|
||||
else
|
||||
ktail = &info->subkeys.next;
|
||||
}
|
||||
else
|
||||
{
|
||||
k = xtrycalloc (1, sizeof *k);
|
||||
if (!k)
|
||||
{
|
||||
err = gpg_error_from_errno (errno);
|
||||
break;
|
||||
}
|
||||
err = parse_key (data, datalen, k);
|
||||
if (err)
|
||||
{
|
||||
xfree (k);
|
||||
info->nsubkeys--;
|
||||
if (gpg_err_code (err) != GPG_ERR_UNKNOWN_ALGORITHM)
|
||||
break;
|
||||
/* We ignore subkeys with unknown algorithms. */
|
||||
}
|
||||
else
|
||||
{
|
||||
*ktail = k;
|
||||
ktail = &k->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
_keybox_destroy_openpgp_info (info);
|
||||
if (!first
|
||||
&& (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM
|
||||
|| gpg_err_code (err) == GPG_ERR_UNKNOWN_ALGORITHM))
|
||||
{
|
||||
/* We are able to skip to the end of this keyblock. */
|
||||
while (image)
|
||||
{
|
||||
if (next_packet (&image, &imagelen,
|
||||
&data, &datalen, &pkttype, &n) )
|
||||
break; /* Another error - stop here. */
|
||||
|
||||
if (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_SECRET_KEY)
|
||||
break; /* Next keyblock encountered - ready. */
|
||||
|
||||
if (nparsed)
|
||||
*nparsed += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Release any malloced data in INFO but not INFO itself! */
|
||||
void
|
||||
_keybox_destroy_openpgp_info (keybox_openpgp_info_t info)
|
||||
{
|
||||
struct _keybox_openpgp_key_info *k, *k2;
|
||||
struct _keybox_openpgp_uid_info *u, *u2;
|
||||
|
||||
assert (!info->primary.next);
|
||||
for (k=info->subkeys.next; k; k = k2)
|
||||
{
|
||||
k2 = k->next;
|
||||
xfree (k);
|
||||
}
|
||||
|
||||
for (u=info->uids.next; u; u = u2)
|
||||
{
|
||||
u2 = u->next;
|
||||
xfree (u);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user