summaryrefslogtreecommitdiff
path: root/libextra
diff options
context:
space:
mode:
Diffstat (limited to 'libextra')
-rw-r--r--libextra/Makefile.am22
-rw-r--r--libextra/opencdk/Makefile.am11
-rw-r--r--libextra/opencdk/armor.c622
-rw-r--r--libextra/opencdk/cipher.c468
-rw-r--r--libextra/opencdk/cipher.h79
-rw-r--r--libextra/opencdk/compress.c253
-rw-r--r--libextra/opencdk/context.h196
-rw-r--r--libextra/opencdk/encrypt.c1020
-rw-r--r--libextra/opencdk/filters.h98
-rw-r--r--libextra/opencdk/kbnode.c572
-rw-r--r--libextra/opencdk/keydb.c1733
-rw-r--r--libextra/opencdk/keygen.c870
-rw-r--r--libextra/opencdk/keylist.c507
-rw-r--r--libextra/opencdk/keyserver.c184
-rw-r--r--libextra/opencdk/main.c707
-rw-r--r--libextra/opencdk/main.h160
-rw-r--r--libextra/opencdk/md.c184
-rw-r--r--libextra/opencdk/md.h40
-rw-r--r--libextra/opencdk/misc.c570
-rw-r--r--libextra/opencdk/new-packet.c775
-rw-r--r--libextra/opencdk/opencdk.h844
-rw-r--r--libextra/opencdk/packet.h46
-rw-r--r--libextra/opencdk/plaintext.c229
-rw-r--r--libextra/opencdk/pubkey.c1073
-rw-r--r--libextra/opencdk/read-packet.c1091
-rw-r--r--libextra/opencdk/seskey.c562
-rw-r--r--libextra/opencdk/sig-check.c358
-rw-r--r--libextra/opencdk/sign.c493
-rw-r--r--libextra/opencdk/stream.c1073
-rw-r--r--libextra/opencdk/stream.h82
-rw-r--r--libextra/opencdk/sym-cipher.c187
-rw-r--r--libextra/opencdk/trustdb.c269
-rw-r--r--libextra/opencdk/types.h53
-rw-r--r--libextra/opencdk/verify.c310
-rw-r--r--libextra/opencdk/write-packet.c814
-rw-r--r--libextra/openpgp/Makefile.am10
36 files changed, 16551 insertions, 14 deletions
diff --git a/libextra/Makefile.am b/libextra/Makefile.am
index 844b9f2c34..a4ba2d425f 100644
--- a/libextra/Makefile.am
+++ b/libextra/Makefile.am
@@ -1,6 +1,6 @@
-INCLUDES = -I../lib -I../includes -I../lib/minitasn1/ $(LIBOPENCDK_CFLAGS) $(LIBGCRYPT_CFLAGS)
+INCLUDES = -I../lib -I../includes -I../lib/minitasn1/ $(LIBOPENCDK_CFLAGS) $(LIBGCRYPT_CFLAGS) -Iopencdk/
bin_SCRIPTS = libgnutls-extra-config
-SUBDIRS = openpgp
+DIST_SUBDIRS = openpgp opencdk
m4datadir = $(datadir)/aclocal
m4data_DATA = libgnutls-extra.m4
@@ -30,10 +30,20 @@ else
lib_LTLIBRARIES = libgnutls-extra.la
endif
+if ENABLE_OPENPGP
+if ENABLE_INCLUDED_OPENCDK
+SUBDIRS = openpgp opencdk
+PGP_LIBS = openpgp/libpgp.la opencdk/libopencdk.la
+else
+SUBDIRS = openpgp
+PGP_LIBS = openpgp/libpgp.la
+endif
+endif
+
COBJECTS_EXTRA = ext_srp.c \
gnutls_srp.c auth_srp.c auth_srp_passwd.c auth_srp_sb64.c \
- gnutls_extra.c \
- auth_srp_rsa.c gnutls_openpgp.c
+ gnutls_extra.c auth_srp_rsa.c gnutls_openpgp.c
+
libgnutls_extra_la_LDFLAGS = $(libgnutls_extra_version_script_cmd) \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
@@ -42,9 +52,7 @@ libgnutls_extra_la_DEPENDENCIES = $(LZO_OBJECTS)
libgnutls_extra_la_SOURCES = $(COBJECTS_EXTRA)
-libgnutls_extra_la_LIBADD = $(LZO_OBJECTS) \
- openpgp/openpgp.lo openpgp/xml.lo openpgp/privkey.lo \
- openpgp/extras.lo openpgp/verify.lo openpgp/compat.lo \
+libgnutls_extra_la_LIBADD = $(LZO_OBJECTS) $(PGP_LIBS) \
../lib/libgnutls.la
EXTRA_libgnutls_extra_la_SOURCES = minilzo.c
diff --git a/libextra/opencdk/Makefile.am b/libextra/opencdk/Makefile.am
new file mode 100644
index 0000000000..a5bf469b69
--- /dev/null
+++ b/libextra/opencdk/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES = -I../../ -DVERSION=\"gnutls/opencdk\"
+EXTRA_DIST = md.h packet.h opencdk.h context.h \
+ main.h cipher.h stream.h types.h filters.h
+noinst_LTLIBRARIES = libopencdk.la
+libopencdk_la_SOURCES = new-packet.c \
+ read-packet.c write-packet.c main.c \
+ verify.c armor.c sig-check.c sign.c keydb.c \
+ keylist.c seskey.c pubkey.c misc.c encrypt.c \
+ trustdb.c kbnode.c compress.c plaintext.c cipher.c \
+ sym-cipher.c stream.c keyserver.c keygen.c md.c
+
diff --git a/libextra/opencdk/armor.c b/libextra/opencdk/armor.c
new file mode 100644
index 0000000000..e93ac32fab
--- /dev/null
+++ b/libextra/opencdk/armor.c
@@ -0,0 +1,622 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * armor.c - Armor filters
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * ChangeLog for basic BASE64 code (base64_encode, base64_decode):
+ * Original author: Eric S. Raymond (Fetchmail)
+ * Heavily modified by Brendan Cully <brendan@kublai.com> (Mutt)
+ * Modify the code for generic use by Timo Schulz <twoaday@freakmail.de>
+ */
+/* X-TODO-STATUS: OK */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/stat.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "filters.h"
+
+#ifdef __MINGW32__
+# define LF "\r\n"
+#else
+# define LF "\n"
+#endif
+
+#define CRCINIT 0xB704CE
+
+#define BAD -1
+#define b64val(c) index64[(unsigned int)(c)]
+
+static u32 crc_table[] = {
+0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC, 0x9F7F17,
+0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23, 0xB8B2D5, 0x3EFE2E,
+0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868, 0xD0E493, 0xDC7D65, 0x5A319E,
+0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646, 0xF72951, 0x7165AA, 0x7DFC5C, 0xFBB0A7,
+0x0CD1E9, 0x8A9D12, 0x8604E4, 0x00481F, 0x9F3708, 0x197BF3, 0x15E205, 0x93AEFE,
+0xAD50D0, 0x2B1C2B, 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C, 0x322FC7,
+0xC99F60, 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C, 0x56E077,
+0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5, 0xF7614E,
+0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8, 0x00903E, 0x86DCC5,
+0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A, 0xAD88F1, 0xA11107, 0x275DFC,
+0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD, 0x4F0BBA, 0xC94741, 0xC5DEB7, 0x43924C,
+0x7D6C62, 0xFB2099, 0xF7B96F, 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E, 0xE21375,
+0x15723B, 0x933EC0, 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7, 0x8A0D2C,
+0xB4F302, 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE, 0x2B8C15,
+0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E, 0x4F43A5,
+0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791, 0x688E67, 0xEEC29C,
+0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145, 0x26EDBE, 0x2A7448, 0xAC38B3,
+0x92C69D, 0x148A66, 0x181390, 0x9E5F6B, 0x01207C, 0x876C87, 0x8BF571, 0x0DB98A,
+0xF6092D, 0x7045D6, 0x7CDC20, 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1, 0x69763A,
+0x578814, 0xD1C4EF, 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8, 0xC8F703,
+0x3F964D, 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1, 0xA0E95A,
+0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498, 0x016863,
+0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE, 0xE3EB28, 0x65A7D3,
+0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C, 0x4EF3E7, 0x426A11, 0xC426EA,
+0x2AE476, 0xACA88D, 0xA0317B, 0x267D80, 0xB90297, 0x3F4E6C, 0x33D79A, 0xB59B61,
+0x8B654F, 0x0D29B4, 0x01B042, 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3, 0x141A58,
+0xEFAAFF, 0x69E604, 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913, 0x70D5E8,
+0x4E2BC6, 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A, 0xD154D1,
+0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673, 0xB94A88,
+0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC, 0x9E874A, 0x18CBB1,
+0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7, 0xF6D10C, 0xFA48FA, 0x7C0401,
+0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9, 0xD11CCE, 0x575035, 0x5BC9C3, 0xDD8538
+};
+
+static const char * armor_begin[] = {
+ "BEGIN PGP MESSAGE",
+ "BEGIN PGP PUBLIC KEY BLOCK",
+ "BEGIN PGP PRIVATE KEY BLOCK",
+ "BEGIN PGP SIGNATURE",
+ NULL
+};
+
+static const char * armor_end[] = {
+ "END PGP MESSAGE",
+ "END PGP PUBLIC KEY BLOCK",
+ "END PGP PRIVATE KEY BLOCK",
+ "END PGP SIGNATURE",
+ NULL
+};
+
+static const char * valid_headers[] = {
+ "Comment",
+ "Version",
+ "MessageID",
+ "Hash",
+ "Charset",
+ NULL
+};
+
+static char b64chars[64] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int index64[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
+};
+
+
+/* encode a raw binary buffer to a null-terminated base64 strings */
+static int
+base64_encode( byte * out, const byte * in, size_t len, size_t olen )
+{
+ if( !out || !in )
+ return CDK_Inv_Value;
+
+ while( len >= 3 && olen > 10 ) {
+ *out++ = b64chars[in[0] >> 2];
+ *out++ = b64chars[((in[0] << 4) & 0x30) | (in[1] >> 4)];
+ *out++ = b64chars[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
+ *out++ = b64chars[in[2] & 0x3f];
+ olen -= 4;
+ len -= 3;
+ in += 3;
+ }
+
+ /* clean up remainder */
+ if( len > 0 && olen > 4 ) {
+ byte fragment = 0;
+ *out++ = b64chars[in[0] >> 2];
+ fragment = (in[0] << 4) & 0x30;
+ if( len > 1 )
+ fragment |= in[1] >> 4;
+ *out++ = b64chars[fragment];
+ *out++ = (len < 2) ? '=' : b64chars[(in[1] << 2) & 0x3c];
+ *out++ = '=';
+ }
+ *out = '\0';
+ return 0;
+}
+
+
+/* Convert '\0'-terminated base64 string to raw byte buffer.
+ Returns length of returned buffer, or -1 on error. */
+static int
+base64_decode( char * out, const char * in )
+{
+ int len = 0;
+ byte digit1, digit2, digit3, digit4;
+
+ if( !out || !in )
+ return -1;
+
+ do {
+ digit1 = in[0];
+ if( digit1 > 127 || b64val (digit1) == BAD )
+ return -1;
+ digit2 = in[1];
+ if( digit2 > 127 || b64val (digit2) == BAD )
+ return -1;
+ digit3 = in[2];
+ if( digit3 > 127 || ((digit3 != '=') && (b64val (digit3) == BAD)) )
+ return -1;
+ digit4 = in[3];
+ if( digit4 > 127 || ((digit4 != '=') && (b64val (digit4) == BAD)) )
+ return -1;
+ in += 4;
+
+ /* digits are already sanity-checked */
+ *out++ = (b64val (digit1) << 2) | (b64val (digit2) >> 4);
+ len++;
+ if( digit3 != '=' ) {
+ *out++ = ((b64val (digit2) << 4) & 0xf0) | (b64val (digit3) >> 2);
+ len++;
+ if( digit4 != '=' ) {
+ *out++ = ((b64val (digit3) << 6) & 0xc0) | b64val (digit4);
+ len++;
+ }
+ }
+ } while( *in && digit4 != '=' );
+ return len;
+}
+
+
+static int
+is_compressed( cdk_stream_t inp, int *r_zipalgo )
+{
+ char buf[128], plain[256];
+ int nread, check = 0, pkttype;
+
+ if( r_zipalgo )
+ *r_zipalgo = 0;
+ cdk_stream_seek( inp, 0 );
+ while( !cdk_stream_eof( inp ) ) {
+ nread = _cdk_stream_gets( inp, buf, sizeof buf-1 );
+ if( !nread )
+ break;
+ if( nread == 1 && !cdk_stream_eof( inp )
+ && (nread = _cdk_stream_gets( inp, buf, sizeof buf-1)) ) {
+ base64_decode( plain, buf );
+ if( !(*plain & 0x80) )
+ break;
+ pkttype = *plain & 0x40 ? (*plain & 0x3f) : ((*plain >> 2) & 0xf);
+ if( pkttype == CDK_PKT_COMPRESSED && r_zipalgo ) {
+ _cdk_log_debug( "armor compressed (algo=%d)\n", *(plain+1) );
+ *r_zipalgo = *(plain + 1);
+ }
+ break;
+ }
+ }
+ return check;
+}
+
+
+static int
+check_armor( cdk_stream_t inp, int *r_zipalgo )
+{
+ char buf[4096];
+ size_t nread;
+ int check = 0;
+
+ nread = cdk_stream_read( inp, buf, sizeof buf-1 );
+ if( nread > 0 ) {
+ buf[nread] = '\0';
+ if( strstr( buf, "-----BEGIN PGP" ) ) {
+ is_compressed( inp, r_zipalgo );
+ check = 1;
+ }
+ cdk_stream_seek( inp, 0 );
+ }
+ return check;
+}
+
+
+static int
+is_armored( int ctb )
+{
+ int pkttype = 0;
+
+ if( !(ctb & 0x80) )
+ return 1; /* invalid packet: assume it is armored */
+ pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb >> 2) & 0xf);
+
+ switch( pkttype ) {
+ case CDK_PKT_MARKER:
+ case CDK_PKT_SYMKEY_ENC:
+ case CDK_PKT_ONEPASS_SIG:
+ case CDK_PKT_PUBLIC_KEY:
+ case CDK_PKT_SECRET_KEY:
+ case CDK_PKT_PUBKEY_ENC:
+ case CDK_PKT_SIGNATURE:
+ case CDK_PKT_OLD_COMMENT:
+ case CDK_PKT_LITERAL:
+ case CDK_PKT_COMPRESSED:
+ case CDK_PKT_ENCRYPTED:
+ return 0; /* seems to be a regular packet: not armored */
+ }
+ return 1;
+}
+
+
+static unsigned
+update_crc( unsigned crc, const byte *buf, size_t count )
+{
+ int j;
+
+ if( !crc )
+ crc = CRCINIT;
+ for( j = 0; j < count; j++ )
+ crc = (crc << 8) ^ crc_table[0xff & ((crc >> 16) ^ buf[j])];
+ crc &= 0xffffff;
+ return crc;
+}
+
+
+static int
+armor_encode( void * opaque, FILE * in, FILE * out )
+{
+ armor_filter_t * afx = opaque;
+ struct stat statbuf;
+ char crcbuf[5], buf[128], raw[49];
+ byte crcbuf2[3];
+ size_t nread = 0;
+ const char * lf;
+ int rc = 0;
+
+ if( !afx )
+ return CDK_Inv_Value;
+
+ afx->crc = 0;
+ if( afx->idx < 0 || afx->idx > DIM (armor_begin)
+ || afx->idx2 < 0 || afx->idx2 > DIM (armor_end) )
+ return CDK_Inv_Value;
+
+ _cdk_log_debug( "armor filter: encode\n" );
+
+ memset( crcbuf, 0, sizeof crcbuf );
+
+ lf = afx->le ? afx->le : LF;
+ fprintf( out, "-----%s-----%s", armor_begin[afx->idx], lf );
+ fprintf( out, "Version: OpenPrivacy "VERSION"%s", lf );
+ if( afx->hdrlines)
+ fwrite( afx->hdrlines, 1, strlen( afx->hdrlines), out );
+ fprintf( out, "%s", lf );
+
+ if( fstat( fileno( in ), &statbuf ) )
+ return CDK_General_Error;
+
+ while( !feof( in ) ) {
+ nread = fread( raw, 1, sizeof raw-1, in );
+ if( !nread )
+ break;
+ if( ferror( in ) ) {
+ rc = CDK_File_Error;
+ break;
+ }
+ afx->crc = update_crc( afx->crc, raw, nread );
+ base64_encode( buf, raw, nread, sizeof buf-1 );
+ fprintf( out, "%s%s", buf, lf );
+ }
+
+ if( !rc ) {
+ crcbuf2[0] = afx->crc >> 16;
+ crcbuf2[1] = afx->crc >> 8;
+ crcbuf2[2] = afx->crc;
+ crcbuf[0] = b64chars[crcbuf2[0] >> 2];
+ crcbuf[1] = b64chars[((crcbuf2[0] << 4) & 0x30) |( crcbuf2[1] >> 4)];
+ crcbuf[2] = b64chars[((crcbuf2[1] << 2) & 0x3c) |( crcbuf2[2] >> 6)];
+ crcbuf[3] = b64chars[crcbuf2[2] & 0x3f];
+ fprintf( out, "=%s%s", crcbuf, lf );
+ fprintf( out, "-----%s-----%s", armor_end[afx->idx2], lf );
+ }
+
+ return rc;
+}
+
+
+/**
+ * cdk_armor_filter_use:
+ * @inp: the stream to check
+ *
+ * Check if the stream contains armored data.
+ **/
+cdk_error_t
+cdk_armor_filter_use( cdk_stream_t inp )
+{
+ int c = 0, check = 0;
+ int zipalgo;
+
+ c = cdk_stream_getc( inp );
+ if( c == EOF )
+ return 0; /* EOF, doesn't matter whether armored or not */
+ cdk_stream_seek( inp, 0 );
+ check = is_armored( c );
+ if( check ) {
+ check = check_armor( inp, &zipalgo );
+ if( zipalgo )
+ cdk_stream_control( inp, CDK_STREAMCTL_COMPRESSED, zipalgo );
+ }
+ return check;
+}
+
+
+static int
+search_header( const char *buf, const char **array )
+{
+ const char *s;
+ int i;
+
+ if( strlen( buf ) < 5 || strncmp( buf, "-----", 5 ) )
+ return -1;
+ for( i = 0; (s = array[i]); i++ ) {
+ if( !strncmp( s, buf + 5, strlen( s ) ) )
+ return i;
+ }
+ return -1;
+}
+
+
+const char *
+_cdk_armor_get_lineend( void )
+{
+ return LF;
+}
+
+
+static int
+armor_decode (void * opaque, FILE *in, FILE *out)
+{
+ armor_filter_t * afx = opaque;
+ const char * s = NULL;
+ unsigned char buf[127], crcbuf[4];
+ byte raw[128];
+ u32 crc2 = 0;
+ size_t nread = 0;
+ int i, pgp_data = 0;
+ int rc = 0;
+
+ if( !afx )
+ return CDK_Inv_Value;
+
+ _cdk_log_debug ("armor filter: decode\n");
+
+ fseek( in, 0, SEEK_SET );
+
+ /* Search the begin of the message */
+ while( !feof( in ) && !pgp_data ) {
+ s = fgets( buf, sizeof buf-1, in );
+ if( !s )
+ break;
+ afx->idx = search_header( buf, armor_begin );
+ if( afx->idx >= 0 )
+ pgp_data = 1;
+ }
+
+ if( feof( in ) || !pgp_data ) {
+ rc = CDK_Armor_Error; /* no data found */
+ goto leave;
+ }
+
+ /* Parse header until the empty line is reached */
+ while( !feof( in ) ) {
+ s = fgets( buf, sizeof buf-1, in );
+ if( !s )
+ goto leave;
+ if( strlen( s ) == strlen( LF ) ) {
+ rc = 0;
+ break; /* empty line */
+ }
+ /* From RFC2440: OpenPGP should consider improperly formatted Armor
+ Headers to be corruption of the ASCII Armor. A colon and a single
+ space separate the key and value. */
+ if( !strstr( buf, ": " ) ) {
+ rc = CDK_Armor_Error;
+ goto leave;
+ }
+ rc = CDK_General_Error;
+ for( i = 0; (s = valid_headers[i]); i++ ) {
+ if( !strncmp( s, buf, strlen( s ) ) )
+ rc = 0;
+ }
+ if( rc ) {
+ /* From RFC2440: Unknown keys should be reported to the user,
+ but OpenPGP should continue to process the message. */
+ _cdk_log_info ("unknown header: `%s'\n", buf);
+ rc = 0;
+ }
+ }
+
+ /* Read the data body */
+ while( !feof( in ) ) {
+ s = fgets( buf, sizeof buf-1, in );
+ if( !s )
+ break;
+ buf[strlen (buf) - strlen (LF)] = '\0';
+ if( buf[0] == '=' && strlen( s ) == 5 ) { /* CRC */
+ memset( crcbuf, 0, sizeof crcbuf );
+ base64_decode( crcbuf, buf + 1 );
+ crc2 = crcbuf[0] << 16 | crcbuf[1] << 8 | crcbuf[2];
+ break; /* stop here */
+ }
+ else {
+ nread = base64_decode( raw, buf );
+ if( !nread )
+ break;
+ afx->crc = update_crc( afx->crc, raw, nread );
+ fwrite( raw, 1, nread, out );
+ }
+ }
+
+ /* Search the tail of the message */
+ s = fgets( buf, sizeof buf-1, in );
+ if( s ) {
+ buf[strlen (buf) - strlen (LF)] = '\0';
+ rc = CDK_General_Error;
+ afx->idx2 = search_header( buf, armor_end );
+ if( afx->idx2 >= 0 )
+ rc = 0;
+ }
+
+ /* this catches error when no tail was found or the header is
+ different then the tail line. */
+ if( rc || afx->idx != afx->idx2 )
+ rc = CDK_Armor_Error;
+
+ afx->crc_okay = (afx->crc == crc2) ? 1 : 0;
+ if( !afx->crc_okay && !rc )
+ rc = CDK_Armor_CRC_Error;
+
+ leave:
+ return rc;
+}
+
+
+/**
+ * cdk_file_armor:
+ * @hd: Handle
+ * @file: Name of the file to protect.
+ * @output: Output filename.
+ *
+ * Protect a file with ASCII armor.
+ **/
+cdk_error_t
+cdk_file_armor( cdk_ctx_t hd, const char * file, const char * output )
+{
+ cdk_stream_t inp, out;
+ int rc;
+
+ rc = _cdk_check_args( hd->opt.overwrite, file, output );
+ if( rc )
+ return rc;
+
+ rc = cdk_stream_open( file, &inp );
+ if( rc )
+ return rc;
+
+ rc = cdk_stream_new( output, &out );
+ if( rc ) {
+ cdk_stream_close( inp );
+ return rc;
+ }
+
+ cdk_stream_set_armor_flag( out, CDK_ARMOR_MESSAGE );
+ if( hd->opt.compress )
+ rc = cdk_stream_set_compress_flag( out, hd->compress.algo,
+ hd->compress.level );
+ if( !rc )
+ rc = cdk_stream_set_literal_flag( out, 0, file );
+ if( !rc )
+ rc = cdk_stream_kick_off( inp, out );
+ if( !rc )
+ rc = _cdk_stream_get_errno( out );
+
+ cdk_stream_close( out );
+ cdk_stream_close( inp );
+ return rc;
+}
+
+
+/**
+ * cdk_file_dearmor:
+ * @file: Name of the file to unprotect.
+ * @output: Output filename.
+ *
+ * Remove ASCII armor from a file.
+ **/
+cdk_error_t
+cdk_file_dearmor (const char * file, const char * output)
+{
+ cdk_stream_t inp, out;
+ int rc = 0, zipalgo;
+
+ rc = _cdk_check_args( 1, file, output );
+ if( rc )
+ return rc;
+
+ rc = cdk_stream_open( file, &inp );
+ if( rc )
+ return rc;
+
+ rc = cdk_stream_create( output, &out );
+ if( rc ) {
+ cdk_stream_close( inp );
+ return rc;
+ }
+
+ if( cdk_armor_filter_use( inp ) ) {
+ rc = cdk_stream_set_literal_flag( inp, 0, NULL );
+ zipalgo = cdk_stream_control( inp, CDK_STREAMCTL_COMPRESSED, -1 );
+ if( zipalgo )
+ rc = cdk_stream_set_compress_flag( inp, zipalgo, 0 );
+ if( !rc )
+ rc = cdk_stream_set_armor_flag( inp, 0 );
+ if( !rc )
+ rc = cdk_stream_kick_off( inp, out );
+ if( !rc )
+ rc = _cdk_stream_get_errno( inp );
+ }
+
+ cdk_stream_close( inp );
+ cdk_stream_close( out );
+ return rc;
+}
+
+
+int
+_cdk_filter_armor( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ if( ctl == STREAMCTL_READ )
+ return armor_decode( opaque, in, out );
+ else if( ctl == STREAMCTL_WRITE )
+ return armor_encode( opaque, in, out );
+ else if( ctl == STREAMCTL_FREE ) {
+ armor_filter_t * afx = opaque;
+ if( afx ) {
+ _cdk_log_debug( "free armor filter\n" );
+ afx->idx = afx->idx2 = 0;
+ afx->crc = afx->crc_okay = 0;
+ }
+ }
+ return CDK_Inv_Mode;
+}
+
+
diff --git a/libextra/opencdk/cipher.c b/libextra/opencdk/cipher.c
new file mode 100644
index 0000000000..cc4c6ce5c7
--- /dev/null
+++ b/libextra/opencdk/cipher.c
@@ -0,0 +1,468 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * cipher.c - Cipher filters
+ * Copyright (C) 2002, 2003 Timo Schulz
+ * Copyright (C) 1998-2001 Free Software Foundation
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <assert.h>
+#include <sys/stat.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "filters.h"
+#include "cipher.h"
+
+
+static void (*progress_cb)( void * hd, unsigned off, unsigned len );
+static void * progress_cb_value = NULL;
+
+
+static off_t
+fp_get_length( FILE * fp )
+{
+ struct stat statbuf;
+
+ if( fstat( fileno( fp ), &statbuf ) )
+ return (off_t)-1;
+ return statbuf.st_size;
+}
+
+
+static int
+hash_encode( void * opaque, FILE * in, FILE * out )
+{
+ md_filter_t * mfx = opaque;
+ byte buf[8192];
+ int nread;
+
+ if( !mfx )
+ return CDK_Inv_Value;
+
+ _cdk_log_debug( "hash filter: encode (algo=%d)\n", mfx->digest_algo );
+
+ if( !mfx->md ) {
+ mfx->md = cdk_md_open( mfx->digest_algo, 0 );
+ if( !mfx->md )
+ return CDK_Inv_Algo;
+ }
+
+ while( !feof( in ) ) {
+ nread = fread( buf, 1, sizeof buf-1, in );
+ if( !nread )
+ break;
+ cdk_md_write( mfx->md, buf, nread );
+ }
+
+ wipemem( buf, sizeof buf );
+ return 0;
+}
+
+
+int
+_cdk_filter_hash( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ if( ctl == STREAMCTL_READ )
+ return hash_encode( opaque, in, out );
+ else if( ctl == STREAMCTL_FREE ) {
+ md_filter_t * mfx = opaque;
+ if( mfx ) {
+ _cdk_log_debug( "free hash filter\n" );
+ cdk_md_close( mfx->md );
+ mfx->md = NULL;
+ return 0;
+ }
+ }
+ return CDK_Inv_Mode;
+}
+
+
+static int
+write_header( cipher_filter_t * cfx, FILE * out )
+{
+ struct cdk_pkt_encrypted_s ed;
+ CDK_PACKET pkt;
+ cdk_dek_t dek = cfx->dek;
+ byte temp[18];
+ size_t blocksize = 0;
+ int use_mdc = 0, nprefix;
+ int rc = 0;
+
+ blocksize = cdk_cipher_get_algo_blklen( dek->algo );
+ if( blocksize < 8 || blocksize > 16 )
+ return CDK_Inv_Algo;
+
+ use_mdc = dek->use_mdc;
+ if( blocksize != 8 )
+ use_mdc = 1; /* enabled by default for all 128-bit block cipher */
+
+ if( use_mdc && cfx->datalen )
+ cfx->datalen += 22;
+
+ memset( &ed, 0, sizeof ed );
+ if( !cfx->blkmode.on ) {
+ ed.len = cfx->datalen;
+ ed.extralen = blocksize + 2;
+ }
+
+ if( use_mdc ) {
+ ed.mdc_method = CDK_MD_SHA1;
+ cfx->mdc = cdk_md_open( CDK_MD_SHA1, 0 );
+ if( !cfx->mdc )
+ return CDK_Inv_Algo;
+ }
+
+ cdk_pkt_init( &pkt );
+ pkt.old_ctb = cfx->dek->rfc1991 && !cfx->blkmode.on? 1 : 0;
+ pkt.pkttype = use_mdc ? CDK_PKT_ENCRYPTED_MDC : CDK_PKT_ENCRYPTED;
+ pkt.pkt.encrypted = &ed;
+ rc = _cdk_pkt_write_fp( out, &pkt );
+ if( rc )
+ return rc;
+ nprefix = blocksize;
+ gcry_randomize( temp, nprefix, GCRY_STRONG_RANDOM );
+ temp[nprefix] = temp[nprefix - 2];
+ temp[nprefix + 1] = temp[nprefix - 1];
+ cfx->hd = cdk_cipher_open( dek->algo, use_mdc==0? 1 : 0,
+ dek->key, dek->keylen, NULL, 0 );
+ if( !cfx->hd )
+ return CDK_Inv_Algo;
+ if( cfx->mdc )
+ cdk_md_write( cfx->mdc, temp, nprefix + 2 );
+ rc = cdk_cipher_encrypt( cfx->hd, temp, temp, nprefix + 2 );
+ cdk_cipher_sync( cfx->hd );
+ if( !rc )
+ fwrite( temp, 1, nprefix+2, out );
+ return rc;
+}
+
+
+static int
+write_mdc_packet( FILE * out, cipher_filter_t * cfx )
+{
+ byte pktdata[22];
+ int dlen = cdk_md_get_algo_dlen( CDK_MD_SHA1 );
+ int rc;
+
+ if( !out || !cfx )
+ return CDK_Inv_Value;
+
+ if( dlen != 20 )
+ return CDK_Inv_Algo;
+ /* we must hash the prefix of the MDC packet here */
+ pktdata[0] = 0xd3;
+ pktdata[1] = 0x14;
+ cdk_md_putc( cfx->mdc, pktdata[0] );
+ cdk_md_putc( cfx->mdc, pktdata[1] );
+ cdk_md_final( cfx->mdc );
+ memcpy( pktdata + 2, cdk_md_read( cfx->mdc, CDK_MD_SHA1 ), dlen );
+ rc = cdk_cipher_encrypt( cfx->hd, pktdata, pktdata, dlen+2 );
+ if( !rc )
+ fwrite( pktdata, 1, dlen+2, out );
+ wipemem( pktdata, sizeof pktdata );
+ return rc;
+}
+
+
+static int
+num2bits( size_t n )
+{
+ int i;
+ if( !n )
+ return -1;
+ for( i = 0; n > 1; i++ )
+ n >>= 1;
+ return i;
+}
+
+
+static size_t
+pow2( size_t y )
+{
+ size_t x = 1, i;
+ for( i = 0; i < y; i++ )
+ x <<= 1;
+ return x;
+}
+
+
+static int
+write_partial_block( FILE * in, FILE * out, size_t * r_len,
+ cipher_filter_t * cfx )
+{
+ size_t n, nbytes;
+ int nread, rc;
+ byte buf[8193];
+
+ if( !out || !cfx )
+ return CDK_Inv_Value;
+
+ if( *r_len > 512 ) {
+ n = num2bits( *r_len );
+ nbytes = pow2( n );
+ fputc( 0xe0 | n, out );
+ (*r_len) -= nbytes;
+ }
+ else {
+ size_t pktlen = nbytes = *r_len;
+ if( pktlen < 192 )
+ fputc( pktlen, out );
+ else if( pktlen < 8384 ) {
+ pktlen -= 192;
+ fputc( (pktlen/256) + 192, out );
+ fputc( pktlen % 256, out );
+ }
+ (*r_len) -= nbytes;
+ }
+ while( nbytes > 0 ) {
+ nread = fread( buf, 1, sizeof buf-1, in );
+ if( !nread )
+ break;
+ if( cfx->mdc )
+ cdk_md_write( cfx->mdc, buf, nread );
+ rc = cdk_cipher_encrypt( cfx->hd, buf, buf, nread );
+ if( rc )
+ break;
+ nbytes -= nread;
+ fwrite( buf, 1, nread, out );
+ }
+ return 0;
+}
+
+
+static int
+cipher_encode_file( void * opaque, FILE * in, FILE * out )
+{
+ struct stat statbuf;
+ cipher_filter_t * cfx = opaque;
+ byte buf[8192];
+ size_t len, len2;
+ int rc = 0, nread;
+
+ if( !cfx || !in || !out )
+ return CDK_Inv_Value;
+
+ if( fstat( fileno( in ), &statbuf ) )
+ return CDK_File_Error;
+ len = len2 = statbuf.st_size;
+ while( !feof( in ) ) {
+ if( cfx->blkmode.on ) {
+ rc = write_partial_block( in, out, &len2, cfx );
+ if( rc )
+ break;
+ continue;
+ }
+ nread = fread( buf, 1, sizeof buf -1, in );
+ if( !nread )
+ break;
+ if( cfx->mdc )
+ cdk_md_write( cfx->mdc, buf, nread );
+ rc = cdk_cipher_encrypt( cfx->hd, buf, buf, nread );
+ if( rc )
+ break;
+ if( progress_cb )
+ progress_cb( progress_cb_value, ftell( in ), len );
+ fwrite( buf, 1, nread, out );
+ }
+ wipemem( buf, sizeof buf );
+ if( !rc && cfx->mdc )
+ rc = write_mdc_packet( out, cfx );
+ return rc;
+}
+
+
+static int
+read_header( cipher_filter_t * cfx, FILE * in )
+{
+ cdk_dek_t dek;
+ byte temp[32];
+ int blocksize, nprefix;
+ int i = 0, c = 0, rc = 0;
+
+ if( !cfx || !in )
+ return CDK_Inv_Value;
+
+ dek = cfx->dek;
+ blocksize = cdk_cipher_get_algo_blklen( dek->algo );
+ if( blocksize < 8 || blocksize > 16 )
+ return CDK_Inv_Algo;
+
+ nprefix = blocksize;
+ if( cfx->datalen && cfx->datalen < (nprefix + 2) )
+ return CDK_Inv_Value;
+ if( cfx->mdc_method ) {
+ cfx->mdc = cdk_md_open( cfx->mdc_method, 0 );
+ if( !cfx->mdc )
+ return CDK_Inv_Algo;
+ }
+ cfx->hd = cdk_cipher_open( dek->algo, cfx->mdc_method==0? 1 : 0,
+ dek->key, dek->keylen, NULL, 0 );
+ if( !cfx->hd )
+ return CDK_Inv_Algo;
+ for( i = 0; i < (nprefix + 2); i++ ) {
+ c = fgetc( in );
+ if( c == EOF )
+ return CDK_File_Error;
+ temp[i] = c;
+ }
+ rc = cdk_cipher_decrypt( cfx->hd, temp, temp, nprefix + 2 );
+ if( rc )
+ return rc;
+ cdk_cipher_sync( cfx->hd );
+ i = nprefix;
+ if( temp[i - 2] != temp[i] || temp[i - 1] != temp[i + 1] )
+ rc = CDK_Chksum_Error;
+ if( cfx->mdc )
+ cdk_md_write( cfx->mdc, temp, nprefix + 2 );
+ if( cfx->blkmode.on )
+ cfx->blkmode.size -= (nprefix + 2);
+ return rc;
+}
+
+
+static int
+finalize_mdc( cdk_md_hd_t md, const byte * buf, size_t nread )
+{
+ byte mdcbuf[20];
+ int dlen = cdk_md_get_algo_dlen( CDK_MD_SHA1 );
+ int rc = 0;
+
+ if( cdk_md_get_algo( md ) != CDK_MD_SHA1 || dlen != 20 )
+ return CDK_Inv_Algo;
+
+ if( buf[nread - dlen - 2] == 0xd3 && buf[nread - dlen - 1] == 0x14 ) {
+ cdk_md_write( md, buf, nread - dlen );
+ cdk_md_final( md );
+ memcpy( mdcbuf, cdk_md_read( md, 0 ), dlen );
+ if( memcmp( mdcbuf, buf + nread - dlen, dlen ) )
+ rc = CDK_Bad_MDC;
+ return rc;
+ }
+ wipemem( mdcbuf, sizeof mdcbuf );
+ return CDK_Inv_Packet;
+}
+
+
+static int
+cipher_decode_file( void * opaque, FILE * in, FILE * out )
+{
+ cipher_filter_t * cfx = opaque;
+ byte buf[8192];
+ int rc = 0, nread, nreq;
+
+ if( !cfx || !in || !out )
+ return CDK_Inv_Value;
+
+ while( !feof( in ) ) {
+ _cdk_log_debug( "partial on=%d size=%lu\n",
+ cfx->blkmode.on, cfx->blkmode.size );
+ nreq = cfx->blkmode.on? cfx->blkmode.size: sizeof buf-1;
+ nread = fread( buf, 1, nreq, in );
+ if( !nread )
+ break;
+ rc = cdk_cipher_decrypt( cfx->hd, buf, buf, nread );
+ if( rc )
+ break;
+ if( feof( in ) && cfx->mdc )
+ rc = finalize_mdc( cfx->mdc, buf, nread );
+ else if( cfx->mdc )
+ cdk_md_write( cfx->mdc, buf, nread );
+ fwrite( buf, 1, nread, out );
+ if( cfx->blkmode.on ) {
+ cfx->blkmode.size = _cdk_pkt_read_len( in, &cfx->blkmode.on );
+ if( cfx->blkmode.size == (size_t)EOF )
+ return CDK_Inv_Packet;
+ }
+ }
+ wipemem( buf, sizeof buf );
+ return rc;
+}
+
+
+int
+cipher_decode( void * opaque, FILE * in, FILE * out )
+{
+ cipher_filter_t * cfx = opaque;
+ int rc;
+
+ _cdk_log_debug( "cipher filter: decode\n" );
+
+ if( !cfx || !in || !out )
+ return CDK_Inv_Value;
+
+ rc = read_header( cfx, in );
+ if( !rc )
+ rc = cipher_decode_file( cfx, in, out );
+ return rc;
+}
+
+
+int
+cipher_encode( void * opaque, FILE * in, FILE * out )
+{
+ cipher_filter_t * cfx = opaque;
+ int rc;
+
+ _cdk_log_debug( "cipher filter: encode\n" );
+
+ if( !cfx || !in || !out )
+ return CDK_Inv_Value;
+
+ cfx->datalen = fp_get_length( in );
+ if( cfx->datalen < 8192 && cfx->blkmode.on )
+ cfx->blkmode.on = 0;
+ rc = write_header( cfx, out );
+ if( !rc )
+ rc = cipher_encode_file( cfx, in, out );
+ return rc;
+}
+
+
+int
+_cdk_filter_cipher( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ if( ctl == STREAMCTL_READ )
+ return cipher_decode( opaque, in, out );
+ else if( ctl == STREAMCTL_WRITE )
+ return cipher_encode( opaque, in, out );
+ else if( ctl == STREAMCTL_FREE ) {
+ cipher_filter_t * cfx = opaque;
+ if( cfx ) {
+ _cdk_log_debug( "free cipher filter\n" );
+ cdk_md_close( cfx->mdc );
+ cfx->mdc = NULL;
+ cdk_cipher_close( cfx->hd );
+ cfx->hd = NULL;
+ }
+ }
+ return CDK_Inv_Mode;
+}
+
+
+void
+cdk_set_progress_handler( void (*cb)(void * hd, unsigned off, unsigned size),
+ void * cb_value )
+{
+ progress_cb = cb;
+ progress_cb_value = cb_value;
+}
+
diff --git a/libextra/opencdk/cipher.h b/libextra/opencdk/cipher.h
new file mode 100644
index 0000000000..ab73f2824f
--- /dev/null
+++ b/libextra/opencdk/cipher.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * cipher.h
+ * Copyright (C) 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CDK_CIPHER_H
+#define CDK_CIPHER_H
+
+#define MAX_BLOCKSIZE 16
+
+
+typedef int (*cipher_setkey_t) (void *c,
+ const unsigned char *key,
+ unsigned keylen);
+
+typedef void (*cipher_encrypt_t) (void *c,
+ unsigned char *outbuf,
+ const unsigned char *inbuf);
+
+typedef void (*cipher_decrypt_t) (void *c,
+ unsigned char *outbuf,
+ const unsigned char *inbuf);
+
+typedef struct {
+ const char *name;
+ int id;
+ size_t blocksize;
+ size_t keylen;
+ size_t contextsize;
+ cipher_setkey_t setkey;
+ cipher_encrypt_t encrypt;
+ cipher_decrypt_t decrypt;
+} cipher_spec_t;
+
+extern cipher_spec_t cipher_spec_blowfish;
+extern cipher_spec_t cipher_spec_twofish;
+extern cipher_spec_t cipher_spec_3des;
+extern cipher_spec_t cipher_spec_cast5;
+extern cipher_spec_t cipher_spec_aes;
+extern cipher_spec_t cipher_spec_aes192;
+extern cipher_spec_t cipher_spec_aes256;
+
+
+cdk_cipher_hd_t cdk_cipher_new( int algo, int pgp_sync );
+cdk_cipher_hd_t cdk_cipher_open( int algo, int pgp_sync,
+ const byte * key, size_t keylen,
+ const byte * ivbuf, size_t ivlen );
+void cdk_cipher_close( cdk_cipher_hd_t hd );
+int cdk_cipher_encrypt( cdk_cipher_hd_t hd,
+ byte * outbuf, const byte *inbuf, size_t n );
+int cdk_cipher_decrypt( cdk_cipher_hd_t hd,
+ byte * outbuf, const byte *inbuf, size_t n );
+void cdk_cipher_sync( cdk_cipher_hd_t hd );
+int cdk_cipher_setiv( cdk_cipher_hd_t hd, const byte *ivbuf, size_t n );
+int cdk_cipher_setkey( cdk_cipher_hd_t hd, const byte *keybuf, size_t n );
+int cdk_cipher_get_algo_blklen( int algo );
+int cdk_cipher_get_algo_keylen( int algo );
+int cdk_cipher_test_algo( int algo );
+
+#endif /*CDK_CIPHER_H*/
+
+
+
diff --git a/libextra/opencdk/compress.c b/libextra/opencdk/compress.c
new file mode 100644
index 0000000000..4a8a5cd95c
--- /dev/null
+++ b/libextra/opencdk/compress.c
@@ -0,0 +1,253 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * compress.c - Compression filters
+ * Copyright (C) 2002, 2003 Timo Schulz
+ * Copyright (C) 1998-2002 Free Software Foundation
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <time.h>
+#ifdef HAVE_LIBZ
+# include <zlib.h>
+#endif
+
+#include "opencdk.h"
+#include "main.h"
+#include "filters.h"
+
+#ifdef HAVE_LIBZ
+static int
+compress_data( z_stream * zs, int flush,
+ byte * inbuf, size_t insize, FILE * out )
+{
+ int nbytes, zrc;
+ byte buf[4096];
+
+ zs->next_in = inbuf;
+ zs->avail_in = insize;
+
+ do {
+ zs->next_out = buf;
+ zs->avail_out = sizeof buf;
+
+ zrc = deflate( zs, flush );
+ if( zrc == Z_STREAM_END && flush == Z_FINISH )
+ ;
+ else if( zrc != Z_OK )
+ break;
+ nbytes = sizeof( buf ) - zs->avail_out;
+ fwrite( buf, 1, nbytes, out );
+ }
+ while( zs->avail_out == 0 || (flush == Z_FINISH && zrc != Z_STREAM_END) );
+ return zrc;
+}
+
+
+static int
+decompress_data( compress_filter_t * zfx, z_stream * zs,
+ FILE * in, size_t * ret_len )
+{
+ int nread = 0, nold;
+ int rc = 0, zrc = 0;
+
+ while( zs->avail_out != 0 ) {
+ if( !zs->avail_in ) {
+ nread = fread( zfx->inbuf, 1, zfx->inbufsize, in );
+ zs->next_in = zfx->inbuf;
+ zs->avail_in = nread;
+ }
+ nold = zs->avail_out;
+ zrc = inflate( zs, Z_SYNC_FLUSH );
+ if( zrc != Z_OK && zrc != Z_STREAM_END ) {
+ rc = CDK_Zlib_Error;
+ break;
+ }
+ *ret_len = zfx->outbufsize - zs->avail_out;
+ if( nold == zs->avail_out )
+ break;
+ if( zrc == Z_STREAM_END ) {
+ rc = EOF; /* eof */
+ break;
+ }
+ }
+ if( !nread && feof( in ) )
+ rc = -1;
+ return rc;
+}
+
+
+/* in some cases it might be possible that the packet header was not
+ readed, so we do it here. but we assume the the algo was already set. */
+static void
+skip_pktheader( FILE * in )
+{
+ int ctb, pkttype, pos;
+
+ pos = ftell (in);
+ ctb = fgetc (in);
+ if (ctb & 0x80) {
+ pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb >> 2) & 0xf);
+ if (pkttype == CDK_PKT_COMPRESSED) {
+ fgetc (in);
+ return;
+ }
+ }
+ fseek (in, pos, SEEK_SET);
+}
+
+
+static int
+compress_decode( void * opaque, FILE * in, FILE * out )
+{
+ compress_filter_t * zfx = opaque;
+ z_stream * zs;
+ int zrc, rc = 0;
+ size_t nbytes;
+
+ _cdk_log_debug ("compress filter: decode (algo=%d)\n", zfx->algo);
+
+ if (!zfx || !in || !out)
+ return CDK_Inv_Value;
+
+ zs = cdk_calloc (1, sizeof *zs);
+ if (!zs)
+ return CDK_Out_Of_Core;
+
+ if (zfx->algo == CDK_COMPRESS_ZIP)
+ zrc = inflateInit2 (zs, -13);
+ else
+ zrc = inflateInit (zs);
+ if (zrc != Z_OK)
+ return CDK_Zlib_Error;
+
+ zfx->outbufsize = 8192;
+ zfx->inbufsize = 2048;
+ memset (zfx->inbuf, 0, sizeof zfx->inbuf);
+ zs->avail_in = 0;
+
+ skip_pktheader (in);
+ while (rc != -1) {
+ zs->next_out = zfx->outbuf;
+ zs->avail_out = 8192;
+ rc = decompress_data (zfx, zs, in, &nbytes);
+ fwrite (zfx->outbuf, 1, nbytes, out);
+ }
+ inflateEnd (zs);
+ cdk_free (zs);
+ if (rc == CDK_EOF)
+ rc = 0;
+ return rc;
+}
+
+
+static int
+compress_encode( void * opaque, FILE * in, FILE * out )
+{
+ compress_filter_t * zfx = opaque;
+ z_stream * zs;
+ struct cdk_pkt_compressed_s cd;
+ CDK_PACKET pkt;
+ clock_t start, end;
+ int rc, zrc, nread;
+
+ _cdk_log_debug ("compress filter: encode\n");
+
+ if( !zfx || !in || !out )
+ return CDK_Inv_Value;
+
+ if( !zfx->algo )
+ zfx->algo = CDK_COMPRESS_ZIP;
+
+ memset( &cd, 0, sizeof cd );
+ cd.len = 0;
+ cd.algorithm = zfx->algo;
+ pkt.pkttype = CDK_PKT_COMPRESSED;
+ pkt.pkt.compressed = &cd;
+ rc = _cdk_pkt_write_fp( out, &pkt );
+ if( rc )
+ return rc;
+
+ zs = cdk_calloc( 1, sizeof *zs );
+ if( !zs )
+ return CDK_Out_Of_Core;
+ if( zfx->algo == CDK_COMPRESS_ZIP )
+ rc = deflateInit2( zs, zfx->level, Z_DEFLATED, -13, 8,
+ Z_DEFAULT_STRATEGY );
+ else
+ rc = deflateInit( zs, zfx->level );
+ if( rc != Z_OK ) {
+ cdk_free( zs );
+ return CDK_Zlib_Error;
+ }
+ zfx->outbufsize = 8192;
+ memset( zfx->outbuf, 0, sizeof zfx->outbuf );
+
+ start = clock ();
+ while (!feof (in)) {
+ nread = fread (zfx->outbuf, 1, zfx->outbufsize, in);
+ if (!nread)
+ break;
+ zrc = compress_data (zs, Z_NO_FLUSH, zfx->outbuf, nread, out);
+ if (zrc) {
+ rc = CDK_Zlib_Error;
+ break;
+ }
+ }
+ if (!rc) {
+ nread = 0;
+ zrc = compress_data (zs, Z_FINISH, zfx->outbuf, nread, out);
+ if (zrc != Z_STREAM_END)
+ rc = CDK_Zlib_Error;
+ }
+ end = clock ();
+ /*printf ("compress encode: elapsed time %d\n", end - start);*/
+ deflateEnd (zs);
+ cdk_free (zs);
+ return rc;
+}
+
+
+int
+_cdk_filter_compress( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ if( ctl == STREAMCTL_READ )
+ return compress_decode( opaque, in, out );
+ else if( ctl == STREAMCTL_WRITE )
+ return compress_encode( opaque, in, out );
+ else if( ctl == STREAMCTL_FREE ) {
+ compress_filter_t * zfx = opaque;
+ if( zfx ) {
+ _cdk_log_debug( "free compress filter\n" );
+ zfx->level = 0;
+ zfx->algo = 0;
+ }
+ }
+ return CDK_Inv_Mode;
+}
+#else
+int
+_cdk_filter_compress( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ return CDK_Not_Implemented;
+}
+#endif /* HAVE_LIBZ */
+
+
diff --git a/libextra/opencdk/context.h b/libextra/opencdk/context.h
new file mode 100644
index 0000000000..e4e42009b1
--- /dev/null
+++ b/libextra/opencdk/context.h
@@ -0,0 +1,196 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * context.h
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CDK_CONTEXT_H
+#define CDK_CONTEXT_H
+
+#include "types.h"
+
+struct cdk_listkey_s {
+ unsigned init:1;
+ cdk_stream_t inp;
+ cdk_keydb_hd_t db;
+ int type;
+ union {
+ char * patt;
+ cdk_strlist_t fpatt;
+ } u;
+ cdk_strlist_t t;
+};
+
+struct cdk_sesskey_s {
+ gcry_mpi_t a;
+};
+
+struct cdk_verify_result_s {
+ int sig_ver;
+ int sig_len;
+ int sig_status;
+ int sig_flags;
+ unsigned int keyid[2];
+ unsigned int created;
+ unsigned int expires;
+ int pubkey_algo;
+ int digest_algo;
+ char * notation;
+ unsigned char * sig_data;
+};
+
+struct cdk_s2k_s {
+ int mode;
+ unsigned char hash_algo;
+ unsigned char salt[8];
+ unsigned int count;
+};
+
+struct cdk_ctx_s {
+ int trust_model;
+ int cipher_algo;
+ int digest_algo;
+ struct {
+ int algo;
+ int level;
+ } compress;
+ struct {
+ int mode;
+ int digest_algo;
+ int cipher_algo;
+ } _s2k;
+ struct {
+ unsigned armor:1;
+ unsigned textmode:1;
+ unsigned compress:1;
+ unsigned mdc:1;
+ unsigned compat:1;
+ unsigned rfc1991:1;
+ unsigned overwrite;
+ unsigned force_digest:1;
+ } opt;
+ struct {
+ _cdk_verify_result_t verify;
+ } result;
+ struct {
+ cdk_pkt_seckey_t sk;
+ unsigned on:1;
+ } cache;
+ cdk_dek_t dek;
+ cdk_s2k_t s2k;
+ struct {
+ cdk_keydb_hd_t sec;
+ cdk_keydb_hd_t pub;
+ } db;
+ void (*callback) (void * opaque, int type, const char * s);
+ void * callback_value;
+ char *(*passphrase)(void * opaque, const char * prompt);
+ void * passphrase_value;
+};
+
+struct cdk_prefitem_s {
+ unsigned char type;
+ unsigned char value;
+};
+
+struct cdk_desig_revoker_s {
+ struct cdk_desig_revoker_s * next;
+ unsigned char class;
+ unsigned char algid;
+ unsigned char fpr[20];
+};
+
+struct cdk_subpkt_s {
+ struct cdk_subpkt_s * next;
+ unsigned int size;
+ unsigned char type;
+ unsigned char d[1];
+};
+
+struct cdk_mpi_s {
+ unsigned short bits;
+ unsigned short bytes;
+ unsigned char data[1];
+};
+
+struct key_idx_s {
+ unsigned int offset;
+ unsigned int keyid[2];
+ unsigned char fpr[20];
+};
+
+
+struct cdk_dbsearch_s
+{
+ union {
+ char * pattern;
+ unsigned int keyid[2];
+ unsigned char fpr[20];
+ } u;
+ int type;
+};
+typedef struct cdk_dbsearch_s *cdk_dbsearch_t;
+
+
+struct key_table_s {
+ struct key_table_s * next;
+ unsigned int offset;
+ cdk_dbsearch_t desc;
+};
+
+
+
+struct cdk_keydb_hd_s {
+ int type;
+ cdk_stream_t buf; /* NULL if the name item is valid */
+ cdk_stream_t idx;
+ cdk_dbsearch_t dbs;
+ char * name;
+ char * idx_name;
+ struct key_table_s * cache;
+ int ncache;
+ unsigned int secret:1;
+ unsigned int isopen:1;
+ unsigned int no_cache:1;
+ unsigned int search:1;
+};
+
+
+struct cdk_keylist_s {
+ struct cdk_keylist_s * next;
+ union {
+ cdk_pkt_pubkey_t pk;
+ cdk_pkt_seckey_t sk;
+ } key;
+ int type;
+};
+
+struct cdk_dek_s {
+ int algo;
+ int keylen;
+ int use_mdc;
+ unsigned rfc1991:1;
+ unsigned char key[32]; /* 256-bit */
+};
+
+struct cdk_strlist_s {
+ struct cdk_strlist_s * next;
+ char d[1];
+};
+
+#endif /* CDK_CONTEXT_H */
diff --git a/libextra/opencdk/encrypt.c b/libextra/opencdk/encrypt.c
new file mode 100644
index 0000000000..7b97d6c11a
--- /dev/null
+++ b/libextra/opencdk/encrypt.c
@@ -0,0 +1,1020 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * encrypt.c
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <stdarg.h>
+#include <malloc.h>
+#include <assert.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "filters.h"
+#include "stream.h"
+#include "packet.h"
+
+
+struct mainproc_ctx_s {
+ cdk_dek_t dek;
+ cdk_stream_t s;
+ cdk_kbnode_t node;
+ cdk_stream_t tmpfp;
+ struct {
+ unsigned present:1;
+ unsigned one_pass:1;
+ cdk_md_hd_t md;
+ int digest_algo;
+ int is_expired;
+ cdk_pkt_pubkey_t pk;
+ unsigned pt_offset;
+ } sig;
+ unsigned eof_seen:1;
+ unsigned key_seen:1;
+ char * file; /* for detached signatures */
+ const char * output;
+};
+typedef struct mainproc_ctx_s * CTX;
+
+
+static void
+write_marker_packet( cdk_stream_t out )
+{
+ byte buf[5];
+
+ buf[0] = (0x80 | (10<<2));
+ buf[1] = 3;
+ buf[2] = 0x50;
+ buf[3] = 0x47;
+ buf[4] = 0x50;
+ cdk_stream_write( out, buf, 5 );
+}
+
+
+static void
+literal_set_rfc1991( cdk_stream_t out )
+{
+ literal_filter_t * pfx;
+ pfx = _cdk_stream_get_opaque( out, fLITERAL );
+ if( pfx )
+ pfx->rfc1991 = 1;
+}
+
+
+static int
+sym_stream_encrypt (cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out)
+{
+ cdk_packet_t pkt = NULL;
+ cdk_pkt_symkey_enc_t enc;
+ char * pw = NULL;
+ int rc = 0;
+
+ if( !hd || !inp || !out )
+ return CDK_Inv_Value;
+
+ pw = _cdk_passphrase_get( hd, "Enter Passphrase: " );
+ if( !pw )
+ goto fail;
+
+ cdk_free( hd->s2k );
+ rc = cdk_s2k_new( &hd->s2k, hd->_s2k.mode, hd->_s2k.digest_algo, NULL );
+ if( rc )
+ goto fail;
+
+ cdk_dek_free( hd->dek );
+ rc = cdk_dek_from_passphrase( &hd->dek, hd->cipher_algo, hd->s2k, 2, pw );
+ if( rc )
+ goto fail;
+
+ if( hd->opt.rfc1991 ) {
+ hd->dek->rfc1991 = 1;
+ goto start; /* skip the pkt_symkey_enc packet. */
+ }
+
+ cdk_stream_set_cache( out, 1 );
+ if( hd->opt.compat )
+ write_marker_packet( out );
+
+ enc = cdk_calloc( 1, sizeof *enc );
+ if( !enc ) {
+ rc = CDK_Out_Of_Core;
+ goto fail;
+ }
+ pkt = cdk_calloc( 1, sizeof * pkt );
+ if( !pkt ) {
+ rc = CDK_Out_Of_Core;
+ goto fail;
+ }
+ enc->version = 4;
+ enc->cipher_algo = hd->dek->algo;
+ enc->s2k = hd->s2k;
+ pkt->pkttype = CDK_PKT_SYMKEY_ENC;
+ pkt->pkt.symkey_enc = enc;
+ rc = cdk_pkt_write( out, pkt );
+ cdk_free( enc );
+ if( rc )
+ goto fail;
+ cdk_stream_set_cache( out, 0 );
+
+ start:
+ if( hd->opt.armor )
+ cdk_stream_set_armor_flag( out, 0 );
+ cdk_stream_set_cipher_flag( out, hd->dek, hd->opt.mdc );
+ if( hd->opt.compress )
+ cdk_stream_set_compress_flag( out, hd->compress.algo,
+ hd->compress.level );
+ cdk_stream_set_literal_flag( out, 0, _cdk_stream_get_fname( inp ) );
+ if( hd->opt.rfc1991 )
+ literal_set_rfc1991( out );
+ rc = cdk_stream_kick_off( inp, out );
+
+fail:
+ _cdk_passphrase_free( pw, pw? strlen( pw ) : 0 );
+ cdk_free( pkt );
+ return rc;
+}
+
+
+static int
+use_rfc1991_format( cdk_ctx_t hd, cdk_keylist_t kl )
+{
+ cdk_keylist_t l;
+
+ if( hd->opt.rfc1991 )
+ return 1;
+ for( l = kl; l; l = l->next ) {
+ if( l->type == CDK_PKT_PUBLIC_KEY && l->key.pk->version == 3 )
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+select_cipher_algo( int rfc1991, cdk_keylist_t kl )
+{
+ int pgp2 = _cdk_is_idea_available( );
+ int def_cipher = pgp2 && rfc1991? CDK_CIPHER_IDEA : CDK_CIPHER_CAST5;
+ return rfc1991?def_cipher : cdk_pklist_select_algo( kl, CDK_PREFTYPE_SYM );
+}
+
+
+int
+_cdk_check_args( int overwrite, const char * in, const char * out )
+{
+ if( !in || !out )
+ return CDK_Inv_Value;
+ if( !_cdk_strcmp( in, out ) )
+ return CDK_Inv_Mode;
+ if( !overwrite && !_cdk_check_file( out ) )
+ return CDK_Inv_Mode;
+ return 0;
+}
+
+
+/**
+ * cdk_stream_encrypt: Encrypt a stream.
+ * @hd: Handle
+ * @remusr: List of recipients
+ * @inp: Input stream handle
+ * @out: Output stream handle
+ *
+ * If remusr is NULL, then symmetric encryption is used. Via the
+ * handle the caller can set or unset multiple options.
+ */
+cdk_error_t
+cdk_stream_encrypt( cdk_ctx_t hd, cdk_strlist_t remusr,
+ cdk_stream_t inp, cdk_stream_t out )
+{
+ cdk_keylist_t pkl = NULL;
+ int cipher_algo, compress_algo = 0;
+ int use_rfc1991 = 0;
+ int rc = 0;
+
+ if( !hd || !inp || !out )
+ return CDK_Inv_Value;
+
+ if( !remusr )
+ return sym_stream_encrypt( hd, inp, out );
+
+ rc = cdk_pklist_build( &pkl, hd->db.pub, remusr, PK_USAGE_ENCR );
+ if( rc )
+ return rc;
+
+ use_rfc1991 = use_rfc1991_format( hd, pkl );
+ cipher_algo = select_cipher_algo( use_rfc1991, pkl );
+ cdk_dek_free( hd->dek );
+ rc = cdk_dek_new( &hd->dek );
+ if( !rc )
+ rc = cdk_dek_set_cipher( hd->dek, cipher_algo );
+ if( !rc )
+ rc = cdk_dek_set_key( hd->dek, NULL, 0 );
+ if( rc ) {
+ cdk_pklist_release( pkl );
+ return rc;
+ }
+ compress_algo = use_rfc1991? CDK_COMPRESS_ZIP: hd->compress.algo;
+
+ if( !hd->opt.rfc1991 && !hd->opt.compat )
+ cdk_dek_set_mdc_flag( hd->dek, cdk_pklist_use_mdc( pkl ) );
+ hd->dek->rfc1991 = use_rfc1991;
+
+ cdk_stream_set_cache( out, 1 );
+ if( hd->opt.compat && !hd->opt.rfc1991 )
+ write_marker_packet( out );
+
+ rc = cdk_pklist_encrypt( pkl, hd->dek, out );
+ cdk_pklist_release( pkl );
+ if( rc )
+ return rc;
+
+ cdk_stream_set_cache( out, 0 );
+
+ if( hd->opt.armor )
+ cdk_stream_set_armor_flag( out, 0 );
+ cdk_stream_set_cipher_flag( out, hd->dek, 0 );
+ if( hd->opt.compress )
+ cdk_stream_set_compress_flag( out, compress_algo, hd->compress.level );
+ cdk_stream_set_literal_flag( out, 0, _cdk_stream_get_fname( inp ) );
+ if( hd->dek->rfc1991 )
+ literal_set_rfc1991( out );
+
+ return cdk_stream_kick_off( inp, out );
+}
+
+
+/**
+ * cdk_file_encrypt: Encrypt a file.
+ * @hd: Handle
+ * @remusr: List of recipient
+ * @file: Input file
+ * @output: Output file
+ *
+ **/
+cdk_error_t
+cdk_file_encrypt (cdk_ctx_t hd, cdk_strlist_t remusr,
+ const char * file, const char * output)
+{
+ cdk_stream_t inp = NULL, out = NULL;
+ int rc;
+
+ rc = _cdk_check_args( hd->opt.overwrite, file, output );
+ if( !rc )
+ rc = cdk_stream_open( file, &inp );
+ if( !rc )
+ rc = cdk_stream_new( output, &out );
+ if( !rc )
+ rc = cdk_stream_encrypt( hd, remusr, inp, out );
+ cdk_stream_close( inp );
+ cdk_stream_close( out );
+ return rc;
+}
+
+
+static void
+write_status (cdk_ctx_t hd, int type, const char * fmt, ...)
+{
+ va_list arg_ptr;
+ char * buf;
+ int n;
+
+ if (!hd->callback)
+ return;
+
+ va_start (arg_ptr, fmt);
+ n = _cdk_vasprintf (&buf, fmt, arg_ptr);
+ buf[n] = '\0';
+ hd->callback (hd->callback_value, type, buf);
+ _cdk_vasprintf_free (buf);
+ va_end (arg_ptr);
+}
+
+
+static int
+is_openpgp_ext (const char * file)
+{
+ return (strstr (file, ".asc") || strstr (file, ".sig")
+ || strstr (file, ".gpg") || strstr (file, ".pgp")) ? 1 : 0;
+}
+
+
+static int
+hash_data_file( char * file, int digest_algo, cdk_md_hd_t * r_md )
+{
+ md_filter_t * mfx;
+ cdk_stream_t s;
+ int rc;
+
+ if( file && is_openpgp_ext( file ) )
+ file[strlen( file ) - 4] = '\0';
+ else
+ return CDK_General_Error;
+
+ rc = cdk_stream_open( file, &s );
+ if( rc )
+ return rc;
+
+ cdk_stream_set_hash_flag( s, digest_algo );
+ cdk_stream_read( s, NULL, 0 );
+ mfx = _cdk_stream_get_opaque( s, fHASH );
+ if( mfx && mfx->md )
+ *r_md = cdk_md_copy( mfx->md );
+ cdk_stream_close( s );
+ return 0;
+}
+
+
+static int
+handle_symkey_enc (CTX c, cdk_ctx_t hd, cdk_packet_t pkt)
+{
+ cdk_pkt_symkey_enc_t key;
+ char * pw = NULL;
+ int rc = 0;
+
+ assert (pkt->pkttype == CDK_PKT_SYMKEY_ENC);
+
+ c->key_seen = 1;
+ if (c->dek)
+ return 0; /* we already decrypted the session key */
+
+ pw = _cdk_passphrase_get( hd, "Enter Passphrase: " );
+ if( !pw )
+ return CDK_Out_Of_Core;
+
+ key = pkt->pkt.symkey_enc;
+ rc = cdk_dek_from_passphrase( &c->dek, key->cipher_algo, key->s2k, 0, pw );
+ _cdk_passphrase_free( pw, pw? strlen( pw ) : 0 );
+ return rc;
+}
+
+
+static int
+get_seckey( cdk_ctx_t hd, cdk_keydb_hd_t db, u32 * keyid,
+ cdk_pkt_seckey_t*r_sk )
+{
+ int rc;
+
+ if( !r_sk )
+ return CDK_Inv_Value;
+ if( hd->cache.on && hd->cache.sk ) {
+ cdk_pkt_seckey_t sk = hd->cache.sk;
+ cdk_sk_get_keyid( sk, NULL );
+ if( sk->keyid[0] == keyid[0] && sk->keyid[1] == keyid[1] ) {
+ *r_sk = sk;
+ return 0;
+ }
+ }
+ rc = cdk_keydb_get_sk( db, keyid, r_sk );
+ if( hd->cache.on )
+ hd->cache.sk = *r_sk;
+ return rc;
+}
+
+
+static int
+handle_pubkey_enc( CTX c, cdk_ctx_t hd, cdk_packet_t pkt )
+{
+ cdk_pkt_pubkey_enc_t enc;
+ cdk_pkt_seckey_t sk = NULL;
+ int rc = 0;
+
+ assert( pkt->pkttype == CDK_PKT_PUBKEY_ENC );
+
+ c->key_seen = 1;
+ enc = pkt->pkt.pubkey_enc;
+ write_status( hd, CDK_CB_PUBKEY_ENC, "%08lX%08lX %d %d",
+ enc->keyid[0], enc->keyid[1], enc->pubkey_algo,
+ (enc->mpi[0]->bits+7)/8*8 );
+
+ if( c->dek )
+ return 0; /* we already decrypted the session key */
+
+ /* we checked before that there is at least one secret key so we
+ skip this packet and continue without errors */
+ if( cdk_keydb_check_sk( hd->db.sec, enc->keyid ) )
+ return 0;
+ rc = get_seckey( hd, hd->db.sec, enc->keyid, &sk );
+ if( !rc )
+ rc = cdk_dek_extract( &c->dek, hd, enc, sk );
+ if( !hd->cache.on )
+ _cdk_free_seckey( sk );
+ return rc;
+}
+
+
+static int
+rfc1991_get_sesskey (cdk_dek_t * r_dek, cdk_ctx_t hd)
+{
+ cdk_s2k_t s2k;
+ char * pw;
+ int rc;
+
+ if (!r_dek)
+ return CDK_Inv_Value;
+
+ pw = _cdk_passphrase_get (hd, "Enter Passphrase: ");
+ if (!pw)
+ return CDK_Out_Of_Core;
+ rc = cdk_s2k_new( &s2k, 0, CDK_MD_MD5, NULL );
+ if( rc ) {
+ _cdk_passphrase_free( pw, pw? strlen( pw ) : 0 );
+ return CDK_Out_Of_Core;
+ }
+ rc = cdk_dek_from_passphrase( r_dek, CDK_CIPHER_IDEA, s2k, 0, pw );
+ _cdk_passphrase_free( pw, pw? strlen( pw ) : 0 );
+ cdk_free( s2k );
+ return rc;
+}
+
+
+static int
+handle_encrypted (CTX c, cdk_ctx_t hd, cdk_packet_t pkt, int use_mdc)
+{
+ cdk_pkt_encrypted_t enc;
+ int pgp2_compat = _cdk_is_idea_available ();
+ int rc = 0, pkttype = pkt->pkttype;
+
+ assert (CDK_PKT_IS_ENCRYPTED (pkttype));
+
+ if (!c->dek) {
+ if (!pgp2_compat)
+ return CDK_Error_No_Key;
+ else if (!c->key_seen) {
+ _cdk_log_debug ("RFC1991 message was detected.\n");
+ rc = rfc1991_get_sesskey (&c->dek, hd);
+ if (rc)
+ return rc;
+ }
+ else
+ return CDK_Error_No_Key;
+ }
+
+ enc = pkt->pkt.encrypted;
+ cdk_stream_set_cipher_flag (enc->buf, c->dek, use_mdc);
+ rc = cdk_stream_read (enc->buf, NULL, 0);
+ if (!rc)
+ c->s = enc->buf;
+ else
+ rc = _cdk_stream_get_errno (enc->buf);
+ return rc;
+}
+
+
+static int
+handle_compressed( CTX c, cdk_packet_t pkt )
+{
+ cdk_pkt_compressed_t zip;
+ int rc;
+
+ assert( pkt->pkttype == CDK_PKT_COMPRESSED );
+
+ zip = pkt->pkt.compressed;
+ cdk_stream_set_compress_flag( c->s, zip->algorithm, 0 );
+ rc = cdk_stream_read( c->s, NULL, 0 );
+ if( rc )
+ rc = _cdk_stream_get_errno( c->s );
+ return rc;
+}
+
+
+static int
+handle_onepass_sig( CTX c, cdk_packet_t pkt )
+{
+ int rc = 0;
+
+ assert (pkt->pkttype == CDK_PKT_ONEPASS_SIG);
+
+ if( c->sig.md )
+ return 0; /* already open */
+ c->sig.digest_algo = pkt->pkt.onepass_sig->digest_algo;
+ if( cdk_md_test_algo( c->sig.digest_algo ) )
+ return CDK_Inv_Algo;
+ c->sig.md = cdk_md_open( c->sig.digest_algo, 0 );
+ if( !c->sig.md )
+ rc = CDK_Gcry_Error;
+ return rc;
+}
+
+
+static int
+handle_literal( CTX c, cdk_packet_t pkt, cdk_stream_t * ret_out )
+{
+ literal_filter_t * pfx;
+ cdk_pkt_literal_t pt;
+ cdk_stream_t out;
+ const char * s;
+ int rc = 0;
+
+ assert( pkt->pkttype == CDK_PKT_LITERAL );
+
+ if( !ret_out )
+ return CDK_Inv_Value;
+
+ if( !c->tmpfp ) {
+ /* fixme: handle _CONSOLE */
+ s = c->output? c->output : pkt->pkt.literal->name;
+ rc = cdk_stream_create( s, ret_out );
+ if( rc )
+ return rc;
+ }
+ else
+ *ret_out = c->tmpfp;
+ out = *ret_out;
+ pt = pkt->pkt.literal;
+ cdk_stream_seek( c->s, c->sig.present? c->sig.pt_offset : 0 );
+ cdk_stream_set_literal_flag( c->s, 0, NULL );
+ if( c->sig.present ) {
+ _cdk_log_debug( "enable hash filter (algo=%d)\n", c->sig.digest_algo );
+ pfx = _cdk_stream_get_opaque( c->s, fLITERAL );
+ if( pfx )
+ pfx->md = c->sig.md;
+ }
+ return cdk_stream_kick_off( c->s, out );
+}
+
+
+static byte *
+mpi_encode( cdk_pkt_signature_t sig )
+{
+ cdk_mpi_t a;
+ byte * p;
+ size_t len, i, nsig = 0, pos = 0;
+
+ nsig = cdk_pk_get_nsig( sig->pubkey_algo );
+ for( i = 0, len = 0; i < nsig; i++ )
+ len += sig->mpi[i]->bytes + 2;
+ p = cdk_calloc( 1, len + 1 );
+ if( !p )
+ return NULL;
+ for( i = 0; i < nsig; i++ ) {
+ a = sig->mpi[i];
+ p[pos++] = a->bits >> 8;
+ p[pos++] = a->bits;
+ memcpy( p + pos, a->data, a->bytes );
+ pos += a->bytes;
+ }
+ return p;
+}
+
+
+static void
+store_verify_result( cdk_pkt_signature_t sig, _cdk_verify_result_t res )
+{
+ res->sig_len = sig->mpi[0]->bits;
+ res->sig_ver = sig->version;
+ res->keyid[0] = sig->keyid[0];
+ res->keyid[1] = sig->keyid[1];
+ res->created = sig->timestamp;
+ res->pubkey_algo = sig->pubkey_algo;
+ res->digest_algo = sig->digest_algo;
+ if( sig->flags.expired )
+ res->sig_flags |= CDK_FLAG_SIG_EXPIRED;
+ res->sig_data = mpi_encode( sig );
+}
+
+
+static int
+handle_signature (cdk_ctx_t hd, CTX c, cdk_packet_t pkt)
+{
+ _cdk_verify_result_t res;
+ cdk_pkt_signature_t sig;
+ u32 keyid[2];
+ int rc;
+
+ assert( pkt->pkttype == CDK_PKT_SIGNATURE );
+
+ if( !c->sig.present )
+ return CDK_Inv_Packet;
+
+ _cdk_result_verify_free( hd->result.verify );
+ res = hd->result.verify = _cdk_result_verify_new( );
+ if( !hd->result.verify )
+ return CDK_Out_Of_Core;
+
+ sig = pkt->pkt.signature;
+ if( !c->sig.one_pass && !c->sig.md ) {
+ if( cdk_md_test_algo( sig->digest_algo ) )
+ return CDK_Inv_Algo;
+ rc = hash_data_file( c->file, sig->digest_algo, &c->sig.md );
+ if( rc )
+ return rc;
+ }
+
+ cdk_sig_get_keyid( sig, keyid );
+ store_verify_result( sig, res );
+
+ rc = cdk_keydb_get_pk( hd->db.pub, keyid, &c->sig.pk );
+ if( rc ) {
+ res->sig_status = CDK_SIGSTAT_NOKEY;
+ return rc;
+ }
+ if( c->sig.pk->is_revoked )
+ res->sig_flags |= CDK_FLAG_KEY_REVOKED;
+ if( c->sig.pk->has_expired )
+ res->sig_flags |= CDK_FLAG_KEY_EXPIRED;
+
+ rc = _cdk_sig_check( c->sig.pk, sig, c->sig.md, &c->sig.is_expired );
+ res->sig_status = !rc? CDK_SIGSTAT_GOOD : CDK_SIGSTAT_BAD;
+ if( !rc )
+ _cdk_log_debug("good signature from %08lX%08lX (expired %d)\n",
+ keyid[0], keyid[1], c->sig.is_expired );
+ return rc;
+}
+
+
+static void
+free_mainproc( CTX c )
+{
+ if( !c )
+ return;
+ cdk_kbnode_release( c->node );
+ c->node = NULL;
+ if( c->sig.present ) {
+ cdk_md_close( c->sig.md );
+ c->sig.md = NULL;
+ _cdk_free_pubkey( c->sig.pk );
+ c->sig.pk = NULL;
+ }
+ cdk_free (c->file);
+ c->file = NULL;
+ cdk_free (c->dek);
+ c->dek = NULL;
+ cdk_free (c);
+}
+
+
+static int
+do_proc_packets( cdk_ctx_t hd, CTX c, cdk_stream_t inp,
+ cdk_stream_t * ret_out )
+{
+ cdk_packet_t pkt = NULL;
+ cdk_kbnode_t n = NULL, node;
+ const char * s;
+ int rc = 0, npos, with_mdc = 0;
+
+ if( !hd || !c )
+ return CDK_Inv_Value;
+
+ s = _cdk_stream_get_fname (inp);
+ c->file = cdk_strdup (s? s : " ");
+ if (!c->file) {
+ cdk_free (c);
+ return CDK_Out_Of_Core;
+ }
+
+ while (!cdk_stream_eof (inp)) {
+ pkt = cdk_calloc (1, sizeof *pkt);
+ if (!pkt)
+ return CDK_Out_Of_Core;
+ rc = cdk_pkt_read (inp, pkt);
+ _cdk_log_debug ("type=%d old_ctb=%d len=%d (%d)\n",
+ pkt->pkttype, pkt->old_ctb, pkt->pktlen, rc);
+ if (rc == CDK_EOF)
+ c->eof_seen = 1;
+ if (rc)
+ break;
+
+ n = cdk_kbnode_new (pkt);
+ if (!c->node)
+ c->node = n;
+ else
+ _cdk_kbnode_add (c->node, n);
+
+ switch (pkt->pkttype) {
+ case CDK_PKT_SYMKEY_ENC:
+ rc = handle_symkey_enc (c, hd, pkt);
+ _cdk_log_debug (" handle_symkey_enc (%d)\n", rc);
+ break;
+
+ case CDK_PKT_PUBKEY_ENC:
+ rc = handle_pubkey_enc (c, hd, pkt);
+ _cdk_log_debug (" handle_pubkey_enc (%d)\n", rc);
+ break;
+
+ case CDK_PKT_ENCRYPTED_MDC:
+ case CDK_PKT_ENCRYPTED:
+ with_mdc = pkt->pkttype == CDK_PKT_ENCRYPTED_MDC;
+ rc = handle_encrypted (c, hd, pkt, with_mdc);
+ _cdk_log_debug (" handle_encrypted (%d)\n", rc);
+ if (!rc)
+ inp = c->s;
+ break;
+
+ case CDK_PKT_COMPRESSED:
+ if (!c->s)
+ c->s = inp;
+ rc = handle_compressed (c, pkt);
+ _cdk_log_debug (" handle_compressed (%d)\n", rc);
+ break;
+
+ case CDK_PKT_ONEPASS_SIG:
+ if (!c->s)
+ c->s = inp;
+ _cdk_log_debug (" handle_onepass_sig (0)\n");
+ c->sig.present = 1;
+ c->sig.one_pass = 1;
+ c->sig.pt_offset = cdk_stream_tell (c->s);
+ break;
+
+ case CDK_PKT_LITERAL:
+ /* skip rest of the packet */
+ if (!c->s)
+ c->s = inp;
+ if( !_cdk_stream_get_blockmode( c->s ) ) {
+ npos = cdk_stream_tell (c->s) + pkt->pkt.literal->len;
+ cdk_stream_seek (c->s, npos);
+ }
+ else
+ cdk_stream_seek( c->s, cdk_stream_get_length( c->s ) );
+ break;
+
+ case CDK_PKT_SIGNATURE:
+ if (!c->sig.present)
+ c->sig.present = 1;
+ break; /* handle it later */
+
+ case CDK_PKT_MDC:
+ _cdk_log_debug( "MDC packet detected.\n" );
+ break;
+
+ case CDK_PKT_MARKER:
+ _cdk_log_debug( "marker packet detected.\n" );
+ break;
+
+ default:
+ rc = CDK_Inv_Packet;
+ break;
+ }
+ if (rc)
+ break;
+ }
+ if( c->eof_seen == 1 )
+ rc = 0;
+ for( node = c->node; !rc && node; node = node->next ) {
+ pkt = node->pkt;
+ switch (pkt->pkttype) {
+ case CDK_PKT_ONEPASS_SIG:
+ rc = handle_onepass_sig (c, pkt);
+ _cdk_log_debug (" _handle_onepass_sig (%d)\n", rc);
+ break;
+
+ case CDK_PKT_LITERAL:
+ rc = handle_literal (c, pkt, ret_out);
+ _cdk_log_debug (" _handle_literal (%d)\n", rc);
+ break;
+
+ case CDK_PKT_SIGNATURE:
+ rc = handle_signature (hd, c, pkt);
+ _cdk_log_debug (" _handle_signature (%d)\n", rc);
+ break;
+
+ default:
+ _cdk_log_debug ("skip packet type %d\n", pkt->pkttype);
+ break;
+ }
+ if (rc)
+ break;
+ }
+ if( rc == CDK_EOF )
+ rc = CDK_Wrong_Seckey;
+ return rc;
+}
+
+
+int
+_cdk_proc_packets( cdk_ctx_t hd, cdk_stream_t inp,
+ const char * output, cdk_stream_t outstream,
+ cdk_md_hd_t md )
+{
+ cdk_stream_t out = NULL;
+ CTX c;
+ int rc;
+
+ if( !inp )
+ return CDK_Inv_Value;
+ if( output && outstream )
+ return CDK_Inv_Mode;
+
+ c = cdk_calloc( 1, sizeof *c );
+ if( !c )
+ return CDK_Out_Of_Core;
+ if( output )
+ c->output = output;
+ if( outstream )
+ c->tmpfp = outstream;
+ if( md )
+ c->sig.md = md;
+ rc = do_proc_packets( hd, c, inp, &out );
+ if( !c->tmpfp )
+ cdk_stream_close( out );
+ free_mainproc( c );
+ return rc;
+}
+
+
+static int
+check_pubkey_enc_list( cdk_stream_t in, cdk_keydb_hd_t hd )
+{
+ cdk_packet_t pkt;
+ int n = 0, nenc = 0;
+
+ if( !in || !hd )
+ return CDK_Inv_Value;
+
+ if( cdk_armor_filter_use( in ) )
+ cdk_stream_set_armor_flag( in, 0 );
+ pkt = cdk_calloc( 1, sizeof * pkt );
+ cdk_pkt_init( pkt );
+ while( !cdk_pkt_read( in, pkt ) ) {
+ if( pkt->pkttype != CDK_PKT_PUBKEY_ENC ) {
+ if( CDK_PKT_IS_ENCRYPTED( pkt->pkttype ) )
+ cdk_free( pkt->pkt.encrypted );
+ else
+ cdk_pkt_free( pkt );
+ break;
+ }
+ nenc++;
+ if( !cdk_keydb_check_sk( hd, pkt->pkt.pubkey_enc->keyid ) )
+ n++;
+ cdk_pkt_free( pkt );
+ cdk_pkt_init( pkt );
+ }
+ cdk_free( pkt );
+ cdk_stream_seek( in, 0 );
+ if( !nenc )
+ return 0;
+ _cdk_log_debug( "found %d secret keys\n", n );
+ return n? 0 : CDK_Error_No_Key;
+}
+
+
+/**
+ * cdk_file_decrypt - Decrypt a file.
+ * @hd: Handle.
+ * @file: Name of the file to decrypt.
+ * @output: Output filename.
+ *
+ * When the operation was successfull, hd can contain information about
+ * the signature (when present) and more.
+ **/
+cdk_error_t
+cdk_file_decrypt( cdk_ctx_t hd, const char * file, const char * output )
+{
+ cdk_stream_t inp = NULL;
+ int rc = 0;
+
+ if( !file )
+ return CDK_Inv_Value;
+
+ if( file && output )
+ rc = _cdk_check_args( hd->opt.overwrite, file, output );
+ if( !rc )
+ rc = cdk_stream_open( file, &inp );
+ if( !rc )
+ rc = check_pubkey_enc_list( inp, hd->db.sec );
+ if( !rc )
+ rc = _cdk_proc_packets( hd, inp, output, NULL, NULL );
+
+ cdk_stream_close( inp );
+ return rc;
+}
+
+
+cdk_error_t
+cdk_stream_decrypt( cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out )
+{
+ int rc;
+
+ rc = check_pubkey_enc_list( inp, hd->db.sec );
+ if( !rc )
+ rc = _cdk_proc_packets( hd, inp, NULL, out, NULL );
+ return rc;
+}
+
+
+/**
+ * cdk_data_transform:
+ * @hd: session handle
+ * @mode: crypto mode
+ * @locusr: local user list (sign mode only)
+ * @remusr: remote users 'recipients'
+ * @inbuf: input buffer with data
+ * @insize: length of data in bytes
+ * @outbuf: pointer to the output data (will be allocated)
+ * @outsize: size of the new data in bytes
+ * @modval: value for the modus (for example sign mode)
+ *
+ * This function transforms data into the given openpgp mode. It works
+ * exactly like the cdk_file_xxx functions with the exception that it can
+ * be used with memory and not with streams or files.
+ **/
+cdk_error_t
+cdk_data_transform( cdk_ctx_t hd, enum cdk_crypto_mode_t mode,
+ cdk_strlist_t locusr, cdk_strlist_t remusr,
+ const void * inbuf, size_t insize,
+ byte ** outbuf, size_t * outsize,
+ int modval )
+{
+ cdk_stream_t inp, out;
+ cdk_keydb_hd_t db;
+ cdk_kbnode_t knode = NULL;
+ int rc, res[4];
+
+ if( !hd )
+ return CDK_Inv_Value;
+ if( !mode )
+ return 0;
+ if( mode == CDK_CRYPTYPE_SIGN && !locusr )
+ return CDK_Inv_Value;
+ if( !inbuf || !insize || !outbuf )
+ return CDK_Inv_Value;
+
+ inp = cdk_stream_tmp_from_mem( inbuf, insize );
+ if( !inp )
+ return CDK_Out_Of_Core;
+ out = cdk_stream_tmp( );
+ if( !out ) {
+ cdk_stream_close( inp );
+ return CDK_Out_Of_Core;
+ }
+
+ cdk_stream_tmp_set_mode( inp, 0 );
+ cdk_stream_tmp_set_mode( out, 1 );
+
+ switch( mode ) {
+ case CDK_CRYPTYPE_ENCRYPT:
+ rc = cdk_stream_encrypt( hd, remusr, inp, out );
+ break;
+
+ case CDK_CRYPTYPE_DECRYPT:
+ rc = cdk_stream_decrypt( hd, inp, out );
+ break;
+
+ case CDK_CRYPTYPE_SIGN:
+ rc = cdk_stream_sign( hd, inp, out, locusr, remusr, 0, modval );
+ break;
+
+ case CDK_CRYPTYPE_VERIFY:
+ rc = cdk_stream_verify( hd, inp, out );
+ break;
+
+ case CDK_CRYPTYPE_EXPORT:
+ if( cdk_handle_control( hd, CDK_CTLF_GET, CDK_CTL_ARMOR ) )
+ cdk_stream_set_armor_flag( out, CDK_ARMOR_PUBKEY );
+ db = cdk_handle_get_keydb( hd, CDK_DBTYPE_PK_KEYRING );
+ rc = cdk_keydb_export( db, out, remusr );
+ break;
+
+ case CDK_CRYPTYPE_IMPORT:
+ if( cdk_armor_filter_use( inp ) )
+ cdk_stream_set_armor_flag( inp, 0 );
+ rc = cdk_keydb_get_keyblock( inp, &knode );
+ if( knode ) {
+ db = cdk_handle_get_keydb( hd, CDK_DBTYPE_PK_KEYRING );
+ rc = cdk_keydb_import( db, knode, res );
+ if( !rc ) {
+ *outbuf = NULL; /* FIXME */
+ *outsize = strlen( *outbuf );
+ }
+ cdk_kbnode_release( knode );
+ }
+ break;
+
+ default:
+ rc = CDK_Inv_Mode;
+ break;
+ }
+
+ cdk_stream_close( inp );
+ if( !rc && mode != CDK_CRYPTYPE_VERIFY ) {
+ cdk_stream_tmp_set_mode( out, 0 );
+ rc = cdk_stream_mmap( out, outbuf, outsize );
+ }
+ else if( !rc && mode == CDK_CRYPTYPE_VERIFY ) {
+ *outbuf = NULL; /* FIXME */
+ *outsize = *outbuf? strlen( *outbuf ) : 0;
+ }
+ cdk_stream_close( out );
+ return rc;
+}
+
+
diff --git a/libextra/opencdk/filters.h b/libextra/opencdk/filters.h
new file mode 100644
index 0000000000..48c3c7c2f3
--- /dev/null
+++ b/libextra/opencdk/filters.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * filters.h - Filter structs
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CDK_FILTERS_H
+#define CDK_FILTERS_H
+
+enum {
+ STREAMCTL_READ = 0,
+ STREAMCTL_WRITE = 1,
+ STREAMCTL_FREE = 2
+};
+
+typedef struct {
+ cdk_cipher_hd_t hd;
+ cdk_md_hd_t mdc;
+ int mdc_method;
+ cdk_dek_t dek;
+ u32 datalen;
+ struct {
+ int on;
+ size_t size;
+ } blkmode;
+ cdk_stream_t s;
+} cipher_filter_t;
+
+typedef struct {
+ int digest_algo;
+ cdk_md_hd_t md;
+} md_filter_t;
+
+typedef struct {
+ const char * le; /* line endings */
+ const char * hdrlines;
+ u32 crc;
+ int crc_okay;
+ int idx, idx2;
+} armor_filter_t;
+
+typedef struct {
+ int mode;
+ unsigned rfc1991:1;
+ char * filename;
+ cdk_md_hd_t md;
+ struct {
+ int on;
+ size_t size;
+ } blkmode;
+} literal_filter_t;
+
+typedef struct {
+ size_t inbufsize;
+ byte inbuf[8192];
+ size_t outbufsize;
+ byte outbuf[8192];
+ int algo; /* compress algo */
+ int level;
+} compress_filter_t;
+
+typedef struct {
+ const char * lf;
+} text_filter_t;
+
+
+/*-- armor.c -*/
+int _cdk_filter_armor( void * opaque, int ctl, FILE * in, FILE * out );
+
+/*-- cipher.c --*/
+int _cdk_filter_hash( void * opaque, int ctl, FILE * in, FILE * out );
+int _cdk_filter_cipher( void * opaque, int ctl, FILE * in, FILE * out );
+
+/*-- plaintext.c --*/
+int _cdk_filter_literal( void * opaque, int ctl, FILE * in, FILE * out );
+int _cdk_filter_text( void * opaque, int ctl, FILE * in, FILE * out );
+
+/*-- compress.c --*/
+int _cdk_filter_compress( void * opaque, int ctl, FILE * in, FILE * out );
+
+#endif /* CDK_FILTERS_H */
+
+
diff --git a/libextra/opencdk/kbnode.c b/libextra/opencdk/kbnode.c
new file mode 100644
index 0000000000..72e941a043
--- /dev/null
+++ b/libextra/opencdk/kbnode.c
@@ -0,0 +1,572 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * kbnode.c - keyblock node utility functions
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+/* X-TODO-STATUS: OK */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+
+#define is_deleted_kbnode(a) ((a)->private_flag & 1)
+#define is_cloned_kbnode(a) ((a)->private_flag & 2)
+
+
+/**
+ * cdk_kbnode_new:
+ * @pkt: the packet to add
+ *
+ * Allocate a new key node and add the packet.
+ **/
+cdk_kbnode_t
+cdk_kbnode_new( cdk_packet_t pkt )
+{
+ cdk_kbnode_t n;
+ n = cdk_calloc( 1, sizeof * n );
+ if( !n )
+ return NULL;
+ n->pkt = pkt;
+ return n;
+}
+
+
+void
+_cdk_kbnode_clone( cdk_kbnode_t node )
+{
+ if( node )
+ node->private_flag |= 2; /* mark cloned */
+}
+
+
+/**
+ * cdk_kbnode_release:
+ * @n: the key node
+ *
+ * Release the memory of the node.
+ **/
+void
+cdk_kbnode_release( cdk_kbnode_t node )
+{
+ cdk_kbnode_t n2;
+
+ while( node ) {
+ n2 = node->next;
+ node->pkt->pkttype = 0;
+ if( !is_cloned_kbnode( node ) )
+ cdk_pkt_release( node->pkt );
+ cdk_free( node );
+ node = n2;
+ }
+}
+
+
+/**
+ * cdk_kbnode_delete:
+ * @node: the ke keynode.
+ *
+ * Delete @node.
+ **/
+void
+cdk_kbnode_delete( cdk_kbnode_t node )
+{
+ if( node )
+ node->private_flag |= 1;
+}
+
+
+/****************
+ * Append NODE to ROOT. ROOT must exist!
+ */
+void
+_cdk_kbnode_add( cdk_kbnode_t root, cdk_kbnode_t node )
+{
+ cdk_kbnode_t n1;
+
+ for( n1 = root; n1->next; n1 = n1->next )
+ ;
+ n1->next = node;
+}
+
+
+/**
+ * cdk_kbnode_insert:
+ * @root: the root key node
+ * @node: the node to add
+ * @pkttype: packet type
+ *
+ * Insert @node into the list after @root but before a packet which is not of
+ * type @pkttype (only if @pkttype != 0).
+ **/
+void
+cdk_kbnode_insert( cdk_kbnode_t root, cdk_kbnode_t node, int pkttype )
+{
+ if( !pkttype ) {
+ node->next = root->next;
+ root->next = node;
+ }
+ else {
+ cdk_kbnode_t n1;
+ for( n1 = root; n1->next; n1 = n1->next )
+ if( pkttype != n1->next->pkt->pkttype ) {
+ node->next = n1->next;
+ n1->next = node;
+ return;
+ }
+ /* no such packet, append */
+ node->next = NULL;
+ n1->next = node;
+ }
+}
+
+
+/**
+ * cdk_kbnode_find_prev:
+ * @root: the root key node
+ * @node: the key node
+ * @pkttype: packet type
+ *
+ * Find the previous node (if @pkttype = 0) or the previous node
+ * with pkttype @pkttype in the list starting with @root of @node.
+ **/
+cdk_kbnode_t
+cdk_kbnode_find_prev( cdk_kbnode_t root, cdk_kbnode_t node, int pkttype )
+{
+ cdk_kbnode_t n1;
+
+ for( n1 = NULL; root && root != node; root = root->next ) {
+ if( !pkttype || root->pkt->pkttype == pkttype )
+ n1 = root;
+ }
+ return n1;
+}
+
+
+/**
+ * cdk_kbnode_find_next:
+ * @node: the key node
+ * @pkttype: packet type
+ *
+ * Ditto, but find the next packet. The behaviour is trivial if
+ * @pkttype is 0 but if it is specified, the next node with a packet
+ * of this type is returned. The function has some knowledge about
+ * the valid ordering of packets: e.g. if the next signature packet
+ * is requested, the function will not return one if it encounters
+ * a user-id.
+ **/
+cdk_kbnode_t
+cdk_kbnode_find_next( cdk_kbnode_t node, int pkttype )
+{
+ for( node = node->next; node; node = node->next ) {
+ if( !pkttype )
+ return node;
+ else if( pkttype == CDK_PKT_USER_ID
+ && (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || node->pkt->pkttype == CDK_PKT_SECRET_KEY))
+ return NULL;
+ else if (pkttype == CDK_PKT_SIGNATURE
+ && (node->pkt->pkttype == CDK_PKT_USER_ID
+ || node->pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || node->pkt->pkttype == CDK_PKT_SECRET_KEY))
+ return NULL;
+ else if (node->pkt->pkttype == pkttype)
+ return node;
+ }
+ return NULL;
+}
+
+
+/**
+ * cdk_kbnode_find:
+ * @node: the key node
+ * @pkttype: packet type
+ *
+ * Try to find the next node with the packettype @pkttype.
+ **/
+cdk_kbnode_t
+cdk_kbnode_find( cdk_kbnode_t node, int pkttype )
+{
+ for( ; node; node = node->next ) {
+ if( node->pkt->pkttype == pkttype )
+ return node;
+ }
+ return NULL;
+}
+
+
+/**
+ * cdk_kbnode_find_packet:
+ * @node: the key node
+ * @pkttype: packet type
+ *
+ * Same as cdk_kbnode_find but it returns the packet instead of the node.
+ **/
+cdk_packet_t
+cdk_kbnode_find_packet( cdk_kbnode_t node, int pkttype )
+{
+ cdk_kbnode_t res;
+
+ res = cdk_kbnode_find( node, pkttype );
+ if( res )
+ return res->pkt;
+ return NULL;
+}
+
+
+/****************
+ * Walk through a list of kbnodes. This function returns
+ * the next kbnode for each call; before using the function the first
+ * time, the caller must set CONTEXT to NULL (This has simply the effect
+ * to start with ROOT).
+ */
+cdk_kbnode_t
+cdk_kbnode_walk( cdk_kbnode_t root, cdk_kbnode_t * context, int all )
+{
+ cdk_kbnode_t n;
+
+ do {
+ if( !*context ) {
+ *context = root;
+ n = root;
+ }
+ else {
+ n = (*context)->next;
+ *context = n;
+ }
+ }
+ while( !all && n && is_deleted_kbnode( n ) );
+ return n;
+}
+
+
+/****************
+ * Commit changes made to the kblist at ROOT. Note that ROOT my change,
+ * and it is therefore passed by reference.
+ * The function has the effect of removing all nodes marked as deleted.
+ * returns true if any node has been changed
+ */
+int
+cdk_kbnode_commit( cdk_kbnode_t * root )
+{
+ cdk_kbnode_t n, nl;
+ int changed = 0;
+
+ for( n = *root, nl = NULL; n; n = nl->next ) {
+ if (is_deleted_kbnode (n)) {
+ if( n == *root )
+ *root = nl = n->next;
+ else
+ nl->next = n->next;
+ if( !is_cloned_kbnode( n ) ) {
+ cdk_pkt_release( n->pkt );
+ cdk_free( n->pkt );
+ }
+ cdk_free( n );
+ changed = 1;
+ }
+ else
+ nl = n;
+ }
+ return changed;
+}
+
+
+void
+cdk_kbnode_remove( cdk_kbnode_t * root, cdk_kbnode_t node )
+{
+ cdk_kbnode_t n, nl;
+
+ for( n = *root, nl = NULL; n; n = nl->next ) {
+ if( n == node ) {
+ if( n == *root )
+ *root = nl = n->next;
+ else
+ nl->next = n->next;
+ if( !is_cloned_kbnode( n ) ) {
+ cdk_pkt_release( n->pkt );
+ cdk_free( n->pkt);
+ }
+ cdk_free( n );
+ }
+ else
+ nl = n;
+ }
+}
+
+
+/****************
+ * Move NODE behind right after WHERE or to the beginning if WHERE is NULL.
+ */
+void
+cdk_kbnode_move (cdk_kbnode_t * root, cdk_kbnode_t node, cdk_kbnode_t where)
+{
+ cdk_kbnode_t tmp, prev;
+
+ if (!root || !*root || !node)
+ return; /* sanity check */
+ for (prev = *root; prev && prev->next != node; prev = prev->next)
+ ;
+ if (!prev)
+ return; /* node is not in the list */
+
+ if (!where) { /* move node before root */
+ if (node == *root) /* move to itself */
+ return;
+ prev->next = node->next;
+ node->next = *root;
+ *root = node;
+ return;
+ }
+ /* move it after where */
+ if (node == where)
+ return;
+ tmp = node->next;
+ node->next = where->next;
+ where->next = node;
+ prev->next = tmp;
+}
+
+
+/**
+ * cdk_kbnode_get_packet:
+ * @node: the key node
+ *
+ * Return the packet which is stored inside the node in @node.
+ **/
+cdk_packet_t
+cdk_kbnode_get_packet( cdk_kbnode_t node )
+{
+ if( node )
+ return node->pkt;
+ return NULL;
+}
+
+
+/**
+ * cdk_kbnode_read_from_mem:
+ * @ret_node: the new key node
+ * @buf: the buffer which stores the key sequence
+ * @buflen: the length of the buffer
+ *
+ * Try to read a key node from the memory buffer @buf.
+ **/
+cdk_error_t
+cdk_kbnode_read_from_mem( cdk_kbnode_t * ret_node,
+ const byte * buf, size_t buflen )
+{
+ cdk_stream_t inp;
+ int rc;
+
+ if( !buflen || !ret_node )
+ return CDK_Inv_Value;
+
+ *ret_node = NULL;
+ inp = cdk_stream_tmp_from_mem( buf, buflen );
+ if( !inp )
+ return CDK_Out_Of_Core;
+ rc = cdk_keydb_get_keyblock( inp, ret_node );
+ if( rc == CDK_EOF && *ret_node )
+ rc = 0;
+ cdk_stream_close( inp );
+ return rc;
+}
+
+
+/**
+ * cdk_kbnode_write_to_mem:
+ * @node: the key node
+ * @buf: the buffer to store the node data
+ * @r_nbytes: the new length of the buffer.
+ *
+ * Try to write the contents of the key node to the buffer @buf and
+ * return the length of it in @r_nbytes. If buf is zero, only the
+ * length of the node is calculated and returned in @r_nbytes.
+ **/
+cdk_error_t
+cdk_kbnode_write_to_mem( cdk_kbnode_t node, byte * buf, size_t * r_nbytes )
+{
+ cdk_kbnode_t n;
+ cdk_stream_t s;
+ int rc = 0, len;
+
+ if( !node )
+ return CDK_Inv_Value;
+
+ s = cdk_stream_tmp( );
+ if( !s )
+ return CDK_Out_Of_Core;
+
+ for( n = node; n; n = n->next ) {
+ if( n->pkt->pkttype != CDK_PKT_PUBLIC_KEY
+ && n->pkt->pkttype != CDK_PKT_PUBLIC_SUBKEY
+ && n->pkt->pkttype != CDK_PKT_SECRET_KEY
+ && n->pkt->pkttype != CDK_PKT_SECRET_SUBKEY
+ && n->pkt->pkttype != CDK_PKT_SIGNATURE
+ && n->pkt->pkttype != CDK_PKT_USER_ID )
+ continue;
+ rc = cdk_pkt_write( s, n->pkt );
+ if( rc )
+ break;
+ }
+ if( !rc ) {
+ cdk_stream_seek( s, 0 );
+ len = cdk_stream_get_length( s );
+ if( !buf ) {
+ *r_nbytes = len; /* only return the length of the buffer */
+ cdk_stream_close( s );
+ return 0;
+ }
+ if( *r_nbytes < len )
+ rc = CDK_Too_Short;
+ if( !rc )
+ *r_nbytes = cdk_stream_read( s, buf, len );
+ }
+ cdk_stream_close( s );
+ return rc;
+}
+
+
+/**
+ * cdk_kbnode_get_attr:
+ * @node: the key node
+ * @pkttype: the packet type which the attribute should be retrieved from
+ * @attr: the attribute to retrive
+ *
+ * Extract a single attribute from the specified packet type. If no
+ * packet type is given, it is assumed that the public key is meant.
+ * If the attr was found, it is returned as a pointer which can be cast
+ * to a proper type.
+ **/
+void *
+cdk_kbnode_get_attr( cdk_kbnode_t node, int pkttype, int attr )
+{
+ cdk_packet_t pkt;
+ cdk_pkt_pubkey_t pk;
+ cdk_pkt_userid_t id;
+ cdk_pkt_signature_t sig;
+
+ if( !node || !attr )
+ return NULL;
+ if( !pkttype )
+ pkttype = CDK_PKT_PUBLIC_KEY;
+ pkt = cdk_kbnode_find_packet( node, pkttype );
+ if( !pkt )
+ return NULL;
+ switch( pkttype ) {
+ case CDK_PKT_SECRET_KEY:
+ case CDK_PKT_PUBLIC_KEY:
+ if( pkttype == CDK_PKT_PUBLIC_KEY )
+ pk = pkt->pkt.public_key;
+ else
+ pk = pkt->pkt.secret_key->pk;
+ assert( pk );
+ switch( attr ) {
+ case CDK_ATTR_CREATED: return (long *)pk->timestamp;
+ case CDK_ATTR_EXPIRE : return (long *)pk->expiredate;
+ case CDK_ATTR_VERSION: return (byte *)pk->version;
+ case CDK_ATTR_LEN : return (long *)cdk_pk_get_nbits( pk );
+ case CDK_ATTR_KEYID:
+ if( !pk->keyid[0] || !pk->keyid[1] )
+ cdk_pk_get_keyid( pk, pk->keyid );
+ return pk->keyid;
+ case CDK_ATTR_FPR:
+ if( !pk->fpr[0] )
+ cdk_pk_get_fingerprint( pk, pk->fpr );
+ return pk->fpr;
+ case CDK_ATTR_ALGO_PK: return (byte *)pk->pubkey_algo;
+ default: return NULL;
+ }
+ break;
+
+ case CDK_PKT_USER_ID:
+ id = pkt->pkt.user_id;
+ switch( attr ) {
+ case CDK_ATTR_LEN : return (long *)id->len;
+ case CDK_ATTR_NAME: return id->name;
+ default: return NULL;
+ }
+ break;
+
+ case CDK_PKT_SIGNATURE:
+ sig = pkt->pkt.signature;
+ switch( attr ) {
+ case CDK_ATTR_ALGO_MD: return (byte *)sig->digest_algo;
+ case CDK_ATTR_ALGO_PK: return (byte *)sig->pubkey_algo;
+ case CDK_ATTR_VERSION: return (byte *)sig->version;
+ case CDK_ATTR_KEYID : return (u32 *)cdk_sig_get_keyid( sig, NULL );
+ default: return NULL;
+ }
+ break;
+
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+
+/**
+ * cdk_kbnode_hash:
+ * @node: the key node
+ * @hashctx: opaque pointer to the hash context
+ * @is_v4: OpenPGP signature (yes=1, no=0)
+ * @pkttype: packet type to hash (if zero use the packet type from the node)
+ * @flags: flags which depend on the operation
+ *
+ * Hash the key node contents. Two modes are supported. If the packet
+ * type is used (!= 0) then the function searches the first node with
+ * this type. Otherwise the node is seen as a single node and the type
+ * is extracted from it.
+ **/
+cdk_error_t
+cdk_kbnode_hash( cdk_kbnode_t node, cdk_md_hd_t md, int is_v4,
+ int pkttype, int flags )
+{
+ cdk_packet_t pkt;
+
+ if( !node || !md )
+ return CDK_Inv_Value;
+ if( !pkttype )
+ pkttype = node->pkt->pkttype;
+ pkt = cdk_kbnode_find_packet( node, pkttype );
+ if( !pkt )
+ return CDK_Inv_Packet;
+ switch( pkttype ) {
+ case CDK_PKT_PUBLIC_KEY:
+ case CDK_PKT_PUBLIC_SUBKEY:
+ _cdk_hash_pubkey( pkt->pkt.public_key, md, flags & 1 ); break;
+ case CDK_PKT_USER_ID:
+ _cdk_hash_userid( pkt->pkt.user_id, is_v4, md ); break;
+ case CDK_PKT_SIGNATURE:
+ _cdk_hash_sig_data( pkt->pkt.signature, md ); break;
+ default:
+ return CDK_Inv_Mode;
+ }
+ return 0;
+}
+
+
diff --git a/libextra/opencdk/keydb.c b/libextra/opencdk/keydb.c
new file mode 100644
index 0000000000..28966a1dcb
--- /dev/null
+++ b/libextra/opencdk/keydb.c
@@ -0,0 +1,1733 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * keydb.c - Key database routines
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <time.h>
+#include <ctype.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "filters.h"
+#include "stream.h"
+
+
+#define KEYID_CMP( a, b ) ((a[0]) == (b[0]) && (a[1]) == (b[1]))
+#define KEYDB_CACHE_ENTRIES 8
+
+typedef struct key_table_s * key_table_t;
+typedef struct key_idx_s * key_idx_t;
+
+
+static void keydb_cache_free( key_table_t cache );
+static int keydb_search_copy( cdk_dbsearch_t * r_dst, cdk_dbsearch_t src );
+static int classify_data( const byte * buf, size_t len );
+void keydb_search_free( cdk_dbsearch_t dbs );
+
+
+static char *
+keydb_idx_mkname( const char * file )
+{
+ char * fname;
+
+ fname = cdk_calloc( 1, strlen( file ) + 5 );
+ if( !fname )
+ return NULL;
+ sprintf( fname, "%s.idx", file );
+ return fname;
+}
+
+
+/* This functions builds an index of the keyring into a separate file
+ with the name keyring.ext.idx. It contains the offset of all public-
+ and public subkeys. The format of the file is:
+ --------
+ 4 octets offset of the packet
+ 8 octets keyid
+ 20 octets fingerprint
+ --------
+ We store the keyid and the fingerprint due to the fact we can't get
+ the keyid from a v3 fingerprint directly.
+*/
+static int
+keydb_idx_build( const char * file )
+{
+ cdk_packet_t pkt;
+ cdk_stream_t inp, out = NULL;
+ byte buf[8], fpr[20];
+ char * fname;
+ u32 keyid[2];
+ int rc, pos;
+
+ if( !file )
+ return CDK_Inv_Value;
+
+ pkt = cdk_calloc( 1, sizeof * pkt );
+ if( !pkt )
+ return CDK_Out_Of_Core;
+
+ fname = keydb_idx_mkname( file );
+ if( !fname ) {
+ rc = CDK_Out_Of_Core;
+ goto leave;
+ }
+
+ rc = cdk_stream_open( file, &inp );
+ if( !rc )
+ rc = cdk_stream_create( fname, &out );
+ if( rc )
+ goto leave;
+
+ while( !cdk_stream_eof( inp ) ) {
+ pos = cdk_stream_tell( inp );
+ rc = cdk_pkt_read( inp, pkt );
+ if( rc )
+ break;
+ if( pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY ) {
+ _cdk_u32tobuf( pos, buf );
+ cdk_stream_write( out, buf, 4 );
+ cdk_pk_get_keyid( pkt->pkt.public_key, keyid );
+ _cdk_u32tobuf( keyid[0], buf );
+ _cdk_u32tobuf( keyid[1], buf + 4 );
+ cdk_stream_write( out, buf, 8 );
+ cdk_pk_get_fingerprint( pkt->pkt.public_key, fpr );
+ cdk_stream_write( out, fpr, 20 );
+ }
+ cdk_pkt_free( pkt );
+ cdk_pkt_init( pkt );
+ }
+ cdk_stream_close( out );
+ leave:
+ cdk_stream_close( inp );
+ cdk_free( fname );
+ cdk_free( pkt );
+ return rc;
+}
+
+
+/**
+ * cdk_keydb_idx_rebuild:
+ * @hd: key database handle
+ *
+ * Rebuild the key index files for the given key database.
+ **/
+cdk_error_t
+cdk_keydb_idx_rebuild( cdk_keydb_hd_t hd )
+{
+ int rc;
+
+ if( !hd || !hd->name )
+ return CDK_Inv_Value;
+ if( hd->secret )
+ return 0;
+
+ cdk_stream_close( hd->idx );
+ if( !hd->idx_name ) {
+ hd->idx_name = keydb_idx_mkname( hd->name );
+ if( !hd->idx_name )
+ return CDK_Out_Of_Core;
+ }
+ rc = keydb_idx_build( hd->name );
+ if( !rc )
+ rc = cdk_stream_open( hd->idx_name, &hd->idx );
+ return rc;
+}
+
+
+static int
+keydb_idx_parse( cdk_stream_t inp, key_idx_t * r_idx )
+{
+ key_idx_t idx;
+ byte buf[4];
+ int i;
+
+ if( !inp || !r_idx )
+ return CDK_Inv_Value;
+
+ idx = cdk_calloc( 1, sizeof * idx );
+ if( !idx )
+ return CDK_Out_Of_Core;
+
+ while( !cdk_stream_eof( inp ) ) {
+ i = cdk_stream_read( inp, buf, 4 );
+ if( i == CDK_EOF )
+ break;
+ idx->offset = _cdk_buftou32( buf );
+ cdk_stream_read( inp, buf, 4 );
+ idx->keyid[0] = _cdk_buftou32( buf );
+ cdk_stream_read( inp, buf, 4 );
+ idx->keyid[1] = _cdk_buftou32( buf );
+ cdk_stream_read( inp, idx->fpr, 20 );
+#if 0
+ _cdk_log_debug( "%08lu: keyid=%08lX fpr=", idx->offset,idx->keyid[1] );
+ for( i = 0; i < 20; i++ )
+ _cdk_log_debug( "%02X", idx->fpr[i] );
+ _cdk_log_debug( "\n" );
+#endif
+ break;
+ }
+ *r_idx = idx;
+ return cdk_stream_eof( inp )? CDK_EOF : 0;
+}
+
+
+static int
+keydb_idx_search( cdk_stream_t inp, u32 * keyid,
+ const byte * fpr, u32 * r_off )
+{
+ key_idx_t idx;
+
+ if( !inp || !r_off )
+ return CDK_Inv_Value;
+ if( (keyid && fpr) || (!keyid && !fpr) )
+ return CDK_Inv_Mode;
+
+ *r_off = 0;
+ cdk_stream_seek( inp, 0 );
+ while( keydb_idx_parse( inp, &idx ) != CDK_EOF ) {
+ if( keyid && KEYID_CMP( keyid, idx->keyid ) ) {
+ *r_off = idx->offset;
+ break;
+ }
+ else if( fpr && !memcmp( idx->fpr, fpr, 20 ) ) {
+ *r_off = idx->offset;
+ break;
+ }
+ cdk_free( idx );
+ idx = NULL;
+ }
+ cdk_free( idx );
+ return *r_off? 0 : CDK_EOF;
+}
+
+
+/**
+ * cdk_keydb_new:
+ * @r_hd: handle to store the new keydb object
+ * @type: type of the keyring
+ * @data: data which depends on the keyring type
+ * @count: length of the data
+ *
+ * Create a new keydb object
+ **/
+cdk_error_t
+cdk_keydb_new( cdk_keydb_hd_t * r_hd, int type, void * data, size_t count )
+{
+ cdk_keydb_hd_t hd;
+
+ if( !r_hd )
+ return CDK_Inv_Value;
+
+ hd = cdk_calloc( 1, sizeof *hd );
+ if( !hd )
+ return CDK_Out_Of_Core;
+
+ switch( type ) {
+ case CDK_DBTYPE_PK_KEYRING:
+ case CDK_DBTYPE_SK_KEYRING:
+ hd->name = cdk_strdup( data );
+ if( !hd->name ) {
+ cdk_free( hd );
+ return CDK_Out_Of_Core;
+ }
+ break;
+
+ case CDK_DBTYPE_DATA:
+ hd->buf = cdk_stream_tmp_from_mem( data, count );
+ if( !hd->buf ) {
+ cdk_free( hd );
+ return CDK_Out_Of_Core;
+ }
+ break;
+
+ default:
+ cdk_free( hd );
+ return CDK_Inv_Mode;
+ }
+ hd->type = type;
+ if( type == CDK_DBTYPE_SK_KEYRING )
+ hd->secret = 1;
+ *r_hd = hd;
+ return 0;
+}
+
+
+/**
+ * cdk_keydb_free:
+ * @hd: the keydb object
+ *
+ * Free the keydb object.
+ **/
+void
+cdk_keydb_free( cdk_keydb_hd_t hd )
+{
+ if( !hd )
+ return;
+ if( hd->isopen && hd->name ) {
+ hd->isopen = 0;
+ cdk_free( hd->name );
+ hd->name = NULL;
+ cdk_stream_close( hd->buf );
+ hd->buf = NULL;
+ }
+ if( !hd->secret ) {
+ cdk_stream_close( hd->idx );
+ hd->idx = NULL;
+ }
+ hd->no_cache = 0;
+ hd->secret = 0;
+ keydb_cache_free( hd->cache );
+ hd->cache = NULL;
+ keydb_search_free( hd->dbs );
+ hd->dbs = NULL;
+ cdk_free( hd );
+}
+
+
+/**
+ * cdk_keydb_open:
+ * @hd: keydb object
+ * @ret_kr: the STREAM object which contains the data of the keyring
+ *
+ * Open a STREAM with the contents of the keyring from @hd
+ **/
+cdk_error_t
+cdk_keydb_open( cdk_keydb_hd_t hd, cdk_stream_t * ret_kr )
+{
+ int rc = 0, ec;
+
+ if( !hd || !ret_kr )
+ return CDK_Inv_Value;
+
+ if( hd->type == CDK_DBTYPE_DATA && hd->buf )
+ cdk_stream_seek( hd->buf, 0 );
+ else if( hd->type == CDK_DBTYPE_PK_KEYRING
+ || hd->type == CDK_DBTYPE_SK_KEYRING ) {
+ if( !hd->isopen && hd->name ) {
+ rc = cdk_stream_open( hd->name, &hd->buf );
+ if( rc )
+ goto leave;
+ if( cdk_armor_filter_use( hd->buf ) )
+ cdk_stream_set_armor_flag( hd->buf, 0 );
+ hd->isopen = 1;
+ cdk_free( hd->idx_name );
+ hd->idx_name = keydb_idx_mkname( hd->name );
+ if( !hd->idx_name ) {
+ rc = CDK_Out_Of_Core;
+ goto leave;
+ }
+ ec = cdk_stream_open( hd->idx_name, &hd->idx );
+ if( ec && !hd->secret ) {
+ rc = keydb_idx_build( hd->name );
+ if( !rc )
+ rc = cdk_stream_open( hd->idx_name, &hd->idx );
+ if( !rc )
+ _cdk_log_debug( "create key index table\n" );
+ if( rc ) {
+ /* this is no real error, it just means we can't create
+ the index at the given directory. maybe we've no write
+ access. in this case, we simply disable the index. */
+ _cdk_log_debug( "disable key index table\n" );
+ rc = 0;
+ hd->no_cache = 1;
+ }
+ }
+ }
+ else {
+ /* We use the cache to search keys, so we always rewind the
+ STREAM. Except when the _NEXT search mode is used because
+ this mode is an enumeration and no seeking is needed. */
+ if( !hd->search ||
+ (hd->search && hd->dbs->type != CDK_DBSEARCH_NEXT) )
+ cdk_stream_seek( hd->buf, 0 );
+ }
+ }
+ else
+ return CDK_Inv_Mode;
+
+ leave:
+ if( rc ) {
+ cdk_stream_close( hd->buf );
+ hd->buf = NULL;
+ }
+ *ret_kr = hd->buf;
+ return rc;
+}
+
+
+static int
+find_by_keyid( cdk_kbnode_t knode, cdk_dbsearch_t ks )
+{
+ cdk_kbnode_t node;
+ u32 keyid[2];
+ int found = 0;
+
+ for( node = knode; node; node = node->next ) {
+ if( node->pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == CDK_PKT_SECRET_KEY
+ || node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY ) {
+ _cdk_pkt_get_keyid( node->pkt, keyid );
+ switch( ks->type ) {
+ case CDK_DBSEARCH_SHORT_KEYID:
+ if( keyid[1] == ks->u.keyid[1] ) {
+ found = 1;
+ break;
+ }
+ break;
+
+ case CDK_DBSEARCH_KEYID:
+ if( KEYID_CMP( keyid, ks->u.keyid ) ) {
+ found = 1;
+ break;
+ }
+ break;
+
+ default: /* invalid mode */
+ return 0;
+ }
+ }
+ }
+ return found;
+}
+
+
+static int
+find_by_fpr( cdk_kbnode_t knode, cdk_dbsearch_t ks )
+{
+ cdk_kbnode_t node;
+ int found = 0;
+ byte fpr[20];
+
+ if( ks->type != CDK_DBSEARCH_FPR )
+ return found;
+
+ for( node = knode; node; node = node->next ) {
+ if( node->pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == CDK_PKT_SECRET_KEY
+ || node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY ) {
+ _cdk_pkt_get_fingerprint( node->pkt, fpr );
+ if( !memcmp( ks->u.fpr, fpr, 20 ) ) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ return found;
+}
+
+
+static int
+find_by_pattern( cdk_kbnode_t knode, cdk_dbsearch_t ks )
+{
+ cdk_kbnode_t node;
+ size_t uidlen;
+ char * name;
+ int found = 0;
+
+ for( node = knode; node; node = node->next ) {
+ if( node->pkt->pkttype != CDK_PKT_USER_ID )
+ continue;
+ uidlen = node->pkt->pkt.user_id->len;
+ name = node->pkt->pkt.user_id->name;
+ switch( ks->type ) {
+ case CDK_DBSEARCH_EXACT:
+ if( name && (strlen( ks->u.pattern ) == uidlen
+ && !strncmp( ks->u.pattern, name, uidlen ) ) ) {
+ found = 1;
+ break;
+ }
+ break;
+
+ case CDK_DBSEARCH_SUBSTR:
+ if( uidlen > 65536 )
+ break;
+ if( name && strlen( ks->u.pattern ) > uidlen )
+ break;
+ if( name && _cdk_memistr( name, uidlen, ks->u.pattern ) ) {
+ found = 1;
+ break;
+ }
+ break;
+
+ default: /* invalid mode */
+ return 0;
+ }
+ }
+ return found;
+}
+
+
+void
+keydb_search_free( cdk_dbsearch_t dbs )
+{
+ if( !dbs )
+ return;
+ if( dbs->type == CDK_DBSEARCH_EXACT || dbs->type == CDK_DBSEARCH_SUBSTR )
+ cdk_free( dbs->u.pattern );
+ dbs->type = 0;
+ cdk_free( dbs );
+}
+
+
+static void
+keydb_cache_free( key_table_t cache )
+{
+ key_table_t c2;
+
+ while( cache ) {
+ c2 = cache->next;
+ cache->offset = 0;
+ keydb_search_free( cache->desc );
+ cdk_free( cache );
+ cache = c2;
+ }
+}
+
+
+static key_table_t
+keydb_cache_find( key_table_t cache, cdk_dbsearch_t desc )
+{
+ key_table_t t;
+
+ for( t = cache; t; t = t->next ) {
+ if( t->desc->type != desc->type )
+ continue;
+ switch( t->desc->type ) {
+ case CDK_DBSEARCH_SHORT_KEYID:
+ case CDK_DBSEARCH_KEYID:
+ if( KEYID_CMP( t->desc->u.keyid, desc->u.keyid ) )
+ return t;
+ break;
+
+ case CDK_DBSEARCH_EXACT:
+ if( strlen( t->desc->u.pattern ) == strlen( desc->u.pattern )
+ && !strcmp( t->desc->u.pattern, desc->u.pattern ) )
+ return t;
+ break;
+
+ case CDK_DBSEARCH_SUBSTR:
+ if( strstr( t->desc->u.pattern, desc->u.pattern ) )
+ return t;
+ break;
+
+ case CDK_DBSEARCH_FPR:
+ if( !memcmp( t->desc->u.fpr, desc->u.fpr, 20 ) )
+ return t;
+ break;
+ }
+ }
+ return NULL;
+}
+
+
+static int
+keydb_cache_add (cdk_keydb_hd_t hd, cdk_dbsearch_t dbs, u32 offset)
+{
+ key_table_t k;
+
+ if( !hd )
+ return CDK_Inv_Value;
+ if( hd->ncache > KEYDB_CACHE_ENTRIES )
+ return 0;
+ k = cdk_calloc( 1, sizeof *k );
+ if( !k )
+ return CDK_Out_Of_Core;
+ k->offset = offset;
+ keydb_search_copy( &k->desc, dbs );
+ k->next = hd->cache;
+ hd->cache = k;
+ hd->ncache++;
+ _cdk_log_debug ("add entry [o=%d t=%d] to the cache\n", offset, dbs->type);
+ return 0;
+}
+
+
+static int
+keydb_search_copy (cdk_dbsearch_t * r_dst, cdk_dbsearch_t src)
+{
+ cdk_dbsearch_t dst;
+
+ if (!r_dst || !src)
+ return CDK_Inv_Value;
+
+ dst = cdk_calloc( 1, sizeof *dst );
+ if( !dst )
+ return CDK_Out_Of_Core;
+ dst->type = src->type;
+ switch( src->type ) {
+ case CDK_DBSEARCH_EXACT:
+ case CDK_DBSEARCH_SUBSTR:
+ dst->u.pattern = cdk_strdup( src->u.pattern );
+ if( !dst->u.pattern )
+ return CDK_Out_Of_Core;
+ break;
+
+ case CDK_DBSEARCH_SHORT_KEYID:
+ case CDK_DBSEARCH_KEYID:
+ dst->u.keyid[0] = src->u.keyid[0];
+ dst->u.keyid[1] = src->u.keyid[1];
+ break;
+
+ case CDK_DBSEARCH_FPR:
+ memcpy( dst->u.fpr, src->u.fpr, 20 );
+ break;
+ }
+ *r_dst = dst;
+ return 0;
+}
+
+
+/**
+ * cdk_keydb_search_start:
+ * @db: key database handle
+ * @type: specifies the search type
+ * @desc: description which depends on the type
+ *
+ * Create a new keydb search object.
+ **/
+cdk_error_t
+cdk_keydb_search_start( cdk_keydb_hd_t db, int type, void * desc )
+{
+ cdk_dbsearch_t dbs;
+ u32 * keyid;
+ char * p, tmp[3];
+ int i;
+
+ if( !db )
+ return CDK_Inv_Value;
+ if( type != CDK_DBSEARCH_NEXT && !desc )
+ return CDK_Inv_Mode;
+
+ dbs = cdk_calloc( 1, sizeof *dbs );
+ if( !dbs )
+ return CDK_Out_Of_Core;
+ dbs->type = type;
+ switch( type ) {
+ case CDK_DBSEARCH_EXACT:
+ case CDK_DBSEARCH_SUBSTR:
+ cdk_free( dbs->u.pattern );
+ dbs->u.pattern = cdk_strdup( desc );
+ if( !dbs->u.pattern ) {
+ cdk_free( dbs );
+ return CDK_Out_Of_Core;
+ }
+ break;
+
+ case CDK_DBSEARCH_SHORT_KEYID:
+ keyid = desc;
+ dbs->u.keyid[1] = keyid[0];
+ break;
+
+ case CDK_DBSEARCH_KEYID:
+ keyid = desc;
+ dbs->u.keyid[0] = keyid[0];
+ dbs->u.keyid[1] = keyid[1];
+ break;
+
+ case CDK_DBSEARCH_FPR:
+ memcpy( dbs->u.fpr, desc, 20 );
+ break;
+
+ case CDK_DBSEARCH_NEXT:
+ break;
+
+ case CDK_DBSEARCH_AUTO:
+ /* override the type with the actual db search type. */
+ dbs->type = classify_data( desc, strlen( desc ) );
+ switch( dbs->type ) {
+ case CDK_DBSEARCH_SUBSTR:
+ case CDK_DBSEARCH_EXACT:
+ cdk_free( dbs->u.pattern );
+ p = dbs->u.pattern = cdk_strdup( desc );
+ if( !p ) {
+ cdk_free( dbs );
+ return CDK_Out_Of_Core;
+ }
+ break;
+
+ case CDK_DBSEARCH_SHORT_KEYID:
+ case CDK_DBSEARCH_KEYID:
+ p = desc;
+ if( !strncmp( p, "0x", 2 ) )
+ p += 2;
+ if( strlen( p ) == 8 ) {
+ dbs->u.keyid[0] = 0;
+ dbs->u.keyid[1] = strtoul( p, NULL, 16 );
+ }
+ else if( strlen( p ) == 16 ) {
+ dbs->u.keyid[0] = strtoul( p , NULL, 16 );
+ dbs->u.keyid[1] = strtoul( p + 8, NULL, 16 );
+ }
+ else { /* should never happen */
+ cdk_free( dbs );
+ return CDK_Inv_Mode;
+ }
+ break;
+
+ case CDK_DBSEARCH_FPR:
+ p = desc;
+ if( strlen( p ) != 40 ) {
+ cdk_free( dbs );
+ return CDK_Inv_Mode;
+ }
+ for( i = 0; i < 20; i++ ) {
+ tmp[0] = p[2*i];
+ tmp[1] = p[2*i+1];
+ tmp[2] = 0x00;
+ dbs->u.fpr[i] = strtoul( tmp, NULL, 16 );
+ }
+ break;
+ }
+ break;
+
+ default:
+ cdk_free( dbs );
+ return CDK_Inv_Mode;
+ }
+ keydb_search_free( db->dbs );
+ db->dbs = dbs;
+ return 0;
+}
+
+
+static int
+keydb_pos_from_cache( cdk_keydb_hd_t hd, cdk_dbsearch_t ks,
+ int * r_cache_hit, u32 * r_off )
+{
+ key_table_t c;
+ u32 off = 0;
+ int cache_hit = 0;
+
+ if( !hd || !r_cache_hit || !r_off )
+ return CDK_Inv_Value;
+
+ c = keydb_cache_find( hd->cache, ks );
+ if( c ) {
+ _cdk_log_debug( "found entry in cache.\n" );
+ cache_hit = 1;
+ off = c->offset;
+ }
+
+ if( hd->idx && !c ) {
+ if( ks->type == CDK_DBSEARCH_KEYID ) {
+ if( keydb_idx_search( hd->idx, ks->u.keyid, NULL, &off ) )
+ return CDK_Error_No_Key;
+ _cdk_log_debug( "found keyid entry in idx table.\n" );
+ cache_hit = 1;
+ }
+ else if( ks->type == CDK_DBSEARCH_FPR ) {
+ if( keydb_idx_search( hd->idx, NULL, ks->u.fpr, &off ) )
+ return CDK_Error_No_Key;
+ _cdk_log_debug( "found fpr entry in idx table.\n" );
+ cache_hit = 1;
+ }
+ }
+ *r_off = off;
+ *r_cache_hit = cache_hit;
+ return 0;
+}
+
+
+/**
+ * cdk_keydb_search:
+ * @hd: the keydb object
+ * @ks: the keydb search object
+ * @ret_key: kbnode object to store the key
+ *
+ * Search for a key in the given keyring. The search mode is handled
+ * via @ks. If the key was found, @ret_key contains the key data.
+ **/
+cdk_error_t
+cdk_keydb_search( cdk_keydb_hd_t hd, cdk_kbnode_t * ret_key )
+{
+ cdk_stream_t kr = NULL;
+ cdk_kbnode_t knode = NULL;
+ cdk_dbsearch_t ks;
+ u32 off = 0;
+ size_t pos = 0;
+ int key_found = 0, cache_hit = 0;
+ int rc = 0;
+
+ if( !hd || !ret_key )
+ return CDK_Inv_Value;
+
+ *ret_key = NULL;
+ hd->search = 1;
+ rc = cdk_keydb_open( hd, &kr );
+ if( rc )
+ return rc;
+ rc = keydb_pos_from_cache( hd, hd->dbs, &cache_hit, &off );
+ if( rc )
+ return rc;
+
+ ks = hd->dbs;
+ while( !key_found && !rc ) {
+ if( cache_hit && ks->type != CDK_DBSEARCH_NEXT )
+ cdk_stream_seek( kr, off );
+ pos = cdk_stream_tell( kr );
+ rc = cdk_keydb_get_keyblock( kr, &knode );
+ if( rc ) {
+ if( rc == CDK_EOF && knode )
+ rc = 0;
+ if( !knode && rc == CDK_EOF )
+ rc = CDK_Error_No_Key;
+ if( rc )
+ break;
+ }
+
+ switch( ks->type ) {
+ case CDK_DBSEARCH_SHORT_KEYID:
+ case CDK_DBSEARCH_KEYID:
+ key_found = find_by_keyid( knode, ks );
+ break;
+
+ case CDK_DBSEARCH_FPR:
+ key_found = find_by_fpr( knode, ks );
+ break;
+
+ case CDK_DBSEARCH_EXACT:
+ case CDK_DBSEARCH_SUBSTR:
+ key_found = find_by_pattern( knode, ks );
+ break;
+
+ case CDK_DBSEARCH_NEXT:
+ key_found = knode? 1 : 0;
+ break;
+ }
+
+ if( key_found ) {
+ if( !keydb_cache_find( hd->cache, ks ) )
+ keydb_cache_add( hd, ks, pos );
+ break;
+ }
+
+ cdk_kbnode_release( knode );
+ knode = NULL;
+ }
+
+ hd->search = 0;
+ *ret_key = key_found? knode : NULL;
+ return rc;
+}
+
+
+cdk_error_t
+cdk_keydb_get_bykeyid( cdk_keydb_hd_t hd, u32 * keyid, cdk_kbnode_t * ret_pk )
+{
+ int rc;
+
+ if( !hd || !keyid || !ret_pk )
+ return CDK_Inv_Value;
+
+ rc = cdk_keydb_search_start( hd, CDK_DBSEARCH_KEYID, keyid );
+ if( !rc )
+ rc = cdk_keydb_search( hd, ret_pk );
+ return rc;
+}
+
+
+cdk_error_t
+cdk_keydb_get_byfpr( cdk_keydb_hd_t hd, const byte * fpr, cdk_kbnode_t * r_pk )
+{
+ int rc;
+
+ if( !hd || !fpr || !r_pk )
+ return CDK_Inv_Value;
+
+ rc = cdk_keydb_search_start( hd, CDK_DBSEARCH_FPR, (byte *)fpr );
+ if( !rc )
+ rc = cdk_keydb_search( hd, r_pk );
+ return rc;
+}
+
+
+cdk_error_t
+cdk_keydb_get_bypattern( cdk_keydb_hd_t hd, const char * patt,
+ cdk_kbnode_t * ret_pk )
+{
+ int rc;
+
+ if( !hd || !patt || !ret_pk )
+ return CDK_Inv_Value;
+
+ rc = cdk_keydb_search_start( hd, CDK_DBSEARCH_SUBSTR, (char *)patt );
+ if( !rc )
+ rc = cdk_keydb_search( hd, ret_pk );
+ return rc;
+}
+
+
+static int
+keydb_check_key( cdk_packet_t pkt )
+{
+ cdk_pkt_pubkey_t pk;
+ int is_sk = 0, valid = 0;
+
+ if( pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY )
+ pk = pkt->pkt.public_key;
+ else if( pkt->pkttype == CDK_PKT_SECRET_KEY
+ || pkt->pkttype == CDK_PKT_SECRET_SUBKEY ) {
+ pk = pkt->pkt.secret_key->pk;
+ is_sk = 1;
+ }
+ else
+ return 0;
+ valid = !pk->is_revoked && !pk->has_expired;
+ if( is_sk )
+ return valid;
+ return valid && !pk->is_invalid;
+}
+
+
+static cdk_kbnode_t
+keydb_find_byusage( cdk_kbnode_t root, int req_usage, int is_pk )
+{
+ cdk_kbnode_t node;
+ int pkttype = 0, req_type = 0;
+
+ req_type = is_pk? CDK_PKT_PUBLIC_KEY : CDK_PKT_SECRET_KEY;
+ if( !req_usage )
+ return cdk_kbnode_find( root, req_type );
+
+ node = cdk_kbnode_find( root, req_type );
+ if( node && !keydb_check_key( node->pkt ) )
+ return NULL;
+
+ /* xxx: if there are more subkeys, use the one with the requested
+ usage and the newest timestamp. */
+ for( node = root; node; node = node->next ) {
+ pkttype = node->pkt->pkttype;
+ if( is_pk && (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)
+ && keydb_check_key( node->pkt )
+ && (node->pkt->pkt.public_key->pubkey_usage & req_usage) )
+ return node;
+ if( !is_pk && (node->pkt->pkttype == CDK_PKT_SECRET_KEY
+ || node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY)
+ && keydb_check_key( node->pkt )
+ && (node->pkt->pkt.secret_key->pk->pubkey_usage & req_usage) )
+ return node;
+ }
+ return NULL;
+}
+
+
+static cdk_kbnode_t
+keydb_find_bykeyid( cdk_kbnode_t root, u32 * keyid )
+{
+ cdk_kbnode_t node;
+ u32 kid[2];
+
+ for( node = root; node; node = node->next ) {
+ _cdk_pkt_get_keyid (node->pkt, kid);
+ if( kid[1] == keyid[1] )
+ return node;
+ }
+ return NULL;
+}
+
+
+int
+_cdk_keydb_get_sk_byusage( cdk_keydb_hd_t hd, const char * name,
+ cdk_pkt_seckey_t* ret_sk, int usage )
+{
+ cdk_kbnode_t knode = NULL, node = NULL;
+ cdk_pkt_seckey_t sk = NULL;
+ int rc = 0;
+
+ if( !ret_sk || !usage )
+ return CDK_Inv_Value;
+ if( !hd )
+ return CDK_Error_No_Keyring;
+
+ rc = cdk_keydb_search_start( hd, CDK_DBSEARCH_AUTO, (char *)name );
+ if( !rc )
+ rc = cdk_keydb_search( hd, &knode );
+ if( rc )
+ goto leave;
+ node = keydb_find_byusage( knode, usage, 0 );
+ if( !node ) {
+ rc = CDK_Unusable_Key;
+ goto leave;
+ }
+
+ sk = node->pkt->pkt.secret_key;
+ _cdk_kbnode_clone( node );
+ cdk_kbnode_release( knode );
+
+leave:
+ *ret_sk = sk;
+ return rc;
+}
+
+
+int
+_cdk_keydb_get_pk_byusage( cdk_keydb_hd_t hd, const char * name,
+ cdk_pkt_pubkey_t* ret_pk, int usage )
+{
+ cdk_kbnode_t knode, node = NULL;
+ cdk_pkt_pubkey_t pk = NULL;
+ const char * s;
+ int rc = 0;
+
+ if( !ret_pk || !usage )
+ return CDK_Inv_Value;
+ if( !hd )
+ return CDK_Error_No_Keyring;
+
+ rc = cdk_keydb_search_start( hd, CDK_DBSEARCH_AUTO, (char *)name );
+ if( !rc )
+ rc = cdk_keydb_search( hd, &knode );
+ if( rc )
+ goto leave;
+ node = keydb_find_byusage( knode, usage, 1 );
+ if( !node ) {
+ rc = CDK_Unusable_Key;
+ goto leave;
+ }
+
+ _cdk_copy_pubkey( &pk, node->pkt->pkt.public_key );
+ for( node = knode; node; node = node->next ) {
+ if( node->pkt->pkttype == CDK_PKT_USER_ID ) {
+ s = node->pkt->pkt.user_id->name;
+ if( pk && !pk->uid && _cdk_memistr( s, strlen( s ), name ) ) {
+ _cdk_copy_userid( &pk->uid, node->pkt->pkt.user_id );
+ break;
+ }
+ }
+ }
+ cdk_kbnode_release( knode );
+
+leave:
+ *ret_pk = pk;
+ return rc;
+}
+
+
+cdk_error_t
+cdk_keydb_get_pk( cdk_keydb_hd_t hd, u32 * keyid, cdk_pkt_pubkey_t* r_pk )
+{
+ cdk_kbnode_t knode = NULL, node = NULL;
+ cdk_pkt_pubkey_t pk = NULL;
+ int rc = 0;
+
+ if( !keyid || !r_pk )
+ return CDK_Inv_Value;
+ if( !hd )
+ return CDK_Error_No_Keyring;
+
+ rc = cdk_keydb_search_start( hd, !keyid[0]?
+ CDK_DBSEARCH_SHORT_KEYID : CDK_DBSEARCH_KEYID,
+ keyid );
+ if( !rc )
+ rc = cdk_keydb_search( hd, &knode );
+ if( rc )
+ goto leave;
+ node = keydb_find_bykeyid( knode, keyid );
+ if( !node ) {
+ rc = CDK_Error_No_Key;
+ goto leave;
+ }
+ _cdk_copy_pubkey( &pk, node->pkt->pkt.public_key );
+ cdk_kbnode_release( knode );
+
+leave:
+ *r_pk = pk;
+ return rc;
+}
+
+
+cdk_error_t
+cdk_keydb_get_sk( cdk_keydb_hd_t hd, u32 * keyid, cdk_pkt_seckey_t* ret_sk )
+{
+ cdk_kbnode_t snode, node;
+ cdk_pkt_seckey_t sk = NULL;
+ int rc = 0;
+
+ if( !keyid || !ret_sk )
+ return CDK_Inv_Value;
+ if( !hd )
+ return CDK_Error_No_Keyring;
+
+ rc = cdk_keydb_get_bykeyid( hd, keyid, &snode );
+ if( rc )
+ goto leave;
+
+ node = keydb_find_bykeyid( snode, keyid );
+ if( !node ) {
+ rc = CDK_Error_No_Key;
+ goto leave;
+ }
+
+ sk = node->pkt->pkt.secret_key;
+ _cdk_kbnode_clone( node );
+ cdk_kbnode_release( snode );
+
+ leave:
+ *ret_sk = sk;
+ return rc;
+}
+
+
+static int
+is_selfsig( cdk_kbnode_t node, u32 * keyid )
+{
+ cdk_pkt_signature_t sig;
+
+ if( node->pkt->pkttype != CDK_PKT_SIGNATURE )
+ return 0;
+ sig = node->pkt->pkt.signature;
+ if( (sig->sig_class == 0x13 || sig->sig_class == 0x10) &&
+ sig->keyid[0] == keyid[0] && sig->keyid[1] == keyid[1] )
+ return 1;
+ return 0;
+}
+
+
+static int
+keydb_merge_selfsig( cdk_kbnode_t key, u32 * keyid )
+{
+ cdk_kbnode_t node, kbnode, unode;
+ cdk_subpkt_t s = NULL;
+ cdk_pkt_signature_t sig = NULL;
+ cdk_pkt_userid_t uid = NULL;
+ const byte * symalg = NULL, * hashalg = NULL, * compalg = NULL;
+ size_t nsymalg = 0, nhashalg = 0, ncompalg = 0, n = 0;
+ int key_usage = 0, key_expire = 0;
+
+ if (!key)
+ return CDK_Inv_Value;
+
+ for( node = key; node; node = node->next ) {
+ if( !is_selfsig( node, keyid ) )
+ continue;
+ unode = cdk_kbnode_find_prev( key, node, CDK_PKT_USER_ID );
+ if( !unode )
+ return CDK_Error_No_Key;
+ uid = unode->pkt->pkt.user_id;
+ sig = node->pkt->pkt.signature;
+ s = cdk_subpkt_find( sig->hashed, CDK_SIGSUBPKT_PRIMARY_UID );
+ if( s )
+ uid->is_primary = 1;
+ s = cdk_subpkt_find( sig->hashed, CDK_SIGSUBPKT_FEATURES );
+ if( s && s->size == 1 && s->d[0] & 0x01 )
+ uid->mdc_feature = 1;
+ s = cdk_subpkt_find( sig->hashed, CDK_SIGSUBPKT_KEY_EXPIRE );
+ if( s && s->size == 4 )
+ key_expire = _cdk_buftou32( s->d );
+ s = cdk_subpkt_find( sig->hashed, CDK_SIGSUBPKT_KEY_FLAGS );
+ if( s ) {
+ if( s->d[0] & 3 ) /* cert + sign data */
+ key_usage |= PK_USAGE_SIGN;
+ if( s->d[0] & 12 ) /* encrypt comm. + storage */
+ key_usage |= PK_USAGE_ENCR;
+ }
+ s = cdk_subpkt_find( sig->hashed, CDK_SIGSUBPKT_PREFS_SYM );
+ if( s ) {
+ symalg = s->d;
+ nsymalg = s->size;
+ n += s->size + 1;
+ }
+ s = cdk_subpkt_find( sig->hashed, CDK_SIGSUBPKT_PREFS_HASH );
+ if( s ) {
+ hashalg = s->d;
+ nhashalg = s->size;
+ n += s->size + 1;
+ }
+ s = cdk_subpkt_find( sig->hashed, CDK_SIGSUBPKT_PREFS_ZIP );
+ if( s ) {
+ compalg = s->d;
+ ncompalg = s->size;
+ n += s->size + 1;
+ }
+ if( !n || !hashalg || !compalg || !symalg )
+ uid->prefs = NULL;
+ else {
+ uid->prefs = cdk_calloc( 1, sizeof (*uid->prefs) * (n + 1) );
+ if( !uid->prefs )
+ return CDK_Out_Of_Core;
+ n = 0;
+ for( ; nsymalg; nsymalg--, n++ ) {
+ uid->prefs[n].type = CDK_PREFTYPE_SYM;
+ uid->prefs[n].value = *symalg++;
+ }
+ for( ; nhashalg; nhashalg--, n++ ) {
+ uid->prefs[n].type = CDK_PREFTYPE_HASH;
+ uid->prefs[n].value = *hashalg++;
+ }
+ for( ; ncompalg; ncompalg--, n++ ) {
+ uid->prefs[n].type = CDK_PREFTYPE_ZIP;
+ uid->prefs[n].value = *compalg++;
+ }
+ /* end of list marker */
+ uid->prefs[n].type = CDK_PREFTYPE_NONE;
+ uid->prefs[n].value = 0;
+ uid->prefs_size = n;
+
+ kbnode = cdk_kbnode_find_prev( key, node, CDK_PKT_PUBLIC_KEY );
+ if( kbnode ) {
+ cdk_pkt_pubkey_t pk = kbnode->pkt->pkt.public_key;
+ if( uid->prefs && n ) {
+ pk->prefs = _cdk_copy_prefs( uid->prefs );
+ pk->prefs_size = n;
+ }
+ if( key_expire ) {
+ pk->expiredate = pk->timestamp + key_expire;
+ pk->has_expired = pk->expiredate> _cdk_timestamp ()?0 :1;
+ }
+ if( key_usage && !pk->pubkey_usage )
+ pk->pubkey_usage = key_usage;
+ pk->is_invalid = 0;
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int
+keydb_parse_allsigs( cdk_kbnode_t knode, cdk_keydb_hd_t hd, int check )
+{
+ cdk_kbnode_t node, kb;
+ cdk_pkt_signature_t sig;
+ cdk_pkt_pubkey_t pk;
+ struct cdk_subpkt_s * s = NULL;
+ u32 expiredate = 0, curtime = _cdk_timestamp ();
+ u32 keyid[2];
+ int rc = 0;
+
+ if( !knode )
+ return CDK_Inv_Value;
+ if( check && !hd )
+ return CDK_Inv_Mode;
+
+ kb = cdk_kbnode_find( knode, CDK_PKT_SECRET_KEY );
+ if( kb )
+ return 0;
+
+ /* reset */
+ for( node = knode; node; node = node->next ) {
+ if( node->pkt->pkttype == CDK_PKT_USER_ID )
+ node->pkt->pkt.user_id->is_revoked = 0;
+ else if( node->pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY )
+ node->pkt->pkt.public_key->is_revoked = 0;
+ }
+
+ kb = cdk_kbnode_find( knode, CDK_PKT_PUBLIC_KEY );
+ if( !kb )
+ return CDK_Inv_Packet;
+ cdk_pk_get_keyid( kb->pkt->pkt.public_key, keyid );
+
+ for( node = knode; node; node = node->next) {
+ if( node->pkt->pkttype == CDK_PKT_SIGNATURE ) {
+ sig = node->pkt->pkt.signature;
+ /* Revocation certificates for primary keys */
+ if( sig->sig_class == 0x20 ) {
+ kb = cdk_kbnode_find_prev( knode, node, CDK_PKT_PUBLIC_KEY );
+ if( kb ) {
+ kb->pkt->pkt.public_key->is_revoked = 1;
+ if( check )
+ _cdk_pk_check_sig( hd, kb, node );
+ }
+ else
+ return CDK_Error_No_Key;
+ }
+ /* Revocation certificates for subkeys */
+ else if( sig->sig_class == 0x28 ) {
+ kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_SUBKEY);
+ if( kb ) {
+ kb->pkt->pkt.public_key->is_revoked = 1;
+ if( check )
+ _cdk_pk_check_sig( hd, kb, node );
+ }
+ else
+ return CDK_Error_No_Key;
+ }
+ /* Revocation certifcates for user ID's */
+ else if( sig->sig_class == 0x30 ) {
+ if( sig->keyid[0] != keyid[0] || sig->keyid[1] != keyid[1] )
+ continue; /* revokes an earlier signature, no userID. */
+ kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_USER_ID);
+ if( kb ) {
+ kb->pkt->pkt.user_id->is_revoked = 1;
+ if( check )
+ _cdk_pk_check_sig( hd, kb, node );
+ }
+ else
+ return CDK_Error_No_Key;
+ }
+ /* Direct certificates for primary keys */
+ else if( sig->sig_class == 0x1F ) {
+ kb = cdk_kbnode_find_prev( knode, node, CDK_PKT_PUBLIC_KEY );
+ if( kb ) {
+ pk = kb->pkt->pkt.public_key;
+ pk->is_invalid = 0;
+ s = cdk_subpkt_find( node->pkt->pkt.signature->hashed,
+ CDK_SIGSUBPKT_KEY_EXPIRE );
+ if( s ) {
+ expiredate = _cdk_buftou32( s->d );
+ pk->expiredate = pk->timestamp + expiredate;
+ pk->has_expired = pk->expiredate > curtime? 0 : 1;
+ }
+ if( check )
+ _cdk_pk_check_sig( hd, kb, node );
+ }
+ else
+ return CDK_Error_No_Key;
+ }
+ /* Direct certificates for subkeys */
+ else if( sig->sig_class == 0x18 ) {
+ kb = cdk_kbnode_find_prev( knode, node, CDK_PKT_PUBLIC_SUBKEY);
+ if( kb ) {
+ pk = kb->pkt->pkt.public_key;
+ pk->is_invalid = 0;
+ s = cdk_subpkt_find( node->pkt->pkt.signature->hashed,
+ CDK_SIGSUBPKT_KEY_EXPIRE );
+ if( s ) {
+ expiredate = _cdk_buftou32( s->d );
+ pk->expiredate = pk->timestamp + expiredate;
+ pk->has_expired = pk->expiredate > curtime? 0 : 1;
+ }
+ if( check )
+ _cdk_pk_check_sig( hd, kb, node );
+ }
+ else
+ return CDK_Error_No_Key;
+ }
+ }
+ }
+ node = cdk_kbnode_find( knode, CDK_PKT_PUBLIC_KEY );
+ if( node && node->pkt->pkt.public_key->version == 3 ) {
+ /* v3 public keys have no additonal signatures for the key directly.
+ we say the key is valid when we have at least a self signature. */
+ pk = node->pkt->pkt.public_key;
+ for( node = knode; node; node = node->next ) {
+ if( is_selfsig( node, keyid ) ) {
+ pk->is_invalid = 0;
+ break;
+ }
+ }
+ }
+ if( node && (node->pkt->pkt.public_key->is_revoked
+ || node->pkt->pkt.public_key->has_expired) ) {
+ /* if the primary key has been revoked, mark all subkeys as invalid
+ because without a primary key they are not useable */
+ for( node = knode; node; node = node->next ) {
+ if( node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY )
+ node->pkt->pkt.public_key->is_invalid = 1;
+ }
+ }
+ return rc;
+}
+
+
+cdk_error_t
+cdk_keydb_get_keyblock( cdk_stream_t inp, cdk_kbnode_t * r_knode )
+{
+ cdk_packet_t pkt = NULL;
+ cdk_kbnode_t knode = NULL, node = NULL;
+ cdk_desig_revoker_t revkeys = NULL;
+ u32 keyid[2], main_keyid[2];
+ int rc = 0, old_off;
+ int key_seen = 0, got_key = 0;
+
+ if( !inp || !r_knode )
+ return CDK_Inv_Value;
+
+ memset( keyid, 0, sizeof keyid );
+ memset( main_keyid, 0, sizeof main_keyid );
+
+ while( 1 ) {
+ pkt = cdk_calloc( 1, sizeof *pkt );
+ if( !pkt )
+ return CDK_Out_Of_Core;
+ old_off = cdk_stream_tell( inp );
+ rc = cdk_pkt_read( inp, pkt );
+ if( rc )
+ break;
+ if( pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY
+ || pkt->pkttype == CDK_PKT_SECRET_KEY
+ || pkt->pkttype == CDK_PKT_SECRET_SUBKEY) {
+ if (key_seen && (pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || pkt->pkttype == CDK_PKT_SECRET_KEY) ) {
+ cdk_stream_seek( inp, old_off );
+ break;
+ }
+ if( pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ || pkt->pkttype == CDK_PKT_SECRET_KEY ) {
+ _cdk_pkt_get_keyid( pkt, main_keyid );
+ key_seen = 1;
+ }
+ else if( pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY
+ || pkt->pkttype == CDK_PKT_SECRET_SUBKEY ) {
+ if( pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY ) {
+ pkt->pkt.public_key->main_keyid[0] = main_keyid[0];
+ pkt->pkt.public_key->main_keyid[1] = main_keyid[1];
+ }
+ else {
+ pkt->pkt.secret_key->main_keyid[0] = main_keyid[0];
+ pkt->pkt.secret_key->main_keyid[1] = main_keyid[1];
+ }
+ }
+ /* we save this for the signature */
+ _cdk_pkt_get_keyid( pkt, keyid );
+ got_key = 1;
+ }
+ else if( pkt->pkttype == CDK_PKT_USER_ID )
+ ;
+ else if( pkt->pkttype == CDK_PKT_SIGNATURE ) {
+ pkt->pkt.signature->key[0] = keyid[0];
+ pkt->pkt.signature->key[1] = keyid[1];
+ if( pkt->pkt.signature->sig_class == 0x1F &&
+ pkt->pkt.signature->revkeys )
+ revkeys = pkt->pkt.signature->revkeys;
+ }
+ node = cdk_kbnode_new( pkt );
+ if( !knode )
+ knode = node;
+ else
+ _cdk_kbnode_add( knode, node );
+ }
+
+ if( got_key ) {
+ keydb_merge_selfsig( knode, main_keyid );
+ rc = keydb_parse_allsigs( knode, NULL, 0 );
+ if( revkeys ) {
+ node = cdk_kbnode_find( knode, CDK_PKT_PUBLIC_KEY );
+ if( node )
+ node->pkt->pkt.public_key->revkeys = revkeys;
+ }
+ }
+ *r_knode = got_key ? knode : NULL;
+ return rc;
+}
+
+
+cdk_error_t
+cdk_keydb_pk_cache_sigs( cdk_kbnode_t pk, cdk_keydb_hd_t hd )
+{
+ if( !pk || !hd )
+ return CDK_Inv_Value;
+ return keydb_parse_allsigs( pk, hd, 1 );
+}
+
+
+static int
+classify_data( const byte * buf, size_t len )
+{
+ int type = 0;
+ int i;
+
+ if( buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X') ) { /* hex prefix */
+ buf += 2;
+ len -= 2;
+ }
+
+ if( len == 8 || len == 16 || len == 40 ) {
+ for( i = 0; i < len; i++ ) {
+ if( !isxdigit( buf[i] ) )
+ break;
+ }
+ if( i == len ) {
+ switch( len ) {
+ case 8: type = CDK_DBSEARCH_SHORT_KEYID; break;
+ case 16: type = CDK_DBSEARCH_KEYID; break;
+ case 40: type = CDK_DBSEARCH_FPR; break;
+ }
+ }
+ }
+ if( !type )
+ type = CDK_DBSEARCH_SUBSTR;
+ return type;
+}
+
+
+cdk_error_t
+cdk_keydb_export( cdk_keydb_hd_t hd, cdk_stream_t out, cdk_strlist_t remusr )
+{
+ cdk_kbnode_t knode, node;
+ cdk_strlist_t r;
+ int old_ctb = 0;
+ int rc = 0;
+
+ for( r = remusr; r; r = r->next ) {
+ rc = cdk_keydb_search_start( hd, CDK_DBSEARCH_AUTO, r->d );
+ if( !rc )
+ rc = cdk_keydb_search( hd, &knode );
+ if( rc )
+ break;
+ for( node = knode; node; node = node->next ) {
+ /* those packets are not intended for the real wolrd */
+ if( node->pkt->pkttype == CDK_PKT_RING_TRUST )
+ continue;
+ /* we never export local signed signatures */
+ if( node->pkt->pkttype == CDK_PKT_SIGNATURE &&
+ !node->pkt->pkt.signature->flags.exportable )
+ continue;
+ /* filter out invalid signatures */
+ if( node->pkt->pkttype == CDK_PKT_SIGNATURE
+ && !KEY_CAN_SIGN (node->pkt->pkt.signature->pubkey_algo) )
+ continue;
+ if( node->pkt->pkttype == CDK_PKT_PUBLIC_KEY
+ && node->pkt->pkt.public_key->version == 3 )
+ old_ctb = 1;
+ node->pkt->old_ctb = old_ctb;
+ rc = cdk_pkt_write( out, node->pkt );
+ if( rc )
+ break;
+ }
+ cdk_kbnode_release( knode );
+ knode = NULL;
+ }
+ return rc;
+}
+
+
+static cdk_packet_t
+find_key_packet( cdk_kbnode_t knode, int * r_is_sk )
+{
+ cdk_packet_t pkt;
+
+ pkt = cdk_kbnode_find_packet( knode, CDK_PKT_PUBLIC_KEY );
+ if( !pkt ) {
+ pkt = cdk_kbnode_find_packet( knode, CDK_PKT_SECRET_KEY );
+ if( r_is_sk )
+ *r_is_sk = pkt? 1 : 0;
+ }
+ return pkt;
+}
+
+
+cdk_error_t
+cdk_keydb_import( cdk_keydb_hd_t hd, cdk_kbnode_t knode, int *result )
+{
+ cdk_kbnode_t node, chk = NULL;
+ cdk_packet_t pkt;
+ cdk_stream_t out;
+ u32 keyid[2];
+ int rc = 0, is_sk = 0;
+
+ if( !hd || !knode )
+ return CDK_Inv_Value;
+
+ memset( result, 0, 4 * sizeof (int) );
+ pkt = find_key_packet( knode, &is_sk );
+ if( !pkt )
+ return CDK_Inv_Packet;
+ result[is_sk] = 1;
+ _cdk_pkt_get_keyid( pkt, keyid );
+ cdk_keydb_get_bykeyid( hd, keyid, &chk );
+ if( chk ) { /* fixme: search for new signatures */
+ cdk_kbnode_release( chk );
+ return 0;
+ }
+
+ if( hd->buf ) {
+ cdk_stream_close( hd->buf );
+ hd->buf = NULL;
+ }
+
+ rc = _cdk_stream_append( hd->name, &out );
+ if( rc )
+ return rc;
+
+ for( node = knode; node; node = node->next ) {
+ if( node->pkt->pkttype == CDK_PKT_RING_TRUST )
+ continue; /* No uniformed syntax for this packet */
+ rc = cdk_pkt_write( out, node->pkt );
+ if( rc )
+ break;
+ }
+ if( !rc )
+ result[is_sk? 3 : 2] = 1;
+ cdk_stream_close( out );
+ if( !hd->no_cache )
+ cdk_keydb_idx_rebuild( hd );
+ return rc;
+}
+
+
+int
+cdk_keydb_check_sk( cdk_keydb_hd_t hd, u32 * keyid )
+{
+ cdk_stream_t db;
+ cdk_packet_t pkt;
+ u32 kid[2];
+ int rc;
+
+ if( !hd || !keyid )
+ return CDK_Inv_Value;
+ if( !hd->secret )
+ return CDK_Inv_Mode;
+ pkt = cdk_calloc( 1, sizeof * pkt );
+ if( !pkt )
+ return CDK_Out_Of_Core;
+ rc = cdk_keydb_open( hd, &db );
+ if( rc )
+ return rc;
+ cdk_pkt_init( pkt );
+ while( !cdk_pkt_read( db, pkt ) ) {
+ if( pkt->pkttype != CDK_PKT_SECRET_KEY
+ && pkt->pkttype != CDK_PKT_SECRET_SUBKEY )
+ goto next;
+ cdk_sk_get_keyid( pkt->pkt.secret_key, kid );
+ if( KEYID_CMP( kid, keyid ) ) {
+ cdk_pkt_free( pkt );
+ cdk_free( pkt );
+ return 0;
+ }
+ next:
+ cdk_pkt_free( pkt );
+ cdk_pkt_init( pkt );
+ }
+ cdk_free( pkt );
+ return CDK_Error_No_Key;
+}
+
+
+/**
+ * cdk_listkey_start:
+ * @r_ctx: pointer to store the new context
+ * @db: the key database handle
+ * @patt: string pattern
+ * @fpatt: recipients from a stringlist to show
+ *
+ * Prepare a key listing with the given parameters. Two modes are supported.
+ * The first mode uses string pattern to determine if the key should be
+ * returned or not. The other mode uses a string list to request the key
+ * which should be listed.
+ **/
+cdk_error_t
+cdk_listkey_start( cdk_listkey_t * r_ctx, cdk_keydb_hd_t db,
+ const char * patt, cdk_strlist_t fpatt )
+{
+ cdk_listkey_t ctx;
+ cdk_stream_t inp;
+ int rc;
+
+ if( !r_ctx || !db )
+ return CDK_Inv_Value;
+ if( (patt && fpatt) || (!patt && !fpatt) )
+ return CDK_Inv_Mode;
+ rc = cdk_keydb_open( db, &inp );
+ if( rc )
+ return rc;
+ ctx = cdk_calloc( 1, sizeof * ctx );
+ if( !ctx )
+ return CDK_Out_Of_Core;
+ ctx->db = db;
+ ctx->inp = inp;
+ if( patt ) {
+ ctx->u.patt = cdk_strdup( patt );
+ if( !ctx->u.patt )
+ return CDK_Out_Of_Core;
+ }
+ else if( fpatt ) {
+ cdk_strlist_t l;
+ for( l = fpatt; l; l = l->next )
+ cdk_strlist_add( &ctx->u.fpatt, l->d );
+ }
+ ctx->type = patt? 1 : 0;
+ ctx->init = 1;
+ *r_ctx = ctx;
+ return 0;
+}
+
+
+/**
+ * cdk_listkey_close:
+ * @ctx: the list key context
+ *
+ * Free the list key context.
+ **/
+void
+cdk_listkey_close( cdk_listkey_t ctx )
+{
+ if( ctx ) {
+ if( ctx->type )
+ cdk_free( ctx->u.patt );
+ else
+ cdk_strlist_free( ctx->u.fpatt );
+ cdk_free( ctx );
+ }
+}
+
+
+/**
+ * cdk_listkey_next:
+ * @ctx: list key context
+ * @r_key: the pointer to the new key node object
+ *
+ * Retrieve the next key from the pattern of the key list context.
+ **/
+cdk_error_t
+cdk_listkey_next( cdk_listkey_t ctx, cdk_kbnode_t * ret_key )
+{
+ if( !ctx || !ret_key )
+ return CDK_Inv_Value;
+ if( !ctx->init )
+ return CDK_Inv_Mode;
+
+ if( ctx->type && ctx->u.patt[0] == '*' )
+ return cdk_keydb_get_keyblock( ctx->inp, ret_key );
+ else if( ctx->type ) {
+ cdk_kbnode_t node;
+ struct cdk_dbsearch_s ks;
+ int rc;
+
+ for( ;; ) {
+ rc = cdk_keydb_get_keyblock( ctx->inp, &node );
+ if( rc )
+ return rc;
+ memset( &ks, 0, sizeof ks );
+ ks.type = CDK_DBSEARCH_SUBSTR;
+ ks.u.pattern = ctx->u.patt;
+ if( find_by_pattern( node, &ks ) ) {
+ *ret_key = node;
+ return 0;
+ }
+ cdk_kbnode_release( node );
+ node = NULL;
+ }
+ }
+ else {
+ if( !ctx->t )
+ ctx->t = ctx->u.fpatt;
+ else if( ctx->t->next )
+ ctx->t = ctx->t->next;
+ else
+ return CDK_EOF;
+ return cdk_keydb_get_bypattern( ctx->db, ctx->t->d, ret_key );
+ }
+ return CDK_General_Error;
+}
+
diff --git a/libextra/opencdk/keygen.c b/libextra/opencdk/keygen.c
new file mode 100644
index 0000000000..c1f0ad1773
--- /dev/null
+++ b/libextra/opencdk/keygen.c
@@ -0,0 +1,870 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * keygen.c - OpenPGP key generation
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "cipher.h"
+#include "types.h"
+
+struct key_ctx_s {
+ u32 expire_date;
+ int algo;
+ int len;
+ gcry_mpi_t resarr[6];
+ size_t n;
+ cdk_pkt_pubkey_t pk;
+ cdk_pkt_seckey_t sk;
+};
+
+
+struct cdk_keygen_ctx_s {
+ char * user_id;
+ cdk_pkt_userid_t id;
+ byte * sym_prefs;
+ size_t sym_len;
+ byte * hash_prefs;
+ size_t hash_len;
+ byte * zip_prefs;
+ size_t zip_len;
+ unsigned mdc_feature:1;
+ unsigned ks_no_modify:1;
+ char * ks_pref_url;
+ cdk_pkt_signature_t sig;
+ unsigned protect:1;
+ struct key_ctx_s key[2];
+ char * pass;
+ size_t pass_len;
+};
+
+
+/* default preferences */
+static byte def_sym_prefs[] = {CDK_CIPHER_AES, CDK_CIPHER_CAST5,
+ CDK_CIPHER_TWOFISH, CDK_CIPHER_AES192,
+ CDK_CIPHER_AES256, CDK_CIPHER_3DES,
+ CDK_CIPHER_BLOWFISH};
+static byte def_hash_prefs[] = {CDK_MD_SHA1, CDK_MD_RMD160, CDK_MD_MD5};
+static byte def_zip_prefs[] = {CDK_COMPRESS_ZIP, CDK_COMPRESS_ZLIB};
+
+
+static int
+check_pref_array( const byte * p, size_t n, int type )
+{
+ int i;
+
+ if( !p )
+ return 0;
+
+ if( type == CDK_PREFTYPE_SYM ) {
+ for( i = 0; i < n; i++ ) {
+ if( cdk_cipher_test_algo( p[i] ) )
+ return -1;
+ }
+ }
+ else if( type == CDK_PREFTYPE_HASH ) {
+ for( i = 0; i < n; i++ ) {
+ if( cdk_md_test_algo( p[i] ) )
+ return -1;
+ }
+ }
+ else if( type == CDK_PREFTYPE_ZIP ) {
+ if( n > 2 )
+ return -1;
+ if( p[0] > 2 || p[1] > 2 )
+ return -1;
+ }
+ else
+ return -1;
+ return 0;
+}
+
+
+/**
+ * cdk_keygen_set_prefs: Set the preferences for the userID
+ * @hd: the keygen object
+ * @hd: the preference type
+ * @array: one-octet array with algorithm numers
+ *
+ **/
+cdk_error_t
+cdk_keygen_set_prefs( cdk_keygen_ctx_t hd, enum cdk_pref_type_t type,
+ const byte * array, size_t n )
+{
+ int rc;
+
+ if( !hd )
+ return CDK_Inv_Value;
+
+ rc = check_pref_array( array, n, type );
+ if( rc )
+ return CDK_Inv_Value;
+
+ switch( type) {
+ case CDK_PREFTYPE_SYM:
+ hd->sym_len = array? n : DIM( def_sym_prefs );
+ hd->sym_prefs = cdk_calloc( 1, hd->sym_len );
+ if( hd->sym_prefs )
+ memcpy( hd->sym_prefs, array? array : def_sym_prefs, hd->sym_len );
+ break;
+
+ case CDK_PREFTYPE_HASH:
+ hd->hash_len = array? n : DIM( def_hash_prefs );
+ hd->hash_prefs = cdk_calloc( 1, hd->hash_len );
+ if( hd->hash_prefs )
+ memcpy( hd->hash_prefs, array? array : def_hash_prefs,
+ hd->hash_len );
+ break;
+
+ case CDK_PREFTYPE_ZIP:
+ hd->zip_len = array? n : DIM( def_zip_prefs );
+ hd->zip_prefs = cdk_calloc( 1, hd->zip_len );
+ if( hd->zip_prefs )
+ memcpy( hd->zip_prefs, array? array : def_zip_prefs, hd->zip_len );
+ break;
+
+ default:
+ return CDK_Inv_Mode;
+ }
+
+ return 0;
+}
+
+
+/**
+ * cdk_keygen_set_name: set the userid name for the key
+ * @hd: the keygen object
+ * @name: name
+ *
+ * The name will be encoded in UTF8 to avoid problems.
+ **/
+void
+cdk_keygen_set_name( cdk_keygen_ctx_t hd, const char * name )
+{
+ if( hd ) {
+ cdk_free( hd->user_id );
+ hd->user_id = cdk_utf8_encode( name );
+ }
+}
+
+
+static int
+check_bits( int bits, int algo )
+{
+ if( bits < 768 )
+ return 768;
+ if( algo == CDK_PK_DSA && bits > 1024 )
+ return 1024;
+ if( bits > 4096 )
+ return 4096;
+ return bits;
+}
+
+
+/**
+ * cdk_keygen_set_algo_info: set the length and type of the key
+ * @hd: the keygen object.
+ * @type: key type (primary=0, subkey=1)
+ * @algo: algorithm compliant with rfc2440
+ * @bits: lengt of the key in bits
+ *
+ **/
+cdk_error_t
+cdk_keygen_set_algo_info( cdk_keygen_ctx_t hd, int type,
+ enum cdk_pk_algo_t algo, int bits )
+{
+ int rc;
+ int usage = type? PK_USAGE_ENCR : PK_USAGE_SIGN;
+
+ if( !hd )
+ return CDK_Inv_Value;
+ if( type < 0 || type > 1 )
+ return CDK_Inv_Value;
+
+ if( bits % 128 != 0 )
+ bits = bits + ( bits % 128 );
+
+ rc = _cdk_pk_test_algo( algo, usage );
+ if( rc )
+ return rc;
+
+ /* type=0 primary type=1 sub */
+ hd->key[type].algo = algo;
+ hd->key[type].len = check_bits( bits, algo );
+
+ return 0;
+}
+
+
+/**
+ * cdk_keygen_set_mdc_feature: set the mdc feature for the key
+ * @hd: keygen object
+ * @val: boolean( yes=1, no=0)
+ *
+ * if you want a RFC2440 compliant key, you've to disable this feature
+ * until the rfc2440-bis8 becomes the next standard.
+ **/
+void
+cdk_keygen_set_mdc_feature( cdk_keygen_ctx_t hd, int val )
+{
+ if( hd )
+ hd->mdc_feature = val;
+}
+
+
+
+void
+cdk_keygen_set_keyserver_flags( cdk_keygen_ctx_t hd, int no_modify,
+ const char *pref_url )
+{
+ if( no_modify )
+ hd->ks_no_modify = 1;
+ if( pref_url )
+ hd->ks_pref_url = cdk_strdup( pref_url );
+} /* cdk_keygen_set_keyserver_flags */
+
+
+/**
+ * cdk_keygen_set_expire_date: set the expire date of the primary key
+ * @hd: keygen object
+ * @type: key type( 0=primary, 1=seconardy)
+ * @timestamp: the date the key should expire
+ *
+ **/
+void
+cdk_keygen_set_expire_date( cdk_keygen_ctx_t hd, int type, long timestamp )
+{
+ if( !hd )
+ return;
+ if( type < 0 || type > 1 )
+ return;
+ if( timestamp < 0 || timestamp < _cdk_timestamp( ) )
+ timestamp = 0;
+ hd->key[type].expire_date = timestamp;
+}
+
+
+void
+cdk_keygen_set_passphrase( cdk_keygen_ctx_t hd, const char * pass )
+{
+ if( !hd )
+ return;
+ if( pass ) {
+ size_t n = strlen( pass );
+ _cdk_sec_free( hd->pass, hd->pass_len );
+ hd->pass = cdk_salloc( n + 1, 1 );
+ if( hd->pass ) {
+ memcpy( hd->pass, pass, n );
+ hd->pass[n] = '\0';
+ hd->pass_len = n;
+ hd->protect = 1;
+ }
+ }
+}
+
+
+static int
+read_single_mpi( gcry_sexp_t s_key, const char * val, gcry_mpi_t * r_resarr )
+{
+ gcry_sexp_t list;
+
+ if( !r_resarr )
+ return CDK_Inv_Value;
+ list = gcry_sexp_find_token( s_key, val, 0 );
+ if( list )
+ *r_resarr = gcry_sexp_nth_mpi( list, 1, 0 );
+ gcry_sexp_release( list );
+ return list? 0 : CDK_Gcry_Error;
+}
+
+
+static int
+read_dsa_key( gcry_sexp_t s_key, gcry_mpi_t * resarr )
+{
+ int rc = read_single_mpi( s_key, "p", &resarr[0] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "q", &resarr[1] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "g", &resarr[2] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "y", &resarr[3] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "x", &resarr[4] );
+ return rc;
+}
+
+
+static int
+read_elg_key( gcry_sexp_t s_key, gcry_mpi_t * resarr )
+{
+ int rc = read_single_mpi( s_key, "p", &resarr[0] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "g", &resarr[1] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "y", &resarr[2] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "x", &resarr[3] );
+ return rc;
+}
+
+
+static int
+read_rsa_key( gcry_sexp_t s_key, gcry_mpi_t * resarr )
+{
+ int rc = read_single_mpi( s_key, "n", &resarr[0] );
+ if( !rc )
+ rc =read_single_mpi( s_key, "e", &resarr[1] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "d", &resarr[2] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "p", &resarr[3] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "q", &resarr[4] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "u", &resarr[5] );
+ return rc;
+}
+
+
+static int
+generate_subkey( cdk_keygen_ctx_t hd )
+{
+ gcry_sexp_t s_params = NULL, s_key;
+ size_t n = hd->key[1].len;
+ int rc;
+
+ if( !hd )
+ return CDK_Inv_Value;
+
+ if( is_DSA( hd->key[1].algo) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(dsa(nbits %d)))", n );
+ else if( is_ELG( hd->key[1].algo) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(elg(nbits %d)))", n );
+ else if( is_RSA( hd->key[1].algo) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(rsa(nbits %d)))", n );
+ else
+ rc = CDK_Inv_Algo;
+ if( !rc )
+ rc = gcry_pk_genkey( &s_key, s_params );
+ gcry_sexp_release( s_params );
+ if( !rc ) {
+ if( is_DSA( hd->key[1].algo) )
+ rc = read_dsa_key( s_key, hd->key[1].resarr );
+ else if( is_ELG( hd->key[1].algo) )
+ rc = read_elg_key( s_key, hd->key[1].resarr );
+ else if( is_RSA( hd->key[1].algo) )
+ rc = read_rsa_key( s_key, hd->key[1].resarr );
+ }
+ hd->key[1].n = cdk_pk_get_npkey( hd->key[1].algo );
+ gcry_sexp_release( s_key );
+ return rc;
+}
+
+
+/**
+ * cdk_keygen_start: kick off the key generation
+ * @hd: the keygen object
+ *
+ **/
+cdk_error_t
+cdk_keygen_start( cdk_keygen_ctx_t hd )
+{
+ gcry_sexp_t s_params = NULL, s_key = NULL;
+ size_t n;
+ int rc = 0;
+
+ if( !hd || !hd->user_id )
+ return CDK_Inv_Value;
+ if( is_ELG( hd->key[0].algo ) )
+ return CDK_Inv_Mode;
+ if( !hd->key[0].len )
+ hd->key[0].len = 1024;
+ n = hd->key[0].len;
+
+ if( !hd->sym_prefs )
+ cdk_keygen_set_prefs( hd, CDK_PREFTYPE_SYM, NULL, 0 );
+ if( !hd->hash_prefs )
+ cdk_keygen_set_prefs( hd, CDK_PREFTYPE_HASH, NULL, 0 );
+ if( !hd->zip_prefs )
+ cdk_keygen_set_prefs( hd, CDK_PREFTYPE_ZIP, NULL, 0 );
+
+ if( is_DSA( hd->key[0].algo ) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(dsa(nbits %d)))", n );
+ else if( is_RSA( hd->key[0].algo ) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(rsa(nbits %d)))", n );
+ else
+ rc = CDK_Inv_Algo;
+ if( !rc )
+ rc = gcry_pk_genkey( &s_key, s_params );
+ gcry_sexp_release( s_params );
+ if( !rc ) {
+ if( is_DSA( hd->key[0].algo ) )
+ rc = read_dsa_key( s_key, hd->key[0].resarr );
+ else if( is_RSA( hd->key[0].algo ) )
+ rc = read_rsa_key( s_key, hd->key[0].resarr );
+ hd->key[0].n = cdk_pk_get_npkey( hd->key[0].algo );
+ }
+ gcry_sexp_release( s_key );
+ if( !rc ) {
+ if( hd->key[1].algo && hd->key[1].len )
+ rc = generate_subkey( hd );
+ }
+ return rc;
+}
+
+
+static int
+gcry_mpi_to_native( cdk_keygen_ctx_t hd, size_t nkey, int type,
+ cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk )
+{
+ gcry_mpi_t * resarr;
+ cdk_mpi_t a = NULL;
+ size_t nbytes;
+ int i = 0, j = 0, nbits;
+ int rc = 0;
+
+ if( !hd )
+ return CDK_Inv_Value;
+ if( !pk && !sk )
+ return CDK_Inv_Value;
+ if( type < 0 || type > 1 )
+ return CDK_Inv_Value;
+
+ resarr = hd->key[type].resarr;
+ if( sk )
+ i += cdk_pk_get_npkey( sk->pubkey_algo );
+ while( j != nkey ) {
+ nbits = gcry_mpi_get_nbits( resarr[i] );
+ if( pk )
+ a = cdk_calloc( 1, sizeof * a + (nbits + 7) / 8 + 2 + 1 );
+ else if( sk )
+ a = cdk_salloc( sizeof * a + (nbits + 7) / 8 + 2 + 1, 1 );
+ a->bits = nbits;
+ a->bytes = ( nbits + 7 ) / 8;
+ nbytes = a->bytes;
+ a->data[0] = nbits >> 8;
+ a->data[1] = nbits;
+ rc = gcry_mpi_print( GCRYMPI_FMT_USG, a->data+2, nbytes, &nbytes, resarr[i] );
+ if( rc )
+ break;
+ if( pk )
+ pk->mpi[j++] = a;
+ else if( sk )
+ sk->mpi[j++] = a;
+ i++;
+ }
+ return rc;
+}
+
+
+static cdk_pkt_pubkey_t
+pk_create( cdk_keygen_ctx_t hd, int type )
+{
+ cdk_pkt_pubkey_t pk;
+ int rc = 0, npkey = 0;
+
+ if( type < 0 || type > 1 )
+ return NULL;
+ pk = cdk_calloc( 1, sizeof * pk );
+ if( !pk )
+ return NULL;
+ pk->version = 4;
+ pk->pubkey_algo = hd->key[type].algo;
+ pk->timestamp = _cdk_timestamp( );
+ if( hd->key[type].expire_date )
+ pk->expiredate = pk->timestamp + hd->key[type].expire_date;
+ npkey = cdk_pk_get_npkey( pk->pubkey_algo );
+ rc = gcry_mpi_to_native( hd, npkey, type, pk, NULL );
+ if( rc ) {
+ cdk_free( pk );
+ pk = NULL;
+ }
+ return pk;
+}
+
+
+static cdk_pkt_seckey_t
+sk_create( cdk_keygen_ctx_t hd, int type )
+{
+ cdk_pkt_seckey_t sk;
+ int nskey, rc = 0;
+
+ if( type < 0 || type > 1 )
+ return NULL;
+ sk = cdk_calloc( 1, sizeof * sk );
+ if( !sk )
+ return NULL;
+ _cdk_copy_pubkey( &sk->pk, hd->key[type].pk );
+ sk->version = 4;
+ sk->pubkey_algo = hd->key[type].algo;
+ sk->csum = 0;
+ sk->is_protected = 0;
+ nskey = cdk_pk_get_nskey( sk->pubkey_algo );
+ rc = gcry_mpi_to_native( hd, nskey, type, NULL, sk );
+ if( rc ) {
+ cdk_free( sk );
+ sk = NULL;
+ }
+ return sk;
+}
+
+
+static cdk_pkt_userid_t
+uid_create( cdk_keygen_ctx_t hd )
+{
+ cdk_pkt_userid_t id;
+
+ if( !hd->user_id )
+ return NULL;
+ id = cdk_calloc( 1, sizeof * id + strlen( hd->user_id ) + 1 );
+ if( !id )
+ return NULL;
+ strcpy( id->name, hd->user_id );
+ id->len = strlen( hd->user_id );
+ return id;
+}
+
+
+static cdk_pkt_signature_t
+sig_subkey_create( cdk_keygen_ctx_t hd )
+{
+ cdk_md_hd_t md;
+ cdk_subpkt_t node;
+ cdk_pkt_signature_t sig;
+ cdk_pkt_pubkey_t pk = hd->key[0].pk;
+ cdk_pkt_pubkey_t sub_pk = hd->key[1].pk;
+ cdk_pkt_seckey_t sk = hd->key[0].sk;
+ byte buf[4];
+ int rc;
+
+ sig = cdk_calloc( 1, sizeof * sig );
+ if( !sig )
+ return NULL;
+ _cdk_sig_create( pk, sig );
+ sig->sig_class = 0x18;
+ sig->digest_algo = CDK_MD_SHA1;
+
+ if( sub_pk->expiredate ) {
+ _cdk_u32tobuf( sub_pk->expiredate - sub_pk->timestamp, buf );
+ node = cdk_subpkt_new( 4 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_KEY_EXPIRE, buf, 4 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ md = cdk_md_open( sig->digest_algo, 0 );
+ if( !md ) {
+ _cdk_free_signature( sig );
+ return NULL;
+ }
+
+ _cdk_hash_pubkey( pk, md, 0 );
+ _cdk_hash_pubkey( sub_pk, md, 0 );
+ rc = _cdk_sig_complete( sig, sk, md );
+ cdk_md_close( md );
+ if( rc ) {
+ _cdk_free_signature( sig );
+ return NULL;
+ }
+ return sig;
+}
+
+
+static cdk_pkt_signature_t
+sig_self_create( cdk_keygen_ctx_t hd )
+{
+ cdk_md_hd_t md;
+ cdk_subpkt_t node;
+ cdk_pkt_signature_t sig;
+ cdk_pkt_pubkey_t pk = hd->key[0].pk;
+ cdk_pkt_userid_t id = hd->id;
+ cdk_pkt_seckey_t sk = hd->key[0].sk;
+ u32 keyid[2];
+ byte buf[8], * p;
+ int rc;
+
+ sig = cdk_calloc( 1, sizeof * sig );
+ if( !sig )
+ return NULL;
+ sig->version = 4;
+ sig->timestamp = _cdk_timestamp( );
+ sig->sig_class = 0x13;
+ sig->pubkey_algo = hd->key[0].algo;
+ sig->digest_algo = CDK_MD_SHA1;
+
+ _cdk_u32tobuf( sig->timestamp, buf );
+ sig->hashed = node = cdk_subpkt_new( 4 );
+ if( node )
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_SIG_CREATED, buf, 4 );
+
+ p = hd->sym_prefs;
+ node = cdk_subpkt_new( hd->sym_len + 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_PREFS_SYM, p, hd->sym_len );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+
+ p = hd->hash_prefs;
+ node = cdk_subpkt_new( hd->hash_len + 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_PREFS_HASH, p, hd->hash_len );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+
+ p = hd->zip_prefs;
+ node = cdk_subpkt_new( hd->zip_len + 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_PREFS_ZIP, p, hd->zip_len );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+
+ if( hd->mdc_feature ) {
+ buf[0] = 0x01;
+ node = cdk_subpkt_new( 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_FEATURES, buf, 1 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ if( hd->ks_no_modify ) {
+ buf[0] = 0x80;
+ node = cdk_subpkt_new( 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_KS_FLAGS, buf, 1 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ if( hd->ks_pref_url ) {
+ node = cdk_subpkt_new( strlen( hd->ks_pref_url ) + 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_PREF_KS,
+ hd->ks_pref_url, strlen( hd->ks_pref_url ) );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ if( pk->expiredate ) {
+ node = cdk_subpkt_new( 4 );
+ if( node ) {
+ _cdk_u32tobuf( pk->expiredate - pk->timestamp, buf );
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_KEY_EXPIRE, buf, 4 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ sig->unhashed = node = cdk_subpkt_new( 8 );
+ if( node ) {
+ cdk_pk_get_keyid( pk, keyid );
+ _cdk_u32tobuf( keyid[0], buf );
+ _cdk_u32tobuf( keyid[1], buf + 4 );
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_ISSUER, buf, 8 );
+ }
+
+ md = cdk_md_open( sig->digest_algo, 0 );
+ if( !md ) {
+ _cdk_free_signature( sig );
+ return NULL;
+ }
+
+ _cdk_hash_pubkey( pk, md, 0 );
+ _cdk_hash_userid( id, sig->version == 4, md );
+ rc = _cdk_sig_complete( sig, sk, md );
+ cdk_md_close( md );
+ if( rc ) {
+ _cdk_free_signature( sig );
+ return NULL;
+ }
+ return sig;
+}
+
+
+/**
+ * cdk_keygen_save: save the generated keys to disk
+ * @hd: the keygen object
+ * @pub: name of the file to store the public key
+ * @sec: name of the file to store the secret key
+ *
+ **/
+cdk_error_t
+cdk_keygen_save( cdk_keygen_ctx_t hd, const char * pubf, const char * secf )
+{
+ cdk_stream_t out = NULL;
+ CDK_PACKET pkt;
+ int rc;
+
+ hd->key[0].pk = pk_create( hd, 0 );
+ if( !hd->key[0].pk )
+ return CDK_Inv_Packet;
+ hd->key[0].sk = sk_create( hd, 0 );
+ if( !hd->key[0].sk )
+ return CDK_Inv_Packet;
+ hd->id = uid_create( hd );
+ if( !hd->id )
+ return CDK_Inv_Packet;
+ hd->sig = sig_self_create( hd );
+ if( !hd->sig )
+ return CDK_Inv_Packet;
+
+ rc = cdk_stream_create( pubf, &out );
+ if( rc )
+ return rc;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_PUBLIC_KEY;
+ pkt.pkt.public_key = hd->key[0].pk;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_USER_ID;
+ pkt.pkt.user_id = hd->id;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SIGNATURE;
+ pkt.pkt.signature = hd->sig;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ if( hd->key[1].algo ) {
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_PUBLIC_SUBKEY;
+ pkt.pkt.public_key = hd->key[1].pk = pk_create( hd, 1 );
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SIGNATURE;
+ pkt.pkt.signature = sig_subkey_create( hd );
+ rc = cdk_pkt_write( out, &pkt );
+ cdk_pkt_free( &pkt );
+ if( rc )
+ goto fail;
+ }
+
+ cdk_stream_close( out );
+ out = NULL;
+
+ rc = cdk_stream_create( secf, &out );
+ if( rc )
+ goto fail;
+
+ if( hd->protect ) {
+ rc = cdk_sk_protect( hd->key[0].sk, hd->pass );
+ if( rc )
+ goto fail;
+ }
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SECRET_KEY;
+ pkt.pkt.secret_key = hd->key[0].sk;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_USER_ID;
+ pkt.pkt.user_id = hd->id;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SIGNATURE;
+ pkt.pkt.signature = hd->sig;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ if( hd->key[1].algo ) {
+ hd->key[1].sk = sk_create( hd, 1 );
+ if( hd->protect && (rc = cdk_sk_protect( hd->key[1].sk, hd->pass )) )
+ goto fail;
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SECRET_SUBKEY;
+ pkt.pkt.secret_key = hd->key[1].sk;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+ }
+
+ fail:
+ cdk_stream_close( out );
+ return rc;
+}
+
+
+/**
+ * cdk_keygen_free: free the keygen object
+ * @hd: the keygen object
+ *
+ **/
+void
+cdk_keygen_free( cdk_keygen_ctx_t hd )
+{
+ if( hd ) {
+ _cdk_free_pubkey( hd->key[0].pk );
+ _cdk_free_pubkey( hd->key[1].pk );
+ _cdk_free_seckey( hd->key[0].sk );
+ _cdk_free_seckey( hd->key[1].sk );
+ _cdk_free_userid( hd->id );
+ _cdk_free_signature( hd->sig );
+ cdk_free( hd->sym_prefs );
+ cdk_free( hd->hash_prefs );
+ cdk_free( hd->zip_prefs );
+ _cdk_sec_free( hd->pass, hd->pass_len );
+ _cdk_free_mpibuf( hd->key[0].n, hd->key[0].resarr );
+ _cdk_free_mpibuf( hd->key[1].n, hd->key[1].resarr );
+ cdk_free( hd );
+ }
+}
+
+
+/**
+ * cdk_keygen_new:
+ * @r_hd: the new object
+ *
+ **/
+cdk_error_t
+cdk_keygen_new( cdk_keygen_ctx_t * r_hd )
+{
+ cdk_keygen_ctx_t hd;
+
+ if( !r_hd )
+ return CDK_Inv_Value;
+ hd = cdk_calloc( 1, sizeof * hd );
+ if( !hd )
+ return CDK_Out_Of_Core;
+ *r_hd = hd;
+ return 0;
+}
diff --git a/libextra/opencdk/keylist.c b/libextra/opencdk/keylist.c
new file mode 100644
index 0000000000..e42c3b92cc
--- /dev/null
+++ b/libextra/opencdk/keylist.c
@@ -0,0 +1,507 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * keylist.c - Linked key lists
+ * Copyright (C) 2002, 2003 Timo Schulz
+ * Copyright (C) 1998-2002 Free Software Foundation, Inc.
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "cipher.h"
+
+
+/* Here we check if *all* keys have the MDC feature. Even if one
+ key doesn't support it, it is not used. */
+int
+cdk_pklist_use_mdc( cdk_keylist_t pk_list )
+{
+ cdk_keylist_t pkr;
+ int mdc = 0;
+
+ if( !pk_list )
+ return CDK_Inv_Value;
+
+ for( pkr = pk_list; pkr; pkr = pkr->next ) {
+ if( pkr->key.pk->uid ) /* selected by user ID */
+ mdc = pkr->key.pk->uid->mdc_feature;
+ if( !mdc )
+ return 0;
+ }
+ return 1;
+}
+
+
+static int
+check_algo( int preftype, int algo )
+{
+ if( preftype == CDK_PREFTYPE_SYM )
+ return algo && !cdk_cipher_test_algo( algo );
+ else if( preftype == CDK_PREFTYPE_HASH )
+ return algo && !cdk_md_test_algo( algo );
+ else if( preftype == CDK_PREFTYPE_ZIP )
+ return !algo || algo == 1 || algo == 2;
+ else
+ return 0;
+}
+
+
+/**
+ * cdk_pklist_select_algo:
+ * @pkl: the keylist
+ * @preftype: preference type
+ *
+ * Select a symmetric cipher algorithm from a list of public keys.
+ * This algorithm is present in all key preferences.
+ **/
+int
+cdk_pklist_select_algo( cdk_keylist_t pkl, int preftype )
+{
+ const struct cdk_prefitem_s * prefs;
+ cdk_keylist_t pkr;
+ u32 bits[8];
+ int compr_hack = 0, any = 0;
+ int i = 0, j = 0;
+
+ if (!pkl)
+ return -1;
+
+ memset (bits, ~0, 8 * sizeof *bits);
+ for (pkr = pkl; pkr; pkr = pkr->next) {
+ u32 mask[8];
+ if (preftype == CDK_PREFTYPE_SYM) {
+ memset (mask, 0, 8 * sizeof *mask);
+ mask[0] |= (1 << 2); /*3DES is implicitly there for everyone else*/
+ }
+ if (pkr->key.pk->uid)
+ prefs = pkr->key.pk->uid->prefs;
+ else
+ prefs = pkr->key.pk->prefs;
+ any = 0;
+ for (i = 0; prefs && prefs[i].type; i++) {
+ if (prefs[i].type == preftype) {
+ mask[prefs[i].value / 32] |= 1 << (prefs[i].value % 32);
+ any = 1;
+ }
+ }
+ if ((!prefs || !any) && preftype == CDK_PREFTYPE_ZIP) {
+ mask[0] |= 3; /* asume no_compression and old pgp */
+ compr_hack = 1;
+ }
+ for (i = 0; i < 8; i++)
+ bits[i] &= mask[i];
+ /* Usable algorithms are now in bits:
+ We now use the last key from pkl to select the algorithm we want
+ to use. There are no preferences for the last key, we select the one
+ corresponding to first set bit. */
+ i = -1;
+ any = 0;
+ for (j = 0; prefs && prefs[j].type; j++) {
+ if (prefs[j].type == preftype) {
+ if ((bits[prefs[j].value / 32] & (1 << (prefs[j].value % 32))))
+ {
+ if (check_algo (preftype, prefs[j].value)) {
+ any = 1;
+ i = prefs[j].value;
+ break;
+ }
+ }
+ }
+ }
+ if (!prefs || !any) {
+ for (j = 0; j < 256; j++)
+ if ((bits[j / 32] & (1 << (j % 32)))) {
+ if (check_algo (preftype, j)) {
+ i = j;
+ break;
+ }
+ }
+ }
+ if (compr_hack && !i) {
+ /* selected no compression, but we should check whether
+ algorithm 1 is also available (the ordering is not relevant
+ in this case). */
+ if (bits[0] & (1 << 1))
+ i = 1; /* yep; we can use compression algo 1 */
+ }
+ }
+ _cdk_log_debug ("selected algo %d from prefs\n", i);
+ return i;
+}
+
+
+static int
+is_duplicated_entry( cdk_strlist_t list, cdk_strlist_t item )
+{
+ for( ; list && list != item; list = list->next ) {
+ if( !strcmp( list->d, item->d ) )
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ * cdk_pklist_release:
+ * @pkl: the keylist
+ *
+ * Free the memory of the key list.
+ **/
+void
+cdk_pklist_release( cdk_keylist_t pkl )
+{
+ cdk_keylist_t pkr;
+
+ for( ; pkl; pkl = pkr ) {
+ pkr = pkl->next;
+ _cdk_free_pubkey( pkl->key.pk );
+ pkl->key.pk = NULL;
+ cdk_free( pkl );
+ }
+}
+
+
+/**
+ * cdk_pklist_build:
+ * @ret_pkl: the new keylist
+ * @hd: the session handle
+ * @remusr: the string list of the recipients
+ * @use: public key usage
+ *
+ * Create a public key list based on the recipient names in @remusr.
+ **/
+cdk_error_t
+cdk_pklist_build( cdk_keylist_t * ret_pkl, cdk_keydb_hd_t hd,
+ cdk_strlist_t remusr, int use )
+{
+ cdk_keylist_t pk_list = NULL, r = NULL, l;
+ cdk_pkt_pubkey_t pk = NULL;
+ int rc = 0;
+
+ if( !hd )
+ return CDK_Inv_Value;
+
+ for( ; remusr; remusr = remusr->next ) {
+ rc = _cdk_keydb_get_pk_byusage( hd, remusr->d, &pk, use );
+ if( rc )
+ break;
+ else {
+ for( l = pk_list; l; l = l->next ) {
+ if( !_cdk_pubkey_compare( l->key.pk, pk ) ) {
+ _cdk_free_pubkey( pk );
+ pk = NULL;
+ continue; /* key already in list so skip it */
+ }
+ }
+ r = cdk_calloc( 1, sizeof *r );
+ if( !r ) {
+ rc = CDK_Out_Of_Core;
+ break;
+ }
+ r->type = CDK_PKT_PUBLIC_KEY;
+ r->key.pk = pk;
+ r->next = pk_list;
+ pk_list = r;
+ }
+ }
+ if( rc ) {
+ cdk_pklist_release( pk_list );
+ pk_list = NULL;
+ }
+ *ret_pkl = pk_list;
+ return rc;
+}
+
+
+/**
+ * cdk_pklist_encrypt:
+ * @pkl: the keylist
+ * @dek: the data encryption key
+ * @outp: the stream to write in the data
+ *
+ * Encrypt the session key with each key of the list and wrap it
+ * into a PUBKEY_ENC packet and write it to @outp.
+ */
+cdk_error_t
+cdk_pklist_encrypt( cdk_keylist_t pk_list, cdk_dek_t dek, cdk_stream_t outp )
+{
+ cdk_pkt_pubkey_t pk = NULL;
+ cdk_pkt_pubkey_enc_t enc = NULL;
+ cdk_packet_t pkt;
+ cdk_sesskey_t frame = NULL;
+ int nbits = 0;
+ int rc = 0;
+
+ if( !pk_list || !dek || !outp )
+ return CDK_Inv_Value;
+
+ if( pk_list->type != CDK_PKT_PUBLIC_KEY )
+ return CDK_Inv_Mode;
+
+ pkt = cdk_calloc( 1, sizeof * pkt );
+ if( !pkt )
+ return CDK_Out_Of_Core;
+ for( ; pk_list; pk_list = pk_list->next ) {
+ pk = pk_list->key.pk;
+ cdk_free( enc );
+ enc = cdk_calloc( 1, sizeof *enc );
+ if( !enc )
+ return CDK_Out_Of_Core;
+ enc->version = 3;
+ enc->pubkey_algo = pk->pubkey_algo;
+ cdk_pk_get_keyid( pk, enc->keyid );
+ nbits = cdk_pk_get_nbits( pk );
+ rc = cdk_dek_encode_pkcs1( dek, nbits, &frame );
+ if( rc )
+ break;
+ rc = cdk_pk_encrypt( pk, enc, frame );
+ cdk_sesskey_free( frame );
+ if( rc )
+ break;
+ else {
+ cdk_pkt_init( pkt );
+ pkt->old_ctb = dek->rfc1991? 1 : 0;
+ pkt->pkttype = CDK_PKT_PUBKEY_ENC;
+ pkt->pkt.pubkey_enc = enc;
+ rc = cdk_pkt_write( outp, pkt );
+ cdk_pkt_free( pkt );
+ if( rc )
+ break;
+ }
+ }
+ cdk_free( pkt );
+ cdk_free( enc );
+ return rc;
+}
+
+
+/**
+ * cdk_sklist_release:
+ * @skl: secret keylist
+ *
+ * Free the memory of the secret keylist.
+ **/
+void
+cdk_sklist_release( cdk_keylist_t sk_list )
+{
+ cdk_keylist_t sk_rover = NULL;
+
+ for( ; sk_list; sk_list = sk_rover ) {
+ sk_rover = sk_list->next;
+ _cdk_free_seckey( sk_list->key.sk );
+ sk_list->key.sk = NULL;
+ cdk_free( sk_list );
+ }
+}
+
+
+cdk_error_t
+cdk_sklist_build( cdk_keylist_t * ret_skl, cdk_keydb_hd_t db, cdk_ctx_t hd,
+ cdk_strlist_t locusr, int unlock, unsigned int use )
+{
+ cdk_keylist_t r = NULL, sk_list = NULL;
+ cdk_pkt_seckey_t sk = NULL;
+ int rc = 0;
+
+ if( !db || !hd || !ret_skl )
+ return CDK_Inv_Value;
+
+ if( !locusr ) { /* use the default one */
+ rc = _cdk_keydb_get_sk_byusage( db, NULL, &sk, use );
+ if( rc ) {
+ _cdk_free_seckey( sk );
+ return rc;
+ }
+ if( unlock ) {
+ rc = _cdk_sk_unprotect_auto( hd, sk );
+ if( rc )
+ return rc;
+ }
+ r = cdk_calloc( 1, sizeof *r );
+ if( !r )
+ return CDK_Out_Of_Core;
+ r->key.sk = sk;
+ r->next = sk_list;
+ r->type = CDK_PKT_SECRET_KEY;
+ sk_list = r;
+ }
+ else {
+ cdk_strlist_t locusr_orig = locusr;
+ for( ; locusr; locusr = locusr->next ) {
+ if( is_duplicated_entry( locusr_orig, locusr ) )
+ continue;
+ rc = _cdk_keydb_get_sk_byusage( db, locusr->d, &sk, use );
+ if( rc ) {
+ _cdk_free_seckey( sk );
+ sk = NULL;
+ }
+ else {
+ if( unlock && (rc = _cdk_sk_unprotect_auto( hd, sk )) )
+ break;
+ r = cdk_calloc( 1, sizeof *r );
+ if( !r )
+ return CDK_Out_Of_Core;
+ r->key.sk = sk;
+ r->next = sk_list;
+ r->type = CDK_PKT_SECRET_KEY;
+ sk_list = r;
+ }
+ }
+ }
+ if( rc ) {
+ cdk_sklist_release( sk_list );
+ sk_list = NULL;
+ }
+ *ret_skl = sk_list;
+ return rc;
+}
+
+
+/**
+ * cdk_sklist_write_onepass:
+ * @skl: secret keylist
+ * @outp: the stream to write in the data
+ * @sigclass: the class of the sig to create
+ * @mdalgo: the message digest algorithm
+ *
+ * Write a one-pass signature for each key in the list into @outp.
+ **/
+cdk_error_t
+cdk_sklist_write_onepass( cdk_keylist_t skl, cdk_stream_t outp,
+ int sigclass, int mdalgo )
+{
+ cdk_pkt_onepass_sig_t ops;
+ cdk_keylist_t r;
+ cdk_packet_t pkt;
+ int i, skcount = 0;
+ int rc = 0;
+
+ if( !skl || !outp )
+ return CDK_Inv_Value;
+
+ if( skl->type != CDK_PKT_SECRET_KEY )
+ return CDK_Inv_Mode;
+
+ pkt = cdk_calloc( 1, sizeof * pkt );
+ if( !pkt )
+ return CDK_Out_Of_Core;
+
+ for( skcount = 0, r = skl; r; r = r->next )
+ skcount++;
+ for( ; skcount; skcount-- ) {
+ for( i = 0, r = skl; r; r = r->next ) {
+ if( ++i == skcount )
+ break;
+ }
+ ops = cdk_calloc( 1, sizeof *ops );
+ if( !ops )
+ return CDK_Out_Of_Core;
+ ops->version = 3;
+ cdk_sk_get_keyid( r->key.sk, ops->keyid );
+ ops->sig_class = sigclass;
+ if( !mdalgo )
+ mdalgo = _cdk_sig_hash_for( r->key.sk->pubkey_algo,
+ r->key.sk->version );
+ ops->digest_algo = mdalgo;
+ ops->pubkey_algo = r->key.sk->pubkey_algo;
+ ops->last = (skcount == 1);
+
+ cdk_pkt_init( pkt );
+ pkt->pkttype = CDK_PKT_ONEPASS_SIG;
+ pkt->pkt.onepass_sig = ops;
+ rc = cdk_pkt_write( outp, pkt );
+ cdk_pkt_free( pkt );
+ if( rc )
+ break;
+ }
+ cdk_free( pkt );
+ return rc;
+}
+
+
+/**
+ * cdk_sklist_write:
+ * @skl: secret keylist
+ * @outp: the stream to write in the data
+ * @hash: opaque handle for the message digest operations
+ * @sigclass: the class of the sig
+ * @sigver: version of the sig
+ *
+ * Complete the sig based on @hash and write all signatures to @outp.
+ **/
+cdk_error_t
+cdk_sklist_write( cdk_keylist_t skl, cdk_stream_t outp, cdk_md_hd_t hash,
+ int sigclass, int sigver )
+{
+ cdk_keylist_t r = NULL;
+ cdk_pkt_signature_t sig = NULL;
+ cdk_packet_t pkt;
+ cdk_md_hd_t md = NULL;
+ byte * mdbuf;
+ int rc = 0, digest_algo;
+
+ if( !skl || !outp || !hash )
+ return CDK_Inv_Value;
+
+ if( skl->type != CDK_PKT_SECRET_KEY )
+ return CDK_Inv_Mode;
+
+ pkt = cdk_calloc( 1, sizeof *pkt );
+ if( !pkt )
+ return CDK_Out_Of_Core;
+ digest_algo = cdk_md_get_algo( hash );
+ for( r = skl; r; r = r->next ) {
+ sig = cdk_calloc( 1, sizeof *sig );
+ if( !sig )
+ return CDK_Out_Of_Core;
+ sig->version = sigver;
+ _cdk_sig_create( r->key.sk->pk, sig );
+ if( sig->digest_algo != digest_algo )
+ sig->digest_algo = digest_algo;
+ sig->sig_class = sigclass;
+ md = cdk_md_copy( hash );
+ _cdk_hash_sig_data( sig, md );
+ cdk_md_final( md );
+
+ mdbuf = cdk_md_read( md, sig->digest_algo );
+ rc = cdk_pk_sign( r->key.sk, sig, mdbuf );
+ if( rc )
+ break;
+ cdk_pkt_init( pkt );
+ pkt->old_ctb = sig->version == 3? 1 : 0;
+ pkt->pkttype = CDK_PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ rc = cdk_pkt_write( outp, pkt );
+ cdk_pkt_free( pkt );
+ if( rc )
+ break;
+ cdk_md_close( md );
+ md = NULL;
+ }
+ cdk_free( pkt );
+ cdk_md_close( md );
+ return rc;
+}
+
+
+
diff --git a/libextra/opencdk/keyserver.c b/libextra/opencdk/keyserver.c
new file mode 100644
index 0000000000..9984233617
--- /dev/null
+++ b/libextra/opencdk/keyserver.c
@@ -0,0 +1,184 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * keyserver.c - Keyserver support
+ * Copyright (C) 2002 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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>
+#ifdef HAVE_NETDB_H
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# define closesocket close
+#else
+# include <windows.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "opencdk.h"
+#include "main.h"
+
+
+static int initialized = 0;
+
+
+static void
+init_sockets( void )
+{
+#ifdef __MINGW32__
+ WSADATA wsdata;
+
+ if( initialized )
+ return;
+ if( WSAStartup( 0x0101, &wsdata ) )
+ _cdk_log_debug( "winsock init failed.\n" );
+#endif
+ initialized = 1;
+}
+
+
+static int
+keyserver_hkp( const char * host, int port, u32 keyid, cdk_kbnode_t * ret_key )
+{
+ const char * fmt;
+ struct hostent * hp;
+ struct sockaddr_in saddr;
+ cdk_stream_t a;
+ char * buf, buffer[256];
+ int nwritten, nread, state = 0;
+ int rc = 0, fd;
+
+ _cdk_log_debug( "connect to `%s'\n", host );
+ hp = gethostbyname( host );
+ if( !hp )
+ return CDK_General_Error;
+
+ memset( &saddr, 0, sizeof saddr );
+ memcpy( &saddr.sin_addr, hp->h_addr, hp->h_length );
+ saddr.sin_family = hp->h_addrtype;
+ saddr.sin_port = htons( port );
+
+ fd = socket( AF_INET, SOCK_STREAM, 0 );
+ if( fd == -1 )
+ return CDK_General_Error;
+
+ setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,( char *)1, 1 );
+ if( connect( fd,( struct sockaddr *) &saddr, sizeof saddr ) == -1 ) {
+ closesocket( fd );
+ return CDK_General_Error;
+ }
+
+ fmt = "GET /pks/lookup?op=get&search=0x%08lX HTTP/1.0\r\n"
+ "Host: %s:%d\r\n"
+ "\r\n";
+ buf = cdk_calloc( 1, 64 + strlen( host ) + strlen( fmt ) );
+ if( !buf ) {
+ closesocket( fd );
+ return CDK_Out_Of_Core;
+ }
+ sprintf( buf, fmt, keyid, host, port );
+ _cdk_log_debug( "%s\n", buf );
+
+ nwritten = send( fd, buf, strlen( buf ), 0 );
+ if( nwritten == -1 ) {
+ cdk_free( buf );
+ closesocket( fd );
+ return CDK_File_Error;
+ }
+
+ a = cdk_stream_tmp( );
+ if( !a ) {
+ cdk_free( buf );
+ closesocket( fd );
+ return CDK_Out_Of_Core;
+ }
+
+ while( (nread = recv( fd, buffer, 255, 0 )) > 0 ) {
+ buffer[nread] = '\0';
+ /*_cdk_log_debug( "%s", buffer );*/
+ cdk_stream_write( a, buffer, nread );
+ if( strstr( buffer, "<pre>") || strstr( buffer, "</pre>" ) )
+ state++;
+ }
+ cdk_free( buf );
+
+ if( state != 2 )
+ rc = CDK_Error_No_Key;
+ if( !rc ) {
+ cdk_stream_tmp_set_mode( a, 0 );
+ cdk_stream_set_armor_flag( a, 0 );
+ cdk_stream_seek( a, 0 );
+ cdk_stream_read( a, NULL, 0 );
+ rc = cdk_keydb_get_keyblock( a, ret_key );
+ }
+ if( rc == CDK_EOF && *ret_key )
+ rc = 0;
+ cdk_stream_close( a );
+ closesocket( fd );
+ return rc;
+}
+
+
+/**
+ * cdk_keyserver_recv_key:
+ * @host: URL or hostname of the keyserver
+ * @port: The port to use for the connection
+ * @keyid: KeyID of the key to retrieve
+ * @kid_type: KeyID type (long, short, fingerprint)
+ * @r_knode: The key that was found wrapped in a KBNODE struct
+ *
+ * Receive a key from a keyserver.
+ **/
+cdk_error_t
+cdk_keyserver_recv_key( const char * host, int port,
+ const byte * keyid, int kid_type,
+ cdk_kbnode_t * ret_key )
+{
+ u32 kid = 0;
+
+ if( !host || !keyid || !ret_key )
+ return CDK_Inv_Value;
+
+ if( !initialized )
+ init_sockets( );
+
+ if( !port )
+ port = 11371;
+
+ if( !strncmp( host, "http://", 7 ) )
+ host += 7;
+ else if( !strncmp( host, "hkp://", 6 ) )
+ host += 6;
+ else if( !strncmp( host, "x-hkp://", 8 ) )
+ host += 8;
+
+ switch( kid_type ) {
+ case CDK_DBSEARCH_SHORT_KEYID: kid = _cdk_buftou32( keyid ); break;
+ case CDK_DBSEARCH_KEYID : kid = _cdk_buftou32( keyid + 4 ); break;
+ case CDK_DBSEARCH_FPR : kid = _cdk_buftou32( keyid + 16 ); break;
+ default : return CDK_Inv_Mode;
+ }
+
+ return keyserver_hkp( host, port, kid, ret_key );
+}
diff --git a/libextra/opencdk/main.c b/libextra/opencdk/main.c
new file mode 100644
index 0000000000..9ffbf3c825
--- /dev/null
+++ b/libextra/opencdk/main.c
@@ -0,0 +1,707 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * main.c
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <errno.h>
+#include <malloc.h>
+#ifdef HAVE_PWD_H
+# include <pwd.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "cipher.h"
+
+
+#define DEFAULT_CIPHER_ALGO CDK_CIPHER_CAST5
+#define DEFAULT_DIGEST_ALGO CDK_MD_SHA1
+
+#define SECMEM_SIZE 32768
+
+static void *(*alloc_func) (size_t n) = gcry_xmalloc;
+static void *(*alloc_secure_func) (size_t n) = gcry_malloc_secure;
+static void *(*realloc_func) (void *p, size_t n) = gcry_realloc;
+static void *(*calloc_func) (size_t m, size_t n) = gcry_calloc;
+static void (*free_func) (void *) = gcry_free;
+static int malloc_hooks = 0;
+static int secmem_init = 0;
+
+static cdk_log_fnc_t log_handler = NULL;
+static void *log_handler_value = NULL;
+static int log_level = CDK_LOG_NONE;
+
+
+/**
+ * cdk_strerror:
+ * @ec: the error number
+ *
+ * Return an error text for the given id.
+ **/
+const char *
+cdk_strerror (int ec)
+{
+ static char buf[20];
+
+ switch (ec) {
+ case CDK_EOF: return "End Of File";
+ case CDK_Success: return "No error";
+ case CDK_General_Error: return "General error";
+ case CDK_File_Error: return strerror (errno);
+ case CDK_Bad_Sig: return "Bad signature";
+ case CDK_Inv_Packet: return "Invalid packet";
+ case CDK_Inv_Algo: return "Invalid algorithm";
+ case CDK_Not_Implemented: return "This is not implemented yet";
+ /* FIXME: print the actual gcrypt error */
+ case CDK_Gcry_Error: return "Gcrypt error";
+ case CDK_Armor_Error: return "ASCII armor error";
+ case CDK_Armor_CRC_Error: return "ASCII armored damaged (CRC error)";
+ case CDK_MPI_Error: return "Invalid or missformed MPI";
+ case CDK_Inv_Value: return "Invalid parameter or value";
+ case CDK_Error_No_Key: return "No key available or not found";
+ case CDK_Chksum_Error: return "Check for key does not match";
+ case CDK_Time_Conflict: return "Time conflict";
+ case CDK_Zlib_Error: return "ZLIB error";
+ case CDK_Weak_Key: return "Weak key was detected";
+ case CDK_Out_Of_Core: return "Out of core!!";
+ case CDK_Wrong_Seckey: return "Wrong secret key";
+ case CDK_Bad_MDC: return "Manipulated MDC detected";
+ case CDK_Inv_Mode: return "Invalid mode";
+ case CDK_Error_No_Keyring: return "No keyring available";
+ case CDK_Inv_Packet_Ver: return "Invalid version for packet";
+ case CDK_Too_Short: return "Buffer or object is too short";
+ case CDK_Unusable_Key: return "Unusable public key";
+ default: sprintf (buf, "ec=%d", ec); return buf;
+ }
+ return NULL;
+}
+
+
+static void
+out_of_core (size_t n)
+{
+ fprintf (stderr, "\n ** fatal error: out of memory (%d bytes) **\n", n);
+}
+
+
+/**
+ * cdk_set_malloc_hooks:
+ * @new_alloc_func: malloc replacement
+ * @new_alloc_secure_func: secure malloc replacement
+ * @new_realloc_func: realloc replacement
+ * @new_calloc_func: calloc replacement
+ * @new_free_func: free replacement
+ *
+ * Set private memory hooks for the lib.
+ */
+void
+cdk_set_malloc_hooks (void *(*new_alloc_func) (size_t n),
+ void *(*new_alloc_secure_func) (size_t n),
+ void *(*new_realloc_func) (void *p, size_t n),
+ void *(*new_calloc_func) (size_t m, size_t n),
+ void (*new_free_func) (void *))
+{
+ alloc_func = new_alloc_func;
+ alloc_secure_func = new_alloc_secure_func;
+ realloc_func = new_realloc_func;
+ calloc_func = new_calloc_func;
+ free_func = new_free_func;
+ malloc_hooks = 1;
+}
+
+
+/**
+ * cdk_malloc_hook_initialized:
+ *
+ * Return if the malloc hooks are already initialized.
+ **/
+int
+cdk_malloc_hook_initialized (void)
+{
+ return malloc_hooks;
+}
+
+
+void *
+cdk_malloc (size_t size)
+{
+ void * p = alloc_func (size);
+ if (!p)
+ out_of_core (size);
+ return p;
+}
+
+
+void *
+cdk_calloc (size_t n, size_t m)
+{
+ void * p = calloc_func (n, m);
+ if (!p)
+ out_of_core (m);
+ return p;
+}
+
+
+static void
+_secmem_init (size_t size)
+{
+ if (!size) {
+ gcry_control (GCRYCTL_DROP_PRIVS);
+ return;
+ }
+ if (secmem_init == 1)
+ return;
+ if (size >= SECMEM_SIZE)
+ size = SECMEM_SIZE;
+ gcry_control (GCRYCTL_INIT_SECMEM, size, 0);
+ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+ gcry_control (GCRYCTL_DISABLE_SECMEM_WARN);
+ secmem_init = 1;
+}
+
+
+static void
+_secmem_end (void)
+{
+ gcry_control (GCRYCTL_TERM_SECMEM);
+ secmem_init = 0;
+}
+
+
+void *
+cdk_salloc (size_t size, int clear)
+{
+ static size_t n = 0;
+ void * p;
+
+ if (!secmem_init) {
+ _secmem_init (SECMEM_SIZE);
+ secmem_init = 1;
+ }
+ if (secmem_init == 1) {
+ _secmem_init (0);
+ secmem_init = 2;
+ }
+ /*
+ n += size;
+ _cdk_log_debug ("\ncdk_salloc (%d)\n", n);
+ */
+ p = alloc_secure_func (size);
+ if (!p)
+ out_of_core (size);
+ if (clear)
+ memset (p, 0, size);
+ return p;
+}
+
+
+void *
+cdk_realloc (void *ptr, size_t size)
+{
+ void * p = realloc_func (ptr, size);
+ if (!p)
+ out_of_core (size);
+ return p;
+}
+
+
+char *
+cdk_strdup (const char * ptr)
+{
+ char * p = cdk_malloc (strlen (ptr) + 1);
+ if (p)
+ strcpy (p, ptr);
+ return p;
+}
+
+
+void
+cdk_free (void * ptr)
+{
+ if (ptr)
+ free_func (ptr);
+}
+
+
+void
+_cdk_sec_free( void * ptr, size_t size )
+{
+ if( ptr ) {
+ memset( ptr, 0xff, size );
+ memset( ptr, 0xaa, size );
+ memset( ptr, 0x55, size );
+ memset( ptr, 0x00, size );
+ free_func( ptr );
+ }
+}
+
+
+static void
+_cdk_logv (int level, const char *fmt, va_list arg_ptr)
+{
+
+ if (log_handler)
+ log_handler (log_handler_value, level, fmt, arg_ptr);
+ else {
+ switch (level) {
+ case CDK_LOG_INFO : break;
+ case CDK_LOG_DEBUG: fputs ("DBG: ", stderr); break;
+ case CDK_LOG_NONE : return;
+ }
+ vfprintf (stderr, fmt, arg_ptr);
+ }
+}
+
+
+/**
+ * cdk_set_log_handler:
+ * @logfnc: the function pointer
+ * @opaque: a private values for the function
+ *
+ * set a private handler for logging.
+ */
+void
+cdk_set_log_handler( cdk_log_fnc_t logfnc, void * opaque )
+{
+ log_handler = logfnc;
+ log_handler_value = opaque;
+}
+
+
+/**
+ * cdk_set_log_level:
+ * @lvl: the level
+ *
+ * set the verbosity level.
+ **/
+void
+cdk_set_log_level (int lvl)
+{
+ log_level = lvl;
+}
+
+
+int
+_cdk_get_log_level (void)
+{
+ return log_level;
+}
+
+
+void
+_cdk_log_info (const char *fmt, ...)
+{
+ va_list arg;
+
+ if (log_level == CDK_LOG_NONE)
+ return;
+ va_start (arg, fmt);
+ _cdk_logv (CDK_LOG_INFO, fmt, arg);
+ va_end (arg);
+}
+
+
+void
+_cdk_log_debug (const char *fmt, ...)
+{
+ va_list arg;
+
+ if (log_level < CDK_LOG_DEBUG)
+ return;
+ va_start (arg, fmt);
+ _cdk_logv (CDK_LOG_DEBUG, fmt, arg);
+ va_end (arg);
+}
+
+
+#ifndef HAVE_PWD_H
+char *
+getpass (const char * prompt)
+{
+ return NULL; /* fixme */
+}
+#endif
+
+
+char *
+_cdk_passphrase_get( cdk_ctx_t hd, const char * prompt )
+{
+ char * p, * pass = NULL;
+
+ if( hd->passphrase )
+ return hd->passphrase( hd->passphrase_value, prompt );
+ p = getpass( prompt );
+ if( p )
+ pass = cdk_strdup( p );
+ return pass;
+}
+
+
+void
+_cdk_passphrase_free( char *pw, size_t size )
+{
+ _cdk_sec_free( pw, size );
+}
+
+
+int
+_cdk_is_idea_available (void)
+{
+ int rc = 0;
+#ifdef LIBGCRYPT_WITH_IDEA
+ rc = 1;
+#endif
+ return rc;
+}
+
+
+static void
+handle_set_cipher (cdk_ctx_t hd, int cipher)
+{
+ if( !hd )
+ return;
+ if( cdk_cipher_test_algo( cipher ) )
+ cipher = DEFAULT_CIPHER_ALGO;
+ hd->cipher_algo = cipher;
+}
+
+
+static void
+handle_set_digest (cdk_ctx_t hd, int digest)
+{
+ if( !hd )
+ return;
+ if( cdk_md_test_algo( digest ) )
+ digest = DEFAULT_DIGEST_ALGO;
+ hd->digest_algo = digest;
+}
+
+
+static void
+handle_set_s2k( cdk_ctx_t hd, int mode, int digest, int cipher )
+{
+ if( !hd )
+ return;
+ if( cdk_cipher_test_algo( cipher ) )
+ cipher = DEFAULT_CIPHER_ALGO;
+ if( cdk_md_test_algo( digest ) )
+ digest = DEFAULT_DIGEST_ALGO;
+ if( mode != CDK_S2K_SIMPLE
+ && mode != CDK_S2K_SALTED
+ && mode != CDK_S2K_ITERSALTED )
+ mode = CDK_S2K_ITERSALTED;
+ hd->_s2k.mode = mode;
+ hd->_s2k.digest_algo = digest;
+ hd->_s2k.cipher_algo = cipher;
+}
+
+
+static void
+handle_set_compat( cdk_ctx_t hd, int val )
+{
+ if( !hd )
+ return;
+ hd->opt.compat = val;
+ if( !val )
+ return;
+ hd->opt.mdc = 0;
+ hd->opt.rfc1991 = val == -1? 1: 0;
+ hd->compress.algo = CDK_COMPRESS_ZIP;
+ hd->compress.level = -1;
+ hd->cipher_algo = val == -1? CDK_CIPHER_IDEA : DEFAULT_CIPHER_ALGO;
+ hd->digest_algo = val == -1? CDK_MD_MD5: DEFAULT_DIGEST_ALGO;
+ if( val == -1 )
+ handle_set_s2k( hd, 0, hd->digest_algo, hd->cipher_algo );
+}
+
+
+static void
+handle_set_compress( cdk_ctx_t hd, int algo, int level )
+{
+ if( !hd )
+ return;
+ if( algo < 0 || algo > 2 )
+ algo = 0;
+ hd->compress.algo = algo;
+ if( !algo )
+ hd->opt.compress = 0;
+ else {
+ if( level > 0 && level < 10 )
+ hd->compress.level = level;
+ else
+ hd->compress.level = 6;
+ }
+}
+
+
+/**
+ * cdk_handle_control:
+ * @hd: session handle
+ * @action: flag which indicates whether put or get is requested
+ * @cmd: command id
+ *
+ * Perform various control operations for the current session.
+ **/
+int
+cdk_handle_control( cdk_ctx_t hd, int action, int cmd, ... )
+{
+ va_list arg_ptr;
+ int set = action == CDK_CTLF_SET, val = 0;
+
+ if( !hd )
+ return -1;
+ if( action != CDK_CTLF_SET && action != CDK_CTLF_GET )
+ return -1;
+ va_start( arg_ptr, cmd );
+ switch( cmd ) {
+ case CDK_CTL_ARMOR:
+ if( set )
+ hd->opt.armor = va_arg( arg_ptr, int );
+ else
+ val = hd->opt.armor;
+ break;
+
+ case CDK_CTL_CIPHER:
+ if( set )
+ handle_set_cipher( hd, va_arg( arg_ptr, int ) );
+ else
+ val = hd->cipher_algo;
+ break;
+
+ case CDK_CTL_DIGEST:
+ if( set )
+ handle_set_digest( hd, va_arg( arg_ptr, int ) );
+ else
+ val = hd->digest_algo;
+ break;
+
+ case CDK_CTL_COMPAT:
+ if( set )
+ handle_set_compat( hd, va_arg( arg_ptr, int ) );
+ else
+ val = hd->opt.compat;
+ break;
+
+ case CDK_CTL_OVERWRITE:
+ if( set )
+ hd->opt.overwrite = va_arg( arg_ptr, int );
+ else
+ val = hd->opt.overwrite;
+ break;
+
+ case CDK_CTL_COMPRESS:
+ if( set ) {
+ int algo = va_arg( arg_ptr, int );
+ int level = va_arg( arg_ptr, int );
+ handle_set_compress( hd, algo, level );
+ }
+ else
+ val = hd->compress.algo;
+ break;
+
+ case CDK_CTL_S2K:
+ if( set ) {
+ int mode = va_arg( arg_ptr, int );
+ int digest = va_arg( arg_ptr, int );
+ int cipher = va_arg( arg_ptr, int );
+ handle_set_s2k( hd, mode, digest, cipher );
+ }
+ else
+ val = hd->_s2k.mode;
+ break;
+
+ case CDK_CTL_KEYCACHE_ON:
+ if( set )
+ hd->cache.on = va_arg( arg_ptr, int );
+ else
+ val = hd->cache.on;
+ break;
+
+ case CDK_CTL_KEYCACHE_FREE:
+ _cdk_free_seckey( hd->cache.sk );
+ hd->cache.sk = NULL;
+ break;
+
+ case CDK_CTL_FORCE_DIGEST:
+ if( set )
+ hd->opt.force_digest = va_arg( arg_ptr, int );
+ else
+ val = hd->opt.force_digest;
+ break;
+
+ case CDK_CTL_TRUSTMODEL:
+ if( set )
+ hd->trust_model = va_arg( arg_ptr, int );
+ else
+ val = hd->trust_model;
+ break;
+
+ default:
+ val = -1;
+ break;
+ }
+ va_end( arg_ptr );
+ return val;
+}
+
+
+
+/**
+ * cdk_handle_new:
+ * @r_ctx: context to store the handle
+ *
+ * create a new session handle.
+ **/
+int
+cdk_handle_new( cdk_ctx_t * r_ctx )
+{
+ cdk_ctx_t c;
+
+ if( !r_ctx )
+ return CDK_Inv_Value;
+
+ c = cdk_calloc( 1, sizeof *c );
+ if( !c )
+ return CDK_Out_Of_Core;
+ /* default */
+ c->_s2k.mode = 3;
+ c->_s2k.digest_algo = DEFAULT_DIGEST_ALGO;
+ c->_s2k.cipher_algo = DEFAULT_CIPHER_ALGO;
+
+ c->opt.mdc = 1;
+ c->opt.compress = 1;
+ c->opt.armor = 0;
+ c->opt.textmode = 0;
+
+ c->digest_algo = DEFAULT_DIGEST_ALGO;
+ c->cipher_algo = DEFAULT_CIPHER_ALGO;
+ c->compress.algo = CDK_COMPRESS_ZIP;
+ c->compress.level = 6;
+
+ *r_ctx = c;
+ return 0;
+}
+
+
+/**
+ * cdk_handle_set_keydb:
+ * @hd: session handle
+ * @db: the database handle
+ *
+ * set the key database handle.
+ * the function automatically detects whether this is a public or
+ * secret keyring and the right handle is set.
+ **/
+void
+cdk_handle_set_keydb( cdk_ctx_t hd, cdk_keydb_hd_t db )
+{
+ if( !hd )
+ return;
+ if( db->secret )
+ hd->db.sec = db;
+ else
+ hd->db.pub = db;
+}
+
+
+/**
+ * cdk_handle_get_keydb:
+ * @hd: session handle
+ * @type: type of the keyring
+ *
+ * Return the keydb handle from the session handle.
+ **/
+cdk_keydb_hd_t
+cdk_handle_get_keydb( cdk_ctx_t hd, int type )
+{
+ if( !hd )
+ return NULL;
+ if( type == CDK_DBTYPE_PK_KEYRING )
+ return hd->db.pub;
+ else if( type == CDK_DBTYPE_SK_KEYRING )
+ return hd->db.sec;
+ return NULL;
+}
+
+
+/**
+ * cdk_handle_set_callback:
+ * @hd: the handle
+ * @cb: the callback function
+ * @cb_value: the opaque value for the function
+ *
+ * set the callback for filter operations.
+ **/
+void
+cdk_handle_set_callback (cdk_ctx_t hd,
+ void (*cb) (void * opaque, int type, const char * s),
+ void * cb_value)
+{
+ if( !hd )
+ return;
+ hd->callback = cb;
+ hd->callback_value = cb_value;
+}
+
+
+/**
+ * cdk_handle_set_passphrase_cb:
+ * @hd: session handle
+ * @cb: callback function
+ * @cb_value: the opaque value for the cb function
+ *
+ * set the passphrase callback.
+ **/
+void cdk_handle_set_passphrase_cb( cdk_ctx_t hd,
+ char *(*cb) (void *opa, const char *prompt),
+ void * cb_value )
+{
+ if( !hd )
+ return;
+ hd->passphrase = cb;
+ hd->passphrase_value = cb_value;
+}
+
+
+/**
+ * cdk_handle_free:
+ * @hd: the handle
+ *
+ * free the main handle.
+ **/
+void
+cdk_handle_free( cdk_ctx_t hd )
+{
+ if( !hd )
+ return;
+ _cdk_result_verify_free( hd->result.verify );
+ _cdk_free_seckey( hd->cache.sk );
+ cdk_free( hd->s2k );
+ cdk_free( hd->dek );
+ cdk_free( hd );
+}
+
diff --git a/libextra/opencdk/main.h b/libextra/opencdk/main.h
new file mode 100644
index 0000000000..416b6c34ef
--- /dev/null
+++ b/libextra/opencdk/main.h
@@ -0,0 +1,160 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * main.h
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CDK_MAIN_H
+#define CDK_MAIN_H
+
+#include <config.h>
+#include <gcrypt.h>
+#include "md.h"
+#include "types.h"
+#include "context.h"
+
+#define MAX_MPI_BITS 8192
+#define MAX_MPI_BYTES (MAX_MPI_BITS/8)
+
+#define IS_UID_SIG(s) (((s)->sig_class & ~3) == 0x10)
+#define IS_UID_REV(s) ((s)->sig_class == 0x30)
+
+#define DEBUG_PKT (_cdk_get_log_level () == (CDK_LOG_DEBUG+1))
+
+#define PK_USAGE_SIGN 1
+#define PK_USAGE_ENCR 2
+
+#define KEY_CAN_ENCRYPT(a) (_cdk_pk_algo_usage((a)) & PK_USAGE_ENCR)
+#define KEY_CAN_SIGN(a) (_cdk_pk_algo_usage((a)) & PK_USAGE_SIGN)
+
+#define wipemem(_ptr,_len) \
+do \
+{ \
+ volatile char *_vptr = (volatile char *)(_ptr); \
+ size_t _vlen = (_len); \
+ while (_vlen) \
+ { \
+ *_vptr = 0; \
+ _vptr++; \
+ _vlen--; \
+ } \
+} while (0)
+
+/*-- armor.c --*/
+const char * _cdk_armor_get_lineend (void);
+
+/*-- main.c --*/
+int _cdk_get_log_level (void);
+void _cdk_log_info (const char * fmt, ...);
+void _cdk_log_debug (const char * fmt, ...);
+char * _cdk_passphrase_get( cdk_ctx_t hd, const char * prompt );
+void _cdk_passphrase_free (char * pw, size_t size);
+int _cdk_is_idea_available (void);
+void _cdk_sec_free( void * ptr, size_t size );
+
+/*-- misc.c --*/
+int _cdk_check_file( const char * file );
+u32 _cdk_timestamp (void);
+int _cdk_strcmp (const char * a, const char * b);
+u32 _cdk_buftou32 (const byte * buf);
+void _cdk_u32tobuf (u32 u, byte * buf);
+const char * _cdk_memistr (const char * buf, size_t buflen, const char * sub);
+void _cdk_vasprintf_free (void * p);
+
+#ifndef HAVE_VASPRINTF
+int _cdk_vasprintf ( char **result, const char *format, va_list args);
+#else
+# define _cdk_vasprintf vasprintf
+#endif
+
+/*-- pubkey.c --*/
+u32 _cdk_pkt_get_keyid( cdk_packet_t pkt, u32 * keyid );
+int _cdk_pkt_get_fingerprint( cdk_packet_t pkt, byte * fpr );
+int _cdk_pk_algo_usage( int algo );
+int _cdk_pk_test_algo( int algo, unsigned int usage );
+int _cdk_sk_get_csum( cdk_pkt_seckey_t sk );
+
+/*-- cipher.c --*/
+int _cdk_cipher_test_algo (int algo);
+
+/*-- new-packet.c --*/
+byte * _cdk_subpkt_get_array (cdk_subpkt_t s, int count, size_t * r_nbytes);
+cdk_error_t _cdk_subpkt_copy (cdk_subpkt_t * r_dst, cdk_subpkt_t src);
+cdk_error_t _cdk_subpkt_hash (cdk_subpkt_t hashed, size_t * r_nbytes,
+ cdk_md_hd_t hd);
+
+/*-- sig-check.c --*/
+int _cdk_sig_check (cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig,
+ cdk_md_hd_t digest, int * r_expired);
+void _cdk_hash_sig_data (cdk_pkt_signature_t sig, cdk_md_hd_t hd);
+void _cdk_hash_userid( cdk_pkt_userid_t uid, int sig_version, cdk_md_hd_t md);
+void _cdk_hash_pubkey (cdk_pkt_pubkey_t pk, cdk_md_hd_t md, int use_fpr);
+int _cdk_pk_check_sig( cdk_keydb_hd_t hd, cdk_kbnode_t knode,cdk_kbnode_t snode );
+
+
+/*-- kbnode.c --*/
+void _cdk_kbnode_add (cdk_kbnode_t root, cdk_kbnode_t node);
+void _cdk_kbnode_clone (cdk_kbnode_t node);
+
+/*-- sesskey.c --*/
+int _cdk_digest_encode_pkcs1( byte ** r_md, size_t * r_mdlen, int pk_algo,
+ const byte * md, int digest_algo, unsigned nbits );
+int _cdk_sk_unprotect_auto( cdk_ctx_t hd, cdk_pkt_seckey_t sk );
+
+/*-- keydb.c --*/
+int _cdk_keydb_get_pk_byusage (cdk_keydb_hd_t hd, const char * name,
+ cdk_pkt_pubkey_t* ret_pk, int usage);
+int _cdk_keydb_get_sk_byusage (cdk_keydb_hd_t hd, const char * name,
+ cdk_pkt_seckey_t* ret_sk, int usage);
+char * _cdk_keydb_get_importres_as_xml( int result[4] );
+
+/*-- sign.c --*/
+int _cdk_sig_create (cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig);
+int _cdk_sig_hash_for (int pubkey_algo, int pkt_version);
+void _cdk_trim_string (char * s, int canon);
+int _cdk_sig_complete (cdk_pkt_signature_t sig, cdk_pkt_seckey_t sk,
+ cdk_md_hd_t hd);
+
+/*-- stream.c --*/
+void * _cdk_stream_get_opaque( cdk_stream_t s, int fid );
+const char * _cdk_stream_get_fname( cdk_stream_t s );
+FILE * _cdk_stream_get_fp( cdk_stream_t s );
+int _cdk_stream_gets( cdk_stream_t s, char * buf, size_t count );
+cdk_error_t _cdk_stream_append( const char * file, cdk_stream_t * ret_s );
+int _cdk_stream_get_errno( cdk_stream_t s );
+int _cdk_stream_set_blockmode( cdk_stream_t s, size_t nbytes );
+int _cdk_stream_get_blockmode( cdk_stream_t s );
+int _cdk_stream_puts( cdk_stream_t s, const char * buf );
+cdk_stream_t _cdk_stream_fpopen (FILE * fp, unsigned write_mode);
+
+/*-- verify.c --*/
+void _cdk_result_verify_free (_cdk_verify_result_t res);
+_cdk_verify_result_t _cdk_result_verify_new (void);
+
+/*-- encrypt.c --*/
+int _cdk_check_args( int overwrite, const char * in, const char * out );
+int _cdk_proc_packets( cdk_ctx_t hd, cdk_stream_t inp, const char * output,
+ cdk_stream_t outstream, cdk_md_hd_t md );
+
+/*-- read-packet.c --*/
+size_t _cdk_pkt_read_len( FILE * inp, int * ret_partial );
+
+/** write-packet.c --*/
+int _cdk_pkt_write_fp( FILE * out, cdk_packet_t pkt );
+
+#endif /* CDK_MAIN_H */
diff --git a/libextra/opencdk/md.c b/libextra/opencdk/md.c
new file mode 100644
index 0000000000..189c5b3ce8
--- /dev/null
+++ b/libextra/opencdk/md.c
@@ -0,0 +1,184 @@
+/* md.c
+ * Copyright (C) 2002 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include "opencdk.h"
+#include "main.h"
+#include "md.h"
+
+struct cdk_md_hd_s {
+ gcry_md_hd_t hd;
+ int algo;
+};
+
+inline
+static int cdk_md_to_gcry( int algo)
+{
+ switch(algo) {
+ case CDK_MD_MD5:
+ return GCRY_MD_MD5;
+ case CDK_MD_SHA1:
+ return GCRY_MD_SHA1;
+ case CDK_MD_RMD160:
+ return GCRY_MD_RMD160;
+ default:
+ return -1;
+ }
+
+}
+
+/* flags will be passed to gcrypt as is.
+ */
+cdk_md_hd_t
+cdk_md_open( int algo, unsigned int flags )
+{
+ cdk_md_hd_t hd;
+ gcry_error_t err;
+
+ hd = cdk_calloc( 1, sizeof * hd );
+ if( !hd )
+ return NULL;
+ hd->algo = algo;
+
+ err = gcry_md_open( &hd->hd, cdk_md_to_gcry( algo), flags);
+
+ if (err) {
+ cdk_free( hd );
+ return NULL;
+ }
+
+ return hd;
+}
+
+
+void
+cdk_md_close( cdk_md_hd_t hd )
+{
+ if( hd ) {
+ gcry_md_close( hd->hd);
+ cdk_free( hd );
+ }
+}
+
+int
+cdk_md_test_algo( int algo )
+{
+ return gcry_md_test_algo( cdk_md_to_gcry(algo));
+}
+
+
+void
+cdk_md_write( cdk_md_hd_t hd, const void * buf, size_t buflen )
+{
+ if( hd ) {
+ gcry_md_write( hd->hd, (byte *)buf, buflen );
+ }
+}
+
+
+void
+cdk_md_putc( cdk_md_hd_t hd, int c )
+{
+ if (hd) {
+ gcry_md_putc( hd->hd, c);
+ }
+}
+
+
+int
+cdk_md_final( cdk_md_hd_t hd )
+{
+ if( hd ) {
+ return gcry_md_final(hd->hd);
+ }
+ return CDK_Inv_Value;
+}
+
+
+byte *
+cdk_md_read( cdk_md_hd_t hd, int algo )
+{
+int _algo;
+
+ if (algo==0) _algo = 0;
+ else _algo = cdk_md_to_gcry(algo);
+
+ if (hd) {
+ return gcry_md_read( hd->hd, _algo);
+ }
+ return NULL;
+}
+
+
+cdk_md_hd_t
+cdk_md_copy( cdk_md_hd_t hd )
+{
+ cdk_md_hd_t new;
+ gcry_error_t err;
+
+ new = cdk_calloc( 1, sizeof * hd );
+ if( !new )
+ return NULL;
+
+ err = gcry_md_copy( &new->hd, hd->hd);
+
+ if( err) {
+ cdk_free( new);
+ return NULL;
+ }
+
+ new->algo = hd->algo;
+
+ return new;
+}
+
+
+int
+cdk_md_get_algo_dlen( int algo )
+{
+ return gcry_md_get_algo_dlen( cdk_md_to_gcry( algo ));
+}
+
+
+int
+cdk_md_get_asnoid( int algo, byte * buf, size_t *r_asnlen )
+{
+ return gcry_md_get_asnoid( cdk_md_to_gcry(algo), buf, r_asnlen);
+}
+
+
+int
+cdk_md_reset( cdk_md_hd_t hd )
+{
+ if( hd ) {
+ gcry_md_reset( hd->hd );
+ return 0;
+ }
+ return CDK_Inv_Value;
+}
+
+
+int
+cdk_md_get_algo( cdk_md_hd_t hd )
+{
+ if( hd )
+ return hd->algo;
+ return 0;
+}
diff --git a/libextra/opencdk/md.h b/libextra/opencdk/md.h
new file mode 100644
index 0000000000..a57f57e2fe
--- /dev/null
+++ b/libextra/opencdk/md.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * md.h
+ * Copyright (C) 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CDK_MD_H
+#define CDK_MD_H
+
+cdk_md_hd_t cdk_md_open( int algo, unsigned int flags );
+cdk_md_hd_t cdk_md_copy( cdk_md_hd_t md );
+void cdk_md_write( cdk_md_hd_t md, const void *buf, size_t n );
+void cdk_md_close( cdk_md_hd_t md );
+void cdk_md_putc( cdk_md_hd_t md, int c );
+int cdk_md_test_algo( int algo );
+unsigned char * cdk_md_read( cdk_md_hd_t md, int algo );
+int cdk_md_get_algo_dlen( int algo );
+int cdk_md_final( cdk_md_hd_t md );
+int cdk_md_get_algo( cdk_md_hd_t md );
+int cdk_md_get_asnoid( int algo, unsigned char *buf, size_t *n );
+int cdk_md_reset( cdk_md_hd_t md);
+
+#endif /*CDK_MD_H*/
+
+
diff --git a/libextra/opencdk/misc.c b/libextra/opencdk/misc.c
new file mode 100644
index 0000000000..9cd97e76b1
--- /dev/null
+++ b/libextra/opencdk/misc.c
@@ -0,0 +1,570 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * misc.c
+ * Copyright (C) 2002, 2003 Timo Schulz
+ * Copyright (C) 1998-2002 Free Software Foundation, Inc.
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <malloc.h>
+
+#include "opencdk.h"
+#include "main.h"
+
+
+/* return 0 if the file exists. otherwise 1 */
+int
+_cdk_check_file( const char * file )
+{
+ FILE * fp;
+ int check;
+
+ if( !file )
+ return 1;
+ fp = fopen( file, "r" );
+ check = fp? 0 : 1;
+ if( fp )
+ fclose( fp );
+ return check;
+}
+
+
+u32
+_cdk_timestamp (void)
+{
+ return (u32)time (NULL);
+}
+
+
+u32
+_cdk_buftou32 (const byte * buf)
+{
+ u32 u = 0;
+
+ if (buf) {
+ u = buf[0] << 24;
+ u |= buf[1] << 16;
+ u |= buf[2] << 8;
+ u |= buf[3];
+ }
+ return u;
+}
+
+
+void
+_cdk_u32tobuf (u32 u, byte * buf)
+{
+ if (buf) {
+ buf[0] = u >> 24;
+ buf[1] = u >> 16;
+ buf[2] = u >> 8;
+ buf[3] = u ;
+ }
+}
+
+
+int
+_cdk_strcmp (const char * a, const char * b)
+{
+ int alen, blen;
+
+ alen = strlen (a);
+ blen = strlen (b);
+ if (alen != blen)
+ return alen > blen? 1 : -1;
+ return strcmp (a, b);
+}
+
+
+static const char *
+parse_version_number (const char *s, int *number)
+{
+ int val = 0;
+
+ if (*s == '0' && isdigit(s[1]))
+ return NULL;
+ /* leading zeros are not allowed */
+ for (; isdigit(*s); s++) {
+ val *= 10;
+ val += *s - '0';
+ }
+ *number = val;
+ return val < 0? NULL : s;
+}
+
+
+static const char *
+parse_version_string (const char * s, int * major, int * minor, int * micro)
+{
+ s = parse_version_number (s, major);
+ if (!s || *s != '.')
+ return NULL;
+ s++;
+ s = parse_version_number (s, minor);
+ if (!s || *s != '.')
+ return NULL;
+ s++;
+ s = parse_version_number(s, micro);
+ if (!s)
+ return NULL;
+ return s; /* patchlevel */
+}
+
+
+/**
+ * cdk_check_version - Version control handling.
+ * @req_version: The requested version
+ *
+ * Check that the the version of the library is at minimum the requested
+ * one and return the version string; return NULL if the condition is
+ * not satisfied. If a NULL is passed to this function, no check is done,
+ *but the version string is simply returned.
+ **/
+const char *
+cdk_check_version (const char *req_version)
+{
+ const char *ver = VERSION;
+ int my_major, my_minor, my_micro;
+ int rq_major, rq_minor, rq_micro;
+ const char *my_plvl, *rq_plvl;
+
+ if (!req_version)
+ return ver;
+ my_plvl = parse_version_string (ver, &my_major, &my_minor, &my_micro);
+ if (!my_plvl)
+ return NULL;
+ /* very strange our own version is bogus */
+ rq_plvl = parse_version_string (req_version, &rq_major, &rq_minor,
+ &rq_micro);
+ if (!rq_plvl)
+ return NULL; /* req version string is invalid */
+ if (my_major > rq_major
+ || (my_major == rq_major && my_minor > rq_minor)
+ || (my_major == rq_major && my_minor == rq_minor
+ && my_micro > rq_micro)
+ || (my_major == rq_major && my_minor == rq_minor
+ && my_micro == rq_micro
+ && strcmp (my_plvl, rq_plvl) >= 0)) {
+ return ver;
+ }
+ return NULL;
+}
+
+
+void
+cdk_strlist_free (cdk_strlist_t sl)
+{
+ cdk_strlist_t sl2;
+
+ for(; sl; sl = sl2 ) {
+ sl2 = sl->next;
+ cdk_free (sl);
+ }
+}
+
+
+cdk_strlist_t
+cdk_strlist_add (cdk_strlist_t *list, const char *string)
+{
+ cdk_strlist_t sl;
+
+ if (!string)
+ return NULL;
+
+ sl = cdk_calloc (1, sizeof *sl + strlen (string) + 1);
+ if (!sl)
+ return NULL;
+ strcpy (sl->d, string);
+ sl->next = *list;
+ *list = sl;
+ return sl;
+}
+
+
+const char *
+cdk_strlist_walk (cdk_strlist_t root, cdk_strlist_t * context)
+{
+ cdk_strlist_t n;
+
+ if( ! *context ) {
+ *context = root;
+ n = root;
+ }
+ else {
+ n = (*context)->next;
+ *context = n;
+ }
+
+ return n? n->d : NULL;
+}
+
+
+const char *
+_cdk_memistr (const char *buf, size_t buflen, const char *sub)
+{
+ const byte *t, *s;
+ size_t n;
+
+ for (t = buf, n = buflen, s = sub ; n ; t++, n--) {
+ if (toupper (*t) == toupper (*s)) {
+ for (buf = t++, buflen = n--, s++;
+ n && toupper (*t) == toupper (*s); t++, s++, n--)
+ ;
+ if (!*s)
+ return buf;
+ t = buf;
+ n = buflen;
+ s = sub ;
+ }
+ }
+
+ return NULL;
+}
+
+
+char *
+cdk_utf8_encode (const char * string)
+{
+ const byte * s;
+ char * buffer;
+ byte * p;
+ size_t length = 0;
+
+ for (s = string; *s; s++) {
+ length++;
+ if (*s & 0x80)
+ length++;
+ }
+
+ buffer = cdk_calloc (1, length + 1);
+ for (p = buffer, s = string; *s; s++) {
+ if (*s & 0x80) {
+ *p++ = 0xc0 | ((*s >> 6) & 3);
+ *p++ = 0x80 | (*s & 0x3f);
+ }
+ else
+ *p++ = *s;
+ }
+ *p = 0;
+ return buffer;
+}
+
+
+char *
+cdk_utf8_decode (const char * string, size_t length, int delim)
+{
+ int nleft;
+ int i;
+ byte encbuf[8];
+ int encidx;
+ const byte *s;
+ size_t n;
+ byte *buffer = NULL, *p = NULL;
+ unsigned long val = 0;
+ size_t slen;
+ int resync = 0;
+
+ /* 1. pass (p==NULL): count the extended utf-8 characters */
+ /* 2. pass (p!=NULL): create string */
+ for (;;)
+ {
+ for (slen = length, nleft = encidx = 0, n = 0, s = string; slen;
+ s++, slen--)
+ {
+ if (resync)
+ {
+ if (!(*s < 128 || (*s >= 0xc0 && *s <= 0xfd)))
+ {
+ /* still invalid */
+ if (p)
+ {
+ sprintf (p, "\\x%02x", *s);
+ p += 4;
+ }
+ n += 4;
+ continue;
+ }
+ resync = 0;
+ }
+ if (!nleft)
+ {
+ if (!(*s & 0x80))
+ { /* plain ascii */
+ if (*s < 0x20 || *s == 0x7f || *s == delim ||
+ (delim && *s == '\\'))
+ {
+ n++;
+ if (p)
+ *p++ = '\\';
+ switch (*s)
+ {
+ case '\n':
+ n++;
+ if (p)
+ *p++ = 'n';
+ break;
+ case '\r':
+ n++;
+ if (p)
+ *p++ = 'r';
+ break;
+ case '\f':
+ n++;
+ if (p)
+ *p++ = 'f';
+ break;
+ case '\v':
+ n++;
+ if (p)
+ *p++ = 'v';
+ break;
+ case '\b':
+ n++;
+ if (p)
+ *p++ = 'b';
+ break;
+ case 0:
+ n++;
+ if (p)
+ *p++ = '0';
+ break;
+ default:
+ n += 3;
+ if (p)
+ {
+ sprintf (p, "x%02x", *s);
+ p += 3;
+ }
+ break;
+ }
+ }
+ else
+ {
+ if (p)
+ *p++ = *s;
+ n++;
+ }
+ }
+ else if ((*s & 0xe0) == 0xc0)
+ { /* 110x xxxx */
+ val = *s & 0x1f;
+ nleft = 1;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else if ((*s & 0xf0) == 0xe0)
+ { /* 1110 xxxx */
+ val = *s & 0x0f;
+ nleft = 2;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else if ((*s & 0xf8) == 0xf0)
+ { /* 1111 0xxx */
+ val = *s & 0x07;
+ nleft = 3;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else if ((*s & 0xfc) == 0xf8)
+ { /* 1111 10xx */
+ val = *s & 0x03;
+ nleft = 4;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else if ((*s & 0xfe) == 0xfc)
+ { /* 1111 110x */
+ val = *s & 0x01;
+ nleft = 5;
+ encidx = 0;
+ encbuf[encidx++] = *s;
+ }
+ else
+ { /* invalid encoding: print as \xnn */
+ if (p)
+ {
+ sprintf (p, "\\x%02x", *s);
+ p += 4;
+ }
+ n += 4;
+ resync = 1;
+ }
+ }
+ else if (*s < 0x80 || *s >= 0xc0)
+ { /* invalid */
+ if (p)
+ {
+ for (i = 0; i < encidx; i++)
+ {
+ sprintf (p, "\\x%02x", encbuf[i]);
+ p += 4;
+ }
+ sprintf (p, "\\x%02x", *s);
+ p += 4;
+ }
+ n += 4 + 4 * encidx;
+ nleft = 0;
+ encidx = 0;
+ resync = 1;
+ }
+ else
+ {
+ encbuf[encidx++] = *s;
+ val <<= 6;
+ val |= *s & 0x3f;
+ if (!--nleft)
+ { /* ready native set */
+ if (val >= 0x80 && val < 256)
+ {
+ n++; /* we can simply print this character */
+ if (p)
+ *p++ = val;
+ }
+ else
+ { /* we do not have a translation: print utf8 */
+ if (p)
+ {
+ for (i = 0; i < encidx; i++)
+ {
+ sprintf (p, "\\x%02x", encbuf[i]);
+ p += 4;
+ }
+ }
+ n += encidx * 4;
+ encidx = 0;
+ }
+ }
+ }
+
+ }
+ if (!buffer) /* allocate the buffer after the first pass */
+ buffer = p = cdk_malloc (n + 1);
+ else
+ {
+ *p = 0; /* make a string */
+ return buffer;
+ }
+ }
+}
+
+
+#ifndef HAVE_VASPRINTF
+/*
+ * Like vsprintf but provides a pointer to malloc'd storage, which
+ * must be freed by the caller (gcry_free). Taken from libiberty as
+ * found in gcc-2.95.2 and a little bit modernized.
+ */
+int
+_cdk_vasprintf ( char **result, const char *format, va_list args)
+{
+ const char *p = format;
+ /* Add one to make sure that it is never zero, which might cause malloc
+ to return NULL. */
+ int total_width = strlen (format) + 1;
+ va_list ap;
+
+ /* this is not really portable but works under Windows */
+ memcpy ( &ap, &args, sizeof (va_list));
+
+ while (*p != '\0') {
+ if (*p++ == '%') {
+ while (strchr ("-+ #0", *p))
+ ++p;
+ if (*p == '*') {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ }
+ else {
+ char *endp;
+ total_width += strtoul (p, &endp, 10);
+ p = endp;
+ }
+ if (*p == '.') {
+ ++p;
+ if (*p == '*') {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ }
+ else {
+ char *endp;
+ total_width += strtoul (p, &endp, 10);
+ p = endp;
+ }
+ }
+ while (strchr ("hlL", *p))
+ ++p;
+ /* Should be big enough for any format specifier except %s
+ and floats. */
+ total_width += 30;
+ switch (*p) {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'c':
+ (void) va_arg (ap, int);
+ break;
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ (void) va_arg (ap, double);
+ /* Since an ieee double can have an exponent of 307, we'll
+ make the buffer wide enough to cover the gross case. */
+ total_width += 307;
+
+ case 's':
+ total_width += strlen (va_arg (ap, char *));
+ break;
+ case 'p':
+ case 'n':
+ (void) va_arg (ap, char *);
+ break;
+ }
+ }
+ }
+ *result = gcry_malloc (total_width);
+ if (*result != NULL)
+ return vsprintf (*result, format, args);
+ else
+ return 0;
+}
+
+void
+_cdk_vasprintf_free (void * p)
+{
+ cdk_free (p);
+}
+#else /*!__MINGW32__*/
+void
+_cdk_vasprintf_free (void * p)
+{
+ if (p)
+ free (p);
+}
+#endif
diff --git a/libextra/opencdk/new-packet.c b/libextra/opencdk/new-packet.c
new file mode 100644
index 0000000000..6251fbd2f6
--- /dev/null
+++ b/libextra/opencdk/new-packet.c
@@ -0,0 +1,775 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * new-packet.c - General packet handling (freeing, copying, ...)
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+
+
+void
+_cdk_free_mpibuf( size_t n, gcry_mpi_t * array )
+{
+ while( n-- ) {
+ gcry_mpi_release( array[n] );
+ array[n] = NULL;
+ }
+}
+
+
+cdk_error_t
+cdk_pkt_new( cdk_packet_t* r_pkt )
+{
+ cdk_packet_t pkt;
+
+ if( !r_pkt )
+ return CDK_Inv_Value;
+ pkt = cdk_calloc( 1, sizeof *pkt );
+ if( !pkt )
+ return CDK_Out_Of_Core;
+ *r_pkt = pkt;
+ return 0;
+}
+
+
+void
+cdk_pkt_init( cdk_packet_t pkt )
+{
+ if( pkt )
+ memset( pkt, 0, sizeof * pkt );
+}
+
+
+static void
+free_symkey_enc (cdk_pkt_symkey_enc_t enc)
+{
+ if (enc) {
+ cdk_free (enc->s2k);
+ cdk_free (enc);
+ }
+}
+
+
+static void
+free_pubkey_enc (cdk_pkt_pubkey_enc_t enc)
+{
+ int nenc;
+ if (enc) {
+ nenc = cdk_pk_get_nenc (enc->pubkey_algo);
+ while (enc->mpi && nenc--) {
+ cdk_free (enc->mpi[nenc]);
+ enc->mpi[nenc] = NULL;
+ }
+ cdk_free (enc);
+ }
+}
+
+
+static void
+free_literal (cdk_pkt_literal_t pt)
+{
+ if (pt)
+ cdk_free (pt);
+}
+
+
+void
+_cdk_free_userid (cdk_pkt_userid_t uid)
+{
+ if (uid) {
+ cdk_free (uid->prefs);
+ uid->prefs = NULL;
+ cdk_free (uid->attrib_img);
+ uid->attrib_img = NULL;
+ cdk_free (uid);
+ }
+}
+
+
+void
+_cdk_free_signature (cdk_pkt_signature_t sig)
+{
+ int nsig;
+ cdk_desig_revoker_t r;
+
+ if (sig) {
+ nsig = cdk_pk_get_nsig (sig->pubkey_algo);
+ while (sig->mpi && nsig--) {
+ cdk_free (sig->mpi[nsig]);
+ sig->mpi[nsig] = NULL;
+ }
+ cdk_subpkt_free (sig->hashed);
+ sig->hashed = NULL;
+ cdk_subpkt_free (sig->unhashed);
+ sig->unhashed = NULL;
+ while( sig->revkeys ) {
+ r = sig->revkeys->next;
+ cdk_free( sig->revkeys );
+ sig->revkeys = r;
+ }
+ cdk_free (sig);
+ }
+}
+
+
+void
+_cdk_free_pubkey (cdk_pkt_pubkey_t pk)
+{
+ int npkey;
+ if (pk) {
+ npkey = cdk_pk_get_npkey (pk->pubkey_algo);
+ _cdk_free_userid (pk->uid);
+ pk->uid = NULL;
+ cdk_free (pk->prefs);
+ pk->prefs = NULL;
+ while (pk->mpi && npkey--) {
+ cdk_free (pk->mpi[npkey]);
+ pk->mpi[npkey] = NULL;
+ }
+ cdk_free (pk);
+ }
+}
+
+
+void
+_cdk_free_seckey (cdk_pkt_seckey_t sk)
+{
+ int nskey;
+
+ if (sk) {
+ nskey = cdk_pk_get_nskey (sk->pubkey_algo);
+ while (nskey--) {
+ if (sk->mpi[nskey]) {
+ wipemem (sk->mpi[nskey], sk->mpi[nskey]->bytes);
+ cdk_free (sk->mpi[nskey]);
+ sk->mpi[nskey] = NULL;
+ }
+ }
+ cdk_free (sk->encdata);
+ sk->encdata = NULL;
+ _cdk_free_pubkey (sk->pk);
+ sk->pk = NULL;
+ cdk_free (sk->protect.s2k);
+ sk->protect.s2k = NULL;
+ cdk_free (sk);
+ }
+}
+
+
+static void
+free_encrypted (cdk_pkt_encrypted_t enc)
+{
+ if (enc) {
+ cdk_stream_close (enc->buf);
+ enc->buf = NULL;
+ cdk_free (enc);
+ }
+}
+
+
+void
+cdk_pkt_free (cdk_packet_t pkt)
+{
+ if (!pkt)
+ return;
+
+ switch (pkt->pkttype) {
+ case CDK_PKT_ATTRIBUTE :
+ case CDK_PKT_USER_ID : _cdk_free_userid (pkt->pkt.user_id); break;
+ case CDK_PKT_PUBLIC_KEY :
+ case CDK_PKT_PUBLIC_SUBKEY: _cdk_free_pubkey (pkt->pkt.public_key); break;
+ case CDK_PKT_SECRET_KEY :
+ case CDK_PKT_SECRET_SUBKEY: _cdk_free_seckey (pkt->pkt.secret_key); break;
+ case CDK_PKT_SIGNATURE : _cdk_free_signature (pkt->pkt.signature);break;
+ case CDK_PKT_PUBKEY_ENC : free_pubkey_enc (pkt->pkt.pubkey_enc); break;
+ case CDK_PKT_SYMKEY_ENC : free_symkey_enc (pkt->pkt.symkey_enc); break;
+ case CDK_PKT_MDC : cdk_free (pkt->pkt.mdc); break;
+ case CDK_PKT_ENCRYPTED :
+ case CDK_PKT_ENCRYPTED_MDC: free_encrypted (pkt->pkt.encrypted); break;
+ case CDK_PKT_ONEPASS_SIG : cdk_free (pkt->pkt.onepass_sig); break;
+ case CDK_PKT_LITERAL : free_literal (pkt->pkt.literal); break;
+ case CDK_PKT_COMPRESSED : cdk_free (pkt->pkt.compressed); break;
+ default : break;
+ }
+}
+
+
+void
+cdk_pkt_release (cdk_packet_t pkt)
+{
+ if (pkt) {
+ cdk_pkt_free (pkt);
+ cdk_free (pkt);
+ }
+}
+
+
+cdk_error_t
+cdk_pkt_alloc( cdk_packet_t* r_pkt, int pkttype )
+{
+ cdk_packet_t pkt;
+ int rc = 0;
+
+ if( !r_pkt )
+ return CDK_Inv_Value;
+
+ rc = cdk_pkt_new( &pkt );
+ if( rc )
+ return rc;
+
+ switch (pkttype) {
+ case CDK_PKT_USER_ID:
+ pkt->pkt.user_id = cdk_calloc (1, sizeof pkt->pkt.user_id);
+ if (!pkt->pkt.user_id)
+ return CDK_Out_Of_Core;
+ break;
+
+ case CDK_PKT_PUBLIC_KEY:
+ case CDK_PKT_PUBLIC_SUBKEY:
+ pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key);
+ if (!pkt->pkt.public_key)
+ return CDK_Out_Of_Core;
+ break;
+
+ case CDK_PKT_SECRET_KEY:
+ case CDK_PKT_SECRET_SUBKEY:
+ pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key);
+ pkt->pkt.secret_key->pk =
+ cdk_calloc (1, sizeof *pkt->pkt.secret_key->pk);
+ if (!pkt->pkt.secret_key || !pkt->pkt.secret_key->pk)
+ return CDK_Out_Of_Core;
+ break;
+
+ case CDK_PKT_SIGNATURE:
+ pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature);
+ if (!pkt->pkt.signature)
+ return CDK_Out_Of_Core;
+ break;
+
+ case CDK_PKT_PUBKEY_ENC:
+ pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc);
+ if (!pkt->pkt.pubkey_enc)
+ return CDK_Out_Of_Core;
+ break;
+
+ case CDK_PKT_MDC:
+ pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc);
+ if (!pkt->pkt.mdc)
+ return CDK_Out_Of_Core;
+ break;
+
+ case CDK_PKT_ENCRYPTED_MDC:
+ case CDK_PKT_ENCRYPTED:
+ pkt->pkt.symkey_enc = cdk_calloc (1, sizeof *pkt->pkt.symkey_enc);
+ if (!pkt->pkt.symkey_enc)
+ return CDK_Out_Of_Core;
+ break;
+
+ case CDK_PKT_LITERAL:
+ pkt->pkt.literal = cdk_calloc (1, sizeof *pkt->pkt.literal);
+ if (!pkt->pkt.literal)
+ return CDK_Out_Of_Core;
+ break;
+ }
+ pkt->pkttype = pkttype;
+ *r_pkt = pkt;
+ return 0;
+}
+
+
+byte *
+cdk_userid_pref_get_array( cdk_pkt_userid_t id, int type, size_t *ret_len )
+{
+ cdk_prefitem_t prefs;
+ byte * p;
+ int i = 0, j = 0;
+
+ if( !id || !id->prefs || !ret_len )
+ return NULL;
+
+ prefs = id->prefs;
+ while( prefs[i].type ) {
+ if( prefs[i].type == type )
+ j++;
+ i++;
+ }
+ if( !j )
+ return 0;
+ p = cdk_calloc( 1, j + 1 );
+ *ret_len = j;
+ i = j = 0;
+ while( prefs[i].type ) {
+ if( prefs[i].type == type )
+ p[j++] = prefs[i].value;
+ i++;
+ }
+ p[j] = 0;
+ return p;
+}
+
+
+cdk_prefitem_t
+_cdk_copy_prefs( const cdk_prefitem_t prefs )
+{
+ size_t n = 0;
+ struct cdk_prefitem_s *new_prefs;
+
+ if (!prefs)
+ return NULL;
+
+ for (n = 0; prefs[n].type; n++)
+ ;
+ new_prefs = cdk_calloc (1, sizeof *new_prefs * (n + 1));
+ if (!new_prefs)
+ return NULL;
+ for (n = 0; prefs[n].type; n++) {
+ new_prefs[n].type = prefs[n].type;
+ new_prefs[n].value = prefs[n].value;
+ }
+ new_prefs[n].type = CDK_PREFTYPE_NONE;
+ new_prefs[n].value = 0;
+
+ return new_prefs;
+}
+
+
+cdk_error_t
+_cdk_copy_userid (cdk_pkt_userid_t* dst, cdk_pkt_userid_t src)
+{
+ cdk_pkt_userid_t u;
+
+ if (!dst || !src)
+ return CDK_Inv_Value;
+
+ u = cdk_calloc (1, sizeof *u + strlen (src->name) + 1);
+ if (!u)
+ return CDK_Out_Of_Core;
+ memcpy (u, src, sizeof *u);
+ memcpy (u->name, src->name, strlen (src->name));
+ u->prefs = _cdk_copy_prefs (src->prefs);
+ *dst = u;
+
+ return 0;
+}
+
+
+cdk_error_t
+_cdk_copy_pubkey (cdk_pkt_pubkey_t* dst, cdk_pkt_pubkey_t src)
+{
+ cdk_pkt_pubkey_t k;
+ int i;
+
+ if (!dst || !src)
+ return CDK_Inv_Value;
+
+ k = cdk_calloc (1, sizeof *k);
+ if (!k)
+ return CDK_Out_Of_Core;
+ memcpy (k, src, sizeof *k);
+ if (src->uid)
+ _cdk_copy_userid (&k->uid, src->uid);
+ if (src->prefs)
+ k->prefs = _cdk_copy_prefs (src->prefs);
+ for (i = 0; i < cdk_pk_get_npkey (src->pubkey_algo); i++) {
+ k->mpi[i] = cdk_calloc (1, sizeof **k->mpi + src->mpi[i]->bytes + 2);
+ if (!k->mpi[i])
+ return CDK_Out_Of_Core;
+ k->mpi[i]->bits = src->mpi[i]->bits;
+ k->mpi[i]->bytes = src->mpi[i]->bytes;
+ /* copy 2 extra bytes (prefix) */
+ memcpy (k->mpi[i]->data, src->mpi[i]->data, src->mpi[i]->bytes + 2);
+ }
+ *dst = k;
+
+ return 0;
+}
+
+
+cdk_error_t
+_cdk_copy_seckey (cdk_pkt_seckey_t* dst, cdk_pkt_seckey_t src)
+{
+ cdk_pkt_seckey_t k;
+ cdk_mpi_t a;
+ cdk_s2k_t s2k;
+ int i;
+
+ if (!dst || !src)
+ return CDK_Inv_Value;
+
+ k = cdk_calloc (1, sizeof *k);
+ if (!k)
+ return CDK_Out_Of_Core;
+ memcpy (k, src, sizeof *k);
+ _cdk_copy_pubkey (&k->pk, src->pk);
+
+ if (src->encdata) {
+ k->encdata = cdk_calloc (1, src->enclen + 1);
+ if (!k->encdata)
+ return CDK_Out_Of_Core;
+ memcpy (k->encdata, src->encdata, src->enclen);
+ }
+
+ s2k = k->protect.s2k = cdk_calloc (1, sizeof *k->protect.s2k);
+ if (!k->protect.s2k)
+ return CDK_Out_Of_Core;
+ s2k->mode = src->protect.s2k->mode;
+ s2k->hash_algo = src->protect.s2k->hash_algo;
+ s2k->count = src->protect.s2k->count;
+ memcpy (s2k->salt, src->protect.s2k->salt, 8);
+
+ for (i = 0; i < cdk_pk_get_nskey (src->pubkey_algo); i++) {
+ a = k->mpi[i] = cdk_calloc (1, sizeof **k->mpi + src->mpi[i]->bytes + 2);
+ if (!k->mpi[i])
+ return CDK_Out_Of_Core;
+ a->bits = src->mpi[i]->bits;
+ a->bytes = src->mpi[i]->bytes;
+ /* copy 2 extra bytes (prefix) */
+ memcpy (a->data, src->mpi[i]->data, src->mpi[i]->bytes + 2);
+ }
+ *dst = k;
+
+ return 0;
+}
+
+
+cdk_error_t
+_cdk_copy_pk_to_sk( cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk )
+{
+ if( !pk || !sk )
+ return CDK_Inv_Value;
+
+ sk->version = pk->version;
+ sk->expiredate = pk->expiredate;
+ sk->pubkey_algo = pk->pubkey_algo;
+ sk->has_expired = pk->has_expired;
+ sk->is_revoked = pk->is_revoked;
+ sk->main_keyid[0] = pk->main_keyid[0];
+ sk->main_keyid[1] = pk->main_keyid[1];
+ sk->keyid[0] = pk->keyid[0];
+ sk->keyid[1] = pk->keyid[1];
+
+ return 0;
+}
+
+
+cdk_error_t
+_cdk_copy_signature (cdk_pkt_signature_t* dst, cdk_pkt_signature_t src)
+{
+ cdk_pkt_signature_t s = NULL;
+ struct cdk_subpkt_s *res = NULL;
+
+ if (!dst || !src)
+ return CDK_Inv_Value;
+
+ s = cdk_calloc (1, sizeof *s);
+ if (!s)
+ return CDK_Out_Of_Core;
+ memcpy (s, src, sizeof *src);
+
+ _cdk_subpkt_copy (&res, src->hashed);
+ _cdk_subpkt_copy (&s->hashed, res);
+ cdk_subpkt_free (res);
+ res = NULL;
+ _cdk_subpkt_copy (&res, src->unhashed);
+ _cdk_subpkt_copy (&s->unhashed, res);
+ cdk_subpkt_free (res);
+ res = NULL;
+ *dst = s;
+
+ return 0;
+}
+
+
+cdk_error_t
+_cdk_pubkey_compare (cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b)
+{
+ int na, nb, i;
+
+ if (a->timestamp != b->timestamp || a->pubkey_algo != b->pubkey_algo)
+ return -1;
+ if (a->version < 4 && a->expiredate != b->expiredate)
+ return -1;
+ na = cdk_pk_get_npkey (a->pubkey_algo);
+ nb = cdk_pk_get_npkey (b->pubkey_algo);
+ if (na != nb)
+ return -1;
+
+ for (i = 0; i < na; i++) {
+ if (memcmp (a->mpi[i]->data, b->mpi[i]->data, a->mpi[i]->bytes))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * cdk_subpkt_free:
+ * @ctx: the sub packet node to free
+ *
+ * Release the context.
+ **/
+void
+cdk_subpkt_free( cdk_subpkt_t ctx )
+{
+ cdk_subpkt_t s;
+
+ while( ctx ) {
+ s = ctx->next;
+ cdk_free( ctx );
+ ctx = s;
+ }
+}
+
+
+/**
+ * cdk_subpkt_find:
+ * @ctx: the sub packet node
+ * @type: the packet type to find
+ *
+ * Find the given packet type in the node. If no packet with this
+ * type was found, return null otherwise pointer to the node.
+ **/
+cdk_subpkt_t
+cdk_subpkt_find( cdk_subpkt_t ctx, int type )
+{
+ cdk_subpkt_t s;
+
+ /* xxx: add some code for the case there are more than one sub packet
+ with the same type. */
+ for( s = ctx; s; s = s->next ) {
+ if( s->type == type )
+ return s;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * cdk_subpkt_new:
+ * @size: the size of the new context
+ *
+ * Create a new sub packet node with the size of @size.
+ **/
+cdk_subpkt_t
+cdk_subpkt_new( size_t size )
+{
+ cdk_subpkt_t s;
+
+ if( !size )
+ return NULL;
+ s = cdk_calloc( 1, sizeof *s + size + 1 );
+ if( !s )
+ return NULL;
+ return s;
+}
+
+
+/**
+ * cdk_subpkt_get_data:
+ * @ctx: the sub packet node
+ * @r_type: pointer store the packet type
+ * @r_nbytes: pointer to store the packet size
+ *
+ * Extract the data from the given sub packet. The type is returned
+ * in @r_type and the size in @r_nbytes.
+ **/
+const byte *
+cdk_subpkt_get_data( cdk_subpkt_t ctx, int * r_type, size_t * r_nbytes )
+{
+ if( !ctx || !r_nbytes )
+ return NULL;
+ if( r_type )
+ *r_type = ctx->type;
+ *r_nbytes = ctx->size;
+ return ctx->d;
+}
+
+
+/**
+ * cdk_subpkt_add:
+ * @root: the root node
+ * @node: the node to add
+ *
+ * Add the node in @node to the root node @root.
+ **/
+cdk_error_t
+cdk_subpkt_add( cdk_subpkt_t root, cdk_subpkt_t node )
+{
+ cdk_subpkt_t n1;
+
+ if( !root )
+ return CDK_Inv_Value;
+ for( n1 = root; n1->next; n1 = n1->next )
+ ;
+ n1->next = node;
+ return 0;
+}
+
+
+byte *
+_cdk_subpkt_get_array( cdk_subpkt_t s, int count, size_t * r_nbytes )
+{
+ cdk_subpkt_t list;
+ byte * buf;
+ int n, nbytes;
+
+ if( !s ) {
+ if( r_nbytes )
+ *r_nbytes = 0;
+ return NULL;
+ }
+
+ for( n=0, list = s; list; list = list->next ) {
+ n += list->size + 1;
+ if( list->size < 192 ) n++;
+ else if( list->size < 8384 ) n += 2;
+ else n += 5;
+ }
+ buf = cdk_calloc( 1, n+1 );
+ if( !buf )
+ return NULL;
+
+ n = 0;
+ for( list = s; list; list = list->next ) {
+ nbytes = 1 + list->size; /* type */
+ if( nbytes < 192 )
+ buf[n++] = nbytes;
+ else if( nbytes < 8384 ) {
+ buf[n++] = nbytes / 256 + 192;
+ buf[n++] = nbytes % 256;
+ }
+ else {
+ buf[n++] = 0xFF;
+ buf[n++] = nbytes >> 24;
+ buf[n++] = nbytes >> 16;
+ buf[n++] = nbytes >> 8;
+ buf[n++] = nbytes;
+ }
+ buf[n++] = list->type;
+ memcpy( buf + n, list->d, list->size );
+ n += list->size;
+ }
+ if( count ) {
+ cdk_free( buf );
+ buf = NULL;
+ }
+ if( r_nbytes )
+ *r_nbytes = n;
+ return buf;
+}
+
+
+cdk_error_t
+_cdk_subpkt_copy( cdk_subpkt_t * r_dst, cdk_subpkt_t src )
+{
+ cdk_subpkt_t root, p, node;
+
+ if (!src || !r_dst)
+ return CDK_Inv_Value;
+
+ root = NULL;
+ for (p = src; p; p = p->next) {
+ node = cdk_subpkt_new (p->size);
+ if (node) {
+ memcpy (node->d, p->d, p->size);
+ node->type = p->type;
+ node->size = p->size;
+ }
+ if (!root)
+ root = node;
+ else
+ cdk_subpkt_add (root, node);
+ }
+ *r_dst = root;
+ return 0;
+}
+
+
+cdk_error_t
+_cdk_subpkt_hash( cdk_subpkt_t hashed, size_t * r_nbytes, cdk_md_hd_t hd )
+{
+ byte * p, buf[2];
+ size_t nbytes;
+
+ p = _cdk_subpkt_get_array (hashed, 0, &nbytes);
+ if (!p)
+ return CDK_Out_Of_Core;
+ if (nbytes > 65535)
+ return CDK_Inv_Value;
+ buf[0] = nbytes >> 8;
+ buf[1] = nbytes;
+ cdk_md_write (hd, buf, 2);
+ cdk_md_write (hd, p, nbytes);
+ if (r_nbytes)
+ *r_nbytes = nbytes;
+ return 0;
+}
+
+
+/**
+ * cdk_subpkt_init:
+ * @node: the sub packet node
+ * @type: type of the packet which data should be initialized
+ * @buf: the buffer with the actual data
+ * @buflen: the size of the data
+ *
+ * Set the packet data of the given root and set the type of it.
+ **/
+void
+cdk_subpkt_init( cdk_subpkt_t node, int type, const void *buf, size_t buflen )
+{
+ if( node ) {
+ node->type = type;
+ node->size = buflen;
+ memcpy( node->d, buf, buflen );
+ }
+}
+
+
+const byte*
+cdk_key_desig_revoker_walk( cdk_desig_revoker_t root,
+ cdk_desig_revoker_t * ctx,
+ int *r_class, int *r_algid )
+{
+ cdk_desig_revoker_t n;
+
+ if( !*ctx ) {
+ *ctx = root;
+ n = root;
+ }
+ else {
+ n = (*ctx)->next;
+ *ctx = n;
+ }
+ if( n && r_class && r_algid ) {
+ *r_class = n->class;
+ *r_algid = n->algid;
+ }
+ return n? n->fpr : NULL;
+}
diff --git a/libextra/opencdk/opencdk.h b/libextra/opencdk/opencdk.h
new file mode 100644
index 0000000000..c86bc01def
--- /dev/null
+++ b/libextra/opencdk/opencdk.h
@@ -0,0 +1,844 @@
+/* opencdk.h - Open Crypto Development Kit (OpenCDK)
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef OPENCDK_H
+#define OPENCDK_H
+
+#include <stdarg.h>
+
+#define OPENCDK_VERSION "0.5.4"
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+/* General contexts */
+struct cdk_ctx_s;
+typedef struct cdk_ctx_s *cdk_ctx_t;
+
+struct cdk_strlist_s;
+typedef struct cdk_strlist_s *cdk_strlist_t;
+
+struct cdk_sesskey_s;
+typedef struct cdk_sesskey_s *cdk_sesskey_t;
+
+struct cdk_listkey_s;
+typedef struct cdk_listkey_s *cdk_listkey_t;
+
+struct cdk_mpi_s;
+typedef struct cdk_mpi_s *cdk_mpi_t;
+
+struct cdk_dek_s;
+typedef struct cdk_dek_s *cdk_dek_t;
+
+struct cdk_s2k_s;
+typedef struct cdk_s2k_s *cdk_s2k_t;
+
+struct cdk_stream_s;
+typedef struct cdk_stream_s *cdk_stream_t;
+
+struct cdk_prefitem_s;
+typedef struct cdk_prefitem_s *cdk_prefitem_t;
+
+struct cdk_kbnode_s;
+typedef struct cdk_kbnode_s *cdk_kbnode_t;
+
+struct cdk_keydb_hd_s;
+typedef struct cdk_keydb_hd_s *cdk_keydb_hd_t;
+
+struct cdk_keylist_s;
+typedef struct cdk_keylist_s *cdk_keylist_t;
+
+struct cdk_subpkt_s;
+typedef struct cdk_subpkt_s *cdk_subpkt_t;
+
+struct cdk_keygen_ctx_s;
+typedef struct cdk_keygen_ctx_s *cdk_keygen_ctx_t;
+
+struct cdk_desig_revoker_s;
+typedef struct cdk_desig_revoker_s *cdk_desig_revoker_t;
+
+struct cdk_md_hd_s;
+typedef struct cdk_md_hd_s *cdk_md_hd_t;
+
+struct cdk_cipher_hd_s;
+typedef struct cdk_cipher_hd_s *cdk_cipher_hd_t;
+
+
+typedef enum {
+ CDK_EOF = -1,
+ CDK_Success = 0,
+ CDK_General_Error = 1,
+ CDK_File_Error = 2,
+ CDK_Bad_Sig = 3,
+ CDK_Inv_Packet = 4,
+ CDK_Inv_Algo = 5,
+ CDK_Not_Implemented = 6,
+ CDK_Gcry_Error = 7,
+ CDK_Armor_Error = 8,
+ CDK_Armor_CRC_Error = 9,
+ CDK_MPI_Error = 10,
+ CDK_Inv_Value = 11,
+ CDK_Error_No_Key = 12,
+ CDK_Chksum_Error = 13,
+ CDK_Time_Conflict = 14,
+ CDK_Zlib_Error = 15,
+ CDK_Weak_Key = 16,
+ CDK_Out_Of_Core = 17,
+ CDK_Wrong_Seckey = 18,
+ CDK_Bad_MDC = 19,
+ CDK_Inv_Mode = 20,
+ CDK_Error_No_Keyring = 21,
+ CDK_Wrong_Format = 22,
+ CDK_Inv_Packet_Ver = 23,
+ CDK_Too_Short = 24,
+ CDK_Unusable_Key = 25,
+} cdk_error_t;
+
+
+enum cdk_control_flags {
+ CDK_CTLF_SET = 0,
+ CDK_CTLF_GET = 1,
+ CDK_CTL_DIGEST = 10,
+ CDK_CTL_CIPHER = 11,
+ CDK_CTL_ARMOR = 12,
+ CDK_CTL_COMPRESS = 13,
+ CDK_CTL_COMPAT = 14,
+ CDK_CTL_OVERWRITE = 15,
+ CDK_CTL_S2K = 16,
+ CDK_CTL_KEYCACHE_ON = 17,
+ CDK_CTL_KEYCACHE_FREE = 18,
+ CDK_CTL_FORCE_DIGEST = 19,
+ CDK_CTL_TRUSTMODEL = 20
+};
+
+enum cdk_log_level_t {
+ CDK_LOG_NONE = 0,
+ CDK_LOG_INFO = 1,
+ CDK_LOG_DEBUG = 2
+};
+
+enum cdk_compress_algo_t {
+ CDK_COMPRESS_NONE = 0,
+ CDK_COMPRESS_ZIP = 1,
+ CDK_COMPRESS_ZLIB = 2
+};
+
+enum cdk_pk_algo_t {
+ CDK_PK_RSA = 1,
+ CDK_PK_RSA_E = 2,
+ CDK_PK_RSA_S = 3,
+ CDK_PK_ELG_E = 16,
+ CDK_PK_DSA = 17,
+ CDK_PK_ELG = 20
+};
+
+enum cdk_md_algo_t {
+ CDK_MD_NONE = 0,
+ CDK_MD_MD5 = 1,
+ CDK_MD_SHA1 = 2,
+ CDK_MD_RMD160 = 3,
+ CDK_MD_MD2 = 5,
+ CDK_MD_TIGER = 6, /* will be removed and thus: reserved */
+ CDK_MD_SHA256 = 8,
+};
+
+enum cdk_cipher_algo_t {
+ CDK_CIPHER_NONE = 0,
+ CDK_CIPHER_IDEA = 1,
+ CDK_CIPHER_3DES = 2,
+ CDK_CIPHER_CAST5 = 3,
+ CDK_CIPHER_BLOWFISH = 4,
+ CDK_CIPHER_SAFER_SK128 = 5, /* will be removed and thus: reserved */
+ CDK_CIPHER_DES_SK = 6, /* will be removed and thus: reserved */
+ CDK_CIPHER_AES = 7,
+ CDK_CIPHER_AES192 = 8,
+ CDK_CIPHER_AES256 = 9,
+ CDK_CIPHER_TWOFISH = 10,
+};
+
+enum cdk_s2k_type_t {
+ CDK_S2K_SIMPLE = 0,
+ CDK_S2K_SALTED = 1,
+ CDK_S2K_ITERSALTED = 3
+};
+
+enum cdk_pref_type_t {
+ CDK_PREFTYPE_NONE = 0,
+ CDK_PREFTYPE_SYM = 1,
+ CDK_PREFTYPE_HASH = 2,
+ CDK_PREFTYPE_ZIP = 3
+};
+
+enum cdk_sig_subpacket_t {
+ CDK_SIGSUBPKT_NONE = 0,
+ CDK_SIGSUBPKT_SIG_CREATED = 2,
+ CDK_SIGSUBPKT_SIG_EXPIRE = 3,
+ CDK_SIGSUBPKT_EXPORTABLE = 4,
+ CDK_SIGSUBPKT_TRUST = 5,
+ CDK_SIGSUBPKT_REGEXP = 6,
+ CDK_SIGSUBPKT_REVOCABLE = 7,
+ CDK_SIGSUBPKT_KEY_EXPIRE = 9,
+ CDK_SIGSUBPKT_PREFS_SYM = 11,
+ CDK_SIGSUBPKT_REV_KEY = 12,
+ CDK_SIGSUBPKT_ISSUER = 16,
+ CDK_SIGSUBPKT_NOTATION = 20,
+ CDK_SIGSUBPKT_PREFS_HASH = 21,
+ CDK_SIGSUBPKT_PREFS_ZIP = 22,
+ CDK_SIGSUBPKT_KS_FLAGS = 23,
+ CDK_SIGSUBPKT_PREF_KS = 24,
+ CDK_SIGSUBPKT_PRIMARY_UID = 25,
+ CDK_SIGSUBPKT_POLICY = 26,
+ CDK_SIGSUBPKT_KEY_FLAGS = 27,
+ CDK_SIGSUBPKT_SIGNERS_UID = 28,
+ CDK_SIGSUBPKT_REVOC_REASON = 29,
+ CDK_SIGSUBPKT_FEATURES = 30
+};
+
+
+enum cdk_revoc_code_t {
+ CDK_REVCOD_NOREASON = 0x00,
+ CDK_REVCOD_SUPERCEDED = 0x01,
+ CDK_REVCOD_COMPROMISED= 0x02,
+ CDK_REVCOD_NOLONGUSED = 0x03
+};
+
+enum cdk_armor_type_t {
+ CDK_ARMOR_MESSAGE = 0,
+ CDK_ARMOR_PUBKEY = 1,
+ CDK_ARMOR_SECKEY = 2,
+ CDK_ARMOR_SIGNATURE = 3,
+ CDK_ARMOR_CLEARSIG = 4,
+};
+
+enum cdk_stream_control_t {
+ CDK_STREAMCTL_DISABLE = 2,
+ CDK_STREAMCTL_COMPRESSED = 3,
+};
+
+enum cdk_keydb_flag_t {
+ /* database search modes */
+ CDK_DBSEARCH_EXACT = 1,
+ CDK_DBSEARCH_SUBSTR = 2, /* sub string search */
+ CDK_DBSEARCH_SHORT_KEYID = 3, /* 32-bit keyid search */
+ CDK_DBSEARCH_KEYID = 4, /* 64-bit keyid search */
+ CDK_DBSEARCH_FPR = 5, /* 160-bit fingerprint search */
+ CDK_DBSEARCH_NEXT = 6, /* enumerate all keys */
+ CDK_DBSEARCH_AUTO = 7, /* try automagically class search */
+ /* database types */
+ CDK_DBTYPE_PK_KEYRING = 100,
+ CDK_DBTYPE_SK_KEYRING = 101,
+ CDK_DBTYPE_DATA = 102
+};
+
+
+enum cdk_crypto_mode_t {
+ CDK_CRYPTYPE_NONE = 0,
+ CDK_CRYPTYPE_ENCRYPT = 1,
+ CDK_CRYPTYPE_DECRYPT = 2,
+ CDK_CRYPTYPE_SIGN = 3,
+ CDK_CRYPTYPE_VERIFY = 4,
+ CDK_CRYPTYPE_EXPORT = 5,
+ CDK_CRYPTYPE_IMPORT = 6
+};
+
+enum cdk_key_flag_t {
+ CDK_KEY_VALID = 0,
+ CDK_KEY_INVALID = 1, /* missing or wrong self signature */
+ CDK_KEY_EXPIRED = 2,
+ CDK_KEY_REVOKED = 4
+};
+
+enum cdk_trust_flag_t {
+ CDK_TRUST_UNKNOWN = 0,
+ CDK_TRUST_EXPIRED = 1,
+ CDK_TRUST_UNDEFINED = 2,
+ CDK_TRUST_NEVER = 3,
+ CDK_TRUST_MARGINAL = 4,
+ CDK_TRUST_FULLY = 5,
+ CDK_TRUST_ULTIMATE = 6,
+ /* trust flags */
+ CDK_TFLAG_REVOKED = 32,
+ CDK_TFLAG_SUB_REVOKED = 64,
+ CDK_TFLAG_DISABLED = 128
+};
+
+
+enum cdk_signature_id_t {
+ /* signature status */
+ CDK_SIGSTAT_NONE = 0,
+ CDK_SIGSTAT_GOOD = 1,
+ CDK_SIGSTAT_BAD = 2,
+ CDK_SIGSTAT_NOKEY = 3,
+ /* signature modes */
+ CDK_SIGMODE_NORMAL = 100,
+ CDK_SIGMODE_DETACHED = 101,
+ CDK_SIGMODE_CLEAR = 102
+};
+
+enum cdk_attribute_t {
+ /* cdk attributes */
+ CDK_ATTR_CREATED = 1,
+ CDK_ATTR_EXPIRE = 2,
+ CDK_ATTR_KEYID = 3,
+ CDK_ATTR_STATUS = 4,
+ CDK_ATTR_NOTATION = 5,
+ CDK_ATTR_ALGO_PK = 6,
+ CDK_ATTR_ALGO_MD = 7,
+ CDK_ATTR_VERSION = 8,
+ CDK_ATTR_LEN = 9,
+ CDK_ATTR_FLAGS = 10,
+ CDK_ATTR_MPI = 11,
+ CDK_ATTR_NAME = 12,
+ CDK_ATTR_FPR = 13,
+ CDK_ATTR_URL = 14,
+ /* cdk key flags */
+ CDK_FLAG_KEY_REVOKED = 256,
+ CDK_FLAG_KEY_EXPIRED = 512,
+ CDK_FLAG_SIG_EXPIRED = 1024,
+};
+
+
+enum cdk_callback_id_t {
+ CDK_CB_NONE = 0,
+ CDK_CB_PUBKEY_ENC = 1
+};
+
+
+typedef enum {
+ CDK_PKT_RESERVED = 0,
+ CDK_PKT_PUBKEY_ENC = 1,
+ CDK_PKT_SIGNATURE = 2,
+ CDK_PKT_SYMKEY_ENC = 3,
+ CDK_PKT_ONEPASS_SIG = 4,
+ CDK_PKT_SECRET_KEY = 5,
+ CDK_PKT_PUBLIC_KEY = 6,
+ CDK_PKT_SECRET_SUBKEY = 7,
+ CDK_PKT_COMPRESSED = 8,
+ CDK_PKT_ENCRYPTED = 9,
+ CDK_PKT_MARKER = 10,
+ CDK_PKT_LITERAL = 11,
+ CDK_PKT_RING_TRUST = 12,
+ CDK_PKT_USER_ID = 13,
+ CDK_PKT_PUBLIC_SUBKEY = 14,
+ CDK_PKT_OLD_COMMENT = 16,
+ CDK_PKT_ATTRIBUTE = 17,
+ CDK_PKT_ENCRYPTED_MDC = 18,
+ CDK_PKT_MDC = 19,
+} cdk_packet_type_t;
+
+#define CDK_PKT_IS_ENCRYPTED(pkttype) (\
+ ((pkttype)==CDK_PKT_ENCRYPTED_MDC) \
+ || ((pkttype)==CDK_PKT_ENCRYPTED) \
+ )
+
+struct cdk_pkt_userid_s {
+ unsigned int len;
+ unsigned is_primary:1;
+ unsigned is_revoked:1;
+ unsigned mdc_feature:1;
+ cdk_prefitem_t prefs;
+ unsigned char * attrib_img; /* Tag 17 if not null */
+ size_t attrib_len;
+ size_t prefs_size;
+ unsigned int created;
+ char name[1];
+};
+typedef struct cdk_pkt_userid_s *cdk_pkt_userid_t;
+
+struct cdk_pkt_pubkey_s {
+ unsigned char version;
+ unsigned char pubkey_algo;
+ unsigned char fpr[20];
+ unsigned int keyid[2];
+ unsigned int main_keyid[2];
+ unsigned int timestamp;
+ unsigned int expiredate;
+ cdk_mpi_t mpi[4];
+ unsigned is_revoked:1;
+ unsigned is_invalid:1;
+ unsigned has_expired:1;
+ int pubkey_usage;
+ cdk_pkt_userid_t uid;
+ cdk_prefitem_t prefs;
+ size_t prefs_size;
+ cdk_desig_revoker_t revkeys;
+};
+typedef struct cdk_pkt_pubkey_s *cdk_pkt_pubkey_t;
+
+struct cdk_pkt_seckey_s {
+ cdk_pkt_pubkey_t pk;
+ unsigned int expiredate;
+ int version;
+ int pubkey_algo;
+ unsigned int keyid[2];
+ unsigned int main_keyid[2];
+ unsigned char s2k_usage;
+ struct {
+ unsigned char algo;
+ unsigned char sha1chk; /* SHA1 is used instead of a 16 bit checksum */
+ cdk_s2k_t s2k;
+ unsigned char iv[16];
+ unsigned char ivlen;
+ } protect;
+ unsigned short csum;
+ cdk_mpi_t mpi[4];
+ unsigned char * encdata;
+ size_t enclen;
+ unsigned char is_protected;
+ unsigned is_primary:1;
+ unsigned has_expired:1;
+ unsigned is_revoked:1;
+};
+typedef struct cdk_pkt_seckey_s *cdk_pkt_seckey_t;
+
+struct cdk_pkt_signature_s {
+ unsigned char version;
+ unsigned char sig_class;
+ unsigned int timestamp;
+ unsigned int expiredate;
+ unsigned int keyid[2];
+ unsigned char pubkey_algo;
+ unsigned char digest_algo;
+ unsigned char digest_start[2];
+ unsigned short hashed_size;
+ cdk_subpkt_t hashed;
+ unsigned short unhashed_size;
+ cdk_subpkt_t unhashed;
+ cdk_mpi_t mpi[2];
+ cdk_desig_revoker_t revkeys;
+ struct {
+ unsigned exportable:1;
+ unsigned revocable:1;
+ unsigned policy_url:1;
+ unsigned notation:1;
+ unsigned expired:1;
+ unsigned checked:1;
+ unsigned valid:1;
+ unsigned missing_key:1;
+ } flags;
+ unsigned int key[2]; /* only valid for key signatures */
+};
+typedef struct cdk_pkt_signature_s *cdk_pkt_signature_t;
+
+struct cdk_pkt_onepass_sig_s {
+ unsigned char version;
+ unsigned int keyid[2];
+ unsigned char sig_class;
+ unsigned char digest_algo;
+ unsigned char pubkey_algo;
+ unsigned char last;
+};
+typedef struct cdk_pkt_onepass_sig_s * cdk_pkt_onepass_sig_t;
+
+
+struct cdk_pkt_pubkey_enc_s {
+ unsigned char version;
+ unsigned int keyid[2];
+ int throw_keyid;
+ unsigned char pubkey_algo;
+ cdk_mpi_t mpi[2];
+};
+typedef struct cdk_pkt_pubkey_enc_s * cdk_pkt_pubkey_enc_t;
+
+
+struct cdk_pkt_symkey_enc_s {
+ unsigned char version;
+ unsigned char cipher_algo;
+ cdk_s2k_t s2k;
+ unsigned char seskeylen;
+ unsigned char seskey[32];
+};
+typedef struct cdk_pkt_symkey_enc_s *cdk_pkt_symkey_enc_t;
+
+
+struct cdk_pkt_encrypted_s {
+ unsigned int len;
+ int extralen;
+ unsigned char mdc_method;
+ cdk_stream_t buf;
+};
+typedef struct cdk_pkt_encrypted_s *cdk_pkt_encrypted_t;
+
+
+struct cdk_pkt_mdc_s {
+ unsigned char hash[20];
+};
+typedef struct cdk_pkt_mdc_s *cdk_pkt_mdc_t;
+
+
+struct cdk_pkt_literal_s {
+ unsigned int len;
+ cdk_stream_t buf;
+ int mode;
+ unsigned int timestamp;
+ int namelen;
+ char name[1];
+};
+typedef struct cdk_pkt_literal_s *cdk_pkt_literal_t;
+
+
+struct cdk_pkt_compressed_s {
+ unsigned int len;
+ int algorithm;
+ cdk_stream_t buf;
+};
+typedef struct cdk_pkt_compressed_s *cdk_pkt_compressed_t;
+
+
+struct cdk_packet_s {
+ size_t pktlen; /* real packet length */
+ size_t pktsize; /* length with all headers */
+ int old_ctb;
+ cdk_packet_type_t pkttype;
+ union {
+ cdk_pkt_mdc_t mdc;
+ cdk_pkt_userid_t user_id;
+ cdk_pkt_pubkey_t public_key;
+ cdk_pkt_seckey_t secret_key;
+ cdk_pkt_signature_t signature;
+ cdk_pkt_pubkey_enc_t pubkey_enc;
+ cdk_pkt_symkey_enc_t symkey_enc;
+ cdk_pkt_compressed_t compressed;
+ cdk_pkt_encrypted_t encrypted;
+ cdk_pkt_literal_t literal;
+ cdk_pkt_onepass_sig_t onepass_sig;
+ } pkt;
+};
+typedef struct cdk_packet_s CDK_PACKET;
+typedef struct cdk_packet_s *cdk_packet_t;
+
+/*-- main.c --*/
+/* memory routines */
+typedef void (*cdk_log_fnc_t) (void *, int, const char *, va_list);
+void cdk_set_log_level (int lvl);
+void cdk_set_log_handler (cdk_log_fnc_t logfnc, void * opaque);
+const char* cdk_strerror (int ec);
+void cdk_set_malloc_hooks (void *(*new_alloc_func) (size_t n),
+ void *(*new_alloc_secure_func) (size_t n),
+ void *(*new_realloc_func) (void * p, size_t n),
+ void *(*new_calloc_func) (size_t m, size_t n),
+ void (*new_free_func) (void *));
+int cdk_malloc_hook_initialized (void);
+void * cdk_malloc (size_t size);
+void * cdk_calloc (size_t n, size_t m);
+void * cdk_realloc (void * ptr, size_t size);
+void * cdk_salloc (size_t size, int clear);
+char * cdk_strdup (const char * ptr);
+void cdk_free (void * ptr);
+/* session handle routines */
+int cdk_handle_new (cdk_ctx_t * r_ctx);
+void cdk_handle_free (cdk_ctx_t c);
+void cdk_handle_set_keydb (cdk_ctx_t hd, cdk_keydb_hd_t db);
+cdk_keydb_hd_t cdk_handle_get_keydb( cdk_ctx_t hd, int type );
+int cdk_handle_control( cdk_ctx_t hd, int action, int cmd, ... );
+void cdk_handle_set_callback (cdk_ctx_t hd,
+ void (*cb) (void *opa, int type, const char * s),
+ void * cb_value);
+void cdk_handle_set_passphrase_cb( cdk_ctx_t hd,
+ char *(*cb) (void *opa, const char *prompt),
+ void * cb_value );
+
+/* shortcuts for some controls */
+#define cdk_handle_set_armor( a, val ) \
+ cdk_handle_control( (a), CDK_CTLF_SET, CDK_CTL_ARMOR, (val) )
+
+#define cdk_handle_set_compress( a, algo, level ) \
+ cdk_handle_control( (a), CDK_CTLF_SET, CDK_CTL_COMPRESS, (algo), (level) )
+
+
+/*-- cipher.c --*/
+void cdk_set_progress_handler (void (*cb)(void * hd, unsigned off,
+ unsigned size), void * cb_value);
+
+/*-- new-packet.c --*/
+cdk_error_t cdk_pkt_new( cdk_packet_t * r_pkt );
+void cdk_pkt_init( cdk_packet_t pkt );
+cdk_error_t cdk_pkt_alloc( cdk_packet_t * r_pkt, int pkttype );
+void cdk_pkt_free( cdk_packet_t pkt );
+void cdk_pkt_release( cdk_packet_t pkt );
+cdk_error_t cdk_pkt_read( cdk_stream_t inp, cdk_packet_t pkt );
+cdk_error_t cdk_pkt_write( cdk_stream_t out, cdk_packet_t pkt );
+/* sub packet routines */
+cdk_subpkt_t cdk_subpkt_new( size_t size );
+void cdk_subpkt_free( cdk_subpkt_t ctx );
+cdk_subpkt_t cdk_subpkt_find( cdk_subpkt_t ctx, int type );
+cdk_error_t cdk_subpkt_add( cdk_subpkt_t root, cdk_subpkt_t node );
+const unsigned char * cdk_subpkt_get_data( cdk_subpkt_t ctx,
+ int * r_type, size_t * r_nbytes );
+void cdk_subpkt_init( cdk_subpkt_t node, int type,
+ const void *buf, size_t buflen );
+unsigned char * cdk_userid_pref_get_array( cdk_pkt_userid_t id, int type,
+ size_t *ret_len );
+const unsigned char* cdk_key_desig_revoker_walk( cdk_desig_revoker_t root,
+ cdk_desig_revoker_t * ctx,
+ int *r_class, int *r_algid );
+
+/*-- pubkey.c --*/
+#define is_RSA(a) ((a) == CDK_PK_RSA \
+ || (a) == CDK_PK_RSA_E \
+ || (a) == CDK_PK_RSA_S)
+#define is_ELG(a) ((a) == CDK_PK_ELG || (a) == CDK_PK_ELG_E)
+#define is_DSA(a) ((a) == CDK_PK_DSA)
+
+cdk_error_t cdk_pk_encrypt (cdk_pkt_pubkey_t pk, cdk_pkt_pubkey_enc_t pke,
+ cdk_sesskey_t esk);
+cdk_error_t cdk_pk_decrypt (cdk_pkt_seckey_t sk, cdk_pkt_pubkey_enc_t pke,
+ cdk_sesskey_t *r_sk);
+cdk_error_t cdk_pk_sign (cdk_pkt_seckey_t sk, cdk_pkt_signature_t sig,
+ const unsigned char * md);
+cdk_error_t cdk_pk_verify (cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig,
+ const unsigned char * md);
+cdk_error_t cdk_pk_get_mpi (cdk_pkt_pubkey_t pk, int idx,
+ unsigned char * buf, size_t * r_count, size_t * r_nbits);
+cdk_error_t cdk_sk_get_mpi (cdk_pkt_seckey_t sk, int idx,
+ unsigned char * buf, size_t * r_count, size_t * r_nbits);
+int cdk_pk_get_nbits (cdk_pkt_pubkey_t pk);
+int cdk_pk_get_npkey (int algo);
+int cdk_pk_get_nskey (int algo);
+int cdk_pk_get_nsig (int algo);
+int cdk_pk_get_nenc (int algo);
+int cdk_pk_get_fingerprint (cdk_pkt_pubkey_t pk, unsigned char * fpr);
+unsigned int cdk_pk_fingerprint_get_keyid (const unsigned char * fpr,
+ size_t fprlen,
+ unsigned int * keyid);
+unsigned int cdk_pk_get_keyid (cdk_pkt_pubkey_t pk, unsigned int * keyid);
+unsigned int cdk_sk_get_keyid (cdk_pkt_seckey_t sk, unsigned int * keyid);
+unsigned int cdk_sig_get_keyid (cdk_pkt_signature_t sig,
+ unsigned int * keyid);
+cdk_error_t cdk_sk_unprotect( cdk_pkt_seckey_t sk, const char * pw );
+cdk_error_t cdk_sk_protect( cdk_pkt_seckey_t sk, const char * pw );
+cdk_error_t cdk_pk_from_secret_key( cdk_pkt_seckey_t sk,
+ cdk_pkt_pubkey_t *ret_pk );
+
+/*-- seskey.c --*/
+cdk_error_t cdk_sesskey_new( cdk_sesskey_t * r_sk );
+void cdk_sesskey_free( cdk_sesskey_t sk );
+cdk_error_t cdk_dek_new( cdk_dek_t * r_dek );
+void cdk_dek_free( cdk_dek_t dek );
+cdk_error_t cdk_dek_set_cipher( cdk_dek_t dek, int algo );
+cdk_error_t cdk_dek_set_key( cdk_dek_t dek, const unsigned char *key,
+ size_t keylen );
+cdk_error_t cdk_dek_from_passphrase( cdk_dek_t * ret_dek, int cipher_algo,
+ cdk_s2k_t s2k, int mode,
+ const char * pw );
+cdk_error_t cdk_dek_encode_pkcs1( cdk_dek_t dek, int nbits,
+ cdk_sesskey_t * r_esk );
+cdk_error_t cdk_dek_decode_pkcs1( cdk_dek_t * ret_dek, cdk_sesskey_t esk );
+cdk_error_t cdk_dek_extract( cdk_dek_t * ret_dek, cdk_ctx_t hd,
+ cdk_pkt_pubkey_enc_t enc,
+ cdk_pkt_seckey_t sk );
+void cdk_dek_set_mdc_flag( cdk_dek_t dek, int val );
+/* string to key */
+cdk_error_t cdk_s2k_new (cdk_s2k_t * ret_s2k, int mode, int algo,
+ const unsigned char * salt);
+void cdk_s2k_free (cdk_s2k_t s2k);
+
+/*-- armor.c --*/
+cdk_error_t cdk_file_armor( cdk_ctx_t hd, const char * file,
+ const char * output );
+cdk_error_t cdk_file_dearmor( const char * file, const char * output );
+cdk_error_t cdk_armor_filter_use (cdk_stream_t inp);
+
+/*-- stream.c --*/
+int cdk_stream_control (cdk_stream_t s, int ctl, int val);
+cdk_error_t cdk_stream_open (const char * file, cdk_stream_t * ret_s);
+cdk_error_t cdk_stream_new (const char * file, cdk_stream_t * ret_s);
+cdk_error_t cdk_stream_create (const char * file, cdk_stream_t * ret_s);
+cdk_stream_t cdk_stream_tmp (void);
+cdk_stream_t cdk_stream_tmp_from_mem (const void * buf, size_t count);
+void cdk_stream_tmp_set_mode (cdk_stream_t s, int val);
+cdk_error_t cdk_stream_flush (cdk_stream_t s);
+cdk_error_t cdk_stream_set_cache (cdk_stream_t s, int val);
+cdk_error_t cdk_stream_filter_disable (cdk_stream_t s, int type);
+cdk_error_t cdk_stream_close (cdk_stream_t s);
+unsigned cdk_stream_get_length (cdk_stream_t s);
+int cdk_stream_read (cdk_stream_t s, void * buf, size_t count);
+int cdk_stream_write (cdk_stream_t s, const void * buf, size_t count);
+int cdk_stream_putc (cdk_stream_t s, int c);
+int cdk_stream_getc (cdk_stream_t s);
+int cdk_stream_eof (cdk_stream_t s);
+long cdk_stream_tell (cdk_stream_t s);
+cdk_error_t cdk_stream_seek (cdk_stream_t s, long offset);
+cdk_error_t cdk_stream_set_armor_flag (cdk_stream_t s, int type);
+cdk_error_t cdk_stream_set_literal_flag (cdk_stream_t s, int mode, const char * fname);
+cdk_error_t cdk_stream_set_cipher_flag (cdk_stream_t s, cdk_dek_t dek,
+ int use_mdc);
+cdk_error_t cdk_stream_set_compress_flag (cdk_stream_t s, int algo, int level);
+cdk_error_t cdk_stream_set_hash_flag (cdk_stream_t s, int algo);
+cdk_error_t cdk_stream_set_text_flag (cdk_stream_t s, const char * lf);
+cdk_error_t cdk_stream_kick_off (cdk_stream_t inp, cdk_stream_t out);
+cdk_error_t cdk_stream_mmap( cdk_stream_t s, unsigned char ** ret_buf,
+ size_t * ret_count );
+int cdk_stream_peek( cdk_stream_t inp, unsigned char *s, size_t count );
+
+/*-- keydb.c --*/
+cdk_error_t cdk_keydb_new( cdk_keydb_hd_t * r_hd, int type, void * data,
+ size_t count);
+cdk_error_t cdk_keydb_open( cdk_keydb_hd_t hd, cdk_stream_t * ret_kr );
+int cdk_keydb_check_sk( cdk_keydb_hd_t hd, unsigned int * keyid );
+cdk_error_t cdk_keydb_search_start( cdk_keydb_hd_t hd, int type, void * desc );
+cdk_error_t cdk_keydb_search( cdk_keydb_hd_t hd, cdk_kbnode_t * ret_key );
+void cdk_keydb_free( cdk_keydb_hd_t hd );
+cdk_error_t cdk_keydb_get_bykeyid( cdk_keydb_hd_t hd, unsigned int * keyid,
+ cdk_kbnode_t * ret_pk );
+cdk_error_t cdk_keydb_get_byfpr( cdk_keydb_hd_t hd, const unsigned char * fpr,
+ cdk_kbnode_t * ret_pk );
+cdk_error_t cdk_keydb_get_bypattern( cdk_keydb_hd_t hd, const char * patt,
+ cdk_kbnode_t * ret_pk );
+cdk_error_t cdk_keydb_get_pk( cdk_keydb_hd_t khd, unsigned int * keyid,
+ cdk_pkt_pubkey_t* ret_pk );
+cdk_error_t cdk_keydb_get_sk( cdk_keydb_hd_t khd, unsigned int * keyid,
+ cdk_pkt_seckey_t* ret_sk );
+cdk_error_t cdk_keydb_get_keyblock( cdk_stream_t inp, cdk_kbnode_t * ret_key );
+cdk_error_t cdk_keydb_idx_rebuild( cdk_keydb_hd_t hd );
+cdk_error_t cdk_keydb_export( cdk_keydb_hd_t hd, cdk_stream_t out,
+ cdk_strlist_t remusr );
+cdk_error_t cdk_keydb_import( cdk_keydb_hd_t hd, cdk_kbnode_t knode,
+ int *result );
+cdk_error_t cdk_keydb_pk_cache_sigs( cdk_kbnode_t pk, cdk_keydb_hd_t hd );
+
+/* listing keys */
+cdk_error_t cdk_listkey_start( cdk_listkey_t * r_ctx, cdk_keydb_hd_t db,
+ const char * patt, cdk_strlist_t fpatt );
+void cdk_listkey_close( cdk_listkey_t ctx );
+cdk_error_t cdk_listkey_next( cdk_listkey_t ctx, cdk_kbnode_t * ret_key );
+
+/*-- kbnode.c --*/
+cdk_kbnode_t cdk_kbnode_new (cdk_packet_t pkt);
+cdk_error_t cdk_kbnode_read_from_mem (cdk_kbnode_t * ret_node,
+ const unsigned char * buf,
+ size_t buflen);
+cdk_error_t cdk_kbnode_write_to_mem (cdk_kbnode_t node,
+ unsigned char * buf, size_t * r_nbytes);
+void cdk_kbnode_release (cdk_kbnode_t node);
+cdk_kbnode_t cdk_kbnode_walk (cdk_kbnode_t root, cdk_kbnode_t * ctx, int all);
+cdk_packet_t cdk_kbnode_find_packet (cdk_kbnode_t node, int pkttype);
+cdk_packet_t cdk_kbnode_get_packet (cdk_kbnode_t node);
+cdk_kbnode_t cdk_kbnode_find (cdk_kbnode_t node, int pkttype);
+cdk_kbnode_t cdk_kbnode_find_prev( cdk_kbnode_t root, cdk_kbnode_t node,
+ int pkttype );
+cdk_kbnode_t cdk_kbnode_find_next (cdk_kbnode_t node, int pkttype);
+void * cdk_kbnode_get_attr( cdk_kbnode_t node, int pkttype, int attr );
+cdk_error_t cdk_kbnode_hash( cdk_kbnode_t node, cdk_md_hd_t md, int is_v4,
+ int pkttype, int flags );
+
+/*-- sig-check.c --*/
+cdk_error_t cdk_pk_check_sigs( cdk_kbnode_t knode, cdk_keydb_hd_t hd,
+ int * r_status );
+
+/*-- keylist.c --*/
+int cdk_pklist_select_algo( cdk_keylist_t pkl, int preftype );
+int cdk_pklist_use_mdc (cdk_keylist_t pkl);
+cdk_error_t cdk_pklist_build( cdk_keylist_t *ret_pkl, cdk_keydb_hd_t hd,
+ cdk_strlist_t remusr, int use );
+void cdk_pklist_release (cdk_keylist_t pkl);
+cdk_error_t cdk_pklist_encrypt (cdk_keylist_t pkl, cdk_dek_t dek,
+ cdk_stream_t out);
+/* secret key list */
+cdk_error_t cdk_sklist_build( cdk_keylist_t * ret_skl,
+ cdk_keydb_hd_t db, cdk_ctx_t hd,
+ cdk_strlist_t locusr,
+ int unlock, unsigned int use );
+void cdk_sklist_release (cdk_keylist_t skl);
+cdk_error_t cdk_sklist_write (cdk_keylist_t skl, cdk_stream_t outp,
+ cdk_md_hd_t mdctx,
+ int sigclass, int sigver);
+cdk_error_t cdk_sklist_write_onepass( cdk_keylist_t skl, cdk_stream_t outp,
+ int sigclass, int mdalgo );
+
+/*-- encrypt.c --*/
+cdk_error_t cdk_stream_encrypt (cdk_ctx_t hd, cdk_strlist_t remusr,
+ cdk_stream_t inp, cdk_stream_t out);
+cdk_error_t cdk_file_encrypt (cdk_ctx_t hd, cdk_strlist_t remusr,
+ const char * file, const char * output);
+cdk_error_t cdk_file_decrypt (cdk_ctx_t hd, const char * file,
+ const char * output);
+cdk_error_t cdk_data_transform( cdk_ctx_t hd, enum cdk_crypto_mode_t mode,
+ cdk_strlist_t locusr, cdk_strlist_t remusr,
+ const void * inbuf, size_t insize,
+ unsigned char ** outbuf, size_t * outsize,
+ int modval );
+
+/*-- sign.c --*/
+cdk_error_t cdk_stream_sign( cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out,
+ cdk_strlist_t locusr, cdk_strlist_t remusr,
+ int encryptflag, int sigmode );
+cdk_error_t cdk_file_sign( cdk_ctx_t hd, cdk_strlist_t locusr,
+ cdk_strlist_t remusr,
+ const char * file, const char * output,
+ int sigmode, int encryptflag );
+
+/*-- verify.c --*/
+cdk_error_t cdk_stream_verify( cdk_ctx_t hd, cdk_stream_t inp,
+ cdk_stream_t out );
+cdk_error_t cdk_file_verify( cdk_ctx_t hd, const char * file,
+ const char * output );
+unsigned long cdk_sig_get_ulong_attr( cdk_ctx_t hd, int idx, int what );
+const void * cdk_sig_get_data_attr( cdk_ctx_t hd, int idx, int what );
+
+/*-- trustdb.c --*/
+int cdk_trustdb_get_validity( cdk_stream_t inp, cdk_pkt_userid_t id,
+ int *r_val );
+int cdk_trustdb_get_ownertrust( cdk_stream_t inp, cdk_pkt_pubkey_t pk,
+ int * r_val, int * r_flags );
+
+/*-- misc.c --*/
+void cdk_strlist_free (cdk_strlist_t sl);
+cdk_strlist_t cdk_strlist_add (cdk_strlist_t * list, const char * string);
+const char * cdk_strlist_walk (cdk_strlist_t root, cdk_strlist_t * context);
+const char * cdk_check_version (const char * req_version);
+/* UTF8 */
+char * cdk_utf8_encode( const char * string );
+char * cdk_utf8_decode( const char * string, size_t length, int delim );
+
+/*-- keyserver.c --*/
+cdk_error_t cdk_keyserver_recv_key( const char * host, int port,
+ const unsigned char * keyid, int kid_type,
+ cdk_kbnode_t * r_key );
+
+/*-- keygen.c --*/
+cdk_error_t cdk_keygen_new( cdk_keygen_ctx_t * r_hd );
+void cdk_keygen_free( cdk_keygen_ctx_t hd );
+cdk_error_t cdk_keygen_set_prefs( cdk_keygen_ctx_t hd,
+ enum cdk_pref_type_t type,
+ const unsigned char * array, size_t n );
+cdk_error_t cdk_keygen_set_algo_info( cdk_keygen_ctx_t hd, int type,
+ enum cdk_pk_algo_t algo, int bits );
+void cdk_keygen_set_mdc_feature( cdk_keygen_ctx_t hd, int val );
+void cdk_keygen_set_keyserver_flags( cdk_keygen_ctx_t hd, int no_modify,
+ const char *pref_url );
+void cdk_keygen_set_expire_date( cdk_keygen_ctx_t hd, int type,
+ long timestamp );
+void cdk_keygen_set_name( cdk_keygen_ctx_t hd, const char * name );
+void cdk_keygen_set_passphrase( cdk_keygen_ctx_t hd, const char * pass );
+cdk_error_t cdk_keygen_start( cdk_keygen_ctx_t hd );
+cdk_error_t cdk_keygen_save( cdk_keygen_ctx_t hd,
+ const char * pubf, const char * secf );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OPENCDK_H */
+
diff --git a/libextra/opencdk/packet.h b/libextra/opencdk/packet.h
new file mode 100644
index 0000000000..f8e83555f4
--- /dev/null
+++ b/libextra/opencdk/packet.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * packet.h - Internal packet routines
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CDK_PACKET_H
+#define CDK_PACKET_H
+
+struct cdk_kbnode_s {
+ struct cdk_kbnode_s * next;
+ cdk_packet_t pkt;
+ int private_flag;
+};
+
+/*-- new-packet.c --*/
+void _cdk_free_mpibuf( size_t n, gcry_mpi_t * array );
+void _cdk_free_userid( cdk_pkt_userid_t uid );
+void _cdk_free_signature( cdk_pkt_signature_t sig );
+void _cdk_free_pubkey( cdk_pkt_pubkey_t pk );
+void _cdk_free_seckey( cdk_pkt_seckey_t sk );
+cdk_prefitem_t _cdk_copy_prefs( const cdk_prefitem_t prefs );
+int _cdk_copy_userid( cdk_pkt_userid_t *dst, cdk_pkt_userid_t src );
+int _cdk_copy_pubkey( cdk_pkt_pubkey_t* dst, cdk_pkt_pubkey_t src );
+int _cdk_copy_seckey( cdk_pkt_seckey_t* dst, cdk_pkt_seckey_t src );
+int _cdk_copy_pk_to_sk( cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk );
+int _cdk_copy_signature( cdk_pkt_signature_t* dst, cdk_pkt_signature_t src );
+int _cdk_pubkey_compare( cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b );
+
+#endif /* CDK_PACKET_H */
+
diff --git a/libextra/opencdk/plaintext.c b/libextra/opencdk/plaintext.c
new file mode 100644
index 0000000000..29eacb2263
--- /dev/null
+++ b/libextra/opencdk/plaintext.c
@@ -0,0 +1,229 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * plaintext.c - Literal packet filters
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 "opencdk.h"
+#include "main.h"
+#include "filters.h"
+
+
+static int
+literal_decode( void * opaque, FILE * in, FILE * out )
+{
+ literal_filter_t * pfx = opaque;
+ cdk_stream_t si, so;
+ CDK_PACKET pkt;
+ cdk_pkt_literal_t pt;
+ byte buf[8192];
+ size_t nread;
+ int rc, bufsize;
+
+ _cdk_log_debug( "literal filter: decode\n" );
+
+ if (!pfx || !in || !out)
+ return CDK_Inv_Value;
+
+ si = _cdk_stream_fpopen( in, STREAMCTL_READ );
+ if (!si)
+ return CDK_Out_Of_Core;
+ so = _cdk_stream_fpopen( out, STREAMCTL_WRITE );
+ if( !so ) {
+ cdk_stream_close( si );
+ return CDK_Out_Of_Core;
+ }
+ cdk_pkt_init( &pkt );
+ rc = cdk_pkt_read( si, &pkt );
+ if( pkt.pkttype != CDK_PKT_LITERAL ) {
+ if( pkt.pkttype )
+ cdk_pkt_free( &pkt );
+ return rc;
+ }
+ pt = pkt.pkt.literal;
+ pfx->mode = pt->mode;
+ pfx->filename = cdk_strdup( pt->name? pt->name : " " );
+ if( !pfx->filename ) {
+ cdk_pkt_free( &pkt );
+ return CDK_Out_Of_Core;
+ }
+ while( !feof( in ) ) {
+ _cdk_log_debug( "partial on=%d size=%lu\n",
+ pfx->blkmode.on, pfx->blkmode.size );
+ if( pfx->blkmode.on )
+ bufsize = pfx->blkmode.size;
+ else
+ bufsize = pt->len < sizeof buf-1? pt->len : sizeof buf-1;
+ nread = cdk_stream_read( pt->buf, buf, bufsize );
+ if( nread == EOF ) {
+ rc = CDK_File_Error;
+ break;
+ }
+ if( pfx->md )
+ cdk_md_write (pfx->md, buf, nread);
+ cdk_stream_write( so, buf, nread );
+ pt->len -= nread;
+ if( pfx->blkmode.on ) {
+ pfx->blkmode.size = _cdk_pkt_read_len( in, &pfx->blkmode.on );
+ if( pfx->blkmode.size == (size_t)EOF )
+ return CDK_Inv_Packet;
+ }
+ if( pt->len <= 0 && !pfx->blkmode.on )
+ break;
+ }
+ cdk_stream_close( si );
+ cdk_stream_close( so );
+ cdk_pkt_free( &pkt );
+ return rc;
+}
+
+
+static int
+literal_encode (void * opaque, FILE * in, FILE * out)
+{
+ literal_filter_t * pfx = opaque;
+ cdk_pkt_literal_t pt;
+ cdk_stream_t si;
+ CDK_PACKET pkt;
+ size_t filelen;
+ int rc;
+
+ _cdk_log_debug ("literal filter: encode\n");
+
+ if (!pfx || !in || !out)
+ return CDK_Inv_Value;
+
+ if (!pfx->filename) {
+ pfx->filename = cdk_strdup ("_CONSOLE");
+ if( !pfx->filename )
+ return CDK_Out_Of_Core;
+ }
+
+ si = _cdk_stream_fpopen (in, STREAMCTL_READ);
+ if (!si)
+ return CDK_Out_Of_Core;
+
+ filelen = strlen (pfx->filename);
+ pt = cdk_calloc (1, sizeof *pt + filelen - 1);
+ if (!pt)
+ return CDK_Out_Of_Core;
+ memcpy (pt->name, pfx->filename, filelen);
+ pt->namelen = filelen;
+ pt->name[pt->namelen] = '\0';
+ pt->timestamp = _cdk_timestamp ();
+ pt->mode = pfx->mode ? 't' : 'b';
+ pt->len = cdk_stream_get_length (si);
+ pt->buf = si;
+ cdk_pkt_init (&pkt);
+ pkt.old_ctb = pfx->rfc1991? 1 : 0;
+ pkt.pkttype = CDK_PKT_LITERAL;
+ pkt.pkt.literal = pt;
+ rc = _cdk_pkt_write_fp (out, &pkt);
+
+ cdk_free (pt);
+ cdk_stream_close (si);
+ return rc;
+}
+
+
+int
+_cdk_filter_literal( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ if( ctl == STREAMCTL_READ )
+ return literal_decode( opaque, in, out );
+ else if( ctl == STREAMCTL_WRITE )
+ return literal_encode( opaque, in, out );
+ else if( ctl == STREAMCTL_FREE ) {
+ literal_filter_t * pfx = opaque;
+ if( pfx ) {
+ cdk_free( pfx->filename );
+ pfx->filename = NULL;
+ }
+ }
+ return CDK_Inv_Mode;
+}
+
+
+static int
+text_encode( void * opaque, FILE * in, FILE * out )
+{
+ const char * s;
+ char buf[1024];
+
+ if( !in || !out )
+ return CDK_Inv_Value;
+
+ while( !feof( in ) ) {
+ s = fgets( buf, sizeof buf-1, in );
+ if( !s )
+ break;
+ _cdk_trim_string( buf, 1 );
+ fwrite( buf, 1, strlen( buf ), out );
+ }
+
+ return 0;
+}
+
+
+static int
+text_decode( void * opaque, FILE * in, FILE * out )
+{
+ text_filter_t * tfx = opaque;
+ const char * s;
+ char buf[1024];
+
+ if( !tfx || !in || !out )
+ return CDK_Inv_Value;
+
+ while( !feof( in ) ) {
+ s = fgets( buf, sizeof buf-1, in );
+ if( !s )
+ break;
+ _cdk_trim_string( buf, 0 );
+ fwrite( buf, 1, strlen( buf ), out );
+ fwrite( tfx->lf, 1, strlen( tfx->lf ), out );
+ }
+
+ return 0;
+}
+
+
+int
+_cdk_filter_text( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ if( ctl == STREAMCTL_READ )
+ return text_encode( opaque, in, out );
+ else if( ctl == STREAMCTL_WRITE )
+ return text_decode( opaque, in, out );
+ else if( ctl == STREAMCTL_FREE ) {
+ text_filter_t * tfx = opaque;
+ if( tfx ) {
+ _cdk_log_debug( "free text filter\n" );
+ tfx->lf = NULL;
+ }
+ }
+ return CDK_Inv_Mode;
+}
+
+
+
diff --git a/libextra/opencdk/pubkey.c b/libextra/opencdk/pubkey.c
new file mode 100644
index 0000000000..ad59b825f8
--- /dev/null
+++ b/libextra/opencdk/pubkey.c
@@ -0,0 +1,1073 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * pubkey.c - Public key API
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "cipher.h"
+
+
+static gcry_mpi_t *
+convert_to_gcrympi( cdk_mpi_t m[4], int ncount )
+{
+ gcry_mpi_t * d;
+ size_t nbytes = 0;
+ int i = 0, rc = 0;
+
+ if( !m || ncount > 4 )
+ return NULL;
+ d = cdk_calloc( ncount, sizeof *d );
+ if( !d )
+ return NULL;
+ for( i = 0; i < ncount; i++ ) {
+ nbytes = m[i]->bytes + 2;
+ if( gcry_mpi_scan( &d[i], GCRYMPI_FMT_PGP, m[i]->data, nbytes, &nbytes ) ) {
+ rc = CDK_Gcry_Error;
+ break;
+ }
+ }
+ if( rc ) {
+ _cdk_free_mpibuf( i, d );
+ d = NULL;
+ }
+ return d;
+}
+
+
+static int
+seckey_to_sexp( gcry_sexp_t * r_skey, cdk_pkt_seckey_t sk )
+{
+ gcry_sexp_t sexp = NULL;
+ gcry_mpi_t * mpk = NULL, * msk = NULL;
+ cdk_pkt_pubkey_t pk;
+ const char * fmt = NULL;
+ int ncount = 0, nscount = 0;
+ int rc = 0;
+
+ if( !r_skey || !sk || !sk->pk )
+ return CDK_Inv_Value;
+
+ pk = sk->pk;
+ ncount = cdk_pk_get_npkey( pk->pubkey_algo );
+ mpk = convert_to_gcrympi( pk->mpi, ncount );
+ if( !mpk )
+ return CDK_MPI_Error;
+ nscount = cdk_pk_get_nskey( sk->pubkey_algo );
+ msk = convert_to_gcrympi( sk->mpi, nscount );
+ if( !msk )
+ rc = CDK_MPI_Error;
+ if( !rc && is_RSA( sk->pubkey_algo ) ) {
+ fmt = "(private-key(openpgp-rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, mpk[0], mpk[1],
+ msk[0], msk[1], msk[2], msk[3] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else if( !rc && is_ELG( sk->pubkey_algo ) ) {
+ fmt = "(private-key(openpgp-elg(p%m)(g%m)(y%m)(x%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, mpk[0], mpk[1],
+ mpk[2], msk[0] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else if( !rc && is_DSA( sk->pubkey_algo ) ) {
+ fmt = "(private-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)(x%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, mpk[0], mpk[1], mpk[2],
+ mpk[3], msk[0] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else
+ rc = CDK_Inv_Algo;
+
+ _cdk_free_mpibuf( ncount, mpk );
+ _cdk_free_mpibuf( nscount, msk );
+ *r_skey = sexp;
+ return rc;
+}
+
+
+static int
+pubkey_to_sexp( gcry_sexp_t * r_key, cdk_pkt_pubkey_t pk )
+{
+ gcry_sexp_t sexp = NULL;
+ gcry_mpi_t * m;
+ const char * fmt = NULL;
+ int ncount = 0;
+ int rc = 0;
+
+ if( !r_key || !pk )
+ return CDK_Inv_Value;
+
+ ncount = cdk_pk_get_npkey( pk->pubkey_algo );
+ m = convert_to_gcrympi( pk->mpi, ncount );
+ if( !m )
+ return CDK_MPI_Error;
+ if( is_RSA( pk->pubkey_algo ) ) {
+ fmt = "(public-key(openpgp-rsa(n%m)(e%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else if( is_ELG( pk->pubkey_algo ) ) {
+ fmt = "(public-key(openpgp-elg(p%m)(g%m)(y%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1], m[2] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else if( is_DSA( pk->pubkey_algo ) ) {
+ fmt = "(public-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1], m[2], m[3] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else
+ rc = CDK_Inv_Algo;
+ _cdk_free_mpibuf( ncount, m );
+ *r_key = sexp;
+ return rc;
+}
+
+
+static int
+enckey_to_sexp( gcry_sexp_t * r_sexp, gcry_mpi_t esk )
+{
+ gcry_sexp_t sexp = NULL;
+ int rc = 0;
+
+ if( !r_sexp || !esk )
+ return CDK_Inv_Value;
+ if( gcry_sexp_build( &sexp, NULL, "%m", esk ) )
+ rc = CDK_Gcry_Error;
+ *r_sexp = sexp;
+ return rc;
+}
+
+
+static int
+digest_to_sexp( gcry_sexp_t * r_md, int algo, const byte * md, size_t mdlen )
+{
+ gcry_sexp_t sexp = NULL;
+ gcry_mpi_t m = NULL;
+ size_t nbytes = 0;
+ int rc = 0;
+
+ if( !r_md || !md )
+ return CDK_Inv_Value;
+ nbytes = mdlen ? mdlen : cdk_md_get_algo_dlen( algo );
+ if( !nbytes )
+ return CDK_Inv_Algo;
+ if( gcry_mpi_scan( &m, GCRYMPI_FMT_USG, md, nbytes, &nbytes ) )
+ return CDK_Gcry_Error;
+ if( gcry_sexp_build( &sexp, NULL, "%m", m ) )
+ rc = CDK_Gcry_Error;
+ if( !rc )
+ *r_md = sexp;
+ gcry_mpi_release( m );
+ return rc;
+}
+
+
+static int
+sexp_to_bitmpi( gcry_sexp_t sexp, const char * val, cdk_mpi_t * ret_buf )
+{
+ gcry_sexp_t list = NULL;
+ gcry_mpi_t m = NULL;
+ cdk_mpi_t buf = NULL;
+ size_t nbits = 0, nbytes = 0;
+ int rc = 0;
+
+ if( !sexp || !val || !ret_buf )
+ return CDK_Inv_Value;
+
+ list = gcry_sexp_find_token( sexp, val, 0 );
+ if( !list )
+ return CDK_Gcry_Error;
+ m = gcry_sexp_nth_mpi( list, 1, 0 );
+ if( !m ) {
+ gcry_sexp_release( list );
+ return CDK_Gcry_Error;
+ }
+ nbits = gcry_mpi_get_nbits( m );
+ nbytes = (nbits + 7) / 8;
+ buf = cdk_calloc( 1, sizeof *buf + nbytes );
+ if( !buf ) {
+ rc = CDK_Out_Of_Core;
+ goto leave;
+ }
+ buf->data[0] = nbits >> 8;
+ buf->data[1] = nbits;
+ if( gcry_mpi_print( GCRYMPI_FMT_USG, NULL, nbytes, &nbytes, m ) )
+ rc = CDK_Gcry_Error;
+ else
+ if( gcry_mpi_print( GCRYMPI_FMT_USG, buf->data + 2, nbytes, &nbytes, m ) )
+ rc = CDK_Gcry_Error;
+ if( !rc ) {
+ buf->bytes = nbytes;
+ buf->bits = nbits;
+ *ret_buf = buf;
+ }
+
+ leave:
+ gcry_mpi_release( m );
+ gcry_sexp_release( list );
+ return rc;
+}
+
+
+static int
+sexp_to_sig( cdk_pkt_signature_t sig, gcry_sexp_t sexp)
+{
+ int rc = 0;
+
+ if( !sig || !sexp )
+ return CDK_Inv_Value;
+
+ if( is_RSA( sig->pubkey_algo ) )
+ return sexp_to_bitmpi( sexp, "s", &sig->mpi[0] );
+ else if( is_DSA( sig->pubkey_algo) || is_ELG( sig->pubkey_algo ) ) {
+ rc = sexp_to_bitmpi( sexp, "r", &sig->mpi[0] );
+ if( !rc )
+ rc = sexp_to_bitmpi( sexp, "s", &sig->mpi[1] );
+ return rc;
+ }
+ return CDK_Inv_Algo;
+}
+
+
+static int
+sig_to_sexp( gcry_sexp_t * r_sig, cdk_pkt_signature_t sig )
+{
+ gcry_sexp_t sexp = NULL;
+ gcry_mpi_t * m;
+ const char * fmt;
+ int ncount = 0;
+ int rc = 0;
+
+ if( !r_sig || !sig )
+ return CDK_Inv_Value;
+
+ ncount = cdk_pk_get_nsig( sig->pubkey_algo );
+ m = convert_to_gcrympi( sig->mpi, ncount );
+ if( !m )
+ return CDK_MPI_Error;
+ if( is_RSA( sig->pubkey_algo ) ) {
+ fmt = "(sig-val(openpgp-rsa(s%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, m[0] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else if( is_ELG( sig->pubkey_algo ) ) {
+ fmt = "(sig-val(openpgp-elg(r%m)(s%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else if( is_DSA( sig->pubkey_algo ) ) {
+ fmt = "(sig-val(openpgp-dsa(r%m)(s%m)))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else
+ rc = CDK_Inv_Algo;
+ _cdk_free_mpibuf( ncount, m );
+ *r_sig = sexp;
+ return rc;
+}
+
+
+static int
+sexp_to_pubenc( cdk_pkt_pubkey_enc_t enc, gcry_sexp_t sexp)
+{
+ int rc;
+
+ if( !sexp || !enc )
+ return CDK_Inv_Value;
+
+ if( is_RSA( enc->pubkey_algo) )
+ return sexp_to_bitmpi( sexp, "a", &enc->mpi[0] );
+ else if( is_ELG( enc->pubkey_algo) ) {
+ rc = sexp_to_bitmpi( sexp, "a", &enc->mpi[0] );
+ if( !rc )
+ rc = sexp_to_bitmpi( sexp, "b", &enc->mpi[1] );
+ return rc;
+ }
+ return CDK_Inv_Algo;
+}
+
+
+static int
+pubenc_to_sexp( gcry_sexp_t * r_sexp, cdk_pkt_pubkey_enc_t enc)
+{
+ gcry_sexp_t sexp = NULL;
+ gcry_mpi_t * m;
+ const char * fmt;
+ int ncount;
+ int rc = 0;
+
+ if( !r_sexp || !enc )
+ return CDK_Inv_Value;
+
+ ncount = cdk_pk_get_nenc( enc->pubkey_algo );
+ m = convert_to_gcrympi( enc->mpi, ncount );
+ if( !m )
+ return CDK_MPI_Error;
+ if( is_RSA( enc->pubkey_algo ) ) {
+ fmt = "(enc-val(openpgp-rsa((a%m))))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, m[0] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else if( is_ELG( enc->pubkey_algo ) ) {
+ fmt = "(enc-val(openpgp-elg((a%m)(b%m))))";
+ if( gcry_sexp_build( &sexp, NULL, fmt, m[0], m[1] ) )
+ rc = CDK_Gcry_Error;
+ }
+ else
+ rc = CDK_Inv_Algo;
+ _cdk_free_mpibuf( ncount, m );
+ *r_sexp = sexp;
+ return rc;
+}
+
+
+static int
+is_unprotected( cdk_pkt_seckey_t sk )
+{
+ if( sk->is_protected && !sk->mpi[0] )
+ return 0;
+ return 1;
+}
+
+
+/**
+ * cdk_pk_encrypt:
+ * @pk: the public key
+ * @pke: the public key encrypted packet
+ * @esk: the actual session key
+ *
+ * Encrypt the session key in @esk and write its encrypted content
+ * into the @pke struct.
+ **/
+cdk_error_t
+cdk_pk_encrypt( cdk_pkt_pubkey_t pk, cdk_pkt_pubkey_enc_t pke,
+ cdk_sesskey_t esk )
+{
+ gcry_sexp_t s_data = NULL, s_pkey = NULL, s_ciph = NULL;
+ int rc;
+
+ if( !pk || !esk || !pke )
+ return CDK_Inv_Value;
+
+ if( !KEY_CAN_ENCRYPT( pk->pubkey_algo ) )
+ return CDK_Inv_Algo;
+
+ rc = enckey_to_sexp( &s_data, esk->a );
+ if( !rc )
+ rc = pubkey_to_sexp( &s_pkey, pk );
+ if( !rc )
+ rc = gcry_pk_encrypt( &s_ciph, s_data, s_pkey );
+ if( !rc )
+ rc = sexp_to_pubenc( pke, s_ciph );
+
+ gcry_sexp_release( s_data );
+ gcry_sexp_release( s_pkey );
+ gcry_sexp_release( s_ciph );
+ return rc;
+}
+
+
+/**
+ * cdk_pk_decrypt:
+ * @sk: the secret key
+ * @pke: public key encrypted packet
+ * @r_sk: the object to store the plain session key
+ *
+ * Decrypt the encrypted session key from @pke into @r_sk.
+ **/
+cdk_error_t
+cdk_pk_decrypt( cdk_pkt_seckey_t sk, cdk_pkt_pubkey_enc_t pke,
+ cdk_sesskey_t * r_sk )
+{
+ gcry_sexp_t s_data = NULL, s_skey = NULL, s_plain = NULL;
+ int rc;
+
+ if( !sk || !r_sk || !pke )
+ return CDK_Inv_Value;
+
+ if( !is_unprotected( sk ) )
+ return CDK_Inv_Mode;
+
+ rc = seckey_to_sexp( &s_skey, sk );
+ if( !rc )
+ rc = pubenc_to_sexp( &s_data, pke );
+ if( !rc && gcry_pk_decrypt( &s_plain, s_data, s_skey ) )
+ rc = CDK_Gcry_Error;
+ if( !rc ) {
+ rc = cdk_sesskey_new( r_sk );
+ if( !rc )
+ (*r_sk)->a = gcry_sexp_nth_mpi( s_plain, 0, 0 );
+ }
+
+ gcry_sexp_release( s_data );
+ gcry_sexp_release( s_skey );
+ gcry_sexp_release( s_plain );
+ return rc;
+}
+
+
+/**
+ * cdk_pk_sign:
+ * @sk: secret key
+ * @sig: signature
+ * @md: the message digest
+ *
+ * Sign the message digest from @md and write the result into @sig.
+ **/
+cdk_error_t
+cdk_pk_sign( cdk_pkt_seckey_t sk, cdk_pkt_signature_t sig, const byte * md )
+{
+ gcry_sexp_t s_skey = NULL, s_sig = NULL, s_hash = NULL;
+ byte * encmd = NULL;
+ size_t enclen = 0;
+ int nbits, rc;
+
+ if( !sk || !sk->pk || !sig || !md )
+ return CDK_Inv_Value;
+
+ if( !is_unprotected( sk ) )
+ return CDK_Inv_Mode;
+
+ if( !KEY_CAN_SIGN( sig->pubkey_algo ) )
+ return CDK_Inv_Algo;
+
+ nbits = cdk_pk_get_nbits( sk->pk );
+ rc = _cdk_digest_encode_pkcs1( &encmd, &enclen, sk->pk->pubkey_algo, md,
+ sig->digest_algo, nbits );
+ if( !rc )
+ rc = seckey_to_sexp( &s_skey, sk );
+ if( !rc )
+ rc = digest_to_sexp( &s_hash, sig->digest_algo, encmd, enclen );
+ if( !rc && gcry_pk_sign( &s_sig, s_hash, s_skey ) )
+ rc = CDK_Gcry_Error;
+ if( !rc )
+ rc = sexp_to_sig( sig, s_sig );
+ sig->digest_start[0] = md[0];
+ sig->digest_start[1] = md[1];
+
+ gcry_sexp_release( s_skey );
+ gcry_sexp_release( s_hash );
+ gcry_sexp_release( s_sig );
+ cdk_free( encmd );
+ return rc;
+}
+
+
+/**
+ * cdk_pk_verify:
+ * @pk: the public key
+ * @sig: signature
+ * @md: the message digest
+ *
+ * Verify the signature in @sig and compare it with the message digest in @md.
+ **/
+cdk_error_t
+cdk_pk_verify( cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md)
+{
+ gcry_sexp_t s_pkey = NULL, s_sig = NULL, s_hash = NULL;
+ byte * encmd = NULL;
+ size_t enclen = 0;
+ int nbits, rc;
+
+ if( !pk || !sig || !md )
+ return CDK_Inv_Value;
+
+ nbits = cdk_pk_get_nbits( pk );
+ rc = pubkey_to_sexp( &s_pkey, pk );
+ if( !rc )
+ rc = sig_to_sexp( &s_sig, sig );
+ if( !rc )
+ rc = _cdk_digest_encode_pkcs1( &encmd, &enclen, pk->pubkey_algo, md,
+ sig->digest_algo, nbits );
+ if( !rc )
+ rc = digest_to_sexp( &s_hash, sig->digest_algo, encmd, enclen );
+ if( !rc && gcry_pk_verify( s_sig, s_hash, s_pkey ) )
+ rc = CDK_Bad_Sig;
+
+ gcry_sexp_release( s_sig );
+ gcry_sexp_release( s_hash );
+ gcry_sexp_release( s_pkey );
+ cdk_free( encmd );
+ return rc;
+}
+
+
+int
+cdk_pk_get_nbits( cdk_pkt_pubkey_t pk )
+{
+ if( !pk || !pk->mpi[0] )
+ return 0;
+ return pk->mpi[0]->bits;
+}
+
+
+int
+cdk_pk_get_npkey( int algo )
+{
+size_t bytes;
+
+ if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &bytes))
+ return 0;
+
+ return bytes;
+}
+
+
+int
+cdk_pk_get_nskey( int algo )
+{
+size_t bytes;
+
+ if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &bytes))
+ return 0;
+
+ bytes -= cdk_pk_get_npkey( algo );
+ return bytes;
+}
+
+
+int
+cdk_pk_get_nsig( int algo )
+{
+size_t bytes;
+
+ if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &bytes))
+ return 0;
+ return bytes;
+}
+
+
+int
+cdk_pk_get_nenc( int algo )
+{
+size_t bytes;
+
+ if (gcry_pk_algo_info( algo, GCRYCTL_GET_ALGO_NENCR, NULL, &bytes))
+ return 0;
+ return bytes;
+}
+
+
+int
+_cdk_pk_algo_usage( int algo )
+{
+ int usage = 0;
+
+ switch( algo ) {
+ case CDK_PK_RSA : usage = PK_USAGE_SIGN | PK_USAGE_ENCR; break;
+ case CDK_PK_RSA_E: usage = PK_USAGE_ENCR; break;
+ case CDK_PK_RSA_S: usage = PK_USAGE_SIGN; break;
+ case CDK_PK_ELG : usage = PK_USAGE_SIGN | PK_USAGE_ENCR; break;
+ case CDK_PK_ELG_E: usage = PK_USAGE_ENCR; break;
+ case CDK_PK_DSA : usage = PK_USAGE_SIGN; break;
+ }
+ return usage;
+}
+
+
+int
+_cdk_pk_test_algo( int algo, unsigned int usage_flags )
+{
+ size_t n = usage_flags;
+
+ if( algo < 0 || algo > 110 )
+ return GPG_ERR_INV_PARAMETER;
+ return gcry_pk_algo_info( algo, GCRYCTL_TEST_ALGO, NULL, &n );
+}
+
+
+static int
+read_mpi( cdk_mpi_t a, byte * buf, size_t * r_count, size_t * r_nbits )
+{
+ if( !a || !buf || !r_count )
+ return CDK_Inv_Value;
+
+ if( a->bytes + 2 > *r_count )
+ return CDK_General_Error;
+ *r_count = a->bytes + 2;
+ memcpy( buf, a->data, *r_count );
+ if( r_nbits )
+ *r_nbits = a->bits;
+ return 0;
+}
+
+
+cdk_error_t
+cdk_pk_get_mpi( cdk_pkt_pubkey_t pk, int idx,
+ byte * buf, size_t * r_count, size_t * r_nbits )
+{
+ if( !pk || idx < 0 || !r_count )
+ return CDK_Inv_Value;
+ if( idx > cdk_pk_get_npkey( pk->pubkey_algo ) )
+ return CDK_Inv_Value;
+ return read_mpi( pk->mpi[idx], buf, r_count, r_nbits );
+}
+
+
+cdk_error_t
+cdk_sk_get_mpi( cdk_pkt_seckey_t sk, int idx,
+ byte * buf, size_t * r_count, size_t * r_nbits)
+{
+ if( !sk || idx < 0 || !r_count)
+ return CDK_Inv_Value;
+ if( idx > cdk_pk_get_nskey( sk->pubkey_algo ) )
+ return CDK_Inv_Value;
+ return read_mpi( sk->mpi[idx], buf, r_count, r_nbits );
+}
+
+
+static u16
+checksum_mpi( cdk_mpi_t m )
+{
+ int i;
+ u16 chksum = 0;
+
+ if( !m )
+ return 0;
+ for( i = 0; i < m->bytes + 2; i++)
+ chksum += m->data[i];
+ return chksum;
+}
+
+
+cdk_error_t
+cdk_sk_unprotect( cdk_pkt_seckey_t sk, const char * pw )
+{
+ cdk_cipher_hd_t hd;
+ cdk_dek_t dek = NULL;
+ cdk_mpi_t a;
+ u16 chksum = 0;
+ size_t ndata, nbits;
+ int j, i, dlen, pos = 0, nskey;
+ int rc;
+ byte * data = NULL;
+
+ if( !sk )
+ return CDK_Inv_Value;
+
+ nskey = cdk_pk_get_nskey( sk->pubkey_algo );
+ if( sk->is_protected ) {
+ rc = cdk_dek_from_passphrase( &dek, sk->protect.algo,
+ sk->protect.s2k, 0, pw );
+ if( rc )
+ return rc;
+ hd = cdk_cipher_open( sk->protect.algo, 1,
+ dek->key, dek->keylen,
+ sk->protect.iv, sk->protect.ivlen );
+ if( !hd ) {
+ cdk_free( dek );
+ return CDK_Inv_Algo;
+ }
+ wipemem( dek, sizeof dek );
+ cdk_dek_free( dek );
+ chksum = 0;
+ if( sk->version == 4 ) {
+ ndata = sk->enclen;
+ data = cdk_salloc( ndata, 1 );
+ if( !data )
+ return CDK_Out_Of_Core;
+ cdk_cipher_decrypt( hd, data, sk->encdata, ndata );
+ if( sk->protect.sha1chk ) {
+ /* This is the new SHA1 checksum method to detect tampering
+ with the key as used by the Klima/Rosa attack */
+ sk->csum = 0;
+ chksum = 1;
+ dlen = cdk_md_get_algo_dlen( CDK_MD_SHA1 );
+ if( ndata < dlen ) {
+ cdk_free( data );
+ return CDK_Inv_Packet;
+ }
+ else {
+ cdk_md_hd_t md = cdk_md_open( CDK_MD_SHA1, 1 );
+ if( !md )
+ return CDK_Gcry_Error;
+ cdk_md_write( md, data, ndata - dlen );
+ cdk_md_final( md );
+ if( !memcmp( cdk_md_read( md, CDK_MD_SHA1 ),
+ data + ndata - dlen, dlen ) )
+ chksum = 0; /* digest does match */
+ cdk_md_close( md );
+ }
+ }
+ else {
+ for( i = 0; i < ndata - 2; i++)
+ chksum += data[i];
+ sk->csum = data[ndata - 2] << 8 | data[ndata - 1];
+ }
+ if( sk->csum == chksum ) {
+ for( i = 0; i < nskey; i++ ) {
+ nbits = data[pos] << 8 | data[pos + 1];
+ ndata = (nbits + 7) / 8;
+ a = sk->mpi[i] = cdk_salloc( sizeof *a + ndata + 2, 1 );
+ if( !a ) {
+ cdk_free( data );
+ return CDK_Out_Of_Core;
+ }
+ a->bits = nbits;
+ a->bytes = ndata;
+ for( j = 0; j < ndata + 2; j++ )
+ a->data[j] = data[pos++];
+ }
+ }
+ wipemem( data, sk->enclen );
+ cdk_free( data );
+ }
+ else {
+ chksum = 0;
+ for( i = 0; i < nskey; i++ ) {
+ a = sk->mpi[i];
+ cdk_cipher_sync( hd );
+ cdk_cipher_decrypt( hd, a->data+2, a->data+2, a->bytes );
+ chksum += checksum_mpi( a );
+ }
+ }
+ cdk_cipher_close( hd );
+ }
+ else {
+ chksum = 0;
+ for( i = 0; i < nskey; i++ )
+ chksum += checksum_mpi( sk->mpi[i] );
+ }
+ if( chksum != sk->csum )
+ return CDK_Chksum_Error;
+ sk->is_protected = 0;
+ return 0;
+}
+
+
+cdk_error_t
+cdk_sk_protect( cdk_pkt_seckey_t sk, const char * pass )
+{
+ cdk_cipher_hd_t hd;
+ cdk_md_hd_t md;
+ cdk_mpi_t a;
+ cdk_dek_t dek;
+ cdk_s2k_t s2k;
+ byte * p;
+ size_t enclen = 0, nskey, i;
+ int rc;
+
+ rc = cdk_s2k_new( &s2k, 3, CDK_MD_SHA1, NULL );
+ if( rc )
+ return rc;
+ rc = cdk_dek_from_passphrase( &dek, CDK_CIPHER_3DES, s2k, 2, pass );
+ if( rc )
+ return rc;
+
+ nskey = cdk_pk_get_nskey( sk->pubkey_algo );
+ for( i = 0; i < nskey; i++ ) {
+ enclen += 2;
+ enclen += sk->mpi[i]->bytes;
+ }
+ p = sk->encdata = cdk_calloc( 1, enclen + 20 + 1 );
+ if( !p )
+ return CDK_Out_Of_Core;
+ enclen = 0;
+ for( i = 0; i < nskey; i++ ) {
+ a = sk->mpi[i];
+ p[enclen++] = a->bits >> 8;
+ p[enclen++] = a->bits;
+ memcpy( p + enclen, a->data, a->bytes );
+ enclen += a->bytes;
+ }
+ enclen += 20;
+ sk->enclen = enclen;
+ sk->protect.s2k = s2k;
+ sk->protect.algo = CDK_CIPHER_3DES;
+ sk->protect.ivlen = cdk_cipher_get_algo_blklen( sk->protect.algo );
+ gcry_randomize( sk->protect.iv, sk->protect.ivlen, GCRY_STRONG_RANDOM );
+ hd = cdk_cipher_open( sk->protect.algo, 1,
+ dek->key, dek->keylen,
+ sk->protect.iv, sk->protect.ivlen );
+ if( !hd ) {
+ cdk_free( p );
+ return CDK_Gcry_Error;
+ }
+
+ md = cdk_md_open( CDK_MD_SHA1, GCRY_CIPHER_SECURE );
+ if( !md ) {
+ cdk_cipher_close( hd );
+ cdk_free( p );
+ return CDK_Gcry_Error;
+ }
+ sk->protect.sha1chk = 1;
+ sk->is_protected = 1;
+ sk->csum = 0;
+ cdk_md_write( md, p, enclen - 20 );
+ cdk_md_final( md );
+ memcpy( p + enclen - 20, cdk_md_read( md, 0 ), 20 );
+ cdk_md_close( md );
+ rc = cdk_cipher_encrypt( hd, p, p, enclen );
+ cdk_cipher_close( hd );
+ cdk_dek_free( dek );
+ return rc;
+}
+
+
+/**
+ * cdk_pk_from_secret_key:
+ * @sk: the secret key
+ * @ret_pk: the new public key
+ *
+ * Create a new public key from a secret key.
+ **/
+cdk_error_t
+cdk_pk_from_secret_key( cdk_pkt_seckey_t sk, cdk_pkt_pubkey_t* ret_pk )
+{
+ if( !sk )
+ return CDK_Inv_Value;
+ return _cdk_copy_pubkey( ret_pk, sk->pk );
+}
+
+
+int
+cdk_pk_revoke_import( cdk_keydb_hd_t db, const char * revcert )
+{
+ /* due to the fact the keydb code can't insert or modify packets
+ it is not possible to handle this step yet */
+ return 0;
+}
+
+
+int
+cdk_pk_revoke_create( cdk_pkt_seckey_t sk, int code, const char * inf,
+ char ** ret_revcert )
+{
+ cdk_md_hd_t md;
+ cdk_subpkt_t node;
+ cdk_pkt_signature_t sig;
+ char * p = NULL, * dat;
+ int n;
+
+ if( !sk || !ret_revcert )
+ return CDK_Inv_Value;
+ if( code < 0 || code > 3 )
+ return CDK_Inv_Value;
+
+ sig = cdk_calloc( 1, sizeof * sig );
+ if( !sig )
+ return CDK_Out_Of_Core;
+ _cdk_sig_create( sk->pk, sig );
+ n = 1;
+ if( inf ) {
+ n += strlen( p );
+ p = cdk_utf8_encode( inf );
+ }
+ dat = cdk_calloc( 1, n+1 );
+ if( !dat ) {
+ _cdk_free_signature( sig );
+ return CDK_Out_Of_Core;
+ }
+ dat[0] = code;
+ if( inf )
+ memcpy( dat+1, p, strlen( p ) );
+ cdk_free( p );
+
+ node = cdk_subpkt_new( n );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_REVOC_REASON, dat, n );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ cdk_free( dat );
+
+ md = cdk_md_open( CDK_MD_SHA1, 0 );
+ if( !md ) {
+ _cdk_free_signature( sig );
+ return CDK_Gcry_Error;
+ }
+ _cdk_hash_pubkey( sk->pk, md, 0 );
+ _cdk_free_signature( sig );
+ return 0;
+}
+
+
+int
+_cdk_sk_get_csum( cdk_pkt_seckey_t sk )
+{
+ u16 csum = 0, i;
+
+ if( !sk )
+ return 0;
+ for( i = 0; i < cdk_pk_get_nskey( sk->pubkey_algo ); i++ )
+ csum += checksum_mpi( sk->mpi[i] );
+ return csum;
+}
+
+
+int
+cdk_pk_get_fingerprint( cdk_pkt_pubkey_t pk, byte * fpr )
+{
+ cdk_md_hd_t hd;
+ int md_algo;
+ int dlen = 0;
+
+ if( !pk || !fpr )
+ return CDK_Inv_Value;
+
+ if( pk->version < 4 && is_RSA( pk->pubkey_algo ) )
+ md_algo = CDK_MD_MD5; /* special */
+ else
+ md_algo = pk->version < 4 ? CDK_MD_RMD160 : CDK_MD_SHA1;
+ dlen = cdk_md_get_algo_dlen( md_algo );
+ hd = cdk_md_open( md_algo, 0 );
+ if( !hd )
+ return CDK_Gcry_Error;
+ _cdk_hash_pubkey( pk, hd, 1 );
+ cdk_md_final( hd );
+ memcpy( fpr, cdk_md_read( hd, md_algo ), dlen );
+ cdk_md_close( hd );
+ if( dlen == 16 )
+ memset( fpr + 16, 0, 4 );
+ return 0;
+}
+
+
+u32
+cdk_pk_fingerprint_get_keyid( const byte * fpr, size_t fprlen, u32 * keyid )
+{
+ u32 lowbits = 0;
+
+ /* in this case we say the key is a V3 RSA key and we can't
+ use the fingerprint to get the keyid. */
+ if( fpr && fprlen == 16 )
+ return 0;
+ else if( keyid && fpr ) {
+ keyid[0] = _cdk_buftou32( fpr + 12 );
+ keyid[1] = _cdk_buftou32( fpr + 16 );
+ lowbits = keyid[1];
+ }
+ else if( fpr )
+ lowbits = _cdk_buftou32( fpr + 16 );
+ return lowbits;
+}
+
+
+u32
+cdk_pk_get_keyid( cdk_pkt_pubkey_t pk, u32 * keyid )
+{
+ u32 lowbits = 0;
+ byte buf[24];
+
+ if( pk &&( !pk->keyid[0] || !pk->keyid[1] ) ) {
+ if( pk->version < 4 && is_RSA( pk->pubkey_algo ) ) {
+ size_t n = pk->mpi[0]->bytes;
+ const byte * p = pk->mpi[0]->data + 2;
+ pk->keyid[0] = p[n-8] << 24 | p[n-7] << 16 | p[n-6] << 8 | p[n-5];
+ pk->keyid[1] = p[n-4] << 24 | p[n-3] << 16 | p[n-2] << 8 | p[n-1];
+ }
+ else if( pk->version == 4 ) {
+ cdk_pk_get_fingerprint( pk, buf );
+ pk->keyid[0] = _cdk_buftou32( buf + 12 );
+ pk->keyid[1] = _cdk_buftou32( buf + 16 );
+ }
+ }
+ lowbits = pk ? pk->keyid[1] : 0;
+ if( keyid && pk ) {
+ keyid[0] = pk->keyid[0];
+ keyid[1] = pk->keyid[1];
+ }
+ return lowbits;
+}
+
+
+u32
+cdk_sk_get_keyid( cdk_pkt_seckey_t sk, u32 * keyid )
+{
+ u32 lowbits = 0;
+
+ if( sk && sk->pk ) {
+ lowbits = cdk_pk_get_keyid( sk->pk, keyid );
+ sk->keyid[0] = sk->pk->keyid[0];
+ sk->keyid[1] = sk->pk->keyid[1];
+ }
+ return lowbits;
+}
+
+
+u32
+cdk_sig_get_keyid( cdk_pkt_signature_t sig, u32 * keyid )
+{
+ u32 lowbits = sig ? sig->keyid[1] : 0;
+
+ if( keyid && sig ) {
+ keyid[0] = sig->keyid[0];
+ keyid[1] = sig->keyid[1];
+ }
+ return lowbits;
+}
+
+
+u32
+_cdk_pkt_get_keyid( cdk_packet_t pkt, u32 * keyid )
+{
+ u32 lowbits;
+
+ if( !pkt )
+ return 0;
+
+ switch( pkt->pkttype ) {
+ case CDK_PKT_PUBLIC_KEY:
+ case CDK_PKT_PUBLIC_SUBKEY:
+ lowbits = cdk_pk_get_keyid( pkt->pkt.public_key, keyid );
+ break;
+
+ case CDK_PKT_SECRET_KEY:
+ case CDK_PKT_SECRET_SUBKEY:
+ lowbits = cdk_sk_get_keyid( pkt->pkt.secret_key, keyid );
+ break;
+
+ case CDK_PKT_SIGNATURE:
+ lowbits = cdk_sig_get_keyid( pkt->pkt.signature, keyid );
+ break;
+
+ default:
+ lowbits = 0;
+ }
+ return lowbits;
+}
+
+
+int
+_cdk_pkt_get_fingerprint( cdk_packet_t pkt, byte * fpr )
+{
+ if( !pkt || !fpr )
+ return CDK_Inv_Value;
+
+ switch( pkt->pkttype ) {
+ case CDK_PKT_PUBLIC_KEY:
+ case CDK_PKT_PUBLIC_SUBKEY:
+ return cdk_pk_get_fingerprint( pkt->pkt.public_key, fpr );
+
+ case CDK_PKT_SECRET_KEY:
+ case CDK_PKT_SECRET_SUBKEY:
+ return cdk_pk_get_fingerprint( pkt->pkt.secret_key->pk, fpr );
+
+ default:
+ return CDK_Inv_Packet;
+ }
+ return 0;
+}
diff --git a/libextra/opencdk/read-packet.c b/libextra/opencdk/read-packet.c
new file mode 100644
index 0000000000..1dadf529fa
--- /dev/null
+++ b/libextra/opencdk/read-packet.c
@@ -0,0 +1,1091 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * read-packet.c - Read OpenPGP packets
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "cipher.h"
+
+
+static int
+stream_getc( cdk_stream_t s )
+{
+ return cdk_stream_getc( s );
+}
+
+
+
+static int
+stream_read (cdk_stream_t s, void * buf, size_t count, size_t * r_nread)
+{
+ int nread = cdk_stream_read (s, buf, count);
+ if( !nread )
+ return CDK_File_Error;
+ if( r_nread )
+ *r_nread = nread;
+ return 0;
+}
+
+
+static u32
+read_32 (cdk_stream_t buf)
+{
+ u32 u = 0;
+ int c;
+
+ if( !buf )
+ return (u32) -1;
+
+ if( (c = stream_getc( buf )) == EOF )
+ return (u32)-1;
+ u |= c << 24;
+ if( (c = stream_getc( buf )) == EOF )
+ return (u32)-1;
+ u |= c << 16;
+ if( (c = stream_getc( buf )) == EOF )
+ return (u32)-1;
+ u |= c << 8;
+ if( (c = stream_getc( buf )) == EOF )
+ return (u32)-1;
+ u |= c;
+ return u;
+}
+
+
+static u16
+read_16 (cdk_stream_t buf)
+{
+ u16 u = 0;
+ int c;
+
+ if( !buf )
+ return (u16)-1;
+
+ if( (c = stream_getc( buf )) == EOF )
+ return (u16)-1;
+ u |= c << 8;
+ if( (c = stream_getc( buf )) == EOF )
+ return (u16)-1;
+ u |= c;
+ return u;
+}
+
+
+#define check_s2k_mode( mode ) ( \
+ (mode) == CDK_S2K_SIMPLE \
+ || (mode) == CDK_S2K_SALTED \
+ || (mode) == CDK_S2K_ITERSALTED \
+)
+
+static int
+read_s2k( cdk_stream_t inp, cdk_s2k_t s2k )
+{
+ size_t nread = 0;
+ int rc = 0;
+
+ if( !inp || !s2k )
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug( "** read S2K part\n" );
+
+ s2k->mode = stream_getc( inp );
+ if( s2k->mode == EOF || !check_s2k_mode( s2k->mode ) )
+ return CDK_Inv_Packet;
+ s2k->hash_algo = stream_getc( inp );
+ if( s2k->mode == CDK_S2K_SIMPLE ) {
+ memset( s2k->salt, 0, sizeof s2k->salt );
+ /* nothing else to do */
+ }
+ else if( s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED ) {
+ rc = stream_read( inp, s2k->salt, sizeof s2k->salt, &nread );
+ if( !rc && nread != sizeof s2k->salt )
+ return CDK_Inv_Packet;
+ if( !rc && s2k->mode == CDK_S2K_ITERSALTED ) {
+ s2k->count = stream_getc( inp );
+ if( s2k->count == EOF )
+ return CDK_Inv_Packet;
+ }
+ }
+ else
+ return CDK_Inv_Mode;
+ return rc;
+}
+
+
+static int
+read_mpi (cdk_stream_t inp, cdk_mpi_t * ret_m, int secure)
+{
+ cdk_mpi_t m = NULL;
+ size_t nread = 0, nbits = 0, nbytes = 0;
+ int rc = 0;
+
+ if( !inp || !ret_m )
+ return CDK_Inv_Value;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** read MPI part\n");
+
+ nbits = read_16 (inp);
+ nbytes = (nbits + 7) / 8;
+ if( nbits > MAX_MPI_BITS || nbits == 0 )
+ return CDK_MPI_Error; /* sanity check */
+ m = secure ? cdk_salloc( sizeof *m + nbytes + 2, 1 ) :
+ cdk_calloc( 1, sizeof *m + nbytes + 2 );
+ if( !m )
+ return CDK_Out_Of_Core;
+ m->bytes = nbytes;
+ m->bits = nbits;
+
+ /* the prefix encodes the length of the MPI data */
+ m->data[0] = nbits >> 8;
+ m->data[1] = nbits;
+ rc = stream_read( inp, m->data + 2, nbytes, &nread );
+ if( !rc && nread != nbytes )
+ rc = CDK_MPI_Error;
+ *ret_m = m;
+ return rc;
+}
+
+
+size_t
+_cdk_pkt_read_len( FILE * inp, int * ret_partial )
+{
+ int c1 = 0, c2 = 0;
+ size_t pktlen = 0;
+
+ if( (c1 = fgetc (inp)) == EOF )
+ return (size_t)EOF;
+ if( c1 < 224 || c1 == 255 )
+ *ret_partial = 0; /* end of partial data */
+ if( c1 < 192 )
+ pktlen = c1;
+ else if( c1 >= 192 && c1 <= 223 ) {
+ if( (c2 = fgetc( inp )) == EOF )
+ return (size_t)EOF;
+ pktlen = ((c1 - 192) << 8) + c2 + 192;
+ }
+ else if( c1 == 255 ) {
+ pktlen = fgetc( inp ) << 24;
+ pktlen |= fgetc( inp ) << 16;
+ pktlen |= fgetc( inp ) << 8;
+ pktlen |= fgetc( inp );
+ if( !pktlen )
+ return (size_t)EOF;
+ }
+ else
+ pktlen = 1 << (c1 & 0x1f);
+ return pktlen;
+}
+
+
+static int
+read_encrypted( cdk_stream_t inp, size_t pktlen, cdk_pkt_encrypted_t enc,
+ int partial, int mdc )
+{
+ int rc = 0, version;
+
+ if( !inp || !enc )
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug( "** read encrypted packet %d bytes\n", pktlen );
+
+ if( mdc ) {
+ version = stream_getc( inp );
+ if( version != 1 )
+ return CDK_Inv_Packet;
+ enc->mdc_method = CDK_MD_SHA1;
+ pktlen--;
+ }
+ if( pktlen < 10 )
+ return CDK_Inv_Packet; /* we need at least blocksize + 2 bytes */
+ if( partial )
+ _cdk_stream_set_blockmode( inp, pktlen );
+ enc->len = pktlen;
+ enc->buf = inp;
+ return rc;
+}
+
+
+static int
+read_symkey_enc( cdk_stream_t inp, size_t pktlen, cdk_pkt_symkey_enc_t ske )
+{
+ cdk_s2k_t s2k;
+ size_t nread = 0, minlen = 0;
+ int rc = 0;
+
+ if( !inp || !ske )
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug( "** read symmetric key encrypted packet\n" );
+
+ ske->version = stream_getc( inp );
+ if( ske->version != 4 )
+ return CDK_Inv_Packet;
+
+ s2k = ske->s2k = cdk_calloc( 1, sizeof *ske->s2k );
+ if( !ske->s2k )
+ return CDK_Out_Of_Core;
+
+ ske->cipher_algo = stream_getc( inp );
+ s2k->mode = stream_getc( inp );
+ switch( s2k->mode ) {
+ case 0: minlen = 0; break;
+ case 1: minlen = 8; break;
+ case 3: minlen = 9; break;
+ }
+ s2k->hash_algo = stream_getc( inp );
+ if( s2k->mode == 0 )
+ ; /* nothing to do */
+ else if( s2k->mode == 1 || s2k->mode == 3 ) {
+ rc = stream_read( inp, s2k->salt, DIM (s2k->salt), &nread);
+ if( !rc && nread != DIM( s2k->salt ) )
+ return CDK_Inv_Packet;
+ if( !rc && s2k->mode == 3 )
+ s2k->count = stream_getc( inp );
+ }
+ else
+ return CDK_Inv_Packet;
+ ske->seskeylen = pktlen - 4 - minlen;
+ if( ske->seskeylen > sizeof ske->seskey )
+ return CDK_Inv_Packet;
+ for( nread = 0; nread < ske->seskeylen; nread++ ) {
+ ske->seskey[nread] = stream_getc( inp );
+ if( cdk_stream_eof( inp ) )
+ break;
+ }
+ return rc;
+}
+
+
+static int
+read_pubkey_enc( cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_enc_t pke )
+{
+ int rc = 0;
+ int i, nenc = 0;
+
+ if( !inp || !pke )
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug( "** read public key encrypted packet\n" );
+
+ if( pktlen < 10 )
+ return CDK_Inv_Packet;
+ pke->version = stream_getc( inp );
+ if( pke->version < 2 || pke->version > 3 )
+ return CDK_Inv_Packet;
+ pke->keyid[0] = read_32( inp );
+ pke->keyid[1] = read_32( inp );
+ if( !pke->keyid[0] && !pke->keyid[1] )
+ pke->throw_keyid = 1; /* RFC2440 "speculative" keyID */
+ pke->pubkey_algo = stream_getc( inp );
+ nenc = cdk_pk_get_nenc( pke->pubkey_algo );
+ if( !nenc )
+ return CDK_Inv_Algo;
+ for( i = 0; i < nenc; i++ ) {
+ rc = read_mpi( inp, &pke->mpi[i], 0 );
+ if( rc )
+ break;
+ }
+ return rc;
+}
+
+
+static int
+read_mdc( cdk_stream_t inp, cdk_pkt_mdc_t mdc )
+{
+ size_t n = 0;
+ int rc = 0;
+
+ if( !inp || !mdc )
+ return CDK_Inv_Value;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** read MDC packet\n");
+
+ rc = stream_read( inp, mdc->hash, 20, &n );
+ if( !rc && n != 20 )
+ rc = CDK_Inv_Packet;
+ return rc;
+}
+
+
+static int
+read_compressed( cdk_stream_t inp, size_t pktlen, cdk_pkt_compressed_t c )
+{
+ if (!inp || !c)
+ return CDK_Inv_Value;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** read compressed packet\n");
+
+ c->algorithm = stream_getc( inp );
+ if( c->algorithm > 2 )
+ return CDK_Inv_Packet;
+
+ /* don't know the size, so we read until EOF */
+ if( !pktlen ) {
+ c->len = 0;
+ c->buf = inp;
+ }
+
+ return 0;
+}
+
+
+static int
+read_public_key( cdk_stream_t inp, cdk_pkt_pubkey_t pk )
+{
+ int i = 0, ndays, npkey;
+ int rc = 0;
+
+ if (!inp || !pk)
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug( "** read public key packet\n" );
+
+ pk->is_invalid = 1; /* default to detect missing self signatures */
+ pk->is_revoked = 0;
+ pk->has_expired = 0;
+
+ pk->version = stream_getc( inp );
+ if( pk->version < 2 || pk->version > 4 )
+ return CDK_Inv_Packet_Ver;
+ pk->timestamp = read_32( inp );
+ if( pk->version < 4 ) {
+ ndays = read_16( inp );
+ if( ndays )
+ pk->expiredate = pk->timestamp + ndays * 86400L;
+ }
+ pk->pubkey_algo = stream_getc( inp );
+ npkey = cdk_pk_get_npkey( pk->pubkey_algo );
+ if( !npkey )
+ return CDK_Inv_Algo;
+ for( i = 0; i < npkey; i++ ) {
+ rc = read_mpi( inp, &pk->mpi[i], 0 );
+ if ( rc )
+ break;
+ }
+ pk->pubkey_usage = _cdk_pk_algo_usage( pk->pubkey_algo );
+ return rc;
+}
+
+
+static int
+read_public_subkey( cdk_stream_t inp, cdk_pkt_pubkey_t pk )
+{
+ if( !inp || !pk )
+ return CDK_Inv_Value;
+ return read_public_key( inp, pk );
+}
+
+
+static int
+read_secret_key( cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk )
+{
+ size_t p1 = 0, p2 = 0, nread = 0;
+ int i = 0, blklen = 0, nskey = 0;
+ int rc = 0;
+
+ if( !inp || !sk || !sk->pk )
+ return CDK_Inv_Value;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** read secret key\n");
+
+ p1 = cdk_stream_tell( inp );
+ rc = read_public_key( inp, sk->pk );
+ if( rc )
+ return rc;
+
+ sk->s2k_usage = stream_getc( inp );
+ sk->protect.sha1chk = 0;
+ if( sk->s2k_usage == 254 || sk->s2k_usage == 255 ) {
+ sk->protect.sha1chk = (sk->s2k_usage == 254);
+ sk->protect.algo = stream_getc( inp );
+ sk->protect.s2k = cdk_calloc( 1, sizeof *sk->protect.s2k );
+ if( !sk->protect.s2k )
+ return CDK_Out_Of_Core;
+ rc = read_s2k( inp, sk->protect.s2k );
+ if( rc )
+ return rc;
+ blklen = cdk_cipher_get_algo_blklen( sk->protect.algo );
+ if( !blklen )
+ return CDK_Inv_Packet;
+ sk->protect.ivlen = blklen;
+ rc = stream_read( inp, sk->protect.iv, sk->protect.ivlen, &nread );
+ if( !rc && nread != sk->protect.ivlen )
+ return CDK_Inv_Packet;
+ }
+ else
+ sk->protect.algo = sk->s2k_usage;
+ if( sk->protect.algo == CDK_CIPHER_NONE ) {
+ sk->csum = 0;
+ nskey = cdk_pk_get_nskey( sk->pk->pubkey_algo );
+ if( !nskey )
+ return CDK_Inv_Algo;
+ for( i = 0; i < nskey; i++ ) {
+ rc = read_mpi( inp, &sk->mpi[i], 1 );
+ if( rc )
+ break;
+ }
+ if( !rc ) {
+ sk->csum = read_16( inp );
+ sk->is_protected = 0;
+ }
+ }
+ else if( sk->pk->version < 4 ) {
+ /* mpi size isn't encrypted! */
+ nskey = cdk_pk_get_nskey( sk->pk->pubkey_algo );
+ if( !nskey )
+ return CDK_Inv_Algo;
+ for( i = 0; i < nskey; i++ ) {
+ rc = read_mpi( inp, &sk->mpi[i], 1 );
+ if( rc )
+ break;
+ }
+ if( !rc ) {
+ sk->csum = read_16( inp );
+ sk->is_protected = 1;
+ }
+ }
+ else {
+ /* we need to read the rest of the packet because we don't
+ have any information how long the encrypted mpi's are */
+ p2 = cdk_stream_tell( inp );
+ p2 -= p1;
+ sk->enclen = pktlen - p2;
+ if( sk->enclen < 2 )
+ return CDK_Inv_Packet; /* at least 16 bits for the checksum! */
+ sk->encdata = cdk_calloc( 1, sk->enclen + 1 );
+ if( !sk->encdata )
+ return CDK_Out_Of_Core;
+ rc = stream_read( inp, sk->encdata, sk->enclen, &nread );
+ if( rc )
+ return CDK_Inv_Packet;
+ nskey = cdk_pk_get_nskey( sk->pk->pubkey_algo );
+ if( !nskey )
+ return CDK_Inv_Algo;
+ for( i = 0; i < nskey; i++ )
+ sk->mpi[i] = NULL;
+ sk->is_protected = 1;
+ }
+ sk->is_primary = 1;
+ _cdk_copy_pk_to_sk( sk->pk, sk );
+ return rc;
+}
+
+
+static int
+read_secret_subkey( cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk )
+{
+ int rc = 0;
+
+ if( !inp || !sk || !sk->pk )
+ return CDK_Inv_Value;
+
+ rc = read_secret_key( inp, pktlen, sk );
+ sk->is_primary = 0;
+ return rc;
+}
+
+
+static int
+read_attribute (cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t attr)
+{
+ size_t nread = 0;
+ byte * buf;
+ const byte * p;
+ int len = 0;
+ int rc = 0;
+
+ if( !inp || !attr || !pktlen )
+ return CDK_Inv_Value;
+
+ strcpy( attr->name, "[attribute]" );
+ attr->len = strlen( attr->name );
+ buf = cdk_calloc( 1, pktlen );
+ if( !buf )
+ return CDK_Out_Of_Core;
+ rc = stream_read( inp, buf, pktlen, &nread );
+ if( rc ) {
+ cdk_free( buf );
+ return CDK_Inv_Packet;
+ }
+ p = buf;
+ len = *p++;
+ if (len == 255) {
+ len = _cdk_buftou32( p );
+ p += 4;
+ pktlen -= 4;
+ }
+ else if (len >= 192) {
+ if( pktlen < 2 ) {
+ cdk_free( buf );
+ return CDK_Inv_Packet;
+ }
+ len = ((len - 192) << 8) + *p + 192;
+ p++;
+ pktlen--;
+ }
+ if( *p != 1 ) { /* ATTRIBUTE IMAGE */
+ cdk_free( buf );
+ return CDK_Inv_Packet;
+ }
+ p++;
+
+ attr->attrib_img = cdk_calloc( 1, len );
+ if( !attr->attrib_img )
+ return CDK_Out_Of_Core;
+ attr->attrib_len = len;
+ memcpy( attr->attrib_img, p, len );
+ cdk_free( buf );
+ return rc;
+}
+
+
+static int
+read_user_id( cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t user_id )
+{
+ size_t nread = 0;
+ int rc = 0;
+
+ if( !inp || !user_id )
+ return CDK_Inv_Value;
+ if( !pktlen )
+ return CDK_Inv_Packet;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** read user ID packet\n");
+
+ user_id->len = pktlen;
+ rc = stream_read( inp, user_id->name, pktlen, &nread );
+ if( !rc && nread != pktlen )
+ return CDK_Inv_Packet;
+ user_id->name[nread] = '\0';
+ return rc;
+}
+
+
+static int
+read_subpkt( cdk_stream_t inp, cdk_subpkt_t * r_ctx, size_t * r_nbytes )
+{
+ byte c, c1;
+ size_t size = 0, nread, n = 0;
+ cdk_subpkt_t node;
+ int rc = 0;
+
+ if( !inp || !r_nbytes )
+ return CDK_Inv_Value;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** read sub packet");
+
+ *r_nbytes = 0;
+ c = stream_getc( inp );
+ n++;
+ if( c == 255 ) {
+ size = read_32( inp );
+ n += 4;
+ node = cdk_subpkt_new( size );
+ }
+ else if( c >= 192 && c < 255 ) {
+ c1 = stream_getc( inp );
+ n++;
+ if( c1 == 0 )
+ return 0;
+ size = ((c - 192) << 8) + c1 + 192;
+ node = cdk_subpkt_new( size );
+ }
+ else if( c < 192 ) {
+ size = c;
+ node = cdk_subpkt_new( size );
+ }
+ else
+ return CDK_Inv_Packet;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug (" `%d' bytes\n", size);
+
+ if( !node )
+ return CDK_Out_Of_Core;
+ node->size = size;
+ node->type = stream_getc( inp );
+ n++;
+ node->size--;
+ rc = stream_read( inp, node->d, node->size, &nread );
+ n += nread;
+ if( rc )
+ return rc;
+ *r_nbytes = n;
+ if( !*r_ctx )
+ *r_ctx = node;
+ else
+ cdk_subpkt_add( *r_ctx, node );
+ return rc;
+}
+
+
+static int
+read_onepass_sig( cdk_stream_t inp, size_t pktlen, cdk_pkt_onepass_sig_t sig )
+{
+ if( !inp || !sig )
+ return CDK_Inv_Value;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** read one pass signature packet\n");
+
+ if( pktlen < 13 )
+ return CDK_Inv_Packet;
+ sig->version = stream_getc( inp );
+ if( sig->version != 3 )
+ return CDK_Inv_Packet_Ver;
+ sig->sig_class = stream_getc( inp );
+ sig->digest_algo = stream_getc( inp );
+ sig->pubkey_algo = stream_getc( inp );
+ sig->keyid[0] = read_32( inp );
+ sig->keyid[1] = read_32( inp );
+ sig->last = stream_getc( inp );
+ return 0;
+}
+
+
+static int
+read_signature( cdk_stream_t inp, size_t pktlen, cdk_pkt_signature_t sig )
+{
+ cdk_subpkt_t node = NULL;
+ size_t nbytes;
+ int i, size, nsig;
+ int rc = 0;
+
+ if( !inp || !sig )
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug( "** read signature packet\n" );
+
+ if( pktlen < 10 )
+ return CDK_Inv_Packet;
+ sig->version = stream_getc( inp );
+ if( sig->version < 2 || sig->version > 4 )
+ return CDK_Inv_Packet_Ver;
+
+ sig->flags.exportable = 1;
+ sig->flags.revocable = 1;
+
+ if( sig->version < 4 ) {
+ if( stream_getc( inp ) != 5 )
+ return CDK_Inv_Packet;
+ sig->sig_class = stream_getc( inp );
+ sig->timestamp = read_32( inp );
+ sig->keyid[0] = read_32( inp );
+ sig->keyid[1] = read_32( inp );
+ sig->pubkey_algo = stream_getc( inp );
+ sig->digest_algo = stream_getc( inp );
+ sig->digest_start[0] = stream_getc( inp );
+ sig->digest_start[1] = stream_getc( inp );
+ nsig = cdk_pk_get_nsig( sig->pubkey_algo );
+ if( !nsig )
+ return CDK_Inv_Algo;
+ for( i = 0; i < nsig; i++ ) {
+ rc = read_mpi( inp, &sig->mpi[i], 0 );
+ if( rc )
+ break;
+ }
+ }
+ else {
+ sig->sig_class = stream_getc( inp );
+ sig->pubkey_algo = stream_getc( inp );
+ sig->digest_algo = stream_getc( inp );
+ sig->hashed_size = read_16( inp );
+ size = sig->hashed_size;
+ sig->hashed = NULL;
+ while( size > 0 ) {
+ rc = read_subpkt( inp, &sig->hashed, &nbytes );
+ if( rc )
+ break;
+ size -= nbytes;
+ }
+ sig->unhashed_size = read_16( inp );
+ size = sig->unhashed_size;
+ sig->unhashed = NULL;
+ while( size > 0 ) {
+ rc = read_subpkt( inp, &sig->unhashed, &nbytes );
+ if( rc )
+ break;
+ size -= nbytes;
+ }
+
+ /* Setup the standard packet entries, so we can use V4
+ signatures similar to V3. */
+ for( node = sig->unhashed; node; node = node->next ) {
+ if( node->type == CDK_SIGSUBPKT_ISSUER ) {
+ sig->keyid[0] = _cdk_buftou32( node->d );
+ sig->keyid[1] = _cdk_buftou32( node->d + 4 );
+ }
+ else if( node->type == CDK_SIGSUBPKT_EXPORTABLE
+ && node->d[0] == 0 ) {
+ /* this packet might be also placed in the unhashed area */
+ sig->flags.exportable = 0;
+ }
+ }
+ for( node = sig->hashed; node; node = node->next ) {
+ if( node->type == CDK_SIGSUBPKT_SIG_CREATED )
+ sig->timestamp = _cdk_buftou32( node->d );
+ else if( node->type == CDK_SIGSUBPKT_SIG_EXPIRE ) {
+ sig->expiredate = _cdk_buftou32( node->d );
+ if( sig->expiredate > 0
+ && sig->expiredate < _cdk_timestamp() )
+ sig->flags.expired = 1;
+ }
+ else if( node->type == CDK_SIGSUBPKT_POLICY )
+ sig->flags.policy_url = 1;
+ else if( node->type == CDK_SIGSUBPKT_NOTATION )
+ sig->flags.notation = 1;
+ else if( node->type == CDK_SIGSUBPKT_REVOCABLE && node->d[0] == 0 )
+ sig->flags.revocable = 0;
+ else if( node->type == CDK_SIGSUBPKT_EXPORTABLE
+ && node->d[0] == 0 )
+ sig->flags.exportable = 0;
+ }
+ if( sig->sig_class == 0x1F ) {
+ cdk_desig_revoker_t r, rnode;
+ for( node = sig->hashed; node; node = node->next ) {
+ if( node->type == CDK_SIGSUBPKT_REV_KEY ) {
+ rnode = cdk_calloc( 1, sizeof * rnode );
+ if( !rnode )
+ return CDK_Out_Of_Core;
+ rnode->class = node->d[0];
+ rnode->algid = node->d[1];
+ memcpy( rnode->fpr, node->d+2, 20 );
+ if( !sig->revkeys )
+ sig->revkeys = rnode;
+ else {
+ for( r = sig->revkeys; r->next; r = r->next )
+ ;
+ r->next = rnode;
+ }
+ }
+ }
+ }
+ sig->digest_start[0] = stream_getc( inp );
+ sig->digest_start[1] = stream_getc( inp );
+ nsig = cdk_pk_get_nsig( sig->pubkey_algo );
+ if( !nsig )
+ return CDK_Inv_Algo;
+ for( i = 0; i < nsig; i++ ) {
+ rc = read_mpi( inp, &sig->mpi[i], 0 );
+ if( rc )
+ break;
+ }
+ }
+ return rc;
+}
+
+
+static int
+read_literal( cdk_stream_t inp, size_t pktlen, cdk_pkt_literal_t * ret_pt,
+ int partial )
+{
+ cdk_pkt_literal_t pt = *ret_pt;
+ size_t nread = 0;
+ int rc = 0;
+
+ if( !inp || !pt )
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug( "** read literal packet\n" );
+
+ pt->mode = stream_getc( inp );
+ if( pt->mode != 0x62 && pt->mode != 0x74 )
+ return CDK_Inv_Packet;
+ pt->namelen = stream_getc( inp );
+ if( pt->namelen ) {
+ *ret_pt = pt = cdk_realloc( pt, sizeof * pt + pt->namelen + 1 );
+ if( !pt )
+ return CDK_Out_Of_Core;
+ rc = stream_read( inp, pt->name, pt->namelen, &nread );
+ if( !rc && nread != pt->namelen )
+ return CDK_Inv_Packet;
+ pt->name[pt->namelen] = '\0';
+ }
+ pt->timestamp = read_32( inp );
+ pktlen = pktlen - 6 - pt->namelen;
+ if( partial )
+ _cdk_stream_set_blockmode( inp, pktlen );
+ pt->buf = inp;
+ pt->len = pktlen;
+ return rc;
+}
+
+
+static void
+read_old_length( cdk_stream_t inp, int ctb, size_t *r_len, size_t *r_size )
+{
+ int llen = ctb & 0x03;
+
+ if( llen == 0 ) {
+ *r_len = stream_getc( inp );
+ (*r_size)++;
+ }
+ else if( llen == 1 ) {
+ *r_len = read_16( inp );
+ (*r_size) += 2;
+ }
+ else if( llen == 2 ) {
+ *r_len = read_32( inp );
+ (*r_size) += 4;
+ }
+ else {
+ *r_len = 0;
+ *r_size = 0;
+ }
+}
+
+
+static void
+read_new_length( cdk_stream_t inp,
+ size_t *r_len, size_t *r_size, size_t *r_partial )
+{
+ int c, c1;
+
+ c = stream_getc( inp );
+ (*r_size)++;
+ if( c < 192 )
+ *r_len = c;
+ else if( c >= 192 && c <= 223 ) {
+ c1 = stream_getc( inp );
+ (*r_size)++;
+ *r_len = ((c - 192) << 8) + c1 + 192;
+ }
+ else if( c == 255 ) {
+ *r_len = read_32( inp );
+ (*r_size) += 4;
+ }
+ else {
+ *r_len = 1 << (c & 0x1f);
+ *r_partial = 1;
+ }
+}
+
+
+/* we use a buffer to make it faster to skip larger unknown packets. */
+static void
+skip_packet( cdk_stream_t inp, size_t pktlen )
+{
+ byte buf[4096];
+ size_t nread;
+
+ while( pktlen > 4095 ) {
+ stream_read( inp, buf, sizeof buf-1, &nread );
+ pktlen -= nread;
+ }
+ stream_read( inp, buf, pktlen, &nread );
+ pktlen -= nread;
+ assert( pktlen == 0 );
+}
+
+
+/**
+ * cdk_pkt_read:
+ * @inp: the input stream
+ * @pkt: allocated packet handle to store the packet
+ *
+ * Parse the next packet on the @inp stream and return its contents in @pkt.
+ **/
+cdk_error_t
+cdk_pkt_read( cdk_stream_t inp, cdk_packet_t pkt )
+{
+ int use_mdc = 0;
+ int ctb = 0, is_newctb = 0, is_partial = 0;
+ int rc = 0, pkttype = 0;
+ size_t pktlen = 0, pktsize = 0;
+
+ if( !inp || !pkt )
+ return CDK_Inv_Value;
+
+ ctb = stream_getc( inp );
+ if( cdk_stream_eof( inp ) || ctb == EOF )
+ return CDK_EOF;
+ else if( !ctb )
+ return CDK_Inv_Packet;
+
+ pktsize++;
+ if( !(ctb & 0x80) ) {
+ _cdk_log_info ("no valid openpgp data found. "
+ "(ctb=%02X; fpos=%02X)\n",ctb, cdk_stream_tell( inp ) );
+ return CDK_Inv_Packet;
+ }
+ if( ctb & 0x40 ) { /* RFC2440 */
+ pkttype = ctb & 0x3f;
+ is_newctb = 1;
+ }
+ else { /* RFC1991 */
+ pkttype = ctb & 0x3f;
+ pkttype >>= 2;
+ }
+ if( pkttype > 63 ) {
+ _cdk_log_info ("unknown packet type (%d)\n", pkttype);
+ return CDK_Inv_Packet;
+ }
+ if( is_newctb )
+ read_new_length( inp, &pktlen, &pktsize, &is_partial );
+ else
+ read_old_length( inp, ctb, &pktlen, &pktsize );
+
+ pkt->pkttype = pkttype;
+ pkt->pktlen = pktlen;
+ pkt->pktsize = pktsize + pktlen;
+ pkt->old_ctb = is_newctb? 0 : 1;
+
+ switch( pkt->pkttype ) {
+ case CDK_PKT_ATTRIBUTE:
+ pkt->pkt.user_id = cdk_calloc (1,
+ sizeof *pkt->pkt.user_id + pkt->pktlen);
+ if (!pkt->pkt.user_id)
+ return CDK_Out_Of_Core;
+ rc = read_attribute (inp, pktlen, pkt->pkt.user_id);
+ pkt->pkttype = CDK_PKT_USER_ID; /* treated as an user id */
+ break;
+
+ case CDK_PKT_USER_ID:
+ pkt->pkt.user_id = cdk_calloc (1,
+ sizeof *pkt->pkt.user_id + pkt->pktlen);
+ if (!pkt->pkt.user_id)
+ return CDK_Out_Of_Core;
+ rc = read_user_id (inp, pktlen, pkt->pkt.user_id);
+ break;
+
+ case CDK_PKT_PUBLIC_KEY:
+ pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key);
+ if (!pkt->pkt.public_key)
+ return CDK_Out_Of_Core;
+ rc = read_public_key (inp, pkt->pkt.public_key);
+ break;
+
+ case CDK_PKT_PUBLIC_SUBKEY:
+ pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key);
+ if (!pkt->pkt.public_key)
+ return CDK_Out_Of_Core;
+ rc = read_public_subkey (inp, pkt->pkt.public_key);
+ break;
+
+ case CDK_PKT_SECRET_KEY:
+ pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key);
+ if (!pkt->pkt.secret_key)
+ return CDK_Out_Of_Core;
+ pkt->pkt.secret_key->pk =cdk_calloc (1,
+ sizeof *pkt->pkt.secret_key->pk);
+ if (!pkt->pkt.secret_key->pk)
+ return CDK_Out_Of_Core;
+ rc = read_secret_key (inp, pktlen, pkt->pkt.secret_key);
+ break;
+
+ case CDK_PKT_SECRET_SUBKEY:
+ pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key);
+ if (!pkt->pkt.secret_key)
+ return CDK_Out_Of_Core;
+ pkt->pkt.secret_key->pk =
+ cdk_calloc (1, sizeof *pkt->pkt.secret_key->pk);
+ if (!pkt->pkt.secret_key->pk)
+ return CDK_Out_Of_Core;
+ rc = read_secret_subkey (inp, pktlen, pkt->pkt.secret_key);
+ break;
+
+ case CDK_PKT_LITERAL:
+ pkt->pkt.literal = cdk_calloc( 1, sizeof *pkt->pkt.literal );
+ if (!pkt->pkt.literal)
+ return CDK_Out_Of_Core;
+ rc = read_literal( inp, pktlen, &pkt->pkt.literal, is_partial);
+ break;
+
+ case CDK_PKT_ONEPASS_SIG:
+ pkt->pkt.onepass_sig = cdk_calloc (1, sizeof *pkt->pkt.onepass_sig);
+ if (!pkt->pkt.onepass_sig)
+ return CDK_Out_Of_Core;
+ rc = read_onepass_sig (inp, pktlen, pkt->pkt.onepass_sig);
+ break;
+
+ case CDK_PKT_SIGNATURE:
+ pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature);
+ if (!pkt->pkt.signature)
+ return CDK_Out_Of_Core;
+ rc = read_signature (inp, pktlen, pkt->pkt.signature);
+ break;
+
+ case CDK_PKT_ENCRYPTED_MDC:
+ case CDK_PKT_ENCRYPTED:
+ pkt->pkt.encrypted = cdk_calloc (1, sizeof *pkt->pkt.encrypted);
+ if (!pkt->pkt.encrypted)
+ return CDK_Out_Of_Core;
+ use_mdc = (pkt->pkttype == CDK_PKT_ENCRYPTED_MDC) ? 1 : 0;
+ rc = read_encrypted( inp, pktlen, pkt->pkt.encrypted,
+ is_partial, use_mdc );
+ break;
+
+ case CDK_PKT_SYMKEY_ENC:
+ pkt->pkt.symkey_enc = cdk_calloc (1, sizeof *pkt->pkt.symkey_enc);
+ if (!pkt->pkt.symkey_enc)
+ return CDK_Out_Of_Core;
+ rc = read_symkey_enc (inp, pktlen, pkt->pkt.symkey_enc);
+ break;
+
+ case CDK_PKT_PUBKEY_ENC:
+ pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc);
+ if (!pkt->pkt.pubkey_enc)
+ return CDK_Out_Of_Core;
+ rc = read_pubkey_enc (inp, pktlen, pkt->pkt.pubkey_enc);
+ break;
+
+ case CDK_PKT_COMPRESSED:
+ pkt->pkt.compressed = cdk_calloc (1, sizeof *pkt->pkt.compressed);
+ if (!pkt->pkt.compressed)
+ return CDK_Out_Of_Core;
+ rc = read_compressed (inp, pktlen, pkt->pkt.compressed);
+ break;
+
+ case CDK_PKT_MDC:
+ pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc);
+ if (!pkt->pkt.mdc)
+ return CDK_Out_Of_Core;
+ rc = read_mdc (inp, pkt->pkt.mdc);
+ break;
+
+ default:
+ /* skip all packets we don't understand */
+ skip_packet( inp, pktlen );
+ break;
+ }
+
+ return rc;
+}
diff --git a/libextra/opencdk/seskey.c b/libextra/opencdk/seskey.c
new file mode 100644
index 0000000000..565eb1ac68
--- /dev/null
+++ b/libextra/opencdk/seskey.c
@@ -0,0 +1,562 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * seskey.c - Session key routines
+ * Copyright (C) 2002, 2003 Timo Schulz
+ * Copyright (C) 1998-2002 Free Software Foundation, Inc.
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <assert.h>
+#include <stdio.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "cipher.h"
+
+
+/* We encode the MD in this way:
+ *
+ * 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes)
+ *
+ * PAD consists of FF bytes.
+ */
+static int
+do_encode_md (byte ** r_frame, size_t * r_flen, const byte * md, int algo,
+ size_t len, unsigned nbits, const byte * asn, size_t asnlen)
+{
+ byte * frame = NULL;
+ size_t n = 0;
+ int i, nframe = (nbits + 7) / 8;
+
+ if( !asn || !md || !r_frame || !r_flen )
+ return CDK_Inv_Value;
+
+ if (len + asnlen + 4 > nframe)
+ return CDK_General_Error;
+
+ frame = cdk_calloc (1, nframe);
+ if (!frame)
+ return CDK_Out_Of_Core;
+ frame[n++] = 0;
+ frame[n++] = 1;
+ i = nframe - len - asnlen - 3;
+ if (i < 0) {
+ cdk_free (frame);
+ return CDK_Inv_Value;
+ }
+ memset (frame + n, 0xff, i);
+ n += i;
+ frame[n++] = 0;
+ memcpy (frame + n, asn, asnlen);
+ n += asnlen;
+ memcpy (frame + n, md, len);
+ n += len;
+ if( n != nframe ) {
+ cdk_free( frame );
+ return CDK_Inv_Value;
+ }
+ *r_frame = frame;
+ *r_flen = n;
+ return 0;
+}
+
+
+/* RFC2437 format:
+ *
+ * 0 2 RND(n bytes) 0 [A DEK(k bytes) CSUM(2 bytes)]
+ *
+ * RND - randomized bytes for padding.
+ * A - cipher algorithm.
+ * DEK - random session key.
+ * CKSUM - algebraic checksum of the DEK.
+ */
+cdk_error_t
+cdk_dek_encode_pkcs1( cdk_dek_t dek, int nbits, cdk_sesskey_t * r_esk )
+{
+ gcry_mpi_t a = NULL;
+ byte * p, * frame;
+ size_t n = 0;
+ u16 chksum = 0;
+ int i = 0, nframe = 0;
+ int rc = 0;
+
+ if( !r_esk || !dek )
+ return CDK_Inv_Value;
+
+ for (i = 0; i < dek->keylen; i++)
+ chksum += dek->key[i];
+ nframe = (nbits + 7) / 8;
+ frame = cdk_salloc (nframe + 1, 1);
+ if (!frame)
+ return CDK_Out_Of_Core;
+ n = 0;
+ frame[n++] = 0x00;
+ frame[n++] = 0x02;
+ i = nframe - 6 - dek->keylen;
+ p = gcry_random_bytes (i, GCRY_STRONG_RANDOM);
+ /* replace zero bytes by new values */
+ for (;;) {
+ int j, k;
+ byte * pp;
+
+ /* count the zero bytes */
+ for (j = k = 0; j < i; j++) {
+ if (!p[j])
+ k++;
+ }
+ if (!k)
+ break; /* okay: no zero bytes */
+ k += k / 128; /* better get some more */
+ pp = gcry_random_bytes (k, GCRY_STRONG_RANDOM);
+ for (j = 0; j < i && k; j++) {
+ if (!p[j])
+ p[j] = pp[--k];
+ }
+ cdk_free (pp);
+ }
+ memcpy (frame + n, p, i);
+ cdk_free (p);
+ n += i;
+ frame[n++] = 0;
+ frame[n++] = dek->algo;
+ memcpy (frame + n, dek->key, dek->keylen);
+ n += dek->keylen;
+ frame[n++] = chksum >> 8;
+ frame[n++] = chksum;
+ rc = gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, nframe, &nframe);
+ if (rc)
+ rc = CDK_Gcry_Error;
+ cdk_free (frame);
+ if( !rc ) {
+ rc = cdk_sesskey_new( r_esk );
+ if( rc ) {
+ gcry_mpi_release( a );
+ return rc;
+ }
+ (*r_esk)->a = a;
+ }
+ return rc;
+}
+
+
+cdk_error_t
+cdk_dek_decode_pkcs1( cdk_dek_t *ret_dek, cdk_sesskey_t esk )
+{
+ cdk_dek_t dek;
+ byte frame[4096];
+ size_t nframe, n;
+ u16 csum = 0, csum2 = 0;
+ int rc;
+
+ if( !ret_dek || !esk )
+ return CDK_Inv_Value;
+
+ nframe = sizeof frame-1;
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, esk->a);
+ if( rc )
+ return CDK_Gcry_Error;
+ dek = cdk_salloc( sizeof *dek, 1 );
+ if( !dek )
+ return CDK_Out_Of_Core;
+
+ /* Now get the DEK (data encryption key) from the frame
+ *
+ * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
+ *
+ * (gcry_mpi_print already removed the leading zero).
+ *
+ * RND are non-zero randow bytes.
+ * A is the cipher algorithm
+ * DEK is the encryption key (session key) with length k
+ * CSUM
+ */
+ n = 0;
+ if( frame[n] != 2 ) {
+ cdk_free (dek);
+ return CDK_Inv_Mode;
+ }
+ for( n++; n < nframe && frame[n]; n++ )
+ ;
+ n++;
+ dek->keylen = nframe - (n + 1) - 2;
+ dek->algo = frame[n++];
+ if( dek->keylen != cdk_cipher_get_algo_keylen( dek->algo ) ) {
+ cdk_free( dek );
+ return CDK_Inv_Algo;
+ }
+ csum = frame[nframe-2] << 8;
+ csum |= frame[nframe-1];
+ memcpy( dek->key, frame + n, dek->keylen );
+ for( n = 0; n < dek->keylen; n++ )
+ csum2 += dek->key[n];
+ if( csum != csum2 ) {
+ cdk_free( dek );
+ return CDK_Chksum_Error;
+ }
+ *ret_dek = dek;
+ return 0;
+}
+
+
+/* Do some tests before it calls do_encode_md that depends on the
+ public key algorithm that is used. */
+cdk_error_t
+_cdk_digest_encode_pkcs1( byte ** r_md, size_t * r_mdlen, int pk_algo,
+ const byte * md, int digest_algo, unsigned nbits )
+{
+ int rc = 0;
+ int dlen = cdk_md_get_algo_dlen( digest_algo );
+ byte * p;
+
+ if( !md || !r_md || !r_mdlen )
+ return CDK_Inv_Value;
+
+ if( !dlen )
+ return CDK_Inv_Algo;
+ if( is_DSA( pk_algo ) ) {
+ *r_md = p = cdk_malloc( dlen + 1 );
+ if( !p )
+ return CDK_Out_Of_Core;
+ *r_mdlen = dlen;
+ memcpy( p, md, dlen );
+ return 0;
+ }
+ else {
+ byte * asn = NULL;
+ size_t asnlen = 0;
+
+ rc = cdk_md_get_asnoid( digest_algo, NULL, &asnlen );
+ if( !rc ) {
+ asn = cdk_malloc( asnlen + 1 );
+ if( !asn )
+ return CDK_Out_Of_Core;
+ }
+ if( !rc )
+ rc = cdk_md_get_asnoid( digest_algo, asn, &asnlen );
+ if( !rc )
+ rc = do_encode_md( r_md, r_mdlen, md, digest_algo, dlen,
+ nbits, asn, asnlen );
+ cdk_free( asn );
+ return rc;
+ }
+ return 0;
+}
+
+
+static char *
+passphrase_prompt (cdk_pkt_seckey_t sk)
+{
+ u32 keyid = cdk_pk_get_keyid (sk->pk, NULL);
+ int bits = cdk_pk_get_nbits (sk->pk), pk_algo = sk->pubkey_algo;
+ const char * algo = "???", * fmt;
+ char * p;
+
+ if (is_RSA (pk_algo))
+ algo = "RSA";
+ else if (is_ELG (pk_algo))
+ algo = "ELG";
+ else if (is_DSA (pk_algo))
+ algo = "DSA";
+
+ fmt = "%d-bit %s key, ID %08lX\nEnter Passphrase: ";
+ p = cdk_calloc( 1, 64 + strlen( fmt ) + 1 );
+ if( !p )
+ return NULL;
+ sprintf( p, fmt, bits, algo, keyid );
+ return p;
+}
+
+
+cdk_error_t
+_cdk_sk_unprotect_auto( cdk_ctx_t hd, cdk_pkt_seckey_t sk )
+{
+ char * pw = NULL, * p = NULL;
+ int rc = 0;
+
+ if( sk->is_protected ) {
+ p = passphrase_prompt( sk );
+ pw = _cdk_passphrase_get( hd, p );
+ if( pw )
+ rc = cdk_sk_unprotect( sk, pw );
+ _cdk_passphrase_free( pw, pw? strlen( pw ) : 0 );
+ cdk_free( p );
+ }
+ return rc;
+}
+
+
+cdk_error_t
+cdk_dek_extract( cdk_dek_t * ret_dek, cdk_ctx_t hd,
+ cdk_pkt_pubkey_enc_t enc, cdk_pkt_seckey_t sk )
+{
+ cdk_dek_t dek = NULL;
+ cdk_sesskey_t skey = NULL;
+ int rc = 0;
+
+ if( !enc || !sk || !ret_dek )
+ return CDK_Inv_Value;
+
+ if( sk->is_protected )
+ rc = _cdk_sk_unprotect_auto( hd, sk );
+ if( !rc )
+ rc = cdk_pk_decrypt( sk, enc, &skey );
+ if( !rc )
+ rc = cdk_dek_decode_pkcs1( &dek, skey );
+ cdk_sesskey_free( skey );
+ if( rc ) {
+ cdk_dek_free( dek );
+ dek = NULL;
+ }
+ *ret_dek = dek;
+ return rc;
+}
+
+
+cdk_error_t
+cdk_sesskey_new( cdk_sesskey_t * r_sk )
+{
+ cdk_sesskey_t sk;
+
+ if( !r_sk )
+ return CDK_Inv_Value;
+ sk = cdk_calloc( 1, sizeof **r_sk );
+ if( !sk )
+ return CDK_Out_Of_Core;
+ *r_sk = sk;
+ return 0;
+}
+
+
+void
+cdk_sesskey_free( cdk_sesskey_t sk )
+{
+ if( sk ) {
+ gcry_mpi_release( sk->a );
+ cdk_free( sk );
+ }
+}
+
+
+cdk_error_t
+cdk_dek_new( cdk_dek_t * r_dek )
+{
+ cdk_dek_t dek;
+
+ if( !r_dek )
+ return CDK_Inv_Value;
+ *r_dek = NULL;
+ dek = cdk_salloc( sizeof *dek, 1 );
+ if( !dek )
+ return CDK_Out_Of_Core;
+ *r_dek = dek;
+ return 0;
+}
+
+
+cdk_error_t
+cdk_dek_set_cipher( cdk_dek_t dek, int algo )
+{
+ if( !dek )
+ return CDK_Inv_Value;
+ if( !algo )
+ algo = CDK_CIPHER_CAST5;
+ if( cdk_cipher_test_algo( algo ) )
+ return CDK_Inv_Algo;
+ dek->algo = algo;
+ dek->keylen = cdk_cipher_get_algo_keylen( dek->algo );
+ return 0;
+}
+
+
+cdk_error_t
+cdk_dek_set_key( cdk_dek_t dek, const byte * key, size_t keylen )
+{
+ cdk_cipher_hd_t hd;
+ int i;
+
+ if( !dek )
+ return CDK_Inv_Value;
+ if( keylen > 0 && keylen != dek->keylen )
+ return CDK_Inv_Mode;
+
+ if( !key && !keylen ) {
+ hd = cdk_cipher_new( dek->algo, 1 );
+ if( !hd )
+ return CDK_Inv_Algo;
+ gcry_randomize( dek->key, dek->keylen, GCRY_STRONG_RANDOM );
+ for( i = 0; i < 8; i++ ) {
+ if( !cdk_cipher_setkey( hd, dek->key, dek->keylen ) ) {
+ cdk_cipher_close( hd );
+ return 0;
+ }
+ gcry_randomize( dek->key, dek->keylen, GCRY_STRONG_RANDOM );
+ }
+ return CDK_Weak_Key;
+ }
+ memcpy( dek->key, key, dek->keylen );
+ return 0;
+}
+
+
+void
+cdk_dek_set_mdc_flag( cdk_dek_t dek, int val )
+{
+ if( dek )
+ dek->use_mdc = val;
+}
+
+
+void
+cdk_dek_free( cdk_dek_t dek )
+{
+ cdk_free( dek );
+}
+
+
+static int
+hash_passphrase( cdk_dek_t dek, const char * pw, cdk_s2k_t s2k, int create )
+{
+ cdk_md_hd_t md;
+ int pass, i;
+ int used = 0, pwlen = 0;
+
+ if (!dek || !pw || !s2k)
+ return CDK_Inv_Value;
+
+ if (!s2k->hash_algo)
+ s2k->hash_algo = CDK_MD_SHA1;
+ pwlen = strlen (pw);
+
+ dek->keylen = cdk_cipher_get_algo_keylen (dek->algo);
+ md = cdk_md_open( s2k->hash_algo, GCRY_MD_FLAG_SECURE );
+ if (!md)
+ return CDK_Inv_Algo;
+
+ for (pass = 0; used < dek->keylen; pass++) {
+ if (pass) {
+ cdk_md_reset (md);
+ for (i = 0; i < pass; i++) /* preset the hash context */
+ cdk_md_putc (md, 0);
+ }
+ if (s2k->mode == 1 || s2k->mode == 3) {
+ int len2 = pwlen + 8;
+ u32 count = len2;
+ if (create && !pass) {
+ gcry_randomize (s2k->salt, 8, 1);
+ if (s2k->mode == 3)
+ s2k->count = 96; /* 65536 iterations */
+ }
+ if (s2k->mode == 3) {
+ count = (16ul + (s2k->count & 15)) << ((s2k->count >> 4) + 6);
+ if (count < len2)
+ count = len2;
+ }
+ /* a little bit complicated because we need a ulong for count */
+ while (count > len2) { /* maybe iterated+salted */
+ cdk_md_write (md, s2k->salt, 8);
+ cdk_md_write (md, pw, pwlen);
+ count -= len2;
+ }
+ if (count < 8)
+ cdk_md_write (md, s2k->salt, count);
+ else {
+ cdk_md_write (md, s2k->salt, 8);
+ count -= 8;
+ cdk_md_write (md, pw, count);
+ }
+ }
+ else
+ cdk_md_write (md, pw, pwlen);
+ cdk_md_final (md);
+ i = cdk_md_get_algo_dlen (s2k->hash_algo);
+ if (i > dek->keylen - used)
+ i = dek->keylen - used;
+ memcpy (dek->key + used, cdk_md_read (md, s2k->hash_algo), i);
+ used += i;
+ }
+ cdk_md_close (md);
+ return 0;
+}
+
+
+cdk_error_t
+cdk_dek_from_passphrase( cdk_dek_t * ret_dek, int cipher_algo, cdk_s2k_t s2k,
+ int mode, const char * pw )
+{
+ cdk_dek_t dek;
+ int rc;
+
+ if( !ret_dek )
+ return CDK_Inv_Value;
+ rc = cdk_dek_new( &dek );
+ if( !rc )
+ rc = cdk_dek_set_cipher( dek, cipher_algo );
+ if( rc ) {
+ cdk_dek_free( dek );
+ return rc;
+ }
+ if( !*pw && mode == 2 )
+ dek->keylen = 0;
+ else
+ hash_passphrase( dek, pw, s2k, mode == 2 );
+ *ret_dek = dek;
+ return 0;
+}
+
+
+cdk_error_t
+cdk_s2k_new( cdk_s2k_t * ret_s2k, int mode, int algo, const byte * salt )
+{
+ cdk_s2k_t s2k;
+ int rc;
+
+ if( !ret_s2k )
+ return CDK_Inv_Value;
+ if( mode != 0x00 && mode != 0x01 && mode != 0x03 )
+ return CDK_Inv_Mode;
+
+ rc = cdk_md_test_algo( algo );
+ if( rc )
+ return rc;
+ s2k = cdk_calloc( 1, sizeof *s2k );
+ if( !s2k )
+ return CDK_Out_Of_Core;
+ s2k->mode = mode;
+ s2k->hash_algo = algo;
+ if( salt )
+ memcpy( s2k->salt, salt, 8 );
+ *ret_s2k = s2k;
+ return 0;
+}
+
+
+void
+cdk_s2k_free( cdk_s2k_t s2k )
+{
+ cdk_free( s2k );
+}
+
+
+
+
+
+
+
diff --git a/libextra/opencdk/sig-check.c b/libextra/opencdk/sig-check.c
new file mode 100644
index 0000000000..fff4223b05
--- /dev/null
+++ b/libextra/opencdk/sig-check.c
@@ -0,0 +1,358 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * sig-check.c - Check signatures
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ * Copyright (C) 1998,1999,2000,2001,2002 Free Software Foundation, Inc.
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <time.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+
+
+static void
+hash_mpibuf( cdk_pkt_pubkey_t pk, cdk_md_hd_t md )
+{
+ cdk_mpi_t a;
+ int i, npkey;
+
+ npkey = cdk_pk_get_npkey( pk->pubkey_algo );
+ for( i = 0; i < npkey; i++ ) {
+ a = pk->mpi[i];
+ if( pk->version == 4 ) {
+ cdk_md_putc( md, a->bits >> 8 );
+ cdk_md_putc( md, a->bits );
+ }
+ cdk_md_write( md, a->data + 2, a->bytes );
+ }
+}
+
+
+void
+_cdk_hash_pubkey( cdk_pkt_pubkey_t pk, cdk_md_hd_t md, int usefpr )
+{
+ byte buf[4];
+ u16 n;
+ int i, npkey;
+
+ if( !pk || !md )
+ return;
+
+ npkey = cdk_pk_get_npkey( pk->pubkey_algo );
+ if( usefpr && pk->version < 4 && is_RSA( pk->pubkey_algo ) ) {
+ hash_mpibuf( pk, md );
+ return;
+ }
+ n = pk->version < 4 ? 8 : 6;
+ for( i = 0; i < npkey; i++ ) {
+ n += pk->mpi[i]->bytes;
+ n += 2;
+ }
+
+ cdk_md_putc( md, 0x99 );
+ cdk_md_putc( md, n >> 8 );
+ cdk_md_putc( md, n );
+ cdk_md_putc( md, pk->version );
+
+ buf[0] = pk->timestamp >> 24;
+ buf[1] = pk->timestamp >> 16;
+ buf[2] = pk->timestamp >> 8;
+ buf[3] = pk->timestamp;
+ cdk_md_write( md, buf, 4 );
+
+ if( pk->version < 4 ) {
+ u16 a = 0;
+ if( pk->expiredate )
+ a = (u16)((pk->expiredate - pk->timestamp) / 86400L);
+ cdk_md_putc( md, a >> 8 );
+ cdk_md_putc( md, a );
+ }
+ cdk_md_putc( md, pk->pubkey_algo );
+ hash_mpibuf( pk, md );
+}
+
+
+void
+_cdk_hash_userid( cdk_pkt_userid_t uid, int is_v4, cdk_md_hd_t md )
+{
+ const byte * data;
+ byte buf[5];
+ u32 dlen;
+
+ if( !uid || !md )
+ return;
+
+ if( is_v4 ) {
+ if( uid->attrib_img ) {
+ buf[0] = 0xD1;
+ buf[1] = uid->attrib_len >> 24;
+ buf[2] = uid->attrib_len >> 16;
+ buf[3] = uid->attrib_len >> 8;
+ buf[4] = uid->attrib_len;
+ }
+ else {
+ buf[0] = 0xB4;
+ buf[1] = uid->len >> 24;
+ buf[2] = uid->len >> 16;
+ buf[3] = uid->len >> 8;
+ buf[4] = uid->len;
+ }
+ cdk_md_write( md, buf, 5 );
+ }
+ data = uid->attrib_img ? uid->attrib_img : (byte *) uid->name;
+ dlen = uid->attrib_img ? uid->attrib_len : uid->len;
+ cdk_md_write( md, data, dlen );
+}
+
+
+void
+_cdk_hash_sig_data( cdk_pkt_signature_t sig, cdk_md_hd_t md )
+{
+ byte buf[4];
+ size_t n = 0;
+
+ if( !sig || !md )
+ return;
+
+ if( sig->version == 4 )
+ cdk_md_putc( md, sig->version );
+ cdk_md_putc( md, sig->sig_class );
+ if( sig->version < 4 ) {
+ buf[0] = sig->timestamp >> 24;
+ buf[1] = sig->timestamp >> 16;
+ buf[2] = sig->timestamp >> 8;
+ buf[3] = sig->timestamp;
+ cdk_md_write( md, buf, 4 );
+ }
+ else {
+ cdk_md_putc( md, sig->pubkey_algo );
+ cdk_md_putc( md, sig->digest_algo );
+ if( sig->hashed ) {
+ _cdk_subpkt_hash( sig->hashed, &n, md );
+ sig->hashed_size = n;
+ n = sig->hashed_size + 6;
+ }
+ else {
+ cdk_md_putc( md, 0 );
+ cdk_md_putc( md, 0 );
+ n = 6;
+ }
+ cdk_md_putc( md, sig->version );
+ cdk_md_putc( md, 0xff );
+ buf[0] = n >> 24;
+ buf[1] = n >> 16;
+ buf[2] = n >> 8;
+ buf[3] = n;
+ cdk_md_write( md, buf, 4 );
+ }
+}
+
+
+static void
+cache_sig_result( cdk_pkt_signature_t sig, int res )
+{
+ if( !res ) {
+ sig->flags.checked = 1;
+ sig->flags.valid = 1;
+ }
+ else if( res == CDK_Bad_Sig ) {
+ sig->flags.checked = 1;
+ sig->flags.valid = 0;
+ }
+ else {
+ sig->flags.checked = 0;
+ sig->flags.valid = 0;
+ }
+}
+
+
+int
+_cdk_sig_check( cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig,
+ cdk_md_hd_t digest, int * r_expired )
+{
+ byte md[24];
+ time_t cur_time = _cdk_timestamp( );
+ int digest_algo;
+ int rc;
+
+ if( !pk || !sig || !digest )
+ return CDK_Inv_Value;
+
+ if( sig->flags.checked )
+ return sig->flags.valid ? 0 : CDK_Bad_Sig;
+
+ if( !KEY_CAN_SIGN( pk->pubkey_algo ) )
+ return CDK_Inv_Algo;
+ if( pk->timestamp > sig->timestamp || pk->timestamp > cur_time )
+ return CDK_Time_Conflict;
+
+ digest_algo = sig->digest_algo;
+ if( r_expired && pk->expiredate
+ && (pk->expiredate + pk->timestamp) > cur_time )
+ *r_expired = 1;
+
+ _cdk_hash_sig_data( sig, digest );
+ cdk_md_final( digest );
+ memcpy( md, cdk_md_read (digest, sig->digest_algo ),
+ cdk_md_get_algo_dlen( sig->digest_algo ) );
+ if( md[0] != sig->digest_start[0] || md[1] != sig->digest_start[1] )
+ return CDK_Bad_Sig;
+
+ rc = cdk_pk_verify( pk, sig, md );
+ cache_sig_result( sig, rc );
+ return rc;
+}
+
+
+int
+_cdk_pk_check_sig( cdk_keydb_hd_t hd, cdk_kbnode_t knode, cdk_kbnode_t snode )
+{
+ cdk_md_hd_t md;
+ cdk_pkt_pubkey_t pk = NULL, sig_pk = NULL;
+ cdk_pkt_signature_t sig = NULL;
+ cdk_kbnode_t node;
+ int digest_algo, is_expired = 0;
+ int rc = 0;
+
+ if( !knode || !snode )
+ return CDK_Inv_Value;
+
+ if( knode->pkt->pkttype != CDK_PKT_PUBLIC_KEY
+ || snode->pkt->pkttype != CDK_PKT_SIGNATURE )
+ return CDK_Inv_Value;
+ pk = knode->pkt->pkt.public_key;
+ sig = snode->pkt->pkt.signature;
+ digest_algo = sig->digest_algo;
+
+ md = cdk_md_open( digest_algo, 0 );
+ if( !md )
+ return CDK_Out_Of_Core;
+
+ if( sig->sig_class == 0x20 ) { /* key revocation */
+ cdk_kbnode_hash( knode, md, 0, 0, 0 );
+ rc = _cdk_sig_check( pk, sig, md, &is_expired );
+ }
+ else if( sig->sig_class == 0x28 ) { /* subkey revocation */
+ node = cdk_kbnode_find_prev( knode, snode, CDK_PKT_PUBLIC_SUBKEY );
+ if( !node ) { /* no subkey for subkey revocation packet */
+ rc = CDK_Error_No_Key;
+ goto fail;
+ }
+ cdk_kbnode_hash( knode, md, 0, 0, 0 );
+ cdk_kbnode_hash( node, md, 0, 0, 0 );
+ rc = _cdk_sig_check( pk, sig, md, &is_expired );
+ }
+ else if( sig->sig_class == 0x18 ) { /* key binding */
+ node = cdk_kbnode_find_prev( knode, snode, CDK_PKT_PUBLIC_SUBKEY );
+ if( !node ) { /* no subkey for subkey binding packet */
+ rc = CDK_Error_No_Key;
+ goto fail;
+ }
+ cdk_kbnode_hash( knode, md, 0, 0, 0 );
+ cdk_kbnode_hash( node, md, 0, 0, 0 );
+ rc = _cdk_sig_check( pk, sig, md, &is_expired );
+ }
+ else if( sig->sig_class == 0x1f ) { /* direct key signature */
+ cdk_kbnode_hash( knode, md, 0, 0, 0 );
+ rc = _cdk_sig_check( pk, sig, md, &is_expired );
+ }
+ else { /* all other classes */
+ node = cdk_kbnode_find_prev( knode, snode, CDK_PKT_USER_ID );
+ if( !node ) { /* no user ID for key signature packet */
+ rc = CDK_Error_No_Key;
+ goto fail;
+ }
+ cdk_kbnode_hash( knode, md, 0, 0, 0 );
+ cdk_kbnode_hash( node, md, sig->version==4, 0, 0 );
+ if( pk->keyid[0] == sig->keyid[0] && pk->keyid[1] == sig->keyid[1] )
+ rc = _cdk_sig_check( pk, sig, md, &is_expired );
+ else if( hd ) {
+ rc = cdk_keydb_get_pk( hd, sig->keyid, &sig_pk );
+ if( !rc )
+ rc = _cdk_sig_check( sig_pk, sig, md, &is_expired );
+ _cdk_free_pubkey( sig_pk );
+ }
+ }
+ fail:
+ cdk_md_close( md );
+ return rc;
+}
+
+
+/**
+ * cdk_pk_check_sigs:
+ * @knode: the key node
+ * @hd: the session handle
+ * @r_status: variable to store the status of the key
+ *
+ * Check all signatures. When no key is available for checking, the
+ * sigstat is marked as 'NOKEY'. The @r_status contains the key flags
+ * which are or-ed or zero when there are no flags.
+ **/
+cdk_error_t
+cdk_pk_check_sigs( cdk_kbnode_t knode, cdk_keydb_hd_t hd, int * r_status )
+{
+ cdk_pkt_signature_t sig = NULL;
+ cdk_kbnode_t k;
+ u32 keyid = 0;
+ int key_status = 0;
+ int rc = 0;
+
+ if( !knode || !r_status )
+ return CDK_Inv_Value;
+
+ k = cdk_kbnode_find( knode, CDK_PKT_PUBLIC_KEY );
+ if( !k )
+ return CDK_Error_No_Key;
+ if( k->pkt->pkt.public_key->is_revoked )
+ key_status |= CDK_KEY_REVOKED;
+ if( k->pkt->pkt.public_key->has_expired )
+ key_status |= CDK_KEY_EXPIRED;
+ if( key_status ) {
+ *r_status = key_status;
+ return CDK_General_Error;
+ }
+ keyid = cdk_pk_get_keyid( k->pkt->pkt.public_key, NULL );
+
+ for( k = knode; k && k->pkt->pkttype; k = k->next ) {
+ if( k->pkt->pkttype != CDK_PKT_SIGNATURE )
+ continue;
+ sig = k->pkt->pkt.signature;
+ rc = _cdk_pk_check_sig( hd, knode, k );
+ if( rc && IS_UID_SIG( sig ) && rc == CDK_Error_No_Key ) {
+ sig->flags.missing_key = 1;
+ continue;
+ }
+ else if( rc && rc != CDK_Error_No_Key ) {
+ *r_status = CDK_KEY_INVALID;
+ break; /* invalid self signature or key signature */
+ }
+ _cdk_log_debug( "signature %s: signer %08lX keyid %08lX\n",
+ rc==CDK_Bad_Sig? "BAD" : "good", sig->keyid[1],
+ keyid );
+ }
+ if( !rc || rc == CDK_Error_No_Key )
+ *r_status = CDK_KEY_VALID;
+ return rc;
+}
diff --git a/libextra/opencdk/sign.c b/libextra/opencdk/sign.c
new file mode 100644
index 0000000000..0a68c9f707
--- /dev/null
+++ b/libextra/opencdk/sign.c
@@ -0,0 +1,493 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * sign.c - Signing routines
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <time.h>
+#include <string.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "filters.h"
+#include "stream.h"
+
+
+static int file_clearsign( cdk_ctx_t, cdk_strlist_t,
+ const char *, const char * );
+static int stream_clearsign( cdk_ctx_t, cdk_stream_t,
+ cdk_stream_t, cdk_strlist_t );
+
+
+static void
+calc_subpkt_size( cdk_pkt_signature_t sig )
+{
+ size_t nbytes;
+
+ if( sig->hashed ) {
+ _cdk_subpkt_get_array( sig->hashed, 1, &nbytes );
+ sig->hashed_size = nbytes;
+ }
+ if( sig->unhashed ) {
+ _cdk_subpkt_get_array( sig->unhashed, 1, &nbytes );
+ sig->unhashed_size = nbytes;
+ }
+}
+
+
+int
+_cdk_sig_hash_for( int pkalgo, int pktver )
+{
+ if( is_DSA( pkalgo ) )
+ return CDK_MD_SHA1;
+ else if( is_RSA( pkalgo ) && pktver < 4 )
+ return CDK_MD_MD5;
+ return CDK_MD_SHA1; /* default message digest */
+}
+
+
+int
+_cdk_sig_create( cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig )
+{
+ cdk_subpkt_t node;
+ byte buf[8];
+
+ if( !sig )
+ return CDK_Inv_Value;
+
+ if( pk ) {
+ if( !sig->version )
+ sig->version = pk->version;
+ sig->pubkey_algo = pk->pubkey_algo;
+ sig->digest_algo = _cdk_sig_hash_for( pk->pubkey_algo, pk->version );
+ cdk_pk_get_keyid( pk, sig->keyid );
+ }
+ sig->timestamp = _cdk_timestamp( );
+ if( sig->version == 3 )
+ return 0;
+
+ sig->hashed = sig->unhashed = NULL;
+
+ _cdk_u32tobuf( sig->keyid[0], buf );
+ _cdk_u32tobuf( sig->keyid[1], buf + 4 );
+ node = cdk_subpkt_new( 8 );
+ if( node )
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_ISSUER, buf, 8 );
+ sig->unhashed = node;
+
+ _cdk_u32tobuf( sig->timestamp, buf );
+ node = cdk_subpkt_new( 4 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_SIG_CREATED, buf, 4 );
+ sig->hashed = node;
+ }
+
+ if( sig->expiredate ) {
+ u32 u = sig->expiredate - sig->timestamp;
+ _cdk_u32tobuf( u, buf );
+ node = cdk_subpkt_new( 4 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_SIG_EXPIRE, buf, 4 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+ calc_subpkt_size( sig );
+ return 0;
+}
+
+
+int
+_cdk_sig_complete( cdk_pkt_signature_t sig, cdk_pkt_seckey_t sk,
+ cdk_md_hd_t md )
+{
+ byte digest[24];
+
+ if( !sig || !sk || !md )
+ return CDK_Inv_Value;
+
+ calc_subpkt_size( sig );
+ _cdk_hash_sig_data( sig, md );
+ cdk_md_final( md );
+ memcpy( digest, cdk_md_read( md, sig->digest_algo),
+ cdk_md_get_algo_dlen( sig->digest_algo ) );
+ return cdk_pk_sign( sk, sig, digest );
+}
+
+
+static int
+write_literal( cdk_stream_t inp, cdk_stream_t out )
+{
+ cdk_packet_t pkt;
+ cdk_pkt_literal_t pt;
+ const char * s = _cdk_stream_get_fname( inp );
+ int rc;
+
+ if( !inp || !out )
+ return CDK_Inv_Value;
+
+ pkt = cdk_calloc( 1, sizeof *pkt );
+ if( !pkt )
+ return CDK_Out_Of_Core;
+ cdk_stream_seek( inp, 0 );
+ if( !s )
+ s = "_CONSOLE";
+ pt = cdk_calloc( 1, sizeof *pt + strlen( s ) + 1 );
+ if( !pt )
+ return CDK_Out_Of_Core;
+ pt->len = cdk_stream_get_length( inp );
+ pt->mode = 'b';
+ pt->timestamp = _cdk_timestamp( );
+ pt->namelen = strlen( s );
+ pt->buf = inp;
+ strcpy( pt->name, s );
+ pkt->pkttype = CDK_PKT_LITERAL;
+ pkt->pkt.literal = pt;
+ rc = cdk_pkt_write( out, pkt );
+ cdk_free( pt );
+ cdk_free( pkt );
+ return rc;
+}
+
+
+static int
+write_pubkey_enc_list( cdk_ctx_t hd, cdk_stream_t out, cdk_strlist_t remusr )
+{
+ cdk_keylist_t pkl;
+ int rc;
+
+ if( !hd || !out )
+ return CDK_Inv_Value;
+
+ rc = cdk_pklist_build( &pkl, hd->db.pub, remusr, PK_USAGE_ENCR );
+ if( rc )
+ return rc;
+
+ cdk_dek_free( hd->dek );
+ rc = cdk_dek_new( &hd->dek );
+ if( !rc )
+ rc = cdk_dek_set_cipher( hd->dek, cdk_pklist_select_algo( pkl, 1 ) );
+ if( !rc )
+ rc = cdk_dek_set_key( hd->dek, NULL, 0 );
+ if( !rc ) {
+ cdk_dek_set_mdc_flag( hd->dek, cdk_pklist_use_mdc( pkl ) );
+ rc = cdk_pklist_encrypt( pkl, hd->dek, out );
+ }
+ cdk_pklist_release( pkl );
+ return rc;
+}
+
+
+static int
+sig_get_version( cdk_ctx_t hd, cdk_keylist_t kl )
+{
+ cdk_keylist_t l;
+
+ if( hd && hd->opt.compat )
+ return 3;
+
+ for( l = kl; l; l = l->next ) {
+ if( (l->type == CDK_PKT_PUBLIC_KEY && l->key.pk->version == 3)
+ || (l->type == CDK_PKT_SECRET_KEY && l->key.sk->version == 3))
+ return 3;
+ }
+ return 4;
+}
+
+
+/**
+ * cdk_stream_sign:
+ * @hd: session handle
+ * @inp: input stream
+ * @out: output stream
+ * @locusr: local user list for signing
+ * @remustr: remote user list for encrypting
+ * @encryptflag: shall the output be encrypted? (1/0)
+ * @sigmode: signature mode
+ *
+ * Sign the data from the STREAM @inp.
+ **/
+cdk_error_t
+cdk_stream_sign( cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out,
+ cdk_strlist_t locusr, cdk_strlist_t remusr,
+ int encryptflag, int sigmode )
+{
+ cdk_keylist_t list;
+ cdk_pkt_seckey_t sk;
+ md_filter_t * mfx;
+ int sigver, digest_algo;
+ int rc, detached = sigmode == CDK_SIGMODE_DETACHED;
+
+ if( !hd )
+ return CDK_Inv_Value;
+ if( detached && encryptflag )
+ return CDK_Inv_Mode;
+
+ if( sigmode == CDK_SIGMODE_CLEAR )
+ return stream_clearsign( hd, inp, out, locusr );
+
+ rc = cdk_sklist_build( &list, hd->db.sec, hd, locusr, 1, PK_USAGE_SIGN );
+ if( rc )
+ return rc;
+
+ sk = list->key.sk;
+ digest_algo = _cdk_sig_hash_for( sk->pubkey_algo, sk->version );
+ if( cdk_handle_control( hd, CDK_CTLF_GET, CDK_CTL_FORCE_DIGEST ) )
+ digest_algo = hd->digest_algo;
+
+ if( hd->opt.armor )
+ cdk_stream_set_armor_flag( out, detached? CDK_ARMOR_SIGNATURE : 0 );
+
+ if( encryptflag ) {
+ cdk_stream_set_cache( out, 1 );
+ rc = write_pubkey_enc_list( hd, out, remusr );
+ if( rc ) {
+ cdk_sklist_release( list );
+ return rc;
+ }
+ cdk_stream_set_cipher_flag( out, hd->dek, hd->dek->use_mdc );
+ cdk_stream_set_cache( out, 0 );
+ }
+
+ cdk_stream_set_hash_flag( inp, digest_algo );
+ /* kick off the filter */
+ sigver = sig_get_version( hd, list );
+ cdk_stream_read( inp, NULL, 0 );
+ mfx = _cdk_stream_get_opaque( inp, fHASH );
+ if( mfx && mfx->md ) {
+ if( sigver == 3 ) {
+ rc = cdk_sklist_write( list, out, mfx->md, 0x00, 0x03 );
+ if( !rc && !detached )
+ rc = write_literal( inp, out );
+ }
+ else {
+ if( !detached ) {
+ rc = cdk_sklist_write_onepass( list, out, 0x00, digest_algo );
+ if( !rc )
+ rc = write_literal( inp, out );
+ }
+ if( !rc )
+ rc = cdk_sklist_write( list, out, mfx->md, 0x00, 0x04 );
+ }
+ }
+ cdk_sklist_release( list );
+ return rc;
+}
+
+
+/**
+ * cdk_file_sign:
+ * @locusr: List of userid which should be used for signing
+ * @remusr: If encrypt is valid, the list of recipients
+ * @file: Name of the input file
+ * @output: Name of the output file
+ * @sigmode: Signature mode
+ * @encrypt: enable sign and encrypt
+ *
+ * Sign a file.
+ **/
+cdk_error_t
+cdk_file_sign( cdk_ctx_t hd, cdk_strlist_t locusr, cdk_strlist_t remusr,
+ const char * file, const char * output,
+ int sigmode, int encryptflag )
+{
+ cdk_stream_t inp = NULL, out = NULL;
+ int rc = 0;
+
+ if( !file || !output )
+ return CDK_Inv_Value;
+ if( encryptflag && !remusr )
+ return CDK_Inv_Value;
+ if( (sigmode != CDK_SIGMODE_NORMAL) && encryptflag )
+ return CDK_Inv_Mode;
+ if( !remusr && !locusr )
+ return CDK_Inv_Value;
+ if( sigmode == CDK_SIGMODE_CLEAR )
+ return file_clearsign( hd, locusr, file, output );
+
+ rc = cdk_stream_open( file, &inp );
+ if( rc )
+ return rc;
+
+ if( hd->opt.armor || encryptflag )
+ rc = cdk_stream_new( output, &out );
+ else
+ rc = cdk_stream_create( output, &out );
+ if( rc ) {
+ cdk_stream_close( inp );
+ return rc;
+ }
+ rc = cdk_stream_sign( hd, inp, out, locusr, remusr, encryptflag, sigmode );
+
+ cdk_stream_close( inp );
+ cdk_stream_close( out );
+ return rc;
+}
+
+
+static void
+put_hash_line( cdk_stream_t out, int digest_algo, int is_v4 )
+{
+ const char * s = NULL;
+
+ if( !is_v4 ) {
+ cdk_stream_putc( out, '\n' );
+ return;
+ }
+
+ switch( digest_algo ) {
+ case CDK_MD_MD2 : s = "Hash: MD2\n\n"; break;
+ case CDK_MD_MD5 : s = "Hash: MD5\n\n"; break;
+ case CDK_MD_SHA1 : s = "Hash: SHA1\n\n"; break;
+ case CDK_MD_RMD160 : s = "Hash: RIPEMD160\n\n"; break;
+ case CDK_MD_SHA256 : s = "Hash: SHA256\n\n"; break;
+ default : s = "Hash: SHA1\n\n"; break;
+ }
+ _cdk_stream_puts( out, s );
+}
+
+
+void
+_cdk_trim_string( char * s, int canon )
+{
+ while( s && *s &&( s[strlen( s )-1] == '\t'
+ || s[strlen( s )-1] == '\r'
+ || s[strlen( s )-1] == '\n'
+ || s[strlen( s )-1] == ' '))
+ s[strlen( s ) -1] = '\0';
+ if( canon )
+ strcat( s, "\r\n" );
+}
+
+
+static int
+stream_clearsign( cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out,
+ cdk_strlist_t locusr )
+{
+ cdk_md_hd_t md = NULL;
+ cdk_keylist_t list;
+ cdk_stream_t tmp;
+ cdk_pkt_seckey_t sk;
+ const char * s;
+ char buf[1024+2];
+ int digest_algo, sigver;
+ int rc, nread;
+
+ rc = cdk_sklist_build( &list, hd->db.sec, hd, locusr, 1, PK_USAGE_SIGN );
+ if( rc )
+ return rc;
+
+ sk = list->key.sk;
+ digest_algo = _cdk_sig_hash_for( sk->pubkey_algo, sk->version );
+ md = cdk_md_open( digest_algo, 0 );
+ if( !md ) {
+ cdk_sklist_release( list );
+ return CDK_Gcry_Error;
+ }
+
+ s = _cdk_armor_get_lineend( );
+ strcpy( buf, "-----BEGIN PGP SIGNED MESSAGE-----" );
+ strcat( buf, s );
+ _cdk_stream_puts( out, buf );
+ put_hash_line( out, digest_algo, sk->version == 4 );
+
+ while( !cdk_stream_eof( inp ) ) {
+ nread = _cdk_stream_gets( inp, buf, sizeof buf-1 );
+ if( !nread )
+ break;
+ _cdk_trim_string( buf, 1 );
+ cdk_md_write( md, buf, strlen( buf ) );
+ if( buf[0] == '-' ) {
+ memmove( &buf[2], buf, nread + 1 );
+ buf[1] = ' ';
+ }
+ if( strlen( s ) == 1 ) {
+ buf[strlen( buf ) - 1] = '\0';
+ buf[strlen( buf ) - 1] = '\n';
+ }
+ _cdk_stream_puts( out, buf );
+ }
+ _cdk_stream_puts( out, s );
+ tmp = cdk_stream_tmp( );
+ if( !tmp ) {
+ rc = CDK_Out_Of_Core;
+ goto leave;
+ }
+ cdk_stream_tmp_set_mode( tmp, 1 );
+ cdk_stream_set_armor_flag( tmp, CDK_ARMOR_SIGNATURE );
+
+ sigver = sig_get_version( hd, list );
+ rc = cdk_sklist_write( list, tmp, md, 0x01, sigver );
+ if( rc ) {
+ cdk_stream_close( tmp );
+ goto leave;
+ }
+
+ rc = cdk_stream_flush( tmp );
+ if( rc )
+ goto leave;
+
+ while( !cdk_stream_eof( tmp ) ) {
+ nread = cdk_stream_read( tmp, buf, sizeof buf-1 );
+ if( !nread )
+ break;
+ cdk_stream_write( out, buf, nread );
+ }
+ cdk_stream_close( tmp );
+
+ leave:
+ cdk_md_close( md );
+ cdk_sklist_release( list );
+ return rc;
+}
+
+
+static int
+file_clearsign( cdk_ctx_t hd, cdk_strlist_t locusr,
+ const char * file, const char * output )
+{
+ cdk_stream_t inp = NULL, out = NULL;
+ int rc;
+
+ if( !locusr || !file || !output )
+ return CDK_Inv_Value;
+ if( !hd->opt.overwrite && _cdk_check_file( output ) )
+ return CDK_Inv_Mode;
+
+ rc = cdk_stream_open( file, &inp );
+ if( rc )
+ return rc;
+
+ rc = cdk_stream_create( output, &out );
+ if( rc ) {
+ cdk_stream_close( inp );
+ return rc;
+ }
+
+ rc = stream_clearsign( hd, inp, out, locusr );
+
+ cdk_stream_close( inp );
+ cdk_stream_close( out );
+
+ return rc;
+}
+
diff --git a/libextra/opencdk/stream.c b/libextra/opencdk/stream.c
new file mode 100644
index 0000000000..14d73cd8a7
--- /dev/null
+++ b/libextra/opencdk/stream.c
@@ -0,0 +1,1073 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * stream.c - provides a STREAM object
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <assert.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "opencdk.h"
+#include "main.h"
+#include "filters.h"
+#include "stream.h"
+#include "types.h"
+
+
+static int stream_flush( cdk_stream_t s );
+static int stream_filter_write( cdk_stream_t s );
+static int stream_cache_flush( cdk_stream_t s, FILE * fp );
+
+
+/**
+ * cdk_stream_open: create a new stream based on an existing file.
+ * @file: The file to open
+ * @ret_s: The new STREAM object
+ **/
+cdk_error_t
+cdk_stream_open( const char * file, cdk_stream_t * ret_s )
+{
+ cdk_stream_t s;
+
+ if( !file || !ret_s )
+ return CDK_Inv_Value;
+
+ _cdk_log_debug( "open stream `%s'\n", file );
+ *ret_s = NULL;
+ s = cdk_calloc( 1, sizeof *s );
+ if( !s )
+ return CDK_Out_Of_Core;
+ s->fname = cdk_strdup( file );
+ if( !s->fname ) {
+ cdk_free( s );
+ return CDK_Out_Of_Core;
+ }
+ s->fp = fopen( file, "rb" );
+ if( !s->fp ) {
+ cdk_free( s->fname );
+ cdk_free( s );
+ return CDK_File_Error;
+ }
+ s->flags.write = 0;
+ *ret_s = s;
+ return 0;
+}
+
+
+/**
+ * cdk_stream_new: Create a new stream into into the given file.
+ * @file: The name of the new file
+ * @ret_s: The new STREAM object
+ **/
+cdk_error_t
+cdk_stream_new( const char * file, cdk_stream_t * ret_s )
+{
+ cdk_stream_t s;
+
+ if( !ret_s )
+ return CDK_Inv_Value;
+
+ _cdk_log_debug( "new stream `%s'\n", file? file : "[temp]" );
+ *ret_s = NULL;
+ s = cdk_calloc( 1, sizeof *s );
+ if( !s )
+ return CDK_Out_Of_Core;
+ s->flags.write = 1;
+ if( !file )
+ s->flags.temp = 1;
+ else {
+ s->fname = cdk_strdup( file );
+ if( !s->fname ) {
+ cdk_free( s );
+ return CDK_Out_Of_Core;
+ }
+ }
+ s->fp = tmpfile( );
+ if( !s->fp ) {
+ cdk_free( s->fname );
+ cdk_free( s );
+ return CDK_File_Error;
+ }
+ *ret_s = s;
+ return 0;
+}
+
+
+/**
+ * cdk_stream_create: create a new stream.
+ * @file: the filename
+ * @ret_s: the object
+ *
+ * The difference to cdk_stream_new is, that no filtering can be used with
+ * this kind of stream and everything is written directly to the stream.
+ **/
+cdk_error_t
+cdk_stream_create( const char * file, cdk_stream_t * ret_s )
+{
+ cdk_stream_t s;
+
+ if( !file || !ret_s )
+ return CDK_Inv_Value;
+
+ _cdk_log_debug( "create stream `%s'\n", file );
+ *ret_s = NULL;
+ s = cdk_calloc( 1, sizeof * s );
+ if( !s )
+ return CDK_Out_Of_Core;
+ s->flags.write = 1;
+ s->flags.filtrated = 1;
+ s->fname = cdk_strdup( file );
+ if( !s->fname ) {
+ cdk_free( s );
+ return CDK_Out_Of_Core;
+ }
+ s->fp = fopen( file, "w+b" );
+ if( !s->fp ) {
+ cdk_free( s->fname );
+ cdk_free( s );
+ return CDK_Out_Of_Core;
+ }
+ *ret_s = s;
+ return 0;
+}
+
+
+cdk_stream_t
+cdk_stream_tmp( void )
+{
+ cdk_stream_t s;
+ int rc = cdk_stream_new( NULL, &s );
+ if( !rc )
+ return s;
+ return NULL;
+}
+
+
+cdk_stream_t
+cdk_stream_tmp_from_mem( const void * buf, size_t count )
+{
+ cdk_stream_t s;
+ int nwritten;
+
+ s = cdk_stream_tmp( );
+ if( !s )
+ return NULL;
+
+ nwritten = cdk_stream_write( s, buf, count );
+ if( nwritten == EOF ) {
+ cdk_stream_close( s );
+ return NULL;
+ }
+ cdk_stream_seek( s, 0 );
+ return s;
+}
+
+
+
+cdk_stream_t
+_cdk_stream_fpopen( FILE * fp, unsigned write_mode )
+{
+ cdk_stream_t s;
+
+ s = cdk_calloc( 1, sizeof *s );
+ if( !s )
+ return NULL;
+
+ s->fp = fp;
+ s->flags.filtrated = 1;
+ s->flags.write = write_mode;
+
+ return s;
+}
+
+
+cdk_error_t
+_cdk_stream_append( const char * file, cdk_stream_t * ret_s )
+{
+ cdk_stream_t s;
+ FILE * fp;
+ int rc;
+
+ if( !ret_s )
+ return CDK_Inv_Value;
+ rc = cdk_stream_open( file, &s );
+ if( rc )
+ return rc;
+ fp = fopen( file, "a+b" );
+ if( !fp ) {
+ cdk_stream_close( s );
+ return CDK_File_Error;
+ }
+ fclose( s->fp );
+ s->fp = fp;
+ s->flags.write = 1;
+ *ret_s = s;
+ return 0;
+}
+
+
+int
+cdk_stream_control( cdk_stream_t s, int ctl, int val )
+{
+ if( !s )
+ return CDK_Inv_Value;
+
+ if( val == -1 ) {
+ switch( ctl ) {
+ case CDK_STREAMCTL_COMPRESSED: return s->flags.compressed;
+ }
+ return 0;
+ }
+ switch( ctl ) {
+ case CDK_STREAMCTL_DISABLE: s->flags.no_filter = val; break;
+ case CDK_STREAMCTL_COMPRESSED: s->flags.compressed = val; break;
+ default : return CDK_Inv_Mode;
+ }
+ return 0;
+}
+
+
+cdk_error_t
+cdk_stream_flush( cdk_stream_t s )
+{
+ int rc = 0;
+
+ if( !s )
+ return CDK_Inv_Value;
+
+ if( !s->flags.filtrated ) {
+ if( !cdk_stream_get_length( s ) )
+ return 0;
+ rc = cdk_stream_seek( s, 0 );
+ if( !rc )
+ rc = stream_flush( s );
+ if( !rc ) {
+ rc = stream_filter_write( s );
+ if( rc )
+ s->error = rc;
+ }
+ s->flags.filtrated = 1;
+ }
+ return rc;
+}
+
+
+void
+cdk_stream_tmp_set_mode( cdk_stream_t s, int val )
+{
+ if( s && s->flags.temp )
+ s->fmode = val;
+}
+
+
+/**
+ * cdk_stream_close: Close a stream and flush all buffers.
+ * @s: The STREAM object.
+ *
+ * This function work different for read or write streams. When the
+ * stream is for reading, the filtering is already done and we can
+ * simply close the file and all buffers.
+ * But for the case it's a write stream, we need to apply all registered
+ * filters now. The file is closed in the filter function and not here.
+ **/
+cdk_error_t
+cdk_stream_close( cdk_stream_t s )
+{
+ struct stream_filter_s * f, * f2;
+ int rc = 0;
+
+ if( !s )
+ return CDK_Inv_Value;
+
+ _cdk_log_debug( "close stream `%s'\n", s->fname? s->fname : "[temp]" );
+
+ if( !s->flags.filtrated && !s->error )
+ rc = cdk_stream_flush( s );
+ if( s->fname || s->flags.temp ) {
+ rc = fclose( s->fp );
+ s->fp = NULL;
+ if( rc )
+ rc = CDK_File_Error;
+ }
+ f = s->filters;
+ while( f ) {
+ f2 = f->next;
+ if( f->fnct )
+ f->fnct( f->opaque, STREAMCTL_FREE, NULL, NULL );
+ cdk_free( f );
+ f = f2;
+ }
+ if( s->fname ) {
+ cdk_free( s->fname );
+ s->fname = NULL;
+ }
+ cdk_free( s );
+ return rc;
+}
+
+
+/**
+ * cdk_stream_eof: Return if the associated file handle was set to EOF.
+ * @s: The STREAM object.
+ *
+ * This function will only work with read streams.
+ **/
+int
+cdk_stream_eof( cdk_stream_t s )
+{
+ return s? s->flags.eof : -1;
+}
+
+
+const char *
+_cdk_stream_get_fname( cdk_stream_t s )
+{
+ return s? s->fname : NULL;
+}
+
+
+FILE *
+_cdk_stream_get_fp( cdk_stream_t s )
+{
+ return s? s->fp : NULL;
+}
+
+
+int
+_cdk_stream_get_errno( cdk_stream_t s )
+{
+ return s? s->error : CDK_Inv_Value;
+}
+
+
+/**
+ * cdk_stream_get_length: Return the length of the associated file handle.
+ * @s: The STREAM object.
+ *
+ * This file only works for read stream because it's likely that the
+ * write stream is not flushed or even no data was inserted.
+ **/
+unsigned
+cdk_stream_get_length( cdk_stream_t s )
+{
+ struct stat statbuf;
+ int rc;
+
+ if( !s )
+ return (unsigned)-1;
+
+ rc = stream_flush( s );
+ if( rc ) {
+ s->error = CDK_File_Error;
+ return (unsigned)-1;
+ }
+ if( fstat( fileno( s->fp ), &statbuf ) ) {
+ s->error = CDK_File_Error;
+ return (unsigned)-1;
+ }
+ return statbuf.st_size;
+}
+
+
+static struct stream_filter_s *
+filter_add2( cdk_stream_t s )
+{
+ struct stream_filter_s * f;
+
+ assert( s );
+
+ f = cdk_calloc( 1, sizeof *f );
+ if( !f )
+ return NULL;
+ f->next = s->filters;
+ s->filters = f;
+ return f;
+}
+
+
+static struct stream_filter_s *
+filter_search( cdk_stream_t s, filter_fnct_t fnc )
+{
+ struct stream_filter_s * f;
+
+ assert( s );
+
+ for( f = s->filters; f; f = f->next ) {
+ if( f->fnct == fnc )
+ return f;
+ }
+ return NULL;
+}
+
+
+struct stream_filter_s *
+filter_add( cdk_stream_t s, filter_fnct_t fnc, int type )
+{
+ struct stream_filter_s * f;
+
+ assert( s );
+
+ s->flags.filtrated = 0;
+ f = filter_search( s, fnc );
+ if( f )
+ return f;
+ f = filter_add2( s );
+ if( !f )
+ return NULL;
+ f->fnct = fnc;
+ f->flags.enabled = 1;
+ f->tmp = NULL;
+ f->type = type;
+ switch( type ) {
+ case fARMOR : f->opaque = &f->u.afx; break;
+ case fCIPHER : f->opaque = &f->u.cfx; break;
+ case fLITERAL: f->opaque = &f->u.pfx; break;
+ case fCOMPRESS : f->opaque = &f->u.zfx; break;
+ case fHASH : f->opaque = &f->u.mfx; break;
+ case fTEXT : f->opaque = &f->u.tfx; break;
+ default : f->opaque = NULL;
+ }
+ return f;
+}
+
+
+static int
+stream_get_mode( cdk_stream_t s )
+{
+ assert( s );
+
+ if( s->flags.temp )
+ return s->fmode;
+ return s->flags.write;
+}
+
+
+static filter_fnct_t
+stream_id_to_filter( int type )
+{
+ switch( type ) {
+ case fARMOR : return _cdk_filter_armor;
+ case fLITERAL: return _cdk_filter_literal;
+ case fTEXT : return _cdk_filter_text;
+ case fCIPHER : return _cdk_filter_cipher;
+ case fCOMPRESS : return _cdk_filter_compress;
+ default : return NULL;
+ }
+}
+
+
+/**
+ * cdk_stream_filter_disable: Disable the filter with the type 'type'
+ * @s: The STREAM object
+ * @type: The numberic filter ID.
+ *
+ **/
+cdk_error_t
+cdk_stream_filter_disable( cdk_stream_t s, int type )
+{
+ struct stream_filter_s * f;
+ filter_fnct_t fnc;
+
+ if( !s )
+ return CDK_Inv_Value;
+ fnc = stream_id_to_filter( type );
+ f = filter_search( s, fnc );
+ if( f )
+ f->flags.enabled = 0;
+ return 0;
+}
+
+
+static int
+stream_fp_replace( cdk_stream_t s, FILE ** tmp )
+{
+ int rc;
+
+ assert( s );
+
+ rc = fclose( s->fp );
+ if( rc )
+ return CDK_File_Error;
+ s->fp = *tmp;
+ *tmp = NULL;
+ return 0;
+}
+
+
+/* This function is exactly like filter_read, except the fact that we can't
+ use tmpfile () all the time. That's why we open the real file when there
+ is no last filter. */
+static int
+stream_filter_write( cdk_stream_t s )
+{
+ struct stream_filter_s * f;
+ int rc = 0;
+
+ assert( s );
+
+ if( s->flags.filtrated )
+ return CDK_Inv_Value;
+
+ for( f = s->filters; f; f = f->next ) {
+ if( !f->flags.enabled )
+ continue;
+ /* if there is no next filter, create the final output file */
+ _cdk_log_debug( "filter [write]: last filter=%d fname=%s\n",
+ f->next? 1 : 0, s->fname );
+ if( !f->next && s->fname )
+ f->tmp = fopen( s->fname, "w+b" );
+ else
+ f->tmp = tmpfile( );
+ if( !f->tmp ) {
+ rc = CDK_File_Error;
+ break;
+ }
+ /* If there is no next filter, flush the cache. We also do this
+ when the next filter is the armor filter because this filter
+ is special and before it starts, all data should be written. */
+ if( (!f->next || f->next->type == fARMOR) && s->cache.size ) {
+ rc = stream_cache_flush( s, f->tmp );
+ if( rc )
+ break;
+ }
+ rc = f->fnct( f->opaque, f->ctl, s->fp, f->tmp );
+ _cdk_log_debug( "filter [write]: type=%d rc=%d\n", f->type, rc );
+ if( !rc )
+ rc = stream_fp_replace( s, &f->tmp );
+ if( !rc )
+ rc = cdk_stream_seek( s, 0 );
+ if( rc ) {
+ fclose( f->tmp );
+ break;
+ }
+ }
+ return rc;
+}
+
+
+/* Here all data from the file handle is passed through all filters.
+ The scheme works like this:
+ Create a tempfile and use it for the output of the filter. Then the
+ original file handle will be closed and replace with the temp handle.
+ The file pointer will be set to the begin and the game starts again. */
+static int
+stream_filter_read( cdk_stream_t s )
+{
+ struct stream_filter_s * f;
+ int rc = 0;
+
+ assert( s );
+
+ if( s->flags.filtrated )
+ return 0;
+
+ for( f = s->filters; f; f = f->next ) {
+ if( !f->flags.enabled )
+ continue;
+ f->tmp = tmpfile( );
+ if( !f->tmp ) {
+ rc = CDK_File_Error;
+ break;
+ }
+ rc = f->fnct( f->opaque, f->ctl, s->fp, f->tmp );
+ _cdk_log_debug( "filter %s [read]: type=%d rc=%d\n",
+ s->fname? s->fname : "[temp]", f->type, rc );
+ if( rc )
+ break;
+
+ /* if the filter is read-only, do not replace the FP because
+ the contents were not altered in any way. */
+ if( !f->flags.rdonly ) {
+ rc = stream_fp_replace( s, &f->tmp );
+ if( rc )
+ break;
+ }
+ else {
+ fclose( f->tmp );
+ f->tmp = NULL;
+ }
+ rc = cdk_stream_seek( s, 0 );
+ if( rc )
+ break;
+ /* Disable the filter after it was successfully used. The idea
+ is the following: let's say the armor filter was pushed and
+ later more filters were added. The second time the filter code
+ will be executed, only the new filter should be started but
+ not the old because we already used it. */
+ f->flags.enabled = 0;
+ }
+ return rc;
+}
+
+
+void *
+_cdk_stream_get_opaque( cdk_stream_t s, int fid )
+{
+ struct stream_filter_s * f;
+
+ if( !s )
+ return NULL;
+
+ for( f = s->filters; f; f = f->next ) {
+ if( f->type == fid )
+ return f->opaque;
+ }
+ return NULL;
+}
+
+
+/**
+ * cdk_stream_read: Try to read count bytes from the STREAM object.
+ * @s: The STREAM object.
+ * @buf: The buffer to insert the readed bytes.
+ * @count: Request so much bytes.
+ *
+ * When this function is called the first time, it can take a while
+ * because all filters need to be processed. Please remember that you
+ * need to add the filters in reserved order.
+ **/
+int
+cdk_stream_read( cdk_stream_t s, void * buf, size_t count )
+{
+ int nread;
+ int rc;
+
+ if( !s )
+ return EOF;
+
+ if( s->flags.write && !s->flags.temp )
+ return EOF; /* this is a write stream */
+
+ if( !s->flags.no_filter && !s->cache.on && !s->flags.filtrated ) {
+ rc = stream_filter_read( s );
+ if( rc ) {
+ s->error = rc;
+ return EOF;
+ }
+ s->flags.filtrated = 1;
+ }
+ if( !buf && !count )
+ return 0;
+ nread = fread( buf, 1, count, s->fp );
+ if( !nread )
+ nread = EOF;
+ if( feof( s->fp ) )
+ s->flags.eof = 1;
+ return nread;
+}
+
+
+int
+cdk_stream_getc( cdk_stream_t s )
+{
+ unsigned char buf[2];
+ int nread;
+
+ if( !s )
+ return EOF;
+
+ nread = cdk_stream_read( s, buf, 1 );
+ if( nread == EOF ) {
+ s->error = CDK_File_Error;
+ return EOF;
+ }
+ return buf[0];
+}
+
+
+/**
+ * cdk_stream_write: Try to write count bytes into the stream.
+ * @s: The STREAM object
+ * @buf: The buffer with the values to write.
+ * @count: The size of the buffer.
+ *
+ * In this function we simply write the bytes to the stream. We can't
+ * use the filters here because it would mean they have to support
+ * partial flushing.
+ **/
+int
+cdk_stream_write( cdk_stream_t s, const void * buf, size_t count )
+{
+ int nwritten;
+
+ if( !s )
+ return CDK_Inv_Value;
+
+ if( !s->flags.write )
+ return CDK_Inv_Mode; /* this is a read stream */
+
+ if( !buf && !count )
+ return stream_flush( s );
+
+ if( s->cache.on ) {
+ if( s->cache.size + count > sizeof( s->cache.buf ) )
+ return CDK_EOF;
+ memcpy( s->cache.buf + s->cache.size, buf, count );
+ s->cache.size += count;
+ return 0;
+ }
+
+ nwritten = fwrite( buf, 1, count, s->fp );
+ if( !nwritten )
+ nwritten = EOF;
+ return nwritten;
+}
+
+
+int
+cdk_stream_putc( cdk_stream_t s, int c )
+{
+ unsigned char buf[2];
+ int nwritten;
+
+ if( !s )
+ return EOF;
+ buf[0] = c;
+ nwritten = cdk_stream_write( s, buf, 1 );
+ if( nwritten == EOF ) {
+ s->error = CDK_File_Error;
+ return EOF;
+ }
+ return 0;
+}
+
+
+long
+cdk_stream_tell( cdk_stream_t s )
+{
+ return s? ftell( s->fp ): (long)-1;
+}
+
+
+cdk_error_t
+cdk_stream_seek( cdk_stream_t s, long offset )
+{
+ int rc;
+
+ if( !s )
+ return CDK_Inv_Value;
+ if( offset < cdk_stream_get_length( s ) )
+ s->flags.eof = 0;
+ rc = fseek( s->fp, offset, SEEK_SET );
+ if( rc )
+ rc = CDK_File_Error;
+ return rc;
+}
+
+
+static int
+stream_flush( cdk_stream_t s )
+{
+ int rc;
+
+ assert( s );
+
+ rc = fflush( s->fp );
+ if( rc )
+ rc = CDK_File_Error;
+ return rc;
+}
+
+
+cdk_error_t
+cdk_stream_set_armor_flag( cdk_stream_t s, int type )
+{
+ struct stream_filter_s * f;
+
+ if( !s )
+ return CDK_Inv_Value;
+ f = filter_add( s, _cdk_filter_armor, fARMOR );
+ if( !f )
+ return CDK_Out_Of_Core;
+ f->u.afx.idx = f->u.afx.idx2 = type;
+ f->ctl = stream_get_mode( s );
+ return 0;
+}
+
+
+cdk_error_t
+cdk_stream_set_literal_flag( cdk_stream_t s, int mode, const char * fname )
+{
+ struct stream_filter_s * f;
+
+ if( !s )
+ return CDK_Inv_Value;
+ f = filter_add( s, _cdk_filter_literal, fLITERAL );
+ if( !f )
+ return CDK_Out_Of_Core;
+ f->u.pfx.mode = mode;
+ f->u.pfx.filename = fname? cdk_strdup( fname ) : NULL;
+ f->ctl = stream_get_mode( s );
+ if( s->blkmode ) {
+ f->u.pfx.blkmode.on = 1;
+ f->u.pfx.blkmode.size = s->blkmode;
+ }
+ return 0;
+}
+
+
+cdk_error_t
+cdk_stream_set_cipher_flag( cdk_stream_t s, cdk_dek_t dek, int use_mdc )
+{
+ struct stream_filter_s * f;
+
+ if( !s )
+ return CDK_Inv_Value;
+ f = filter_add( s, _cdk_filter_cipher, fCIPHER );
+ if( !f )
+ return CDK_Out_Of_Core;
+ dek->use_mdc = use_mdc;
+ f->ctl = stream_get_mode( s );
+ f->u.cfx.dek = dek;
+ f->u.cfx.mdc_method = use_mdc? GCRY_MD_SHA1 : 0;
+ if( s->blkmode ) {
+ f->u.cfx.blkmode.on = 1;
+ f->u.cfx.blkmode.size = s->blkmode;
+ }
+ return 0;
+}
+
+
+cdk_error_t
+cdk_stream_set_compress_flag( cdk_stream_t s, int algo, int level )
+{
+ struct stream_filter_s * f;
+
+ if( !s )
+ return CDK_Inv_Value;
+ f = filter_add( s, _cdk_filter_compress, fCOMPRESS );
+ if( !f )
+ return CDK_Out_Of_Core;
+ f->ctl = stream_get_mode( s );
+ f->u.zfx.algo = algo;
+ f->u.zfx.level = level;
+ return 0;
+}
+
+
+cdk_error_t
+cdk_stream_set_text_flag( cdk_stream_t s, const char * lf )
+{
+ struct stream_filter_s * f;
+
+ if( !s )
+ return CDK_Inv_Value;
+ f = filter_add( s, _cdk_filter_text, fTEXT );
+ if( !f )
+ return CDK_Out_Of_Core;
+ f->ctl = stream_get_mode( s );
+ f->u.tfx.lf = lf;
+ return 0;
+}
+
+
+cdk_error_t
+cdk_stream_set_hash_flag( cdk_stream_t s, int algo )
+{
+ struct stream_filter_s * f;
+
+ if( !s )
+ return CDK_Inv_Value;
+ if( stream_get_mode( s ) )
+ return CDK_Inv_Mode;
+ f = filter_add( s, _cdk_filter_hash, fHASH );
+ if( !f )
+ return CDK_Out_Of_Core;
+ f->ctl = stream_get_mode( s );
+ f->u.mfx.digest_algo = algo;
+ f->flags.rdonly = 1;
+ return 0;
+}
+
+
+cdk_error_t
+cdk_stream_set_cache( cdk_stream_t s, int val )
+{
+ if( !s )
+ return CDK_Inv_Value;
+ if( !s->flags.write )
+ return CDK_Inv_Mode;
+ s->cache.on = val;
+ return 0;
+}
+
+
+static int
+stream_cache_flush( cdk_stream_t s, FILE * fp )
+{
+ int nwritten;
+
+ assert( s );
+
+ if( s->cache.size > 0 ) {
+ nwritten = fwrite( s->cache.buf, 1, s->cache.size, fp );
+ if( !nwritten )
+ return CDK_File_Error;
+ s->cache.size = 0;
+ s->cache.on = 0;
+ memset( s->cache.buf, 0, sizeof s->cache.buf );
+ }
+ return 0;
+}
+
+
+cdk_error_t
+cdk_stream_kick_off( cdk_stream_t inp, cdk_stream_t out )
+{
+ byte buf[8192];
+ int nread, nwritten;
+ int rc = 0;
+
+ if( !inp || !out )
+ return CDK_Inv_Value;
+ while( !cdk_stream_eof( inp ) ) {
+ nread = cdk_stream_read( inp, buf, sizeof buf-1 );
+ if( nread == EOF )
+ break;
+ nwritten = cdk_stream_write( out, buf, nread );
+ if( nwritten == EOF )
+ rc = CDK_File_Error;
+ }
+ wipemem( buf, sizeof buf );
+ return rc;
+}
+
+
+/**
+ * cdk_stream_mmap:
+ * @s: the stream
+ * @ret_buf: the buffer to store the content
+ * @ret_count: length of the buffer
+ *
+ * Map the data of the given stream into a memory section. @ret_count
+ * contains the length of the buffer.
+ **/
+cdk_error_t
+cdk_stream_mmap( cdk_stream_t s, byte ** ret_buf, size_t * ret_count )
+{
+ const u32 max_filesize = 16777216;
+ u32 len, oldpos;
+ int n, rc;
+ char * p;
+
+ if( !s || !ret_buf || !ret_count )
+ return CDK_Inv_Value;
+
+ *ret_count = 0;
+ *ret_buf = NULL;
+ oldpos = cdk_stream_tell( s );
+ rc = cdk_stream_flush( s );
+ if( !rc )
+ rc = cdk_stream_seek( s, 0 );
+ if( rc )
+ return rc;
+ len = cdk_stream_get_length( s );
+ if( !len || len > max_filesize )
+ return 0;
+ p = *ret_buf = cdk_calloc( 1, len+1 );
+ if( !p )
+ return 0;
+ *ret_count = len;
+ n = cdk_stream_read( s, p, len );
+ if( n != len )
+ *ret_count = n;
+ rc = cdk_stream_seek( s, oldpos );
+ return rc;
+}
+
+
+/**
+ * cdk_stream_peek:
+ * @inp: the input stream handle
+ * @s: buffer
+ * @count: number of bytes to peek
+ *
+ * The function acts like cdk_stream_read with the difference that
+ * the file pointer is moved to the old position after the bytes were read.
+ **/
+int
+cdk_stream_peek( cdk_stream_t inp, byte *s, size_t count )
+{
+ unsigned off;
+ int nbytes, rc;
+
+ if( !inp || !s )
+ return CDK_Inv_Value;
+ off = cdk_stream_tell( inp );
+ nbytes = _cdk_stream_gets( inp, s, count );
+ rc = cdk_stream_seek( inp, off );
+ if( rc )
+ return 0;
+ return nbytes;
+}
+
+
+int
+_cdk_stream_gets( cdk_stream_t s, char * buf, size_t count )
+{
+ int c, i = 0;
+
+ if( !s )
+ return CDK_Inv_Value;
+ while( !cdk_stream_eof( s ) && count > 0 ) {
+ c = cdk_stream_getc( s );
+ if( c == EOF || c == '\r' || c == '\n' ) {
+ buf[i++] = '\0';
+ break;
+ }
+ buf[i++] = c;
+ count--;
+ }
+ return i;
+}
+
+
+int
+_cdk_stream_puts( cdk_stream_t s, const char * buf )
+{
+ return cdk_stream_write( s, buf, strlen( buf ) );
+}
+
+
+int
+_cdk_stream_set_blockmode( cdk_stream_t s, size_t nbytes )
+{
+ if( !s )
+ return CDK_Inv_Value;
+ _cdk_log_debug( "set block mode for stream (size=%d)\n", nbytes );
+ s->blkmode = nbytes;
+ return 0;
+}
+
+
+int
+_cdk_stream_get_blockmode( cdk_stream_t s )
+{
+ return s? s->blkmode : 0;
+}
+
diff --git a/libextra/opencdk/stream.h b/libextra/opencdk/stream.h
new file mode 100644
index 0000000000..cbc575ed01
--- /dev/null
+++ b/libextra/opencdk/stream.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * stream.h - internal definiton for the STREAM object
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef CDK_STREAM_H
+#define CDK_STREAM_H
+
+enum {
+ fDUMMY = 0,
+ fARMOR = 1,
+ fCIPHER = 2,
+ fLITERAL = 3,
+ fCOMPRESS= 4,
+ fHASH = 5,
+ fTEXT = 6
+};
+
+typedef int (*filter_fnct_t) (void * opaque, int ctl, FILE * in, FILE * out);
+
+struct stream_filter_s {
+ struct stream_filter_s * next;
+ filter_fnct_t fnct;
+ void * opaque;
+ FILE * tmp;
+ union {
+ armor_filter_t afx;
+ cipher_filter_t cfx;
+ literal_filter_t pfx;
+ compress_filter_t zfx;
+ text_filter_t tfx;
+ md_filter_t mfx;
+ } u;
+ struct {
+ unsigned enabled:1;
+ unsigned rdonly:1;
+ } flags;
+ unsigned type;
+ unsigned ctl;
+};
+
+
+struct cdk_stream_s {
+ struct stream_filter_s * filters;
+ int fmode;
+ int error;
+ size_t blkmode;
+ struct {
+ unsigned filtrated:1;
+ unsigned eof:1;
+ unsigned write:1;
+ unsigned temp:1;
+ unsigned reset:1;
+ unsigned no_filter:1;
+ unsigned compressed:3;
+ } flags;
+ struct {
+ unsigned char buf[8192];
+ unsigned on:1;
+ size_t size;
+ } cache;
+ char * fname;
+ FILE * fp;
+};
+
+#endif /* CDK_STREAM_H */
diff --git a/libextra/opencdk/sym-cipher.c b/libextra/opencdk/sym-cipher.c
new file mode 100644
index 0000000000..edcceac678
--- /dev/null
+++ b/libextra/opencdk/sym-cipher.c
@@ -0,0 +1,187 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * sym-cipher.c
+ * Copyright (C) 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include "opencdk.h"
+#include "main.h"
+#include "cipher.h"
+
+
+struct cdk_cipher_hd_s {
+ gcry_cipher_hd_t hd;
+ int algo;
+};
+
+inline
+static int cdk_cipher_to_gcry( int algo)
+{
+ switch( algo ) {
+ case CDK_CIPHER_BLOWFISH: return GCRY_CIPHER_BLOWFISH;
+ case CDK_CIPHER_TWOFISH: return GCRY_CIPHER_TWOFISH;
+ case CDK_CIPHER_3DES: return GCRY_CIPHER_3DES;
+ case CDK_CIPHER_CAST5: return GCRY_CIPHER_CAST5;
+ case CDK_CIPHER_AES: return GCRY_CIPHER_AES;
+ case CDK_CIPHER_AES192: return GCRY_CIPHER_AES192;
+ case CDK_CIPHER_AES256: return GCRY_CIPHER_AES256;
+ default: return -1;
+ }
+}
+
+cdk_cipher_hd_t
+cdk_cipher_new( int algo, int pgp_sync )
+{
+ cdk_cipher_hd_t hd;
+ unsigned int flags = 0;
+ gcry_error_t err;
+
+ if( cdk_cipher_test_algo( algo ) )
+ return NULL;
+ hd = cdk_calloc( 1, sizeof * hd );
+ if( !hd )
+ return NULL;
+
+ if( pgp_sync )
+ flags = GCRY_CIPHER_ENABLE_SYNC;
+
+ hd->algo = algo;
+
+ err = gcry_cipher_open( &hd->hd, cdk_cipher_to_gcry(algo),
+ GCRY_CIPHER_MODE_CFB, flags);
+ if( err ) {
+ cdk_free( hd );
+ return NULL;
+ }
+ return hd;
+}
+
+
+cdk_cipher_hd_t
+cdk_cipher_open( int algo, int pgp_sync,
+ const byte * key, size_t keylen,
+ const byte * ivbuf, size_t ivlen )
+{
+ cdk_cipher_hd_t hd;
+ gcry_error_t err;
+
+ hd = cdk_cipher_new( algo, pgp_sync );
+ if( hd ) {
+ err = gcry_cipher_setkey( hd->hd, key, keylen );
+ if( !err)
+ err = gcry_cipher_setiv( hd->hd, ivbuf, ivlen );
+ if( err) {
+ cdk_cipher_close( hd );
+ hd = NULL;
+ }
+ }
+ return hd;
+}
+
+
+void
+cdk_cipher_close( cdk_cipher_hd_t hd )
+{
+ if( !hd )
+ return;
+ gcry_cipher_close( hd->hd);
+ cdk_free( hd );
+}
+
+
+int
+cdk_cipher_decrypt( cdk_cipher_hd_t hd, byte * outbuf, const byte *inbuf,
+ size_t nbytes )
+{
+gcry_error_t err;
+
+ if( !hd )
+ return CDK_Inv_Value;
+ err = gcry_cipher_decrypt( hd->hd, outbuf, nbytes, inbuf, nbytes);
+
+ if (err) return CDK_Gcry_Error;
+ else return 0;
+}
+
+
+int
+cdk_cipher_encrypt( cdk_cipher_hd_t hd, byte * outbuf, const byte *inbuf,
+ size_t nbytes )
+{
+gcry_error_t err;
+
+ if( !hd )
+ return CDK_Inv_Value;
+ err = gcry_cipher_encrypt( hd->hd, outbuf, nbytes, inbuf, nbytes);
+
+ if (err) return CDK_Gcry_Error;
+ else return 0;
+}
+
+
+void
+cdk_cipher_sync( cdk_cipher_hd_t hd )
+{
+ gcry_cipher_sync( hd->hd);
+}
+
+
+int
+cdk_cipher_setiv( cdk_cipher_hd_t hd, const byte *ivbuf, size_t ivlen )
+{
+ if( !hd )
+ return CDK_Inv_Value;
+
+ gcry_cipher_setiv( hd->hd, ivbuf, ivlen);
+ return 0;
+}
+
+
+int
+cdk_cipher_setkey( cdk_cipher_hd_t hd, const byte *keybuf, size_t keylen )
+{
+ if( !hd )
+ return CDK_Inv_Value;
+
+ gcry_cipher_setkey( hd->hd, keybuf, keylen);
+ return 0;
+}
+
+
+int
+cdk_cipher_get_algo_blklen( int algo )
+{
+ return gcry_cipher_get_algo_blklen( cdk_cipher_to_gcry( algo));
+}
+
+
+int
+cdk_cipher_get_algo_keylen( int algo )
+{
+ return gcry_cipher_get_algo_keylen( cdk_cipher_to_gcry( algo));
+}
+
+
+int
+cdk_cipher_test_algo( int algo )
+{
+ return gcry_cipher_test_algo( cdk_cipher_to_gcry( algo));
+}
diff --git a/libextra/opencdk/trustdb.c b/libextra/opencdk/trustdb.c
new file mode 100644
index 0000000000..cce5418324
--- /dev/null
+++ b/libextra/opencdk/trustdb.c
@@ -0,0 +1,269 @@
+/* -*- Mode: C; c-file-style: "bsd" -*- */
+/* trustdb.c - High level interface for ownertrust handling
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 "opencdk.h"
+#include "main.h"
+
+#define TRUST_MASK 15
+#define RECORD_SIZE 40
+#define MIN_TRUSTDB_VER 3
+
+
+enum {
+ TDB_RECORD_TRUST = 12,
+ TDB_RECORD_VALID = 13,
+};
+
+struct tdb_record_s {
+ int recno;
+ union {
+ struct {
+ byte reserved;
+ byte fpr[20];
+ int ownertrust;
+ byte depth;
+ u32 validlist;
+ } trust;
+ struct {
+ byte reserved;
+ byte fpr[20];
+ u32 next;
+ int valid;
+ } valid;
+ } u;
+};
+typedef struct tdb_record_s *tdb_record_t;
+
+
+static void
+trustdb_rec_release( tdb_record_t rec )
+{
+ if( rec ) {
+ rec->recno = 0;
+ cdk_free( rec );
+ }
+}
+
+
+static tdb_record_t
+trustdb_rec_new( void )
+{
+ tdb_record_t rec;
+
+ rec = cdk_calloc( 1, sizeof *rec );
+ if( !rec )
+ return NULL;
+ return rec;
+}
+
+
+static int
+trustdb_check( cdk_stream_t a, int req_ver )
+{
+ int rc = 0;
+ int c = 0, nread;
+ byte magic[3];
+
+ cdk_stream_seek( a, 0 );
+ c = cdk_stream_getc( a );
+ if( c == EOF || c != 1 )
+ return CDK_General_Error;
+ nread = cdk_stream_read( a, magic, 3 );
+ if( nread == EOF )
+ return CDK_File_Error;
+ c = cdk_stream_getc( a );
+ if( c == EOF )
+ rc = CDK_General_Error;
+ else if( c < req_ver )
+ rc = CDK_Wrong_Format;
+ return rc;
+}
+
+
+static int
+trustdb_rec_parse( cdk_stream_t buf, tdb_record_t r )
+{
+ size_t n;
+ int recno;
+
+ if( !buf || !r )
+ return CDK_Inv_Value;
+
+ recno = cdk_stream_getc( buf );
+ if( recno == EOF )
+ return EOF;
+
+ switch( recno ) {
+ case TDB_RECORD_TRUST: /* trust record: new */
+ r->recno = 12;
+ r->u.trust.reserved = cdk_stream_getc (buf);
+ n = cdk_stream_read (buf, r->u.trust.fpr, 20);
+ r->u.trust.ownertrust = cdk_stream_getc (buf);
+ r->u.trust.depth = cdk_stream_getc (buf);
+ r->u.trust.validlist = 0;
+ n = 4;
+ while (n--)
+ cdk_stream_getc (buf);
+ n = RECORD_SIZE - 28;
+ while (n--)
+ cdk_stream_getc (buf);
+ if (r->u.trust.ownertrust == EOF)
+ return CDK_EOF;
+ break;
+
+ case TDB_RECORD_VALID: /* valid record: new */
+ r->recno = 13;
+ r->u.valid.reserved = cdk_stream_getc (buf);
+ n = cdk_stream_read (buf, r->u.valid.fpr, 20);
+ r->u.valid.valid = cdk_stream_getc (buf);
+ r->u.valid.next = 0;
+ n = 4;
+ while (n--)
+ cdk_stream_getc (buf);
+ n = RECORD_SIZE - 27;
+ while (n--)
+ cdk_stream_getc (buf);
+ if (r->u.valid.valid == EOF)
+ return CDK_EOF;
+ break;
+
+ default:
+ n = RECORD_SIZE - 1;
+ while (n--)
+ cdk_stream_getc (buf);
+ break;
+ }
+ r->recno = recno;
+ return 0;
+}
+
+
+tdb_record_t
+trustdb_rec_byfpr( cdk_stream_t buf, int type,
+ const byte * fpr, size_t fprlen )
+{
+ tdb_record_t rec;
+
+ if (!fpr || !buf)
+ return NULL;
+
+ rec = trustdb_rec_new ();
+ if( !rec )
+ return NULL;
+
+ while( trustdb_rec_parse( buf, rec ) != EOF ) {
+ if( rec->recno != type )
+ continue;
+ switch( type ) {
+ case TDB_RECORD_VALID:
+ if( !memcmp( fpr, rec->u.valid.fpr, fprlen ) )
+ return rec;
+ break;
+
+ case TDB_RECORD_TRUST:
+ if( !memcmp( rec->u.trust.fpr, fpr, fprlen ) )
+ return rec;
+ break;
+ }
+ }
+ trustdb_rec_release ( rec );
+ rec = NULL;
+ return rec;
+}
+
+
+int
+cdk_trustdb_get_ownertrust( cdk_stream_t inp, cdk_pkt_pubkey_t pk,
+ int *r_val, int *r_flags )
+{
+ tdb_record_t rec = NULL;
+ byte fpr[20];
+ int flags = 0;
+ int rc;
+
+ if( !inp || !r_val || !r_flags || !pk )
+ return CDK_Inv_Value;
+
+ rc = trustdb_check( inp, MIN_TRUSTDB_VER );
+ if( rc )
+ return rc;
+
+ *r_val = CDK_TRUST_UNKNOWN;
+ cdk_pk_get_fingerprint( pk, fpr );
+ cdk_stream_seek( inp, 0 );
+
+ rec = trustdb_rec_byfpr( inp, TDB_RECORD_TRUST, fpr, 20 );
+ if( rec ) {
+ *r_val = rec->u.trust.ownertrust & TRUST_MASK;
+ if( *r_val & CDK_TFLAG_REVOKED )
+ flags |= CDK_TFLAG_REVOKED;
+ if( *r_val & CDK_TFLAG_SUB_REVOKED )
+ flags |= CDK_TFLAG_SUB_REVOKED;
+ if( *r_val & CDK_TFLAG_DISABLED )
+ flags |= CDK_TFLAG_DISABLED;
+ *r_flags = flags;
+ rc = 0;
+ }
+ trustdb_rec_release( rec );
+ return rc;
+}
+
+
+int
+cdk_trustdb_get_validity( cdk_stream_t inp, cdk_pkt_userid_t id, int *r_val )
+{
+ cdk_md_hd_t rmd;
+ tdb_record_t rec;
+ byte * fpr;
+ int rc;
+
+ if( !inp || !r_val || !id )
+ return CDK_Inv_Value;
+
+ rc = trustdb_check( inp, MIN_TRUSTDB_VER );
+ if( rc )
+ return rc;
+
+ *r_val = CDK_TRUST_UNKNOWN;
+ rmd = cdk_md_open( CDK_MD_RMD160, 0 );
+ if( !rmd )
+ return CDK_Gcry_Error;
+
+ cdk_md_write( rmd, id->name, id->len );
+ cdk_md_final( rmd );
+ fpr = cdk_md_read( rmd, CDK_MD_RMD160 );
+
+ cdk_stream_seek( inp, 0 );
+ rec = trustdb_rec_byfpr( inp, TDB_RECORD_VALID, fpr, 20 );
+ if( rec ) {
+ *r_val = rec->u.valid.valid;
+ rc = 0;
+ }
+
+ trustdb_rec_release( rec );
+ cdk_md_close( rmd );
+ return rc;
+}
diff --git a/libextra/opencdk/types.h b/libextra/opencdk/types.h
new file mode 100644
index 0000000000..ddccc3de26
--- /dev/null
+++ b/libextra/opencdk/types.h
@@ -0,0 +1,53 @@
+/* types.h - Some type definitions
+ * Copyright (C) 2002 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CDK_TYPES_H
+#define CDK_TYPES_H
+
+#include <gcrypt.h> /*FIXME!*/
+
+#ifndef HAVE_BYTE_TYPEDEF
+# undef byte
+ typedef unsigned char byte;
+# define HAVE_BYTE_TYPEDEF
+#endif
+
+#ifndef HAVE_U16_TYPEDEF
+# undef u16
+ typedef unsigned short u16;
+# define HAVE_U16_TYPEDEF
+#endif
+
+#ifndef HAVE_U32_TYPEDEF
+# undef u32
+ typedef unsigned int u32;
+# define HAVE_U32_TYPEDEF
+#endif
+
+#ifndef DIM
+# define DIM(v) (sizeof (v)/sizeof ((v)[0]))
+# define DIMof(type, member) DIM(((type *)0)->member)
+#endif
+
+
+struct cdk_verify_result_s;
+typedef struct cdk_verify_result_s *_cdk_verify_result_t;
+
+#endif /* CDK_TYPES_H */
diff --git a/libextra/opencdk/verify.c b/libextra/opencdk/verify.c
new file mode 100644
index 0000000000..23f9f08198
--- /dev/null
+++ b/libextra/opencdk/verify.c
@@ -0,0 +1,310 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * verify.c - Verify signatures
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <string.h>
+#include <assert.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "filters.h"
+#include "packet.h"
+
+
+struct {
+ const char *name;
+ int algo;
+} digest_table[] = {
+ {"MD5", CDK_MD_MD5},
+ {"SHA1", CDK_MD_SHA1},
+ {"RIPEMD160", CDK_MD_RMD160},
+ {"MD2", CDK_MD_MD2},
+ {"SHA256", CDK_MD_SHA256},
+ {NULL, 0}
+};
+
+
+static int file_verify_clearsign( cdk_ctx_t, const char *, const char * );
+
+
+cdk_error_t
+cdk_stream_verify( cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out )
+{
+ if( cdk_armor_filter_use( inp ) )
+ cdk_stream_set_armor_flag( inp, 0 );
+ return _cdk_proc_packets( hd, inp, NULL, NULL, NULL );
+}
+
+
+/**
+ * cdk_file_verify:
+ * @hd: the session handle
+ * @file: the input file
+ * @output: the output file
+ *
+ * Verify a signature.
+ **/
+cdk_error_t
+cdk_file_verify( cdk_ctx_t hd, const char * file, const char * output )
+{
+ cdk_stream_t inp;
+ char buf[2048];
+ int rc, n;
+
+ if( !hd || !file )
+ return CDK_Inv_Value;
+ if( output && !hd->opt.overwrite && _cdk_check_file( output ) )
+ return CDK_Inv_Mode;
+
+ rc = cdk_stream_open ( file, &inp );
+ if( rc )
+ return rc;
+ if( cdk_armor_filter_use( inp ) ) {
+ n = cdk_stream_peek( inp, buf, sizeof buf-1 );
+ if( !n )
+ return CDK_EOF;
+ buf[n] = '\0';
+ if( strstr( buf, "BEGIN PGP SIGNED MESSAGE" ) ) {
+ cdk_stream_close( inp );
+ return file_verify_clearsign( hd, file, output );
+ }
+ cdk_stream_set_armor_flag( inp, 0 );
+ }
+ rc = _cdk_proc_packets( hd, inp, NULL, NULL, NULL );
+ cdk_stream_close( inp );
+ return rc;
+}
+
+
+void
+_cdk_result_verify_free( _cdk_verify_result_t res )
+{
+ if( res ) {
+ cdk_free( res->sig_data );
+ cdk_free( res->notation );
+ cdk_free( res );
+ }
+}
+
+
+_cdk_verify_result_t
+_cdk_result_verify_new( void )
+{
+ _cdk_verify_result_t res;
+
+ res = cdk_calloc( 1, sizeof *res );
+ if( !res )
+ return NULL;
+ return res;
+}
+
+
+/**
+ * cdk_sig_get_ulong_attr:
+ * @hd: session handle
+ * @idx: index of the signature
+ * @what: attribute id
+ *
+ * Extract the requested attribute of the signature. The returned value
+ * is always an integer (max. 32-bit).
+ **/
+unsigned long
+cdk_sig_get_ulong_attr( cdk_ctx_t hd, int idx, int what )
+{
+ _cdk_verify_result_t res;
+ u32 val = 0;
+
+ if( !hd || !hd->result.verify )
+ return 0;
+
+ assert( idx == 0 );
+ res = hd->result.verify;
+ switch( what ) {
+ case CDK_ATTR_CREATED: val = res->created; break;
+ case CDK_ATTR_EXPIRE : val = res->expires; break;
+ case CDK_ATTR_KEYID : val = res->keyid[1]; break;
+ case CDK_ATTR_STATUS : val = res->sig_status; break;
+ case CDK_ATTR_ALGO_PK: val = res->pubkey_algo; break;
+ case CDK_ATTR_ALGO_MD: val = res->digest_algo; break;
+ case CDK_ATTR_VERSION: val = res->sig_ver; break;
+ case CDK_ATTR_LEN : val = res->sig_len; break;
+ case CDK_ATTR_FLAGS : val = res->sig_flags; break;
+ default : val = 0; break;
+ }
+
+ return val;
+}
+
+
+/**
+ * cdk_sig_get_data_attr:
+ * @hd: session handle
+ * @idx: index of the signature
+ * @what: attribute id.
+ *
+ * Extract the requested attribute of the signature. The returned value
+ * is always a constant object to the data.
+ **/
+const void *
+cdk_sig_get_data_attr( cdk_ctx_t hd, int idx, int what )
+{
+ _cdk_verify_result_t res;
+ const void * val;
+
+ if( !hd || !hd->result.verify )
+ return NULL;
+
+ assert( idx == 0 );
+ res = hd->result.verify;
+ switch( what ) {
+ case CDK_ATTR_KEYID : val = res->keyid; break;
+ case CDK_ATTR_NOTATION: val = res->notation; break;
+ case CDK_ATTR_MPI : val = res->sig_data; break;
+ default : val = NULL;
+ }
+
+ return val;
+}
+
+
+static int
+file_verify_clearsign( cdk_ctx_t hd, const char * file, const char * output )
+{
+ cdk_stream_t inp = NULL, out = NULL, tmp = NULL;
+ cdk_md_hd_t md = NULL;
+ char buf[512], chk[512];
+ const char * s;
+ int rc = 0;
+ int i, is_signed = 0, nbytes;
+ int digest_algo = 0;
+
+ if( output ) {
+ rc = cdk_stream_create( output, &out );
+ if( rc )
+ return rc;
+ }
+
+ rc = cdk_stream_open( file, &inp );
+ if( rc )
+ return rc;
+
+ s = "-----BEGIN PGP SIGNED MESSAGE-----";
+ while( !cdk_stream_eof( inp ) ) {
+ nbytes = _cdk_stream_gets( inp, buf, sizeof buf-1 );
+ if( !nbytes )
+ break;
+ if( !strncmp( buf, s, strlen( s ) ) ) {
+ is_signed = 1;
+ break;
+ }
+ }
+ if( cdk_stream_eof( inp ) && !is_signed ) {
+ rc = CDK_Armor_Error;
+ goto leave;
+ }
+
+ while( !cdk_stream_eof( inp ) ) {
+ nbytes = _cdk_stream_gets( inp, buf, sizeof buf-1 );
+ if( !nbytes )
+ break;
+ if( nbytes == 1 ) /* empty line */
+ break;
+ else if( !strncmp( buf, "Hash: ", 6 ) ) {
+ for( i = 0; (s = digest_table[i].name); i++ ) {
+ if( !strcmp( buf + 6, s ) ) {
+ digest_algo = digest_table[i].algo;
+ break;
+ }
+ }
+ }
+ }
+
+ if( digest_algo && cdk_md_test_algo( digest_algo ) ) {
+ rc = CDK_Inv_Algo;
+ goto leave;
+ }
+ if( !digest_algo )
+ digest_algo = CDK_MD_MD5;
+ md = cdk_md_open( digest_algo, 0 );
+ if( !md ) {
+ rc = CDK_Inv_Algo;
+ goto leave;
+ }
+
+ s = "-----BEGIN PGP SIGNATURE-----";
+ while( !cdk_stream_eof( inp ) ) {
+ nbytes = _cdk_stream_gets( inp, buf, sizeof buf-1 );
+ if( !nbytes )
+ break;
+ if( !strncmp( buf, s, strlen( s ) ) )
+ break;
+ else {
+ cdk_stream_peek( inp, chk, sizeof chk-1 );
+ i = strncmp( chk, s, strlen( s ) );
+ if( strlen( buf ) == 0 && i == 0 )
+ continue; /* skip last '\n' */
+ _cdk_trim_string( buf, i == 0? 0 : 1 );
+ cdk_md_write( md, buf, strlen( buf ) );
+ }
+ if( !strncmp( buf, "- ", 2 ) )
+ memmove( buf, buf + 2, nbytes - 2 );
+ if( out ) {
+ buf[strlen( buf ) - 1] = 0;
+ buf[strlen( buf ) - 1] = '\n';
+ cdk_stream_write( out, buf, strlen( buf ) );
+ }
+ }
+
+ tmp = cdk_stream_tmp( );
+ if( !tmp ) {
+ rc = CDK_Out_Of_Core;
+ goto leave;
+ }
+
+ /* xxx revamp this part of the function */
+ s = "-----BEGIN PGP SIGNATURE-----\n";
+ _cdk_stream_puts( tmp, s );
+ while( !cdk_stream_eof( inp ) ) {
+ nbytes = _cdk_stream_gets( inp, buf, sizeof buf-1 );
+ if( !nbytes )
+ break;
+ if( nbytes < (sizeof buf -3) ) {
+ buf[nbytes-1] = '\n';
+ buf[nbytes] = '\0';
+ }
+ cdk_stream_write( tmp, buf, nbytes );
+ }
+ cdk_stream_tmp_set_mode( tmp, STREAMCTL_READ );
+ cdk_stream_seek( tmp, 0 );
+ cdk_stream_set_armor_flag( tmp, 0 );
+ cdk_stream_read( tmp, NULL, 0 );
+
+ rc = _cdk_proc_packets( hd, tmp, NULL, NULL, md );
+
+ leave:
+ cdk_stream_close( out );
+ cdk_stream_close( tmp );
+ cdk_stream_close( inp );
+ return rc;
+}
diff --git a/libextra/opencdk/write-packet.c b/libextra/opencdk/write-packet.c
new file mode 100644
index 0000000000..1ef2ba3ac3
--- /dev/null
+++ b/libextra/opencdk/write-packet.c
@@ -0,0 +1,814 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * write-packet.c - Write OpenPGP packets
+ * Copyright (C) 2001, 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK 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.
+ *
+ * OpenCDK 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 OpenCDK; 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 <string.h>
+#include <stdio.h>
+
+#include "opencdk.h"
+#include "main.h"
+
+
+static int
+stream_write( cdk_stream_t s, const void * buf, size_t count )
+{
+ int nwritten = cdk_stream_write( s, buf, count );
+ if( nwritten == EOF )
+ return CDK_File_Error;
+ return 0;
+}
+
+
+static int
+stream_read( cdk_stream_t s, void * buf, size_t count, size_t * r_nread )
+{
+ int nread;
+
+ if( !r_nread )
+ return CDK_Inv_Value;
+ nread = cdk_stream_read( s, buf, count );
+ if( nread == EOF )
+ return CDK_File_Error;
+ *r_nread = nread;
+ return 0;
+}
+
+
+static int
+stream_putc( cdk_stream_t s, int c )
+{
+ int nwritten = cdk_stream_putc( s, c );
+ if( nwritten == EOF )
+ return CDK_File_Error;
+ return 0;
+}
+
+
+static int
+write_32( cdk_stream_t out, u32 u )
+{
+ byte buf[4];
+ buf[0] = u >> 24;
+ buf[1] = u >> 16;
+ buf[2] = u >> 8;
+ buf[3] = u;
+ return stream_write( out, buf, 4 );
+}
+
+
+static int
+write_16( cdk_stream_t out, u16 u )
+{
+ byte buf[2];
+ buf[0] = u >> 8;
+ buf[1] = u;
+ return stream_write( out, buf, 2 );
+}
+
+
+static size_t
+calc_mpisize( cdk_mpi_t mpi[4], int ncount )
+{
+ size_t nbytes, size = 0;
+ int i;
+
+ for( i = 0; i < ncount; i++ ) {
+ nbytes = (mpi[i]->bits + 7) / 8 + 2;
+ size += nbytes;
+ }
+ return size;
+}
+
+
+static int
+write_mpi( cdk_stream_t out, cdk_mpi_t m )
+{
+ if( !out || !m )
+ return CDK_Inv_Value;
+ if( m->bits > MAX_MPI_BITS || !m->bits )
+ return CDK_MPI_Error;
+ if( !m->bytes )
+ m->bytes = (m->bits + 7) / 8;
+ return stream_write( out, m->data, m->bytes + 2 );
+}
+
+
+static int
+write_mpibuf( cdk_stream_t out, cdk_mpi_t mpi[4], int count )
+{
+ int i, rc = 0;
+
+ for( i = 0; i < count; i++ ) {
+ rc = write_mpi( out, mpi[i] );
+ if( rc )
+ break;
+ }
+ return rc;
+}
+
+
+static int
+pkt_encode_len( cdk_stream_t out, size_t pktlen )
+{
+ int rc = 0;
+
+ if( !out )
+ return CDK_Inv_Value;
+ if( !pktlen )
+ return 0;
+ else if( pktlen < 192 )
+ rc = stream_putc( out, pktlen );
+ else if( pktlen < 8384 ) {
+ pktlen -= 192;
+ rc = stream_putc( out, (pktlen / 256) + 192 );
+ if( !rc )
+ rc = stream_putc( out, (pktlen % 256) );
+ }
+ else {
+ rc = stream_putc( out, 255 );
+ if( !rc )
+ rc = write_32( out, pktlen );
+ }
+ return rc;
+}
+
+
+static int
+write_head_new( cdk_stream_t out, size_t size, int type )
+{
+ int rc = 0;
+
+ if( !out )
+ return CDK_Inv_Value;
+ if( type < 0 || type > 63 )
+ return CDK_Inv_Packet;
+ rc = stream_putc( out, (0xC0 | type) );
+ if( !rc )
+ rc = pkt_encode_len( out, size );
+ return rc;
+}
+
+
+static int
+write_head_old( cdk_stream_t out, size_t size, int type )
+{
+ int ctb;
+ int rc;
+
+ if( !out )
+ return CDK_Inv_Value;
+ if( type < 0 || type > 16 )
+ return CDK_Inv_Packet;
+ ctb = 0x80 | (type << 2);
+ if( !size )
+ ctb |= 3;
+ else if( size < 256 )
+ ;
+ else if( size < 65536 )
+ ctb |= 1;
+ else
+ ctb |= 2;
+ rc = stream_putc( out, ctb );
+ if( !size )
+ return rc;
+ if( !rc ) {
+ if( size < 256 )
+ rc = stream_putc( out, size );
+ else if( size < 65536 )
+ rc = write_16( out, size );
+ else
+ rc = write_32( out, size );
+ }
+ return rc;
+}
+
+
+/* write special PGP2 packet header. PGP2 (wrongly) uses two byte header
+ length for signatures and keys even if the size is < 256. */
+static int
+pkt_write_head2( cdk_stream_t out, size_t size, int type )
+{
+ int rc = cdk_stream_putc( out, 0x80 | (type << 2) | 1 );
+ if( !rc )
+ rc = cdk_stream_putc( out, size >> 8 );
+ if( !rc )
+ rc = cdk_stream_putc( out, size & 0xff );
+ return rc;
+}
+
+
+static int
+pkt_write_head( cdk_stream_t out, int old_ctb, size_t size, int type )
+{
+ return old_ctb?
+ write_head_old( out, size, type ) :
+ write_head_new( out, size, type );
+}
+
+
+static int
+write_encrypted( cdk_stream_t out, cdk_pkt_encrypted_t enc, int old_ctb )
+{
+ size_t nbytes;
+ int rc = 0;
+
+ if( !out || !enc )
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug ("** write encrypted packet %lu bytes\n", enc->len);
+
+ nbytes = enc->len ? (enc->len + enc->extralen) : 0;
+ rc = pkt_write_head( out, old_ctb, nbytes, CDK_PKT_ENCRYPTED );
+ /* rest of the packet is ciphertext */
+ return rc;
+}
+
+
+static int
+write_encrypted_mdc( cdk_stream_t out, cdk_pkt_encrypted_t enc )
+{
+ size_t nbytes;
+ int rc = 0;
+
+ if( !out || !enc )
+ return CDK_Inv_Value;
+ if( !enc->mdc_method )
+ return CDK_Inv_Packet;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug ("** write encrypted mdc packet %lu bytes\n", enc->len);
+
+ nbytes = enc->len ? (enc->len + enc->extralen + 1) : 0;
+ rc = pkt_write_head( out, 0, nbytes, CDK_PKT_ENCRYPTED_MDC );
+ if( !rc )
+ rc = stream_putc( out, 1 ); /* version */
+ /* rest of the packet is ciphertext */
+ return rc;
+}
+
+
+static int
+write_symkey_enc( cdk_stream_t out, cdk_pkt_symkey_enc_t ske )
+{
+ cdk_s2k_t s2k;
+ size_t size = 0, s2k_size = 0;
+ int rc = 0;
+
+ if( !out || !ske )
+ return CDK_Inv_Value;
+ if( ske->version != 4 )
+ return CDK_Inv_Packet;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug ("** write symmetric key encrypted packet\n");
+
+ s2k = ske->s2k;
+ if( s2k->mode == 1 || s2k->mode == 3 )
+ s2k_size = 8;
+ if( s2k->mode == 3 )
+ s2k_size++;
+ size = 4 + s2k_size + ske->seskeylen;
+ rc = pkt_write_head( out, 0, size, CDK_PKT_SYMKEY_ENC );
+ if( !rc )
+ rc = stream_putc( out, ske->version );
+ if( !rc )
+ rc = stream_putc( out, ske->cipher_algo );
+ if( !rc )
+ rc = stream_putc( out, s2k->mode );
+ if( !rc )
+ rc = stream_putc( out, s2k->hash_algo );
+ if( s2k->mode == 1 || s2k->mode == 3 ) {
+ rc = stream_write( out, s2k->salt, 8 );
+ if( !rc && s2k->mode == 3 )
+ rc = stream_putc( out, s2k->count );
+ }
+ return rc;
+}
+
+
+static int
+write_pubkey_enc( cdk_stream_t out, cdk_pkt_pubkey_enc_t pke, int old_ctb )
+{
+ size_t size;
+ int rc = 0, nenc = 0;
+
+ if( !out || !pke )
+ return CDK_Inv_Value;
+ if( pke->version < 2 || pke->version > 3 )
+ return CDK_Inv_Packet;
+ if( !KEY_CAN_ENCRYPT( pke->pubkey_algo ) )
+ return CDK_Inv_Algo;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** write public key encrypted packet\n");
+
+ nenc = cdk_pk_get_nenc( pke->pubkey_algo );
+ size = 10 + calc_mpisize( pke->mpi, nenc );
+ rc = pkt_write_head( out, old_ctb, size, CDK_PKT_PUBKEY_ENC );
+ if( !rc )
+ rc = stream_putc( out, pke->version );
+ if( !rc )
+ rc = write_32( out, pke->keyid[0] );
+ if( !rc )
+ rc = write_32( out, pke->keyid[1] );
+ if( !rc )
+ rc = stream_putc( out, pke->pubkey_algo );
+ if( !rc )
+ rc = write_mpibuf( out, pke->mpi, nenc );
+ return rc;
+}
+
+
+static int
+write_mdc( cdk_stream_t out, cdk_pkt_mdc_t mdc )
+{
+ int rc = 0;
+
+ if( !out || !mdc )
+ return CDK_Inv_Value;
+
+ if( DEBUG_PKT )
+ _cdk_log_debug ("** write_mdc\n");
+
+ /* This packet requires a fixed header encoding */
+ rc = stream_putc (out, 0xD3); /* packet ID and 1 byte length */
+ if( !rc )
+ rc = stream_putc( out, 0x14 );
+ if( !rc )
+ rc = stream_write( out, mdc->hash, sizeof mdc->hash );
+ return rc;
+}
+
+
+static size_t
+calc_subpktsize( cdk_subpkt_t s )
+{
+ size_t nbytes;
+
+ if( !s )
+ return 0;
+ _cdk_subpkt_get_array( s, 1, &nbytes );
+ return nbytes;
+}
+
+
+static int
+write_signature( cdk_stream_t out, cdk_pkt_signature_t sig, int old_ctb )
+{
+ byte * buf;
+ size_t nbytes, size;
+ int nsig = 0, rc = 0;
+
+ if( !out || !sig )
+ return CDK_Inv_Value;
+
+ if( !KEY_CAN_SIGN( sig->pubkey_algo ) )
+ return CDK_Inv_Algo;
+ if( sig->version < 3 || sig->version > 4 )
+ return CDK_Inv_Packet;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** write signature packet\n");
+
+ nsig = cdk_pk_get_nsig( sig->pubkey_algo );
+ if( !nsig )
+ return CDK_Inv_Algo;
+ if( sig->version < 4 ) {
+ size = 19 + calc_mpisize( sig->mpi, nsig );
+ if( is_RSA( sig->pubkey_algo ) )
+ rc = pkt_write_head2( out, size, CDK_PKT_SIGNATURE );
+ else
+ rc = pkt_write_head( out, old_ctb, size, CDK_PKT_SIGNATURE );
+ if( !rc )
+ rc = stream_putc( out, sig->version );
+ if( !rc )
+ rc = stream_putc( out, 5 );
+ if( !rc )
+ rc = stream_putc( out, sig->sig_class );
+ if( !rc )
+ rc = write_32( out, sig->timestamp );
+ if( !rc )
+ rc = write_32( out, sig->keyid[0] );
+ if( !rc )
+ rc = write_32( out, sig->keyid[1] );
+ if( !rc )
+ rc = stream_putc( out, sig->pubkey_algo );
+ if( !rc )
+ rc = stream_putc( out, sig->digest_algo );
+ if( !rc )
+ rc = stream_putc( out, sig->digest_start[0] );
+ if( !rc )
+ rc = stream_putc( out, sig->digest_start[1] );
+ }
+ else {
+ size = 10 + calc_subpktsize( sig->hashed )
+ + calc_subpktsize( sig->unhashed )
+ + calc_mpisize( sig->mpi, nsig );
+ rc = pkt_write_head( out, 0, size, CDK_PKT_SIGNATURE );
+ if( !rc )
+ rc = stream_putc( out, 4 );
+ if( !rc )
+ rc = stream_putc( out, sig->sig_class );
+ if( !rc )
+ rc = stream_putc( out, sig->pubkey_algo );
+ if( !rc )
+ rc = stream_putc( out, sig->digest_algo );
+ if( !rc )
+ rc = write_16( out, sig->hashed_size );
+ if( !rc ) {
+ buf = _cdk_subpkt_get_array( sig->hashed, 0, &nbytes );
+ if( !buf )
+ return CDK_Out_Of_Core;
+ rc = stream_write( out, buf, nbytes );
+ cdk_free( buf );
+ }
+ if( !rc )
+ rc = write_16( out, sig->unhashed_size );
+ if( !rc ) {
+ buf = _cdk_subpkt_get_array( sig->unhashed, 0, &nbytes );
+ if( !buf )
+ return CDK_Out_Of_Core;
+ rc = stream_write( out, buf, nbytes );
+ cdk_free( buf );
+ }
+ if( !rc )
+ rc = stream_putc( out, sig->digest_start[0] );
+ if( !rc )
+ rc = stream_putc( out, sig->digest_start[1] );
+ }
+ if( !rc )
+ rc = write_mpibuf( out, sig->mpi, nsig );
+ return rc;
+}
+
+
+static int
+write_public_key( cdk_stream_t out, cdk_pkt_pubkey_t pk,
+ int is_subkey, int old_ctb )
+{
+ int rc = 0;
+ int pkttype, ndays = 0;
+ size_t npkey = 0, size = 6;
+
+ if( !out || !pk )
+ return CDK_Inv_Value;
+ if( pk->version < 2 || pk->version > 4 )
+ return CDK_Inv_Packet;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** write public key packet\n");
+
+ pkttype = is_subkey? CDK_PKT_PUBLIC_SUBKEY : CDK_PKT_PUBLIC_KEY;
+ npkey = cdk_pk_get_npkey( pk->pubkey_algo );
+ if( pk->version < 4 )
+ size += 2; /* expire date */
+ if( is_subkey )
+ old_ctb = 0;
+ size += calc_mpisize( pk->mpi, npkey );
+ if( old_ctb )
+ rc = pkt_write_head2( out, size, pkttype );
+ else
+ rc = pkt_write_head( out, old_ctb, size, pkttype );
+ if( !rc )
+ rc = stream_putc( out, pk->version );
+ if( !rc )
+ rc = write_32( out, pk->timestamp );
+ if( !rc && pk->version < 4 ) {
+ if( pk->expiredate )
+ ndays = (u16) ((pk->expiredate - pk->timestamp) / 86400L);
+ rc = write_16( out, ndays );
+ }
+ if( !rc )
+ rc = stream_putc( out, pk->pubkey_algo );
+ if( !rc )
+ rc = write_mpibuf( out, pk->mpi, npkey );
+ return rc;
+}
+
+
+static int
+calc_s2ksize( cdk_pkt_seckey_t sk )
+{
+ size_t nbytes = 0;
+ cdk_s2k_t s2k;
+
+ if( !sk->is_protected )
+ return 0;
+ s2k = sk->protect.s2k;
+ switch( s2k->mode ) {
+ case 0: nbytes = 2; break;
+ case 1: nbytes = 10; break;
+ case 3: nbytes = 11; break;
+ }
+ nbytes += sk->protect.ivlen;
+ nbytes++; /* single cipher byte */
+ return nbytes;
+}
+
+
+static int
+write_secret_key( cdk_stream_t out, cdk_pkt_seckey_t sk,
+ int is_subkey, int old_ctb )
+{
+ cdk_pkt_pubkey_t pk = NULL;
+ size_t size = 6, npkey, nskey;
+ int pkttype, s2k_mode;
+ int rc = 0;
+
+ if( !out || !sk || !sk->pk )
+ return CDK_Inv_Value;
+
+ pk = sk->pk;
+ if( pk->version < 2 || pk->version > 4 )
+ return CDK_Inv_Packet;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** write secret key packet\n");
+
+ npkey = cdk_pk_get_npkey( pk->pubkey_algo );
+ nskey = cdk_pk_get_nskey( pk->pubkey_algo );
+ if( !npkey || !nskey )
+ return CDK_Inv_Algo;
+ if( pk->version < 4 )
+ size += 2;
+ /* if the key is unprotected, the 3 extra bytes are:
+ 1 octet - cipher algorithm byte (0x00)
+ 2 octets - simple checksum */
+ size = !sk->is_protected? size + 3 : size + 1 + calc_s2ksize( sk );
+ size += calc_mpisize( pk->mpi, npkey );
+ if( sk->version == 3 || !sk->is_protected ) {
+ if( sk->version == 3 ) {
+ size += 2; /* force simple checksum */
+ sk->protect.sha1chk = 0;
+ }
+ else
+ size += sk->protect.sha1chk? 20 : 2;
+ size += calc_mpisize( sk->mpi, nskey );
+ }
+ else /* we do not know anything about the encrypted mpi's so we
+ treat the data as opaque. */
+ size += sk->enclen;
+
+ pkttype = is_subkey? CDK_PKT_SECRET_SUBKEY : CDK_PKT_SECRET_KEY;
+ rc = pkt_write_head( out, old_ctb, size, pkttype );
+ if( !rc )
+ rc = stream_putc( out, pk->version );
+ if( !rc )
+ rc = write_32( out, pk->timestamp );
+ if( !rc && pk->version < 4 ) {
+ u16 ndays = 0;
+ if( pk->expiredate )
+ ndays = (u16) ((pk->expiredate - pk->timestamp) / 86400L);
+ rc = write_16( out, ndays );
+ }
+ if( !rc )
+ rc = stream_putc( out, pk->pubkey_algo );
+ if( !rc )
+ rc = write_mpibuf( out, pk->mpi, npkey );
+ if( sk->is_protected == 0 )
+ rc = stream_putc( out, 0x00 );
+ else {
+ if( is_RSA( pk->pubkey_algo ) && pk->version < 4 )
+ stream_putc( out, sk->protect.algo );
+ else if( sk->protect.s2k ) {
+ s2k_mode = sk->protect.s2k->mode;
+ rc = stream_putc( out, sk->protect.sha1chk? 0xFE : 0xFF );
+ if( !rc )
+ rc = stream_putc( out, sk->protect.algo );
+ if( !rc )
+ rc = stream_putc( out, sk->protect.s2k->mode );
+ if( !rc )
+ rc = stream_putc( out, sk->protect.s2k->hash_algo );
+ if( !rc && (s2k_mode == 1 || s2k_mode == 3) ) {
+ rc = stream_write( out, sk->protect.s2k->salt, 8 );
+ if( !rc && s2k_mode == 3 )
+ rc = stream_putc( out, sk->protect.s2k->count );
+ }
+ }
+ else
+ return CDK_Inv_Value;
+ rc = stream_write( out, sk->protect.iv, sk->protect.ivlen );
+ }
+ if( !rc && sk->is_protected && pk->version == 4 ) {
+ if( sk->encdata && sk->enclen )
+ rc = stream_write( out, sk->encdata, sk->enclen );
+ }
+ else {
+ if( !rc )
+ rc = write_mpibuf( out, sk->mpi, nskey );
+ if( !rc ) {
+ if( !sk->csum )
+ sk->csum = _cdk_sk_get_csum( sk );
+ rc = write_16( out, sk->csum );
+ }
+ }
+ return rc;
+}
+
+
+static int
+write_compressed( cdk_stream_t out, cdk_pkt_compressed_t cd )
+{
+ int rc;
+
+ if( !out || !cd )
+ return CDK_Inv_Value;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** write compressed packet\n");
+
+ rc = pkt_write_head( out, 1, 0, CDK_PKT_COMPRESSED );
+ if( !rc )
+ rc = stream_putc( out, cd->algorithm );
+ return rc;
+}
+
+
+static int
+write_literal( cdk_stream_t out, cdk_pkt_literal_t pt, int old_ctb )
+{
+ byte buf[8192];
+ size_t size;
+ int rc;
+
+ if( !out || !pt )
+ return CDK_Inv_Value;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** write literal packet\n");
+
+ size = 6 + pt->namelen + pt->len;
+ rc = pkt_write_head( out, old_ctb, size, CDK_PKT_LITERAL );
+ if( !rc )
+ rc = stream_putc( out, pt->mode );
+ if( !rc )
+ rc = stream_putc( out, pt->namelen );
+ if( !rc && pt->namelen )
+ rc = stream_write( out, pt->name, pt->namelen );
+ if( !rc )
+ rc = write_32( out, pt->timestamp );
+ if( !rc ) {
+ while( pt->len && !cdk_stream_eof( pt->buf ) && !rc ) {
+ rc = stream_read( pt->buf, buf, sizeof buf-1, &size );
+ if( !rc )
+ rc = stream_write( out, buf, size );
+ }
+ wipemem( buf, sizeof buf );
+ }
+ return rc;
+}
+
+
+static int
+write_onepass_sig( cdk_stream_t out, cdk_pkt_onepass_sig_t sig )
+{
+ size_t size = 0;
+ int rc = 0;
+
+ if( !out || !sig )
+ return CDK_Inv_Value;
+ if( sig->version != 3 )
+ return CDK_Inv_Packet;
+
+ if (DEBUG_PKT)
+ _cdk_log_debug ("** write one pass signature packet\n");
+
+ size = 13;
+ rc = pkt_write_head( out, 0, size, CDK_PKT_ONEPASS_SIG );
+ if( !rc )
+ rc = stream_putc( out, sig->version );
+ if( !rc )
+ rc = stream_putc( out, sig->sig_class );
+ if( !rc )
+ rc = stream_putc( out, sig->digest_algo );
+ if (!rc)
+ rc = stream_putc( out, sig->pubkey_algo );
+ if( !rc )
+ rc = write_32( out, sig->keyid[0] );
+ if( !rc )
+ rc = write_32( out, sig->keyid[1] );
+ if( !rc )
+ rc = stream_putc( out, sig->last );
+ return rc;
+}
+
+
+static int
+write_user_id( cdk_stream_t out, cdk_pkt_userid_t id, int old_ctb )
+{
+ int rc = 0;
+
+ if( !out || !id || !id->name )
+ return CDK_Inv_Value;
+
+ if( id->attrib_img )
+ ;/* todo */
+ else {
+ rc = pkt_write_head( out, old_ctb, id->len, CDK_PKT_USER_ID );
+ if( !rc )
+ rc = stream_write( out, id->name, id->len );
+ }
+ return rc;
+}
+
+
+/**
+ * cdk_pkt_write:
+ * @out: the output stream handle
+ * @pkt: the packet itself
+ *
+ * Write the contents of @pkt into the @out stream.
+ **/
+cdk_error_t
+cdk_pkt_write( cdk_stream_t out, cdk_packet_t pkt )
+{
+ int rc;
+
+ if( !out || !pkt )
+ return CDK_Inv_Value;
+
+ switch( pkt->pkttype ) {
+ case CDK_PKT_LITERAL:
+ rc = write_literal( out, pkt->pkt.literal, pkt->old_ctb );
+ break;
+ case CDK_PKT_ONEPASS_SIG:
+ rc = write_onepass_sig( out, pkt->pkt.onepass_sig );
+ break;
+ case CDK_PKT_MDC:
+ rc = write_mdc( out, pkt->pkt.mdc );
+ break;
+ case CDK_PKT_SYMKEY_ENC:
+ rc = write_symkey_enc( out, pkt->pkt.symkey_enc );
+ break;
+ case CDK_PKT_ENCRYPTED:
+ rc = write_encrypted( out, pkt->pkt.encrypted, pkt->old_ctb );
+ break;
+ case CDK_PKT_ENCRYPTED_MDC:
+ rc = write_encrypted_mdc( out, pkt->pkt.encrypted );
+ break;
+ case CDK_PKT_PUBKEY_ENC:
+ rc = write_pubkey_enc( out, pkt->pkt.pubkey_enc, pkt->old_ctb );
+ break;
+ case CDK_PKT_SIGNATURE:
+ rc = write_signature( out, pkt->pkt.signature, pkt->old_ctb );
+ break;
+ case CDK_PKT_PUBLIC_KEY:
+ rc = write_public_key( out, pkt->pkt.public_key, 0, pkt->old_ctb );
+ break;
+ case CDK_PKT_PUBLIC_SUBKEY:
+ rc = write_public_key( out, pkt->pkt.public_key, 1, pkt->old_ctb );
+ break;
+ case CDK_PKT_COMPRESSED:
+ rc = write_compressed( out, pkt->pkt.compressed );
+ break;
+ case CDK_PKT_SECRET_KEY:
+ rc = write_secret_key( out, pkt->pkt.secret_key, 0, pkt->old_ctb );
+ break;
+ case CDK_PKT_SECRET_SUBKEY:
+ rc = write_secret_key( out, pkt->pkt.secret_key, 1, pkt->old_ctb );
+ break;
+ case CDK_PKT_USER_ID:
+ rc = write_user_id( out, pkt->pkt.user_id, pkt->old_ctb );
+ break;
+ default:
+ rc = CDK_Inv_Packet;
+ break;
+ }
+ return rc;
+}
+
+
+int
+_cdk_pkt_write_fp( FILE * out, cdk_packet_t pkt )
+{
+ cdk_stream_t so;
+ int rc;
+
+ so = _cdk_stream_fpopen( out, 1 );
+ rc = cdk_pkt_write( so, pkt );
+ cdk_stream_close( so );
+ return rc;
+}
+
+
diff --git a/libextra/openpgp/Makefile.am b/libextra/openpgp/Makefile.am
index aa7f7440c0..4931415cd6 100644
--- a/libextra/openpgp/Makefile.am
+++ b/libextra/openpgp/Makefile.am
@@ -1,11 +1,7 @@
-INCLUDES = -I../ -I../../includes/ -I../../lib -I../../lib/minitasn1 $(LIBOPENCDK_CFLAGS) $(LIBGCRYPT_CFLAGS)
-EXTRA_DIST = openpgp.h gnutls_openpgp.h
-
+INCLUDES = -I../ -I../../includes/ -I../../lib -I../../lib/minitasn1 $(LIBOPENCDK_CFLAGS) $(LIBGCRYPT_CFLAGS) -I../opencdk
+EXTRA_DIST = openpgp.h gnutls_openpgp.h
noinst_LTLIBRARIES = libpgp.la
-
-COBJECTS = openpgp.c xml.c verify.c extras.c compat.c privkey.c
-
-libpgp_la_SOURCES = $(COBJECTS)
+libpgp_la_SOURCES = openpgp.c xml.c verify.c extras.c compat.c privkey.c
pgp-api.tex: $(COBJECTS)
@echo "" > pgp-api.tex