From d1b6e0ce0dd8d06ed35a042d446afd2aea841f5c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 21 Feb 2000 21:38:43 +0000 Subject: [PATCH] See ChangeLog: Mon Feb 21 22:43:01 CET 2000 Werner Koch --- g10/ChangeLog | 5 + g10/kbx.h | 26 +++ g10/kbxblob.c | 562 ++++++++++++++++++++++++++++++++++++++++++++++++ tools/ChangeLog | 1 - 4 files changed, 593 insertions(+), 1 deletion(-) create mode 100644 g10/kbx.h create mode 100644 g10/kbxblob.c diff --git a/g10/ChangeLog b/g10/ChangeLog index 9dfd16b54..21b24880e 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,8 @@ +Mon Feb 21 22:43:01 CET 2000 Werner Koch + + * kbx.h: New. + * kbxblob.c: Started to work on the keybox stuff. + Mon Jan 31 16:37:34 CET 2000 Werner Koch * keygen.c (gen_dsa): Modified to work with gcry_pk_genkey. diff --git a/g10/kbx.h b/g10/kbx.h new file mode 100644 index 000000000..9e61ebbd0 --- /dev/null +++ b/g10/kbx.h @@ -0,0 +1,26 @@ +/* kbx.h - The GnuPG Keybox + * Copyright (C) 2000 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 GPG_KBX_H +#define GPG_KBX_H 1 + + + +#endif /*GPG_KBX_H*/ diff --git a/g10/kbxblob.c b/g10/kbxblob.c new file mode 100644 index 000000000..cf3bb7a22 --- /dev/null +++ b/g10/kbxblob.c @@ -0,0 +1,562 @@ +/* kbxblob.c - KBX Blob handling + * Copyright (C) 2000 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 + * + *********************************************** + * adler_cksum() is based on the zlib code with these conditions: + * + * Copyright (C) 1995-1998 Mark Adler + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + + +/* The keybox data formats + +The KeyBox uses an augmented OpenPGP key format. This makes random +access to a keyblock easier and also gives the opportunity to store +additional information (e.g. the fingerprint) along with the key. +All integers are stored in network byte order, offsets are counted from +the beginning of the Blob. + +The first record of a plain KBX file has a special format: + + u32 length of the first record + u32 magic 'KBXf' + byte version number (1) + b[3] reserved + byte marginals used for validity calculation of this file + byte completes ditto. + byte cert_depth ditto. + +The standard KBX Blob looks like this: + + u32 length of this blob (including these 4 bytes) + byte version number (1) + byte reserved + u16 Blob flags + bit 0 = contains secret key material + + u32 offset to the OpenPGP keyblock + u32 length of the keyblock + u16 number of keys (at least 1!) + u16 size of additional key information + n times: + b20 The keys fingerprint + (fingerprints are always 20 bytes, MD5 left padded with zeroes) + u32 offset to the n-th key's keyID (a keyID is always 8 byte) + u16 special key flags + bit 0 = + u16 reserved + u16 number of user IDs + u16 size of additional user ID information + n times: + u32 offset to the n-th user ID + u32 length of this user ID. + u16 special user ID flags. + bit 0 = + byte validity + byte reserved + u16 number of signatures + u16 size of signature information (4) + u32 expiration time of signature with some special values: + 0x00000000 = not checked + 0x00000001 = missing key + 0x00000002 = bad signature + 0x10000000 = valid and expires at some date in 1978. + 0xffffffff = valid and does not expire + u8 assigned ownertrust + u8 all_validity + u16 reserved + u32 recheck_after + u32 size of reserved space (not including this field) + reserved space + + Here we might want to put other data + + Here comes the keyblock + + maybe we put a sigture here later. + + u32 adler checksum + * + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "kbx.h" + +/* special values of the signature status */ +#define SF_NONE(a) ( !(a) ) +#define SF_NOKEY(a) ((a) & (1<<0)) +#define SF_BAD(a) ((a) & (1<<1)) +#define SF_VALID(a) ((a) & (1<<29)) + +#if MAX_FINGERPRINT_LEN < 20 + #error fingerprints are 20 bytes +#endif + +struct kbxblob_key { + char fpr[20]; + u32 off_kid; + ulong off_kid_addr; /* where it is stored in the datablock */ + u16 flags; +}; +struct kbxblob_uid { + u32 off; + ulong off_addr; /* where it is stored in the datablock */ + u32 len; + u16 flags; + byte validity +}; + +struct keyid_list { + struct keyid_list *next; + int seqno; + char kid[8]; +} + +struct kbxblob { + int nkeys; + struct kbxblob_key *keys; + int nuids; + struct kbxblob_uid *uids; + int nsigs; + u32 *sigs; + + struct keyid_list *temp_kids; +} *KBXBLOB; + + +static u32 +adler_cksum ( const byte *buf, size_t len ) +{ + unsigned long s1 = 1; + unsigned long s2 = 0; + + #define X_BASE 65521L /* largest prime smaller than 65536 */ + #define X_NMAX 5552 /* largest n such that */ + /* 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + #define X_DO1(buf,i) {s1 += buf[i]; s2 += s1;} + #define X_DO2(buf,i) X_DO1(buf,i); X_DO1(buf,i+1); + #define X_DO4(buf,i) X_DO2(buf,i); X_DO2(buf,i+2); + #define X_DO8(buf,i) X_DO4(buf,i); X_DO4(buf,i+4); + #define X_DO16(buf) X_DO8(buf,0); X_DO8(buf,8); + while ( len ) { + int k = len < NMAX ? len : NMAX; + len -= k; + while ( k >= 16 ) { + DO16(buf); + buf += 16; + k -= 16; + } + if ( k ) { + do { + s1 += *buf++; + s2 += s1; + } while ( --k ); + } + s1 %= BASE; + s2 %= BASE; + } + #undef X_BASE + #undef X_NMAX + #undef X_DO1 + #undef X_DO2 + #undef X_DO4 + #undef X_DO8 + #undef X_DO16 + return ( s2 << 16 ) | s1; +} + + +/* Note: this functions are only used for temportay iobufs and therefore + * they can't fail */ +static void +put8 ( IOBUF out, byte a ) +{ + iobuf_put ( out, a ) +} + +static void +put16 ( IOBUF out, u16 a ) +{ + iobuf_put ( out, a>>8 ); + iobuf_put ( out, a ) +} + +static void +put32 ( IOBUF out, u32 a ) +{ + iobuf_put (out, a>> 24); + iobuf_put (out, a>> 16); + iobuf_put (out, a>> 8); + iobuf_put (out, a) ) +} + +static void +putn ( IOBUF out, const byte *p, size_t n ) +{ + for ( ; n; p++, n-- ) { + iobuf_put ( out, *p ); + } +} + +/**************** + * special version of put 32, which is used to fixup a value at file offset OFF + */ +static void +put32at ( IOBUF out, u32 a, size_t pos ) +{ + size_t n; + byte *p, + + iobuf_flush_temp ( out ); + p = iobuf_get_temp_buffer( out ); + n = iobuf_get_temp_length( out ); + assert( n >= pos+4 ); + p[0] = a >> 24 ; + p[1] = a >> 16 ; + p[2] = a >> 8 ; + p[3] = a ; +} + + +/**************** + * We must store the keyid at some place becuase we can't calculate the + * offset yet. This is only used for v3 keyIDs. Function returns an index + * value for later fixupd; this must be a non-zero value + */ +static int +temp_store_kid ( KBXBLOB blob, PKT_public_key *pk ) +{ + struct keyid_list *k, *r; + + k = gcry_xmalloc ( sizeof *k ); + k->kid = pk->keyid; + k->seqno = 0; + k->next = blob->temp_kids; + blob->temp_kids = k; + for ( r=k; r; r = r->next ) { + k->seqno++ + } + + return k->seqno; +} + +static void +put_stored_kid( IOBUF a, KBXBLOB blob, int seqno ) +{ + struct keyid_list *r; + + for ( r = blob->temp_kids; r; r = r->next ) { + if( r->seqno == seqno ) { + putn ( a, r->kid, 8 ); + return; + } + } + BUG(); +} + +static int +release_kid_list ( struct keyid_list *kl ) +{ + struct keyid_list *r, *r2; + + for ( r = blob->temp_kids; r; r = r2 ) { + r2 = r->next; + gcry_free( r ); + } +} + + +static int +create_key_part( KBXBLOB blob, KBNODE keyblock ) +{ + KBNODE node; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + int n; + + for ( n=0, node = keyblock; node; node = node->next ) { + if ( node->pkt->pkttype == PKT_PUBLIC_KEY: + || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { + PKT_public_key *pk = node->pkt->pkt.public_key; + + fingerprint_from_pk( pk, blob->keys[n].fpr, &fprlen ); + if ( fprlen != 20 ) { /*v3 fpr - shift right and fill with zeroes*/ + assert( fprlen == 16 ); + memmove( blob->keys[n]+4, blob->keys[n].fpr, 16); + memset( blob->keys[n].fpr, 0, 4 ); + blob->keys[n].off_kid = temp_store_kid( blob, pk ); + } + else { + blob->keys[n].off_kid = 0; /* will be fixed up later */ + } + blob->keys[n].flags = 0; + n++; + } + else if ( node->pkt->pkttype == PKT_SECRET_KEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { + BUG(); /* not yet implemented */ + } + } + assert( n == blob->nkeys ); + return 0; +} + +static int +create_uid_part( KBXBLOB blob, KBNODE keyblock ) +{ + KBNODE node; + int n; + + for ( n=0, node = keyblock; node; node = node->next ) { + if ( node->pkt->pkttype == PKT_USER_ID ) { + PKT_user_id *u = node->pkt->pkt.user_id; + + blob->uids[n].off = 0; /* must calculate this later */ + blob->uids[n].len = u->len; + blob->uids[n].flags = 0; + blob->uids[n].validity = 0; + n++; + } + } + assert( n == blob->nuids ); + return 0; +} + +static int +create_sig_part( KBXBLOB blob, KBNODE keyblock ) +{ + KBNODE node; + int n; + + for ( n=0, node = keyblock; node; node = node->next ) { + if ( node->pkt->pkttype == PKT_SIGNATURE ) { + PKT_signature *sig = node->pkt->pkt.signature; + + blob->sigs[n] = 0; /* FIXME: check the signature here */ + n++; + } + } + assert( n == blob->nsigs ); + return 0; +} + + +static int +create_blob_header( IOBUF a, KBXBLOB blob ) +{ + int i; + + put32 ( a, 0 ); /* blob length, needs fixup */ + put8 ( a, 1 ); /* version number */ + put8 ( a, 0 ); /* reserved */ + put16 ( a, 0 ); /* blob flags */ + + put32 ( a, 0 ); /* offset to the keyblock, needs fixup */ + put32 ( a, 0 ); /* length of the keyblock, needs fixup */ + + put16 ( a, blob->nkeys ); + put16 ( a, 20 + 8 + 2 + 2 ); /* size of key info */ + for ( i=0; i < blob->nkeys; i++ ) { + putn ( a, blob->keys[i].fpr, 20 ); + blob->keys[i].off_kid_addr = iobuf_tell ( out ); + put32 ( a, 0 ); /* offset to keyid, fixed up later */ + put16 ( a, blob->keys[i].flags ); + put16 ( a, 0 ); /* reserved */ + } + + put16 ( a, blob->nuids ); + put16 ( a, 4 + 4 + 2 + 1 + 1 ); /* size of uid info */ + for ( i=0; i < blob->nuids; i++ ) { + blob->uids[i].off_addr = iobuf_tell ( out ); + put32 ( a, 0 ); /* offset to userid, fixed up later */ + put32 ( a, blob->uids[i].len ); + put16 ( a, blob->uids[i].flags ); + put8 ( a, 0 ); /* validity */ + put8 ( a, 0 ); /* reserved */ + } + + put16 ( a, blob->nsigs ); + put16 ( a, 4 ); /* size of sig info */ + for ( i=0; i < blob->nsigs; i++ ) { + put32 ( a, blob->sigs[i] ); + } + + put8 ( a, 0 ); /* assigned ownertrust */ + put8 ( a, 0 ); /* validity of all user IDs */ + put16 ( a, 0 ); /* reserved */ + put32 ( a, 0 ); /* time of next recheck */ + put32 ( a, 0 ); /* size of reserved space */ + /* reserved space (which is currently 0) */ + + /* We need to store the keyids for all v3 keys because those key IDs are + * not part of the fingerprint. While we are doing that, we fixup all + * the keyID offsets */ + for ( i=0; i < blob->nkeys; i++ ) { + if ( blob->keys[i].off_kid ) { /* this is a v3 one */ + put32at ( a, iobuf_tell(), blob->keys[i].off_kid_addr ); + put_stored_kid ( a, blob, blob->keys[i].off_kid ); + } + else { /* the better v4 key IDs - just store an offset 8 bytes back */ + put32at ( a, blob->keys[i].off_kid_addr-8, + blob->keys[i].off_kid_addr ); + } + } + + + return 0; +} + +static int +create_blob_keyblock( IOBUF a, KBXBLOB blob, KBNODE keyblock ) +{ + KBNODE node; + int rc; + + for ( node = keyblock; node; node = node->next ) { + /* we must get some offset into thge keyblock and do some + * fixups */ + rc = build_packet ( fp, node->pkt ); + if ( rc ) { + log_error("build_packet(%d) for kbxblob failed: %s\n", + node->pkt->pkttype, gpg_errstr(rc) ); + return GPGERR_WRITE_FILE; + } + } + return 0; +} + +static int +create_blob_trailer( IOBUF a, KBXBLOB blob ) +{ + return 0; +} + +static int +create_blob_finish( IOBUF a, KBXBLOB blob ) +{ + + + /* fixup the length */ + /* optionally we could sign the whole stuff at this point */ + /* write the checksum */ + return 0; +} + + +int +kbx_create_blob ( KBXBLOB *retkbx, KBNODE keyblock ) +{ + int rc = 0; + KBNODE node; + struct kbxblob *blob; + int nkey, nuid, nsig; + IOBUF a = NULL; + + *retkbx = NULL; + blob = gcry_calloc (1, sizeof *blob ); + if( !blob ) + return GCRYERR_NO_MEM; + + /* fixme: Do some sanity checks on the keyblock */ + + /* count userids and keys so that we can allocate the arrays */ + for ( node = keyblock; node; node = node->next ) { + switch ( node->pkt->pkttype ) { + case PKT_PUBLIC_KEY: + case PKT_SECRET_KEY: + case PKT_PUBLIC_SUBKEY: + case PKT_SECRET_SUBKEY: blob->nkeys++; break; + case PKT_USER_ID: blob->nuids++; break; + case PKT_SIGNATURE: blob->nsigs++, break; + } + } + blob->nkeys = gcry_calloc ( blob->nkeys, sizeof ( blob->keys ) ); + blob->nuids = gcry_calloc ( blob->nuids, sizeof ( blob->uids ) ); + blob->nsigs = gcry_calloc ( blob->nsigs, sizeof ( blob->sigs ) ); + if ( !blob->nkeys || !blob->nuids || !blob->nsigs ) { + rc = GCRYERR_NO_MEM; + goto leave; + } + + rc = create_key_part ( blob, keyblock ), + if( rc ) + goto leave; + rc = create_uid_part ( blob, keyblock ), + if( rc ) + goto leave; + rc = create_sig_part ( blob, keyblock ), + if( rc ) + goto leave; + + a = iobuf_temp(); + rc = create_blob_header ( a, blob ); + if( rc ) + goto leave; + rc = create_blob_keyblock ( a, blob, keyblock ); + if( rc ) + goto leave; + rc = create_blob_trailer ( a, blob ); + if( rc ) + goto leave; + rc = create_blob_finish ( a, blob ); + if( rc ) + goto leave; + + /* now we take the iobuf and copy it to the output */ + + *retkbx = blob; + + failure: + if( a ) + iobuf_cancel( a ); + release_kid_list( blob->temp_kids ); + if ( rc ) { + gcry_free( blob->nkeys ); + gcry_free( blob->nuids ); + gcry_free( blob->nsigs ); + gcry_free( blob ); + } + return rc; +} + + diff --git a/tools/ChangeLog b/tools/ChangeLog index a5566e84b..0227d0721 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -6,7 +6,6 @@ Mon Jan 31 17:46:35 CET 2000 Werner Koch Thu Jul 8 16:21:27 CEST 1999 Werner Koch - * lspgpot: New Wed Jul 7 13:08:40 CEST 1999 Werner Koch