summaryrefslogtreecommitdiffstats
path: root/kbx/keybox-blob.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:14:06 +0000
commiteee068778cb28ecf3c14e1bf843a95547d72c42d (patch)
tree0e07b30ddc5ea579d682d5dbe57998200d1c9ab7 /kbx/keybox-blob.c
parentInitial commit. (diff)
downloadgnupg2-upstream/2.2.40.tar.xz
gnupg2-upstream/2.2.40.zip
Adding upstream version 2.2.40.upstream/2.2.40upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'kbx/keybox-blob.c')
-rw-r--r--kbx/keybox-blob.c1057
1 files changed, 1057 insertions, 0 deletions
diff --git a/kbx/keybox-blob.c b/kbx/keybox-blob.c
new file mode 100644
index 0000000..ac259ea
--- /dev/null
+++ b/kbx/keybox-blob.c
@@ -0,0 +1,1057 @@
+/* keybox-blob.c - KBX Blob handling
+ * Copyright (C) 2000, 2001, 2002, 2003, 2008 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+* The keybox data format
+
+ The KeyBox uses an augmented OpenPGP/X.509 key format. This makes
+ random access to a keyblock/certificate 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.
+
+** Overview of blob types
+
+ | Byte 4 | Blob type |
+ |--------+--------------|
+ | 0 | Empty blob |
+ | 1 | First blob |
+ | 2 | OpenPGP blob |
+ | 3 | X.509 blob |
+
+** The First blob
+
+ The first blob of a plain KBX file has a special format:
+
+ - u32 Length of this blob
+ - byte Blob type (1)
+ - byte Version number (1)
+ - u16 Header flags
+ bit 0 - RFU
+ bit 1 - Is being or has been used for OpenPGP blobs
+ - b4 Magic 'KBXf'
+ - u32 RFU
+ - u32 file_created_at
+ - u32 last_maintenance_run
+ - u32 RFU
+ - u32 RFU
+
+** The OpenPGP and X.509 blobs
+
+ The OpenPGP and X.509 blobs are very similar, things which are
+ X.509 specific are noted like [X.509: xxx]
+
+ - u32 Length of this blob (including these 4 bytes)
+ - byte Blob type
+ 2 = OpenPGP
+ 3 = X509
+ - byte Version number of this blob type
+ 1 = The only defined value
+ - u16 Blob flags
+ bit 0 = contains secret key material (not used)
+ bit 1 = ephemeral blob (e.g. used while querying external resources)
+ - u32 Offset to the OpenPGP keyblock or the X.509 DER encoded
+ certificate
+ - u32 The length of the keyblock or certificate
+ - u16 [NKEYS] Number of keys (at least 1!) [X509: always 1]
+ - u16 Size of the key information structure (at least 28).
+ - NKEYS times:
+ - b20 The fingerprint of the key.
+ 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)
+ or 0 if not known which is the case only for X.509.
+ - u16 Key flags
+ bit 0 = qualified signature (not yet implemented}
+ - u16 RFU
+ - bN Optional filler up to the specified length of this
+ structure.
+ - u16 Size of the serial number (may be zero)
+ - bN The serial number. N as giiven above.
+ - u16 Number of user IDs
+ - u16 [NUIDS] Size of user ID information structure
+ - NUIDS times:
+
+ For X509, the first user ID is the Issuer, the second the
+ Subject and the others are subjectAltNames. For OpenPGP we only
+ store the information from UserID packets here.
+
+ - u32 Blob offset to the n-th user ID
+ - u32 Length of this user ID.
+ - u16 User ID flags.
+ (not yet used)
+ - byte Validity
+ - byte RFU
+
+ - u16 [NSIGS] Number of signatures
+ - u16 Size of signature information (4)
+ - NSIGS times:
+ - u32 Expiration time of signature with some special values.
+ Since version 2.1.20 these special valuesare not anymore
+ used for OpenPGP:
+ - 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 [X509: not used]
+ - u8 All_Validity
+ OpenPGP: See ../g10/trustdb/TRUST_* [not yet used]
+ X509: Bit 4 set := key has been revoked.
+ Note that this value matches TRUST_FLAG_REVOKED
+ - u16 RFU
+ - u32 Recheck_after
+ - u32 Latest timestamp in the keyblock (useful for KS synchronization?)
+ - u32 Blob created at
+ - u32 [NRES] Size of reserved space (not including this field)
+ - bN Reserved space of size NRES for future use.
+ - bN Arbitrary space for example used to store data which is not
+ part of the keyblock or certificate. For example the v3 key
+ IDs go here.
+ - bN Space for the keyblock or certificate.
+ - bN RFU. This is the remaining space after keyblock and before
+ the checksum. It is not covered by the checksum.
+ - b20 SHA-1 checksum (useful for KS synchronization?)
+ Note, that KBX versions before GnuPG 2.1 used an MD5
+ checksum. However it was only created but never checked.
+ Thus we do not expect problems if we switch to SHA-1. If
+ the checksum fails and the first 4 bytes are zero, we can
+ try again with MD5. SHA-1 has the advantage that it is
+ faster on CPUs with dedicated SHA-1 support.
+
+
+*/
+
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+
+#include "keybox-defs.h"
+#include <gcrypt.h>
+
+#ifdef KEYBOX_WITH_X509
+#include <ksba.h>
+#endif
+
+
+#include "../common/gettime.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))
+
+
+struct membuf {
+ size_t len;
+ size_t size;
+ char *buf;
+ int out_of_core;
+};
+
+
+/* #if MAX_FINGERPRINT_LEN < 20 */
+/* #error fingerprints are 20 bytes */
+/* #endif */
+
+struct keyboxblob_key {
+ char fpr[20];
+ u32 off_kid;
+ ulong off_kid_addr;
+ u16 flags;
+};
+struct keyboxblob_uid {
+ u32 off;
+ ulong off_addr;
+ char *name; /* used only with x509 */
+ u32 len;
+ u16 flags;
+ byte validity;
+};
+
+struct keyid_list {
+ struct keyid_list *next;
+ int seqno;
+ byte kid[8];
+};
+
+struct fixup_list {
+ struct fixup_list *next;
+ u32 off;
+ u32 val;
+};
+
+
+struct keyboxblob {
+ byte *blob;
+ size_t bloblen;
+ off_t fileoffset;
+
+ /* stuff used only by keybox_create_blob */
+ unsigned char *serialbuf;
+ const unsigned char *serial;
+ size_t seriallen;
+ int nkeys;
+ struct keyboxblob_key *keys;
+ int nuids;
+ struct keyboxblob_uid *uids;
+ int nsigs;
+ u32 *sigs;
+ struct fixup_list *fixups;
+ int fixup_out_of_core;
+
+ struct keyid_list *temp_kids;
+ struct membuf bufbuf; /* temporary store for the blob */
+ struct membuf *buf;
+};
+
+
+
+/* A simple implementation of a dynamic buffer. Use init_membuf() to
+ create a buffer, put_membuf to append bytes and get_membuf to
+ release and return the buffer. Allocation errors are detected but
+ only returned at the final get_membuf(), this helps not to clutter
+ the code with out of core checks. */
+
+static void
+init_membuf (struct membuf *mb, int initiallen)
+{
+ mb->len = 0;
+ mb->size = initiallen;
+ mb->out_of_core = 0;
+ mb->buf = xtrymalloc (initiallen);
+ if (!mb->buf)
+ mb->out_of_core = 1;
+}
+
+static void
+put_membuf (struct membuf *mb, const void *buf, size_t len)
+{
+ if (mb->out_of_core)
+ return;
+
+ if (mb->len + len >= mb->size)
+ {
+ char *p;
+
+ mb->size += len + 1024;
+ p = xtryrealloc (mb->buf, mb->size);
+ if (!p)
+ {
+ mb->out_of_core = 1;
+ return;
+ }
+ mb->buf = p;
+ }
+ if (buf)
+ memcpy (mb->buf + mb->len, buf, len);
+ else
+ memset (mb->buf + mb->len, 0, len);
+ mb->len += len;
+}
+
+static void *
+get_membuf (struct membuf *mb, size_t *len)
+{
+ char *p;
+
+ if (mb->out_of_core)
+ {
+ xfree (mb->buf);
+ mb->buf = NULL;
+ return NULL;
+ }
+
+ p = mb->buf;
+ *len = mb->len;
+ mb->buf = NULL;
+ mb->out_of_core = 1; /* don't allow a reuse */
+ return p;
+}
+
+
+static void
+put8 (struct membuf *mb, byte a )
+{
+ put_membuf (mb, &a, 1);
+}
+
+static void
+put16 (struct membuf *mb, u16 a )
+{
+ unsigned char tmp[2];
+ tmp[0] = a>>8;
+ tmp[1] = a;
+ put_membuf (mb, tmp, 2);
+}
+
+static void
+put32 (struct membuf *mb, u32 a )
+{
+ unsigned char tmp[4];
+ tmp[0] = a>>24;
+ tmp[1] = a>>16;
+ tmp[2] = a>>8;
+ tmp[3] = a;
+ put_membuf (mb, tmp, 4);
+}
+
+
+
+/* Store a value in the fixup list */
+static void
+add_fixup (KEYBOXBLOB blob, u32 off, u32 val)
+{
+ struct fixup_list *fl;
+
+ if (blob->fixup_out_of_core)
+ return;
+
+ fl = xtrycalloc(1, sizeof *fl);
+ if (!fl)
+ blob->fixup_out_of_core = 1;
+ else
+ {
+ fl->off = off;
+ fl->val = val;
+ fl->next = blob->fixups;
+ blob->fixups = fl;
+ }
+}
+
+
+
+/*
+ OpenPGP specific stuff
+*/
+
+
+/* We must store the keyid at some place because we can't calculate
+ the offset yet. This is only used for v3 keyIDs. Function returns
+ an index value for later fixup or -1 for out of core. The value
+ must be a non-zero value. */
+static int
+pgp_temp_store_kid (KEYBOXBLOB blob, struct _keybox_openpgp_key_info *kinfo)
+{
+ struct keyid_list *k, *r;
+
+ k = xtrymalloc (sizeof *k);
+ if (!k)
+ return -1;
+ memcpy (k->kid, kinfo->keyid, 8);
+ k->seqno = 0;
+ k->next = blob->temp_kids;
+ blob->temp_kids = k;
+ for (r=k; r; r = r->next)
+ k->seqno++;
+
+ return k->seqno;
+}
+
+
+/* Helper for pgp_create_key_part. */
+static gpg_error_t
+pgp_create_key_part_single (KEYBOXBLOB blob, int n,
+ struct _keybox_openpgp_key_info *kinfo)
+{
+ size_t fprlen;
+ int off;
+
+ fprlen = kinfo->fprlen;
+ if (fprlen > 20)
+ fprlen = 20;
+ memcpy (blob->keys[n].fpr, kinfo->fpr, fprlen);
+ if (fprlen != 20) /* v3 fpr - shift right and fill with zeroes. */
+ {
+ memmove (blob->keys[n].fpr + 20 - fprlen, blob->keys[n].fpr, fprlen);
+ memset (blob->keys[n].fpr, 0, 20 - fprlen);
+ off = pgp_temp_store_kid (blob, kinfo);
+ if (off == -1)
+ return gpg_error_from_syserror ();
+ blob->keys[n].off_kid = off;
+ }
+ else
+ blob->keys[n].off_kid = 0; /* Will be fixed up later */
+ blob->keys[n].flags = 0;
+ return 0;
+}
+
+
+static gpg_error_t
+pgp_create_key_part (KEYBOXBLOB blob, keybox_openpgp_info_t info)
+{
+ gpg_error_t err;
+ int n = 0;
+ struct _keybox_openpgp_key_info *kinfo;
+
+ err = pgp_create_key_part_single (blob, n++, &info->primary);
+ if (err)
+ return err;
+ if (info->nsubkeys)
+ for (kinfo = &info->subkeys; kinfo; kinfo = kinfo->next)
+ if ((err=pgp_create_key_part_single (blob, n++, kinfo)))
+ return err;
+
+ assert (n == blob->nkeys);
+ return 0;
+}
+
+
+static void
+pgp_create_uid_part (KEYBOXBLOB blob, keybox_openpgp_info_t info)
+{
+ int n = 0;
+ struct _keybox_openpgp_uid_info *u;
+
+ if (info->nuids)
+ {
+ for (u = &info->uids; u; u = u->next)
+ {
+ blob->uids[n].off = u->off;
+ blob->uids[n].len = u->len;
+ blob->uids[n].flags = 0;
+ blob->uids[n].validity = 0;
+ n++;
+ }
+ }
+
+ assert (n == blob->nuids);
+}
+
+
+static void
+pgp_create_sig_part (KEYBOXBLOB blob, u32 *sigstatus)
+{
+ int n;
+
+ for (n=0; n < blob->nsigs; n++)
+ {
+ blob->sigs[n] = sigstatus? sigstatus[n+1] : 0;
+ }
+}
+
+
+static int
+pgp_create_blob_keyblock (KEYBOXBLOB blob,
+ const unsigned char *image, size_t imagelen)
+{
+ struct membuf *a = blob->buf;
+ int n;
+ u32 kbstart = a->len;
+
+ add_fixup (blob, 8, kbstart);
+
+ for (n = 0; n < blob->nuids; n++)
+ add_fixup (blob, blob->uids[n].off_addr, kbstart + blob->uids[n].off);
+
+ put_membuf (a, image, imagelen);
+
+ add_fixup (blob, 12, a->len - kbstart);
+ return 0;
+}
+
+
+
+#ifdef KEYBOX_WITH_X509
+/*
+ X.509 specific stuff
+ */
+
+/* Write the raw certificate out */
+static int
+x509_create_blob_cert (KEYBOXBLOB blob, ksba_cert_t cert)
+{
+ struct membuf *a = blob->buf;
+ const unsigned char *image;
+ size_t length;
+ u32 kbstart = a->len;
+
+ /* Store our offset for later fixup */
+ add_fixup (blob, 8, kbstart);
+
+ image = ksba_cert_get_image (cert, &length);
+ if (!image)
+ return gpg_error (GPG_ERR_GENERAL);
+ put_membuf (a, image, length);
+
+ add_fixup (blob, 12, a->len - kbstart);
+ return 0;
+}
+
+#endif /*KEYBOX_WITH_X509*/
+
+/* Write a stored keyID out to the buffer */
+static void
+write_stored_kid (KEYBOXBLOB blob, int seqno)
+{
+ struct keyid_list *r;
+
+ for ( r = blob->temp_kids; r; r = r->next )
+ {
+ if (r->seqno == seqno )
+ {
+ put_membuf (blob->buf, r->kid, 8);
+ return;
+ }
+ }
+ never_reached ();
+}
+
+/* Release a list of key IDs */
+static void
+release_kid_list (struct keyid_list *kl)
+{
+ struct keyid_list *r, *r2;
+
+ for ( r = kl; r; r = r2 )
+ {
+ r2 = r->next;
+ xfree (r);
+ }
+}
+
+
+
+static int
+create_blob_header (KEYBOXBLOB blob, int blobtype, int as_ephemeral)
+{
+ struct membuf *a = blob->buf;
+ int i;
+
+ put32 ( a, 0 ); /* blob length, needs fixup */
+ put8 ( a, blobtype);
+ put8 ( a, 1 ); /* blob type version */
+ put16 ( a, as_ephemeral? 2:0 ); /* blob flags */
+
+ put32 ( a, 0 ); /* offset to the raw data, needs fixup */
+ put32 ( a, 0 ); /* length of the raw data, needs fixup */
+
+ put16 ( a, blob->nkeys );
+ put16 ( a, 20 + 4 + 2 + 2 ); /* size of key info */
+ for ( i=0; i < blob->nkeys; i++ )
+ {
+ put_membuf (a, blob->keys[i].fpr, 20);
+ blob->keys[i].off_kid_addr = a->len;
+ put32 ( a, 0 ); /* offset to keyid, fixed up later */
+ put16 ( a, blob->keys[i].flags );
+ put16 ( a, 0 ); /* reserved */
+ }
+
+ put16 (a, blob->seriallen); /*fixme: check that it fits into 16 bits*/
+ if (blob->serial)
+ put_membuf (a, blob->serial, blob->seriallen);
+
+ 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 = a->len;
+ 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 ); /* newest timestamp (none) */
+ put32 ( a, make_timestamp() ); /* creation time */
+ put32 ( a, 0 ); /* size of reserved space */
+ /* reserved space (which is currently of size 0) */
+
+ /* space where we write keyIDs and other stuff so that the
+ pointers can actually point to somewhere */
+ if (blobtype == KEYBOX_BLOBTYPE_PGP)
+ {
+ /* We need to store the keyids for all pgp 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 */
+ add_fixup (blob, blob->keys[i].off_kid_addr, a->len);
+ write_stored_kid (blob, blob->keys[i].off_kid);
+ }
+ else
+ { /* the better v4 key IDs - just store an offset 8 bytes back */
+ add_fixup (blob, blob->keys[i].off_kid_addr,
+ blob->keys[i].off_kid_addr - 8);
+ }
+ }
+ }
+
+ if (blobtype == KEYBOX_BLOBTYPE_X509)
+ {
+ /* We don't want to point to ASN.1 encoded UserIDs (DNs) but to
+ the utf-8 string represenation of them */
+ for (i=0; i < blob->nuids; i++ )
+ {
+ if (blob->uids[i].name)
+ { /* this is a v3 one */
+ add_fixup (blob, blob->uids[i].off_addr, a->len);
+ put_membuf (blob->buf, blob->uids[i].name, blob->uids[i].len);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+static int
+create_blob_trailer (KEYBOXBLOB blob)
+{
+ (void)blob;
+ return 0;
+}
+
+
+static int
+create_blob_finish (KEYBOXBLOB blob)
+{
+ struct membuf *a = blob->buf;
+ unsigned char *p;
+ unsigned char *pp;
+ size_t n;
+
+ /* Write a placeholder for the checksum */
+ put_membuf (a, NULL, 20);
+
+ /* get the memory area */
+ n = 0; /* (Just to avoid compiler warning.) */
+ p = get_membuf (a, &n);
+ if (!p)
+ return gpg_error (GPG_ERR_ENOMEM);
+ assert (n >= 20);
+
+ /* fixup the length */
+ add_fixup (blob, 0, n);
+
+ /* do the fixups */
+ if (blob->fixup_out_of_core)
+ {
+ xfree (p);
+ return gpg_error (GPG_ERR_ENOMEM);
+ }
+
+ {
+ struct fixup_list *fl, *next;
+ for (fl = blob->fixups; fl; fl = next)
+ {
+ assert (fl->off+4 <= n);
+ p[fl->off+0] = fl->val >> 24;
+ p[fl->off+1] = fl->val >> 16;
+ p[fl->off+2] = fl->val >> 8;
+ p[fl->off+3] = fl->val;
+ next = fl->next;
+ xfree (fl);
+ }
+ blob->fixups = NULL;
+ }
+
+ /* Compute and store the SHA-1 checksum. */
+ gcry_md_hash_buffer (GCRY_MD_SHA1, p + n - 20, p, n - 20);
+
+ pp = xtrymalloc (n);
+ if ( !pp )
+ {
+ xfree (p);
+ return gpg_error_from_syserror ();
+ }
+ memcpy (pp , p, n);
+ xfree (p);
+ blob->blob = pp;
+ blob->bloblen = n;
+
+ return 0;
+}
+
+
+
+gpg_error_t
+_keybox_create_openpgp_blob (KEYBOXBLOB *r_blob,
+ keybox_openpgp_info_t info,
+ const unsigned char *image,
+ size_t imagelen,
+ int as_ephemeral)
+{
+ gpg_error_t err;
+ KEYBOXBLOB blob;
+
+ *r_blob = NULL;
+
+ blob = xtrycalloc (1, sizeof *blob);
+ if (!blob)
+ return gpg_error_from_syserror ();
+
+ blob->nkeys = 1 + info->nsubkeys;
+ blob->keys = xtrycalloc (blob->nkeys, sizeof *blob->keys );
+ if (!blob->keys)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ blob->nuids = info->nuids;
+ if (blob->nuids)
+ {
+ blob->uids = xtrycalloc (blob->nuids, sizeof *blob->uids );
+ if (!blob->uids)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ blob->nsigs = info->nsigs;
+ if (blob->nsigs)
+ {
+ blob->sigs = xtrycalloc (blob->nsigs, sizeof *blob->sigs );
+ if (!blob->sigs)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ err = pgp_create_key_part (blob, info);
+ if (err)
+ goto leave;
+ pgp_create_uid_part (blob, info);
+ pgp_create_sig_part (blob, NULL);
+
+ init_membuf (&blob->bufbuf, 1024);
+ blob->buf = &blob->bufbuf;
+ err = create_blob_header (blob, KEYBOX_BLOBTYPE_PGP, as_ephemeral);
+ if (err)
+ goto leave;
+ err = pgp_create_blob_keyblock (blob, image, imagelen);
+ if (err)
+ goto leave;
+ err = create_blob_trailer (blob);
+ if (err)
+ goto leave;
+ err = create_blob_finish (blob);
+ if (err)
+ goto leave;
+
+ leave:
+ release_kid_list (blob->temp_kids);
+ blob->temp_kids = NULL;
+ if (err)
+ _keybox_release_blob (blob);
+ else
+ *r_blob = blob;
+ return err;
+}
+
+
+#ifdef KEYBOX_WITH_X509
+
+/* Return an allocated string with the email address extracted from a
+ DN. Note hat we use this code also in ../sm/keylist.c. */
+static char *
+x509_email_kludge (const char *name)
+{
+ const char *p, *string;
+ unsigned char *buf;
+ int n;
+
+ string = name;
+ for (;;)
+ {
+ p = strstr (string, "1.2.840.113549.1.9.1=#");
+ if (!p)
+ return NULL;
+ if (p == name || (p > string+1 && p[-1] == ',' && p[-2] != '\\'))
+ {
+ name = p + 22;
+ break;
+ }
+ string = p + 22;
+ }
+
+
+ /* This looks pretty much like an email address in the subject's DN
+ we use this to add an additional user ID entry. This way,
+ OpenSSL generated keys get a nicer and usable listing. */
+ for (n=0, p=name; hexdigitp (p) && hexdigitp (p+1); p +=2, n++)
+ ;
+ if (!n)
+ return NULL;
+ buf = xtrymalloc (n+3);
+ if (!buf)
+ return NULL; /* oops, out of core */
+ *buf = '<';
+ for (n=1, p=name; hexdigitp (p); p +=2, n++)
+ buf[n] = xtoi_2 (p);
+ buf[n++] = '>';
+ buf[n] = 0;
+ return (char*)buf;
+}
+
+
+
+/* Note: We should move calculation of the digest into libksba and
+ remove that parameter */
+int
+_keybox_create_x509_blob (KEYBOXBLOB *r_blob, ksba_cert_t cert,
+ unsigned char *sha1_digest, int as_ephemeral)
+{
+ int i, rc = 0;
+ KEYBOXBLOB blob;
+ unsigned char *sn;
+ char *p;
+ char **names = NULL;
+ size_t max_names;
+
+ *r_blob = NULL;
+ blob = xtrycalloc (1, sizeof *blob);
+ if( !blob )
+ return gpg_error_from_syserror ();
+
+ sn = ksba_cert_get_serial (cert);
+ if (sn)
+ {
+ size_t n, len;
+ n = gcry_sexp_canon_len (sn, 0, NULL, NULL);
+ if (n < 2)
+ {
+ xfree (sn);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ blob->serialbuf = sn;
+ sn++; n--; /* skip '(' */
+ for (len=0; n && *sn && *sn != ':' && digitp (sn); n--, sn++)
+ len = len*10 + atoi_1 (sn);
+ if (*sn != ':')
+ {
+ xfree (blob->serialbuf);
+ blob->serialbuf = NULL;
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ sn++;
+ blob->serial = sn;
+ blob->seriallen = len;
+ }
+
+ blob->nkeys = 1;
+
+ /* create list of names */
+ blob->nuids = 0;
+ max_names = 100;
+ names = xtrymalloc (max_names * sizeof *names);
+ if (!names)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ p = ksba_cert_get_issuer (cert, 0);
+ if (!p)
+ {
+ rc = gpg_error (GPG_ERR_MISSING_VALUE);
+ goto leave;
+ }
+ names[blob->nuids++] = p;
+ for (i=0; (p = ksba_cert_get_subject (cert, i)); i++)
+ {
+ if (blob->nuids >= max_names)
+ {
+ char **tmp;
+
+ max_names += 100;
+ tmp = xtryrealloc (names, max_names * sizeof *names);
+ if (!tmp)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ names = tmp;
+ }
+ names[blob->nuids++] = p;
+ if (!i && (p=x509_email_kludge (p)))
+ names[blob->nuids++] = p; /* due to !i we don't need to check bounds*/
+ }
+
+ /* space for signature information */
+ blob->nsigs = 1;
+
+ blob->keys = xtrycalloc (blob->nkeys, sizeof *blob->keys );
+ blob->uids = xtrycalloc (blob->nuids, sizeof *blob->uids );
+ blob->sigs = xtrycalloc (blob->nsigs, sizeof *blob->sigs );
+ if (!blob->keys || !blob->uids || !blob->sigs)
+ {
+ rc = gpg_error (GPG_ERR_ENOMEM);
+ goto leave;
+ }
+
+ memcpy (blob->keys[0].fpr, sha1_digest, 20);
+ blob->keys[0].off_kid = 0; /* We don't have keyids */
+ blob->keys[0].flags = 0;
+
+ /* issuer and subject names */
+ for (i=0; i < blob->nuids; i++)
+ {
+ blob->uids[i].name = names[i];
+ blob->uids[i].len = strlen(names[i]);
+ names[i] = NULL;
+ blob->uids[i].flags = 0;
+ blob->uids[i].validity = 0;
+ }
+ xfree (names);
+ names = NULL;
+
+ /* signatures */
+ blob->sigs[0] = 0; /* not yet checked */
+
+ /* Create a temporary buffer for further processing */
+ init_membuf (&blob->bufbuf, 1024);
+ blob->buf = &blob->bufbuf;
+ /* write out what we already have */
+ rc = create_blob_header (blob, KEYBOX_BLOBTYPE_X509, as_ephemeral);
+ if (rc)
+ goto leave;
+ rc = x509_create_blob_cert (blob, cert);
+ if (rc)
+ goto leave;
+ rc = create_blob_trailer (blob);
+ if (rc)
+ goto leave;
+ rc = create_blob_finish ( blob );
+ if (rc)
+ goto leave;
+
+
+ leave:
+ release_kid_list (blob->temp_kids);
+ blob->temp_kids = NULL;
+ if (names)
+ {
+ for (i=0; i < blob->nuids; i++)
+ xfree (names[i]);
+ xfree (names);
+ }
+ if (rc)
+ {
+ _keybox_release_blob (blob);
+ *r_blob = NULL;
+ }
+ else
+ {
+ *r_blob = blob;
+ }
+ return rc;
+}
+#endif /*KEYBOX_WITH_X509*/
+
+
+
+int
+_keybox_new_blob (KEYBOXBLOB *r_blob,
+ unsigned char *image, size_t imagelen, off_t off)
+{
+ KEYBOXBLOB blob;
+
+ *r_blob = NULL;
+ blob = xtrycalloc (1, sizeof *blob);
+ if (!blob)
+ return gpg_error_from_syserror ();
+
+ blob->blob = image;
+ blob->bloblen = imagelen;
+ blob->fileoffset = off;
+ *r_blob = blob;
+ return 0;
+}
+
+
+void
+_keybox_release_blob (KEYBOXBLOB blob)
+{
+ int i;
+ if (!blob)
+ return;
+ if (blob->buf)
+ {
+ size_t len;
+ xfree (get_membuf (blob->buf, &len));
+ }
+ xfree (blob->keys );
+ xfree (blob->serialbuf);
+ for (i=0; i < blob->nuids; i++)
+ xfree (blob->uids[i].name);
+ xfree (blob->uids );
+ xfree (blob->sigs );
+ xfree (blob->blob );
+ xfree (blob );
+}
+
+
+
+const unsigned char *
+_keybox_get_blob_image ( KEYBOXBLOB blob, size_t *n )
+{
+ *n = blob->bloblen;
+ return blob->blob;
+}
+
+off_t
+_keybox_get_blob_fileoffset (KEYBOXBLOB blob)
+{
+ return blob->fileoffset;
+}
+
+
+
+void
+_keybox_update_header_blob (KEYBOXBLOB blob, int for_openpgp)
+{
+ if (blob->bloblen >= 32 && blob->blob[4] == KEYBOX_BLOBTYPE_HEADER)
+ {
+ u32 val = make_timestamp ();
+
+ /* Update the last maintenance run times tamp. */
+ blob->blob[20] = (val >> 24);
+ blob->blob[20+1] = (val >> 16);
+ blob->blob[20+2] = (val >> 8);
+ blob->blob[20+3] = (val );
+
+ if (for_openpgp)
+ blob->blob[7] |= 0x02; /* OpenPGP data may be available. */
+ }
+}