diff options
Diffstat (limited to 'libextra')
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 |