diff --git a/TODO b/TODO index 064758907..6948e4737 100644 --- a/TODO +++ b/TODO @@ -6,8 +6,6 @@ function of iobuf. * check that all output is filtered when displayed. * add trust stuff - * make ttyio.c work (hide passwords etc..) - * use correct ASN values for DEK encoding * add checking of armor trailers * add real secure memory * look for a way to reuse RSA signatures diff --git a/cipher/primegen.c b/cipher/primegen.c index 8a72973e2..3f6c1f325 100644 --- a/cipher/primegen.c +++ b/cipher/primegen.c @@ -106,20 +106,24 @@ generate_elg_prime( unsigned pbits, unsigned qbits, MPI g ) /* allocate new primes */ for(i=0; i < m; i++ ) { mpi_free(pool[i]); - pool[i] = gen_prime( fbits, 0, 2 ); + pool[i] = NULL; } /* init m_out_of_n() */ perms = m_alloc_clear( m ); for(i=0; i < n; i++ ) { perms[i] = 1; + pool[i] = gen_prime( fbits, 0, 2 ); factors[i] = pool[i]; } } else { m_out_of_n( perms, n, m ); for(i=j=0; i < m && j < n ; i++ ) - if( perms[i] ) + if( perms[i] ) { + if( !pool[i] ) + pool[i] = gen_prime( fbits, 0, 2 ); factors[j++] = pool[i]; + } if( i == n ) { m_free(perms); perms = NULL; fputc('!', stderr); diff --git a/g10/Makefile.am b/g10/Makefile.am index ee22d2ed9..02639904a 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -14,6 +14,8 @@ g10_SOURCES = g10.c \ free-packet.c \ getkey.c \ keydb.h \ + pkclist.c \ + skclist.c \ ringedit.c \ kbnode.c \ keygen.c \ @@ -23,6 +25,8 @@ g10_SOURCES = g10.c \ mdfilter.c \ textfilter.c \ cipher.c \ + elg.c \ + rsa.c \ options.h \ openfile.c \ keyid.c \ diff --git a/g10/Makefile.in b/g10/Makefile.in index b77793783..40cf3aeed 100644 --- a/g10/Makefile.in +++ b/g10/Makefile.in @@ -52,6 +52,8 @@ g10_SOURCES = g10.c \ free-packet.c \ getkey.c \ keydb.h \ + pkclist.c \ + skclist.c \ ringedit.c \ kbnode.c \ keygen.c \ @@ -61,6 +63,8 @@ g10_SOURCES = g10.c \ mdfilter.c \ textfilter.c \ cipher.c \ + elg.c \ + rsa.c \ options.h \ openfile.c \ keyid.c \ @@ -94,10 +98,10 @@ LIBS = @LIBS@ COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) LINK = $(CC) $(LDFLAGS) -o $@ g10_OBJECTS = g10.o build-packet.o compress.o encode.o encr-data.o \ -free-packet.o getkey.o ringedit.o kbnode.o keygen.o mainproc.o armor.o \ -mdfilter.o textfilter.o cipher.o openfile.o keyid.o parse-packet.o \ -passphrase.o plaintext.o pubkey-enc.o seckey-cert.o seskey.o sign.o \ -comment.o sig-check.o +free-packet.o getkey.o pkclist.o skclist.o ringedit.o kbnode.o keygen.o \ +mainproc.o armor.o mdfilter.o textfilter.o cipher.o elg.o rsa.o \ +openfile.o keyid.o parse-packet.o passphrase.o plaintext.o pubkey-enc.o \ +seckey-cert.o seskey.o sign.o comment.o sig-check.o EXTRA_g10_SOURCES = g10_LDADD = $(LDADD) DIST_COMMON = Makefile.am Makefile.in @@ -114,17 +118,19 @@ DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(BUILT_SOURCES) $(HEADERS) \ TAR = tar DEP_FILES = $(srcdir)/.deps/armor.P $(srcdir)/.deps/build-packet.P \ $(srcdir)/.deps/cipher.P $(srcdir)/.deps/comment.P \ -$(srcdir)/.deps/compress.P $(srcdir)/.deps/encode.P \ -$(srcdir)/.deps/encr-data.P $(srcdir)/.deps/free-packet.P \ -$(srcdir)/.deps/g10.P $(srcdir)/.deps/getkey.P $(srcdir)/.deps/kbnode.P \ +$(srcdir)/.deps/compress.P $(srcdir)/.deps/elg.P \ +$(srcdir)/.deps/encode.P $(srcdir)/.deps/encr-data.P \ +$(srcdir)/.deps/free-packet.P $(srcdir)/.deps/g10.P \ +$(srcdir)/.deps/getkey.P $(srcdir)/.deps/kbnode.P \ $(srcdir)/.deps/keygen.P $(srcdir)/.deps/keyid.P \ $(srcdir)/.deps/mainproc.P $(srcdir)/.deps/mdfilter.P \ $(srcdir)/.deps/openfile.P $(srcdir)/.deps/parse-packet.P \ -$(srcdir)/.deps/passphrase.P $(srcdir)/.deps/plaintext.P \ -$(srcdir)/.deps/pubkey-enc.P $(srcdir)/.deps/ringedit.P \ +$(srcdir)/.deps/passphrase.P $(srcdir)/.deps/pkclist.P \ +$(srcdir)/.deps/plaintext.P $(srcdir)/.deps/pubkey-enc.P \ +$(srcdir)/.deps/ringedit.P $(srcdir)/.deps/rsa.P \ $(srcdir)/.deps/seckey-cert.P $(srcdir)/.deps/seskey.P \ $(srcdir)/.deps/sig-check.P $(srcdir)/.deps/sign.P \ -$(srcdir)/.deps/textfilter.P +$(srcdir)/.deps/skclist.P $(srcdir)/.deps/textfilter.P SOURCES = $(g10_SOURCES) OBJECTS = $(g10_OBJECTS) diff --git a/g10/armor.c b/g10/armor.c new file mode 100644 index 000000000..f6e6305d4 --- /dev/null +++ b/g10/armor.c @@ -0,0 +1,696 @@ +/* armor.c - Armor filter + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "errors.h" +#include "iobuf.h" +#include "memory.h" +#include "util.h" +#include "filter.h" +#include "packet.h" +#include "options.h" + + + + +#define CRCINIT 0xB704CE +#define CRCPOLY 0X864CFB +#define CRCUPDATE(a,c) do { \ + a = ((a) << 8) ^ crc_table[((a)&0xff >> 16) ^ (c)]; \ + a &= 0x00ffffff; \ + } while(0) +static u32 crc_table[256]; +static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; +static byte asctobin[256]; /* runtime initialized */ +static int is_initialized; + +typedef enum { + fhdrINIT=0, + fhdrLF, + fhdrWAIT, + fhdrWAITINFO, + fhdrWAITCLEARSIG, + fhdrDASH, + fhdrHEAD, + fhdrDASH2, + fhdrINFO +} fhdr_state_t; + +struct fhdr_struct { + fhdr_state_t state; + int count; + int hdr_line; /* number of the header line */ + char buf[256]; +}; + + +/* if we encounter this armor string with this index, go + * into a mode, which fakes packets and wait for the next armor */ +#define BEGIN_SIGNED_MSG_IDX 3 +static char *head_strings[] = { + "BEGIN PGP MESSAGE", + "BEGIN PGP PUBLIC KEY BLOCK", + "BEGIN PGP SIGNATURE", + "BEGIN PGP SIGNED MESSAGE", + NULL +}; +static char *tail_strings[] = { + "END PGP MESSAGE", + "END PGP PUBLIC KEY BLOCK", + "END PGP SIGNATURE", + NULL +}; + + +static void +initialize(void) +{ + int i, j; + u32 t; + byte *s; + + /* init the crc lookup table */ + crc_table[0] = 0; + for(i=j=0; j < 128; j++ ) { + t = crc_table[j]; + if( t & 0x00800000 ) { + t <<= 1; + crc_table[i++] = t ^ CRCPOLY; + crc_table[i++] = t; + } + else { + t <<= 1; + crc_table[i++] = t; + crc_table[i++] = t ^ CRCPOLY; + } + } + /* build the helptable for radix64 to bin conversion */ + for(i=0; i < 256; i++ ) + asctobin[i] = 255; /* used to detect invalid characters */ + for(s=bintoasc,i=0; *s; s++,i++ ) + asctobin[*s] = i; + + is_initialized=1; +} + +/**************** + * Check wether this is a armored file or not + * See also parse-packet.c for details on this code + * Returns: True if it seems to be armored + */ +static int +is_armored( byte *buf, size_t len ) +{ + int ctb, pkttype; + + if( len < 28 ) + return 0; /* not even enough space for the "---BEGIN"... */ + + ctb = *buf; + if( !(ctb & 0x80) ) + return 1; /* invalid packet: assume it is armored */ + pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf); + /*lenbytes = (ctb & 0x40) || ((ctb&3)==3)? 0 : (1<<(ctb & 3));*/ + switch( pkttype ) { + case PKT_PUBLIC_CERT: + case PKT_SECRET_CERT: + case PKT_PUBKEY_ENC: + case PKT_SIGNATURE: + case PKT_COMMENT: + case PKT_PLAINTEXT: + case PKT_COMPRESSED: + case PKT_ENCRYPTED: + return 0; /* seems to be a regular packet: not armored */ + } + + return 1; +} + + + + +static int +find_header( struct fhdr_struct *fhdr, int c ) +{ + int i; + const char *s; + + if( c == '\n' ) { + switch( fhdr->state ) { + case fhdrINFO: + if( !fhdr->count ) + return 1; /* blank line: data starts with the next line */ + fhdr->buf[fhdr->count] = 0; + log_debug("armor line: '%s'\n", fhdr->buf ); + /* fall through */ + case fhdrWAITINFO: + fhdr->state = fhdrINFO; + fhdr->count = 0; + break; + case fhdrWAITCLEARSIG: + if( fhdr->count++ == 1 ) /* skip the empty line */ + return 1; /* clear signed text follows */ + else if( fhdr->count > 1 ) + fhdr->state = fhdrWAIT; /* was not valid */ + break; + default: + fhdr->state = fhdrLF; + break; + } + } + else { + switch( fhdr->state ) { + case fhdrINIT: + case fhdrLF: + if( c == '-' ) { + fhdr->state = fhdrDASH; + fhdr->count = 1; + } + else + fhdr->state = fhdrWAIT; + break; + case fhdrWAIT: + case fhdrWAITINFO: + case fhdrWAITCLEARSIG: + break; + case fhdrDASH: + if( c == '-' ) { + if( ++fhdr->count == 5 ) { + fhdr->state = fhdrHEAD; + fhdr->count = 0; + } + } + else + fhdr->state = fhdrWAIT; + break; + case fhdrHEAD: + if( c != '-' ) { + if( fhdr->count < DIM(fhdr->buf)-1 ) + fhdr->buf[fhdr->count++] = c; + } + else { + fhdr->buf[fhdr->count] = 0; + for(i=0; (s=head_strings[i]); i++ ) + if( !strcmp(s,fhdr->buf) ) + break; + if( s ) { /* found string; wait for trailing dashes */ + fhdr->hdr_line = i; + fhdr->state = fhdrDASH2; + fhdr->count = 1; + } + else + fhdr->state = fhdrWAIT; + } + break; + + case fhdrDASH2: + if( c == '-' ) { + if( ++fhdr->count == 5 ) { + fhdr->state = fhdrWAITINFO; + log_debug("armor head: '%s'\n", + head_strings[fhdr->hdr_line]); + fhdr->state = fhdr->hdr_line == BEGIN_SIGNED_MSG_IDX + ? fhdrWAITCLEARSIG : fhdrWAITINFO; + if( fhdr->state == fhdrWAITCLEARSIG ) + fhdr->count = 0; + } + } + else + fhdr->state = fhdrWAIT; + break; + + case fhdrINFO: + if( fhdr->count < DIM(fhdr->buf)-1 ) + fhdr->buf[fhdr->count++] = c; + break; + + default: abort(); + } + } + return 0; +} + +/**************** + * check wether the trailer is okay. + * Returns: 0 := still in trailer + * -1 := Okay + * 1 := Error in trailer + */ +static int +check_trailer( struct fhdr_struct *fhdr, int c ) +{ + return -1; + /* FIXME: implement this ! */ +} + + +/* figure out wether the data is armored or not */ +static int +check_input( armor_filter_context_t *afx, IOBUF a ) +{ + int rc = 0; + int c; + size_t n = 0, nn=0, nn_limit=0; + struct fhdr_struct fhdr; + + assert( DIM(afx->helpbuf) >= 50 ); + memset( &fhdr, 0, sizeof(fhdr) ); + + /* read a couple of bytes */ + for( n=0; n < DIM(afx->helpbuf); n++ ) { + if( (c=iobuf_get(a)) == -1 ) + break; + afx->helpbuf[n] = c & 0xff; + } + + if( !n ) + rc = -1; + else if( is_armored( afx->helpbuf, n ) ) { + for(nn=0; nn < n; nn++ ) + if( find_header( &fhdr, afx->helpbuf[nn] ) ) + break; + if( nn == n ) { /* continue read */ + while( (c=iobuf_get(a)) != -1 ) + if( find_header( &fhdr, c ) ) + break; + if( c == -1 ) + rc = -1; /* eof */ + } + if( !rc && fhdr.hdr_line == BEGIN_SIGNED_MSG_IDX ) { + /* start fake package mode (for clear signatures) */ + nn++; + afx->helplen = n; + afx->helpidx = nn; + afx->templen = 0; + afx->tempidx = 0; + afx->fake = m_alloc_clear( sizeof(struct fhdr_struct) ); + } + else if( !rc ) { + /* next byte to read or helpbuf[nn+1] + * is the first rad64 byte */ + nn++; + afx->inp_checked = 1; + afx->crc = CRCINIT; + afx->idx = 0; + afx->radbuf[0] = 0; + afx->helplen = n; + afx->helpidx = nn; + } + } + else { + afx->inp_checked = 1; + afx->inp_bypass = 1; + } + + return rc; +} + + + +/* fake a literal data packet and wait for an armor line */ +static int +fake_packet( armor_filter_context_t *afx, IOBUF a, + size_t *retn, byte *buf, size_t size ) +{ + int rc = 0; + int c; + size_t n = 0, nn=0, nn_limit=0; + struct fhdr_struct *fhdr = afx->fake; + byte *helpbuf = afx->helpbuf; + int helpidx = afx->helpidx; + int helplen = afx->helplen; + byte *tempbuf = afx->tempbuf; + int tempidx = afx->tempidx; + int templen = afx->templen; + int defer=1; + + /* FIXME: have to read one ahead or do some other mimic to + * get rid of the lf before the "begin signed message" + */ + size = 100; /* FIXME: only used for testing (remove it) */ + n = 2; /* reserve 2 bytes for the length header */ + while( n < size-2 ) { /* and 2 for the term header */ + if( templen && (fhdr->state == fhdrWAIT || fhdr->state == fhdrLF) ) { + if( tempidx < templen ) { + buf[n++] = tempbuf[tempidx++]; + continue; + } + tempidx = templen = 0; + } + + if( helpidx < helplen ) + c = helpbuf[helpidx++]; + else if( (c=iobuf_get(a)) == -1 ) + break; + if( find_header( fhdr, c ) ) { + m_free(afx->fake); + afx->fake = NULL; + afx->inp_checked = 1; + afx->crc = CRCINIT; + afx->idx = 0; + afx->radbuf[0] = 0; + /* we don't need to care about the tempbuf */ + break; + } + if( fhdr->state == fhdrWAIT || fhdr->state == fhdrLF ) { + if( templen ) { + tempidx = 0; + continue; + } + buf[n++] = c; + } + else if( fhdr->state == fhdrWAITINFO + || fhdr->state == fhdrINFO ) + ; + else { /* store it in another temp. buf */ + assert( templen < DIM(afx->tempbuf) ); + tempbuf[templen++] = c; + } + } + buf[0] = (n-2) >> 8; + buf[1] = (n-2); + if( !afx->fake ) { /* write last (ending) length header */ + buf[n++] = 0; + buf[n++] = 0; + } + + afx->helpidx = helpidx; + afx->helplen = helplen; + afx->tempidx = tempidx; + afx->templen = templen; + *retn = n; + return rc; +} + + + +static int +radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn, + byte *buf, size_t size ) +{ + byte val; + int c, c2; + int checkcrc=0; + int rc = 0; + size_t n = 0, nn=0; + int idx, i; + u32 crc; + + crc = afx->crc; + idx = afx->idx; + val = afx->radbuf[0]; + for( n=0; n < size; ) { + if( afx->helpidx < afx->helplen ) + c = afx->helpbuf[afx->helpidx++]; + else if( (c=iobuf_get(a)) == -1 ) + break; + if( c == '\n' || c == ' ' || c == '\r' || c == '\t' ) + continue; + else if( c == '=' ) { /* pad character: stop */ + if( idx == 1 ) + buf[n++] = val; + checkcrc++; + break; + } + else if( (c = asctobin[(c2=c)]) == 255 ) { + log_error("invalid radix64 character %02x skipped\n", c2); + continue; + } + switch(idx) { + case 0: val = c << 2; break; + case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break; + case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break; + case 3: val |= c&0x3f; buf[n++] = val; break; + } + idx = (idx+1) % 4; + } + for(i=0; i < n; i++ ) + crc = (crc << 8) ^ crc_table[(crc >> 16)&0xff ^ buf[i]]; + crc &= 0x00ffffff; + afx->crc = crc; + afx->idx = idx; + afx->radbuf[0] = val; + if( checkcrc ) { + afx->inp_eof = 1; /*assume eof */ + for(;;) { /* skip lf and pad characters */ + if( afx->helpidx < afx->helplen ) + c = afx->helpbuf[afx->helpidx++]; + else if( (c=iobuf_get(a)) == -1 ) + break; + if( c == '\n' || c == ' ' || c == '\r' + || c == '\t' || c == '=' ) + continue; + break; + } + if( c == -1 ) + log_error("premature eof (no CRC)\n"); + else { + u32 mycrc = 0; + idx = 0; + do { + if( (c = asctobin[c]) == 255 ) + break; + switch(idx) { + case 0: val = c << 2; break; + case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break; + case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break; + case 3: val |= c&0x3f; mycrc |= val; break; + } + if( afx->helpidx < afx->helplen ) + c = afx->helpbuf[afx->helpidx++]; + else if( (c=iobuf_get(a)) == -1 ) + break; + } while( ++idx < 4 ); + if( c == -1 ) + log_error("premature eof (in CRC)\n"); + else if( idx != 4 ) + log_error("malformed CRC\n"); + else if( mycrc != afx->crc ) + log_error("CRC error; %06lx - %06lx\n", + (ulong)afx->crc, (ulong)mycrc); + else { + struct fhdr_struct fhdr; + + memset( &fhdr, 0, sizeof(fhdr) ); + for(rc=0;!rc;) { + rc = check_trailer( &fhdr, c ); + if( !rc ) { + if( afx->helpidx < afx->helplen ) + c = afx->helpbuf[afx->helpidx++]; + else if( (c=iobuf_get(a)) == -1 ) + rc = 2; + } + } + if( rc == -1 ) + rc = 0; + else if( rc == 2 ) + log_error("premature eof (in Trailer)\n"); + else + log_error("error in trailer line\n"); + } + } + } + + if( !n ) + rc = -1; + + *retn = n; + return rc; +} + + +/**************** + * The filter is used to handle the armor stuff + */ +int +armor_filter( void *opaque, int control, + IOBUF a, byte *buf, size_t *ret_len) +{ + size_t size = *ret_len; + armor_filter_context_t *afx = opaque; + int rc=0, i, c, c2; + byte radbuf[3]; + int idx, idx2; + size_t n=0; + u32 crc; + + if( DBG_FILTER ) + log_debug("armor-filter: control: %d\n", control ); + if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) { + for( n=0; n < size; n++ ) { + if( (c=iobuf_get(a)) == -1 ) + break; + buf[n] = c & 0xff; + } + if( !n ) + rc = -1; + *ret_len = n; + } + else if( control == IOBUFCTRL_UNDERFLOW ) { + if( size < 20 ) + log_bug(NULL); /* supplied buffer maybe too short */ + + if( afx->inp_eof ) { + *ret_len = 0; + if( DBG_FILTER ) + log_debug("armor-filter: eof due to inp_eof flag\n" ); + return -1; + } + + if( afx->fake ) + rc = fake_packet( afx, a, &n, buf, size ); + else if( !afx->inp_checked ) { + rc = check_input( afx, a ); + if( afx->inp_bypass ) + ; + else if( afx->fake ) { + /* the buffer is at least 20 bytes long, so it + * is easy to construct a packet */ + buf[0] = 0xaf; /* old packet format, type 11, var length */ + buf[1] = 0; /* set the length header */ + buf[2] = 6; + buf[3] = 't'; /* canonical text */ + buf[4] = 0; /* namelength */ + buf[5] = buf[6] = buf[7] = buf[8] = 0; /* timestamp */ + n = 9; + } + else if( !rc ) + rc = radix64_read( afx, a, &n, buf, size ); + } + else + rc = radix64_read( afx, a, &n, buf, size ); + + *ret_len = n; + } + else if( control == IOBUFCTRL_FLUSH ) { + if( !afx->status ) { /* write the header line */ + if( afx->what >= DIM(head_strings) ) + log_bug("afx->what=%d", afx->what); + iobuf_writestr(a, "-----"); + iobuf_writestr(a, head_strings[afx->what] ); + iobuf_writestr(a, "-----\n"); + iobuf_writestr(a, "Version: G10 pre-release " VERSION "\n"); + iobuf_writestr(a, "Comment: This is a alpha test version!\n\n"); + afx->status++; + afx->idx = 0; + afx->idx2 = 0; + afx->crc = CRCINIT; + } + crc = afx->crc; + idx = afx->idx; + idx2 = afx->idx2; + for(i=0; i < idx; i++ ) + radbuf[i] = afx->radbuf[i]; + + for(i=0; i < size; i++ ) + crc = (crc << 8) ^ crc_table[(crc >> 16)&0xff ^ buf[i]]; + crc &= 0x00ffffff; + + for( ; size; buf++, size-- ) { + radbuf[idx++] = *buf; + if( idx > 2 ) { + idx = 0; + c = bintoasc[(*radbuf >> 2) & 077]; + iobuf_put(a, c); + c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; + iobuf_put(a, c); + c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; + iobuf_put(a, c); + c = bintoasc[radbuf[2]&077]; + iobuf_put(a, c); + if( ++idx2 > (72/4) ) { + iobuf_put(a, '\n'); + idx2=0; + } + } + } + for(i=0; i < idx; i++ ) + afx->radbuf[i] = radbuf[i]; + afx->idx = idx; + afx->idx2 = idx2; + afx->crc = crc; + } + else if( control == IOBUFCTRL_INIT ) { + if( !is_initialized ) + initialize(); + } + else if( control == IOBUFCTRL_FREE ) { + if( afx->status ) { /* pad, write cecksum, and bottom line */ + crc = afx->crc; + idx = afx->idx; + idx2 = afx->idx2; + for(i=0; i < idx; i++ ) + radbuf[i] = afx->radbuf[i]; + if( idx ) { + c = bintoasc[(*radbuf>>2)&077]; + iobuf_put(a, c); + if( idx == 1 ) { + c = bintoasc[((*radbuf << 4) & 060) & 077]; + iobuf_put(a, c); + iobuf_put(a, '='); + iobuf_put(a, '='); + } + else { /* 2 */ + c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077]; + iobuf_put(a, c); + c = bintoasc[((radbuf[1] << 2) & 074) & 077]; + iobuf_put(a, c); + iobuf_put(a, '='); + } + ++idx2; + } + /* may need a linefeed */ + if( idx2 < (72/4) ) + iobuf_put(a, '\n'); + /* write the CRC */ + iobuf_put(a, '='); + radbuf[0] = crc >>16; + radbuf[1] = crc >> 8; + radbuf[2] = crc; + c = bintoasc[(*radbuf >> 2) & 077]; + iobuf_put(a, c); + c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077]; + iobuf_put(a, c); + c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077]; + iobuf_put(a, c); + c = bintoasc[radbuf[2]&077]; + iobuf_put(a, c); + iobuf_put(a, '\n'); + /* and the the trailer */ + if( afx->what >= DIM(tail_strings) ) + log_bug("afx->what=%d", afx->what); + iobuf_writestr(a, "-----"); + iobuf_writestr(a, tail_strings[afx->what] ); + iobuf_writestr(a, "-----\n"); + } + } + else if( control == IOBUFCTRL_DESC ) + *(char**)buf = "armor_filter"; + return rc; +} + diff --git a/g10/cipher.c b/g10/cipher.c index dedc52efe..6900e896c 100644 --- a/g10/cipher.c +++ b/g10/cipher.c @@ -37,7 +37,7 @@ /**************** - * This filter is used to en/de-cipher data. + * This filter is used to en/de-cipher data with a conventinal algorithm */ int cipher_filter( void *opaque, int control, diff --git a/g10/comment.c b/g10/comment.c new file mode 100644 index 000000000..4d5045260 --- /dev/null +++ b/g10/comment.c @@ -0,0 +1,70 @@ +/* comment.c - write comment stuff + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "iobuf.h" +#include "memory.h" +#include "util.h" +#include "main.h" +#include "keydb.h" + + + +int +write_comment( IOBUF out, const char *s ) +{ + PACKET pkt; + size_t n = strlen(s); + int rc=0; + + pkt.pkttype = PKT_COMMENT; + pkt.pkt.comment = m_alloc( sizeof *pkt.pkt.comment + n - 1 ); + pkt.pkt.comment->len = n; + strcpy(pkt.pkt.comment->data, s); + if( (rc = build_packet( out, &pkt )) ) + log_error("build_packet(comment) failed: %s\n", g10_errstr(rc) ); + free_packet( &pkt ); + return rc; +} + + +KBNODE +make_comment_node( const char *s ) +{ + PACKET *pkt = m_alloc_clear( sizeof *pkt ); + size_t n = strlen(s); + + pkt->pkttype = PKT_COMMENT; + pkt->pkt.comment = m_alloc( sizeof *pkt->pkt.comment + n - 1 ); + pkt->pkt.comment->len = n; + strcpy(pkt->pkt.comment->data, s); + return new_kbnode( pkt ); +} + + diff --git a/g10/elg.c b/g10/elg.c new file mode 100644 index 000000000..9f11d632c --- /dev/null +++ b/g10/elg.c @@ -0,0 +1,98 @@ +/* elg.c + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "iobuf.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" +#include "main.h" + +void +g10_elg_encrypt( PKT_public_cert *pkc, PKT_pubkey_enc *enc, DEK *dek ) +{ + ELG_public_key pkey; + MPI frame; + + assert( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ); + + enc->d.elg.a = mpi_alloc( mpi_get_nlimbs(pkc->d.elg.p) ); + enc->d.elg.b = mpi_alloc( mpi_get_nlimbs(pkc->d.elg.p) ); + keyid_from_pkc( pkc, enc->keyid ); + frame = encode_session_key( dek, mpi_get_nbits(pkc->d.elg.p) ); + pkey.p = pkc->d.elg.p; + pkey.g = pkc->d.elg.g; + pkey.y = pkc->d.elg.y; + if( DBG_CIPHER ) + log_mpidump("Plain DEK frame: ", frame); + elg_encrypt( enc->d.elg.a, enc->d.elg.b, frame, &pkey); + mpi_free( frame ); + if( DBG_CIPHER ) { + log_mpidump("Encry DEK a: ", enc->d.elg.a ); + log_mpidump(" DEK b: ", enc->d.elg.b ); + } + if( opt.verbose ) { + char *ustr = get_user_id_string( enc->keyid ); + log_info("ElGamal encrypted for: %s\n", ustr ); + m_free(ustr); + } +} + + +void +g10_elg_sign( PKT_secret_cert *skc, PKT_signature *sig, MD_HANDLE *md ) +{ + ELG_secret_key skey; + MPI frame; + byte *dp; + + assert( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ); + + dp = md_final( md ); + keyid_from_skc( skc, sig->keyid ); + sig->d.elg.digest_algo = md->algo; + sig->d.elg.digest_start[0] = dp[0]; + sig->d.elg.digest_start[1] = dp[1]; + sig->d.elg.a = mpi_alloc( mpi_get_nlimbs(skc->d.elg.p) ); + sig->d.elg.b = mpi_alloc( mpi_get_nlimbs(skc->d.elg.p) ); + frame = encode_md_value( md, mpi_get_nbits(skc->d.elg.p)); + skey.p = skc->d.elg.p; + skey.g = skc->d.elg.g; + skey.y = skc->d.elg.y; + skey.x = skc->d.elg.x; + elg_sign( sig->d.elg.a, sig->d.elg.b, frame, &skey); + memset( &skey, 0, sizeof skey ); + mpi_free(frame); + if( opt.verbose ) { + char *ustr = get_user_id_string( sig->keyid ); + log_info("ElGamal signature from: %s\n", ustr ); + m_free(ustr); + } +} + diff --git a/g10/encode.c b/g10/encode.c index b3d97d8b5..b73ee28d7 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -36,11 +36,10 @@ #include "filter.h" - - static int encode_simple( const char *filename, int mode ); + /**************** * Encode FILENAME only with the symmetric cipher. Take input from * stdin if FILENAME is NULL. @@ -157,43 +156,36 @@ encode_simple( const char *filename, int mode ) int encode_crypt( const char *filename, STRLIST remusr ) { - IOBUF inp, out; + IOBUF inp = NULL, out = NULL; PACKET pkt; PKT_plaintext *pt; - PKT_public_cert *pkc = NULL; - PKT_pubkey_enc *enc = NULL; - int last_rc, rc = 0; + int rc = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t afx; compress_filter_context_t zfx; - int any_names = 0; - STRLIST local_remusr = NULL; - char *ustr; + PKC_LIST pkc_list, pkc_rover; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); - if( !remusr ) { - remusr = NULL; /* fixme: ask */ - local_remusr = remusr; - } + if( (rc=build_pkc_list( remusr, &pkc_list)) ) + return rc; /* prepare iobufs */ if( !(inp = iobuf_open(filename)) ) { log_error("can't open %s: %s\n", filename? filename: "[stdin]", strerror(errno) ); - free_strlist(local_remusr); - return G10ERR_OPEN_FILE; + rc = G10ERR_OPEN_FILE; + goto leave; } else if( opt.verbose ) - log_error("reding from '%s'\n", filename? filename: "[stdin]"); + log_error("reading from '%s'\n", filename? filename: "[stdin]"); if( !(out = open_outfile( filename, opt.armor? 1:0 )) ) { - iobuf_close(inp); - free_strlist(local_remusr); - return G10ERR_CREATE_FILE; /* or user said: do not overwrite */ + rc = G10ERR_CREATE_FILE; /* or user said: do not overwrite */ + goto leave; } if( opt.armor ) @@ -211,98 +203,30 @@ encode_crypt( const char *filename, STRLIST remusr ) if( DBG_CIPHER ) log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen ); - /* loop over all user ids and build public key packets for each */ - for(last_rc=0 ; remusr; remusr = remusr->next ) { - if( pkc ) - free_public_cert( pkc ); - pkc = m_alloc_clear( sizeof *pkc ); - pkc->pubkey_algo = DEFAULT_PUBKEY_ALGO; + /* loop over all public key certificates */ + for( pkc_rover=pkc_list; pkc_rover; pkc_rover = pkc_rover->next ) { + PKT_public_cert *pkc; + PKT_pubkey_enc *enc; - if( (rc = get_pubkey_byname( pkc, remusr->d )) ) { - last_rc = rc; - log_error("skipped '%s': %s\n", remusr->d, g10_errstr(rc) ); - continue; - } - /* build the pubkey packet */ + pkc = pkc_rover->pkc; enc = m_alloc_clear( sizeof *enc ); enc->pubkey_algo = pkc->pubkey_algo; - if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { - ELG_public_key pkey; - MPI frame; - - enc->d.elg.a = mpi_alloc( mpi_get_nlimbs(pkc->d.elg.p) ); - enc->d.elg.b = mpi_alloc( mpi_get_nlimbs(pkc->d.elg.p) ); - keyid_from_pkc( pkc, enc->keyid ); - frame = encode_session_key( cfx.dek, mpi_get_nbits(pkc->d.elg.p) ); - pkey.p = pkc->d.elg.p; - pkey.g = pkc->d.elg.g; - pkey.y = pkc->d.elg.y; - if( DBG_CIPHER ) - log_mpidump("Plain DEK frame: ", frame); - elg_encrypt( enc->d.elg.a, enc->d.elg.b, frame, &pkey); - mpi_free( frame ); - if( DBG_CIPHER ) { - log_mpidump("Encry DEK a: ", enc->d.elg.a ); - log_mpidump(" DEK b: ", enc->d.elg.b ); - } - if( opt.verbose ) { - ustr = get_user_id_string( enc->keyid ); - log_info("ElGamal encrypteded for: %s\n", ustr ); - m_free(ustr); - } - } - #ifdef HAVE_RSA_CIPHER - else if( enc->pubkey_algo == PUBKEY_ALGO_RSA ) { - RSA_public_key pkey; - - keyid_from_pkc( pkc, enc->keyid ); - enc->d.rsa.rsa_integer = encode_session_key( cfx.dek, - mpi_get_nbits(pkc->d.rsa.rsa_n) ); - pkey.n = pkc->d.rsa.rsa_n; - pkey.e = pkc->d.rsa.rsa_e; - if( DBG_CIPHER ) - log_mpidump("Plain DEK frame: ", enc->d.rsa.rsa_integer); - rsa_public( enc->d.rsa.rsa_integer, enc->d.rsa.rsa_integer, &pkey); - if( DBG_CIPHER ) - log_mpidump("Encry DEK frame: ", enc->d.rsa.rsa_integer); - if( opt.verbose ) { - ustr = get_user_id_string( enc->keyid ); - log_info("RSA encrypteded for: %s\n", ustr ); - m_free(ustr); - } - } - #endif/*HAVE_RSA_CIPHER*/ - else { - last_rc = rc = G10ERR_PUBKEY_ALGO; - log_error("skipped '%s': %s\n", remusr->d, g10_errstr(rc) ); - free_pubkey_enc(enc); - continue; - } + if( enc->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) + g10_elg_encrypt( pkc, enc, cfx.dek ); + else if( enc->pubkey_algo == PUBKEY_ALGO_RSA ) + g10_rsa_encrypt( pkc, enc, cfx.dek ); + else + log_bug(NULL); /* and write it */ init_packet(&pkt); pkt.pkttype = PKT_PUBKEY_ENC; pkt.pkt.pubkey_enc = enc; - if( (rc = build_packet( out, &pkt )) ) { - last_rc = rc; - log_error("build pubkey_enc packet failed: %s\n", g10_errstr(rc) ); - free_pubkey_enc(enc); - continue; - } - /* okay: a pubkey packet has been written */ + rc = build_packet( out, &pkt ); free_pubkey_enc(enc); - any_names = 1; - } - if( pkc ) { - free_public_cert( pkc ); - pkc = NULL; - } - if( !any_names ) { - log_error("no valid keys - aborting further processing\n"); - iobuf_close(inp); - iobuf_cancel(out); - m_free(cfx.dek); /* free and burn the session key */ - free_strlist(local_remusr); - return last_rc; + if( rc ) { + log_error("build pubkey_enc packet failed: %s\n", g10_errstr(rc) ); + goto leave; + } } /* setup the inner packet */ @@ -335,14 +259,43 @@ encode_crypt( const char *filename, STRLIST remusr ) log_error("build_packet failed: %s\n", g10_errstr(rc) ); /* finish the stuff */ + leave: iobuf_close(inp); - iobuf_close(out); /* fixme: check returncode */ + if( rc ) + iobuf_cancel(out); + else + iobuf_close(out); /* fixme: check returncode */ pt->buf = NULL; free_packet(&pkt); m_free(cfx.dek); - free_strlist(local_remusr); + release_pkc_list( pkc_list ); return rc; } +/**************** + * Filter to do a complete public key encryption. + */ + #if 0 +int +encrypt_filter( void *opaque, int control, + IOBUF a, byte *buf, size_t *ret_len) +{ + size_t size = *ret_len; + encrypt_filter_context_t *efx = opaque; + int rc=0; + + if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */ + log_bug(NULL); /* not used */ + } + else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ + } + else if( control == IOBUFCTRL_FREE ) { + } + else if( control == IOBUFCTRL_DESC ) { + *(char**)buf = "encrypt_filter"; + } + return rc; +} + #endif diff --git a/g10/filter.h b/g10/filter.h index 69f5174d4..11c05e4a4 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -37,8 +37,9 @@ typedef struct { u32 crc; byte helpbuf[100]; int helpidx, helplen; - int last_c; - int fake; + byte tempbuf[100]; + int tempidx, templen; + void *fake; int inp_checked; /* set if inp has been checked */ int inp_bypass; /* set if the input is not armored */ int inp_eof; diff --git a/g10/g10.c b/g10/g10.c index 9a1966d02..1df800803 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -1,4 +1,4 @@ -/* g10.c - The G10 re-install utility +/* g10.c - The G10 utility * Copyright (c) 1997 by Werner Koch (dd9jn) * * This file is part of G10. @@ -327,13 +327,15 @@ main( int argc, char **argv ) case aSign: /* sign the given file */ if( argc > 1 ) usage(1); - if( (rc = sign_file(fname, detached_sig, locusr)) ) + if( (rc = sign_file(fname, detached_sig, locusr, 0, NULL)) ) log_error("sign_file('%s'): %s\n", fname_print, g10_errstr(rc) ); break; case aSignEncr: /* sign and encrypt the given file */ - log_fatal("signing and encryption is not yet implemented\n"); - usage(1); /* FIXME */ + if( argc > 1 ) + usage(1); + if( (rc = sign_file(fname, detached_sig, locusr, 1, remusr)) ) + log_error("sign_file('%s'): %s\n", fname_print, g10_errstr(rc) ); break; diff --git a/g10/keydb.h b/g10/keydb.h index fe093205b..f0d8b517b 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -55,7 +55,30 @@ struct keyblock_pos_struct { }; typedef struct keyblock_pos_struct KBPOS; +/* structure to hold a couple of public key certificates */ +typedef struct pkc_list *PKC_LIST; +struct pkc_list { + PKC_LIST next; + PKT_public_cert *pkc; + int mark; +}; +/* structure to hold a couple of secret key certificates */ +typedef struct skc_list *SKC_LIST; +struct skc_list { + SKC_LIST next; + PKT_secret_cert *skc; + int mark; +}; + + +/*-- pkclist.c --*/ +void release_pkc_list( PKC_LIST pkc_list ); +int build_pkc_list( STRLIST remusr, PKC_LIST *ret_pkc_list ); + +/*-- skclist.c --*/ +void release_skc_list( SKC_LIST skc_list ); +int build_skc_list( STRLIST locusr, SKC_LIST *ret_skc_list, int unlock ); /*-- passphrase.h --*/ DEK *get_passphrase_hash( u32 *keyid, char *text ); diff --git a/g10/main.h b/g10/main.h index a8d28af79..0d00dd19a 100644 --- a/g10/main.h +++ b/g10/main.h @@ -34,7 +34,8 @@ int encode_store( const char *filename ); int encode_crypt( const char *filename, STRLIST remusr ); /*-- sign.c --*/ -int sign_file( const char *filename, int detached, STRLIST locusr ); +int sign_file( const char *filename, int detached, STRLIST locusr, + int encrypt, STRLIST remusr ); int sign_key( const char *username, STRLIST locusr ); /*-- sig-check.c --*/ @@ -51,6 +52,7 @@ IOBUF open_sigfile( const char *iname ); /*-- seskey.c --*/ void make_session_key( DEK *dek ); MPI encode_session_key( DEK *dek, unsigned nbits ); +MPI encode_sha1_value( byte *md, unsigned len, unsigned nbits ); MPI encode_rmd160_value( byte *md, unsigned len, unsigned nbits ); MPI encode_md5_value( byte *md, unsigned len, unsigned nbits ); MPI encode_md_value( MD_HANDLE *md, unsigned nbits ); @@ -58,4 +60,13 @@ MPI encode_md_value( MD_HANDLE *md, unsigned nbits ); /*-- comment.c --*/ KBNODE make_comment_node( const char *s ); +/*-- elg.c --*/ +void g10_elg_encrypt( PKT_public_cert *pkc, PKT_pubkey_enc *enc, DEK *dek ); +void g10_elg_sign( PKT_secret_cert *skc, PKT_signature *sig, MD_HANDLE *md ); + +/*-- rsa.c --*/ +void g10_rsa_encrypt( PKT_public_cert *pkc, PKT_pubkey_enc *enc, DEK *dek ); +void g10_rsa_sign( PKT_secret_cert *skc, PKT_signature *sig, MD_HANDLE *md ); + + #endif /*G10_MAIN_H*/ diff --git a/g10/pkclist.c b/g10/pkclist.c new file mode 100644 index 000000000..1356a06f9 --- /dev/null +++ b/g10/pkclist.c @@ -0,0 +1,95 @@ +/* pkclist.c + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" + + +void +release_pkc_list( PKC_LIST pkc_list ) +{ + PKC_LIST pkc_rover; + + for( ; pkc_list; pkc_list = pkc_rover ) { + pkc_rover = pkc_list->next; + free_public_cert( pkc_list->pkc ); + m_free( pkc_list ); + } +} + +int +build_pkc_list( STRLIST remusr, PKC_LIST *ret_pkc_list ) +{ + PKC_LIST pkc_list = NULL; + PKC_LIST pkc_rover = NULL; + int rc; + + if( !remusr ) { /* ask!!! */ + log_bug("ask for public key nyi\n"); + } + else { + for(; remusr; remusr = remusr->next ) { + PKT_public_cert *pkc; + + pkc = m_alloc_clear( sizeof *pkc ); + if( (rc = get_pubkey_byname( pkc, remusr->d )) ) { + free_public_cert( pkc ); pkc = NULL; + log_error("skipped '%s': %s\n", remusr->d, g10_errstr(rc) ); + } + else if( is_valid_pubkey_algo(pkc->pubkey_algo) ) { + PKC_LIST r; + r = m_alloc( sizeof *r ); + r->pkc = pkc; pkc = NULL; + r->next = pkc_list; + r->mark = 0; + pkc_list = r; + } + else { + free_public_cert( pkc ); pkc = NULL; + log_error("skipped '%s': %s\n", remusr->d, g10_errstr(rc) ); + } + } + } + + + if( !rc && !pkc_list ) { + log_error("no valid addressees\n"); + rc = G10ERR_NO_USER_ID; + } + + if( rc ) + release_pkc_list( pkc_list ); + else + *ret_pkc_list = pkc_list; + return rc; +} + + diff --git a/g10/rsa.c b/g10/rsa.c new file mode 100644 index 000000000..fe4ba2eb0 --- /dev/null +++ b/g10/rsa.c @@ -0,0 +1,100 @@ +/* rsa.c + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "iobuf.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" +#include "main.h" + +void +g10_rsa_encrypt( PKT_public_cert *pkc, PKT_pubkey_enc *enc, DEK *dek ) +{ + #ifdef HAVE_RSA_CIPHER + RSA_public_key pkey; + + assert( enc->pubkey_algo == PUBKEY_ALGO_RSA ); + + keyid_from_pkc( pkc, enc->keyid ); + enc->d.rsa.rsa_integer = encode_session_key( dek, + mpi_get_nbits(pkc->d.rsa.rsa_n) ); + pkey.n = pkc->d.rsa.rsa_n; + pkey.e = pkc->d.rsa.rsa_e; + if( DBG_CIPHER ) + log_mpidump("Plain DEK frame: ", enc->d.rsa.rsa_integer); + rsa_public( enc->d.rsa.rsa_integer, enc->d.rsa.rsa_integer, &pkey); + if( DBG_CIPHER ) + log_mpidump("Encry DEK frame: ", enc->d.rsa.rsa_integer); + if( opt.verbose ) { + char *ustr = get_user_id_string( enc->keyid ); + log_info("RSA encrypted for: %s\n", ustr ); + m_free(ustr); + } + #else + log_bug(NULL); + #endif/* ! HAVE_RSA_CIPHER*/ +} + + +void +g10_rsa_sign( PKT_secret_cert *skc, PKT_signature *sig, MD_HANDLE *md ) +{ + #ifdef HAVE_RSA_CIPHER + RSA_secret_key skey; + byte *dp; + + assert( sig->pubkey_algo == PUBKEY_ALGO_RSA ); + + dp = md_final( md ); + + keyid_from_skc( skc, sig->keyid ); + sig->d.rsa.digest_algo = md->algo; + sig->d.rsa.digest_start[0] = dp[0]; + sig->d.rsa.digest_start[1] = dp[1]; + sig->d.rsa.rsa_integer = + encode_md_value( md, mpi_get_nbits(skc->d.rsa.rsa_n)); + skey.e = skc->d.rsa.rsa_e; + skey.n = skc->d.rsa.rsa_n; + skey.p = skc->d.rsa.rsa_p; + skey.q = skc->d.rsa.rsa_q; + skey.d = skc->d.rsa.rsa_d; + skey.u = skc->d.rsa.rsa_u; + rsa_secret( sig->d.rsa.rsa_integer, sig->d.rsa.rsa_integer, &skey); + memset( &skey, 0, sizeof skey ); + if( opt.verbose ) { + char *ustr = get_user_id_string( sig->keyid ); + log_info("RSA signature from: %s\n", ustr ); + m_free(ustr); + } + #else + log_bug(NULL); + #endif/* ! HAVE_RSA_CIPHER*/ +} + diff --git a/g10/seskey.c b/g10/seskey.c index 325234c45..7d3cba27b 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -103,13 +103,14 @@ encode_session_key( DEK *dek, unsigned nbits ) /**************** * Encode a ripemd160 message digest of LEN bytes into NBITS. * returns: A mpi with the session key (caller must free) + * RMD160 Object ID is 1.3.36.3.2.1 */ MPI encode_rmd160_value( byte *md, unsigned len, unsigned nbits ) { - static byte asn[18] = /* FIXME: need other values*/ - { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86,0x48, - 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; + static byte asn[15] = + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, + 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 }; int nframe = (nbits+7) / 8; byte *p; MPI frame; @@ -120,7 +121,7 @@ encode_rmd160_value( byte *md, unsigned len, unsigned nbits ) /* We encode the MD in this way: * - * 0 A PAD(n bytes) 0 ASN(18 bytes) MD(20 bytes) + * 0 A PAD(n bytes) 0 ASN(15 bytes) MD(20 bytes) * * PAD consists of FF bytes. */ @@ -128,7 +129,47 @@ encode_rmd160_value( byte *md, unsigned len, unsigned nbits ) n = 0; for(i=20-1; i >= 0; i--, n++ ) mpi_putbyte(frame, n, md[i] ); - for( i=18-1; i >= 0; i--, n++ ) + for( i=15-1; i >= 0; i--, n++ ) + mpi_putbyte(frame, n, asn[i] ); + mpi_putbyte(frame, n++, 0 ); + while( n < nframe-2 ) + mpi_putbyte(frame, n++, 0xff ); + mpi_putbyte(frame, n++, DIGEST_ALGO_RMD160 ); + mpi_putbyte(frame, n++, 0 ); + assert( n == nframe ); + return frame; +} + +/**************** + * Encode a sha-1 message digest of LEN bytes into NBITS. + * returns: A mpi with the session key (caller must free) + * SHA-1 Objet ID is 1.3.14.3.2.26 + */ +MPI +encode_sha1_value( byte *md, unsigned len, unsigned nbits ) +{ + static byte asn[15] = + { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, + 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 }; + int nframe = (nbits+7) / 8; + byte *p; + MPI frame; + int i,n,c; + + if( (nbits % BITS_PER_MPI_LIMB) || nframe < 42 || len != 20 ) + log_bug("can't encode a %d bit MD into a %d bits frame\n",len*8, nbits); + + /* We encode the MD in this way: + * + * 0 A PAD(n bytes) 0 ASN(15 bytes) MD(20 bytes) + * + * PAD consists of FF bytes. + */ + frame = mpi_alloc_secure( nframe / BYTES_PER_MPI_LIMB ); + n = 0; + for(i=20-1; i >= 0; i--, n++ ) + mpi_putbyte(frame, n, md[i] ); + for( i=15-1; i >= 0; i--, n++ ) mpi_putbyte(frame, n, asn[i] ); mpi_putbyte(frame, n++, 0 ); while( n < nframe-2 ) @@ -143,6 +184,7 @@ encode_rmd160_value( byte *md, unsigned len, unsigned nbits ) /**************** * Encode a md5 message digest of LEN bytes into NBITS. * returns: A mpi with the session key (caller must free) + * MD5 Object ID is 1.2.840.113549.2.5 */ MPI encode_md5_value( byte *md, unsigned len, unsigned nbits ) @@ -187,6 +229,8 @@ encode_md_value( MD_HANDLE *md, unsigned nbits ) return encode_md5_value( p, 16, nbits ); else if( md->algo == DIGEST_ALGO_RMD160 ) return encode_rmd160_value( p, 20, nbits ); + else if( md->algo == DIGEST_ALGO_SHA1 ) + return encode_sha1_value( p, 20, nbits ); else log_bug(NULL); } diff --git a/g10/sig-check.c b/g10/sig-check.c index bbeb3a956..856d57daa 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -106,14 +106,14 @@ signature_check( PKT_signature *sig, MD_HANDLE *digest ) } if( sig->d.rsa.digest_algo == DIGEST_ALGO_RMD160 ) { - static byte asn[18] = /* stored reverse FIXME: need other values*/ - { 0x10, 0x04, 0x00, 0x05, 0x05, 0x02, 0x0d, 0xf7, 0x86, - 0x48, 0x86, 0x2a, 0x08, 0x06, 0x0c, 0x30, 0x20, 0x30 }; + static byte asn[15] = /* stored reverse */ + { 0x14, 0x04, 0x00, 0x05, 0x01, 0x02, 0x03, 0x24, 0x2b, + 0x05, 0x06, 0x09, 0x30, 0x21, 0x30 }; - for(i=20,j=0; (c=mpi_getbyte(result, i)) != -1 && j < 18; i++, j++ ) + for(i=20,j=0; (c=mpi_getbyte(result, i)) != -1 && j < 15; i++, j++ ) if( asn[j] != c ) break; - if( j != 18 || mpi_getbyte(result, i) ) { /* ASN is wrong */ + if( j != 15 || mpi_getbyte(result, i) ) { /* ASN is wrong */ rc = G10ERR_BAD_PUBKEY; goto leave; } diff --git a/g10/sign.c b/g10/sign.c new file mode 100644 index 000000000..67e229f29 --- /dev/null +++ b/g10/sign.c @@ -0,0 +1,581 @@ +/* sign.c - sign data + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "iobuf.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" +#include "main.h" +#include "filter.h" +#include "ttyio.h" + + + + + +static int +complete_sig( PKT_signature *sig, PKT_secret_cert *skc, MD_HANDLE *md ) +{ + int rc=0; + + if( (rc=check_secret_key( skc )) ) + ; + else if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) + g10_elg_sign( skc, sig, md ); + else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) + g10_rsa_sign( skc, sig, md ); + else + log_bug(NULL); + + /* fixme: should we check wether the signature is okay? */ + + return rc; +} + + + + + +/**************** + * Sign the file with name FILENAME. If DETACHED has the value true, + * make a detached signature. If FILENAME is NULL read from stdin + * and ignore the detached mode. Sign the file with all secret keys + * which can be taken from LOCUSR, if this is NULL, use the default one + * If ENCRYPT is true, use REMUSER (or ask if it is NULL) to encrypt the + * signed data for these users. + */ +int +sign_file( const char *filename, int detached, STRLIST locusr, + int encrypt, STRLIST remusr ) +{ + armor_filter_context_t afx; + compress_filter_context_t zfx; + md_filter_context_t mfx; + text_filter_context_t tfx; + IOBUF inp = NULL, out = NULL; + PACKET pkt; + PKT_plaintext *pt = NULL; + u32 filesize; + int last_rc, rc = 0; + PKC_LIST pkc_list = NULL; + SKC_LIST skc_list = NULL; + SKC_LIST skc_rover = NULL; + + memset( &afx, 0, sizeof afx); + memset( &zfx, 0, sizeof zfx); + memset( &mfx, 0, sizeof mfx); + memset( &tfx, 0, sizeof tfx); + init_packet( &pkt ); + + if( (rc=build_skc_list( locusr, &skc_list, 1 )) ) + goto leave; + if( encrypt ) { + if( (rc=build_pkc_list( remusr, &pkc_list )) ) + goto leave; + } + + /* prepare iobufs */ + if( !(inp = iobuf_open(filename)) ) { + log_error("can't open %s: %s\n", filename? filename: "[stdin]", + strerror(errno) ); + rc = G10ERR_OPEN_FILE; + goto leave; + } + + if( !(out = open_outfile( filename, opt.armor? 1: detached? 2:0 )) ) { + rc = G10ERR_CREATE_FILE; + goto leave; + } + + /* prepare to calculate the MD over the input */ + if( opt.textmode && opt.armor ) + iobuf_push_filter( inp, text_filter, &tfx ); + mfx.rmd160 = rmd160_open(0); + iobuf_push_filter( inp, md_filter, &mfx ); + + if( opt.armor ) + iobuf_push_filter( out, armor_filter, &afx ); + write_comment( out, "#Created by G10 pre-release " VERSION ); + if( opt.compress ) + iobuf_push_filter( out, compress_filter, &zfx ); + + if( encrypt ) { + /* prepare for encryption */ + /* FIXME!!!!!!! */ + } + + /* loop over the secret certificates and build headers */ + for( skc_rover = skc_list; skc_rover; skc_rover = skc_rover->next ) { + PKT_secret_cert *skc; + PKT_onepass_sig *ops; + + skc = skc_rover->skc; + ops = m_alloc_clear( sizeof *ops ); + ops->sig_class = opt.textmode? 0x01 : 0x00; + ops->digest_algo = DIGEST_ALGO_RMD160; + ops->pubkey_algo = skc->pubkey_algo; + keyid_from_skc( skc, ops->keyid ); + ops->last = !skc_rover->next; + + init_packet(&pkt); + pkt.pkttype = PKT_ONEPASS_SIG; + pkt.pkt.onepass_sig = ops; + rc = build_packet( out, &pkt ); + free_packet( &pkt ); + if( rc ) { + log_error("build onepass_sig packet failed: %s\n", g10_errstr(rc)); + goto leave; + } + } + + + /* setup the inner packet */ + if( detached ) { + /* read, so that the filter can calculate the digest */ + while( iobuf_get(inp) != -1 ) + ; + } + else { + if( filename ) { + pt = m_alloc( sizeof *pt + strlen(filename) - 1 ); + pt->namelen = strlen(filename); + memcpy(pt->name, filename, pt->namelen ); + if( !(filesize = iobuf_get_filelength(inp)) ) + log_info("warning: '%s' is an empty file\n", filename ); + } + else { /* no filename */ + pt = m_alloc( sizeof *pt - 1 ); + pt->namelen = 0; + filesize = 0; /* stdin */ + } + pt->timestamp = make_timestamp(); + pt->mode = opt.textmode? 't':'b'; + pt->len = filesize; + pt->buf = inp; + pkt.pkttype = PKT_PLAINTEXT; + pkt.pkt.plaintext = pt; + /*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/ + if( (rc = build_packet( out, &pkt )) ) + log_error("build_packet(PLAINTEXT) failed: %s\n", g10_errstr(rc) ); + pt->buf = NULL; + } + + /* loop over the secret certificates */ + for( skc_rover = skc_list; skc_rover; skc_rover = skc_rover->next ) { + PKT_secret_cert *skc; + PKT_signature *sig; + RMDHANDLE rmd; + byte *dp; + + skc = skc_rover->skc; + + /* build the signature packet */ + sig = m_alloc_clear( sizeof *sig ); + sig->pubkey_algo = skc->pubkey_algo; + sig->timestamp = make_timestamp(); + sig->sig_class = opt.textmode? 0x01 : 0x00; + + rmd = rmd160_copy( mfx.rmd160 ); + rmd160_putchar( rmd, sig->sig_class ); + { u32 a = sig->timestamp; + rmd160_putchar( rmd, (a >> 24) & 0xff ); + rmd160_putchar( rmd, (a >> 16) & 0xff ); + rmd160_putchar( rmd, (a >> 8) & 0xff ); + rmd160_putchar( rmd, a & 0xff ); + } + dp = rmd160_final( rmd ); + + if( sig->pubkey_algo == PUBKEY_ALGO_ELGAMAL ) { + ELG_secret_key skey; + MPI frame; + + keyid_from_skc( skc, sig->keyid ); + sig->d.elg.digest_algo = DIGEST_ALGO_RMD160; + sig->d.elg.digest_start[0] = dp[0]; + sig->d.elg.digest_start[1] = dp[1]; + sig->d.elg.a = mpi_alloc( mpi_get_nlimbs(skc->d.elg.p) ); + sig->d.elg.b = mpi_alloc( mpi_get_nlimbs(skc->d.elg.p) ); + frame = encode_rmd160_value( dp, 20, mpi_get_nbits(skc->d.elg.p) ); + skey.p = skc->d.elg.p; + skey.g = skc->d.elg.g; + skey.y = skc->d.elg.y; + skey.x = skc->d.elg.x; + elg_sign( sig->d.elg.a, sig->d.elg.b, frame, &skey); + memset( &skey, 0, sizeof skey ); + mpi_free(frame); + if( opt.verbose ) { + char *ustr = get_user_id_string( sig->keyid ); + log_info("ELG signature from: %s\n", ustr ); + m_free(ustr); + } + } + #ifdef HAVE_RSA_CIPHER + else if( sig->pubkey_algo == PUBKEY_ALGO_RSA ) { + RSA_secret_key skey; + + keyid_from_skc( skc, sig->keyid ); + sig->d.rsa.digest_algo = DIGEST_ALGO_RMD160; + sig->d.rsa.digest_start[0] = dp[0]; + sig->d.rsa.digest_start[1] = dp[1]; + sig->d.rsa.rsa_integer = encode_rmd160_value( dp, 20, + mpi_get_nbits(skc->d.rsa.rsa_n) ); + skey.e = skc->d.rsa.rsa_e; + skey.n = skc->d.rsa.rsa_n; + skey.p = skc->d.rsa.rsa_p; + skey.q = skc->d.rsa.rsa_q; + skey.d = skc->d.rsa.rsa_d; + skey.u = skc->d.rsa.rsa_u; + rsa_secret( sig->d.rsa.rsa_integer, sig->d.rsa.rsa_integer, &skey); + memset( &skey, 0, sizeof skey ); + if( opt.verbose ) { + char *ustr = get_user_id_string( sig->keyid ); + log_info("RSA signature from: %s\n", ustr ); + m_free(ustr); + } + /* fixme: should we check wether the signature is okay? */ + } + #endif/*HAVE_RSA_CIPHER*/ + else + log_bug(NULL); + + rmd160_close( rmd ); + + /* and write it */ + init_packet(&pkt); + pkt.pkttype = PKT_SIGNATURE; + pkt.pkt.signature = sig; + rc = build_packet( out, &pkt ); + free_packet( &pkt ); + if( rc ) { + log_error("build signature packet failed: %s\n", g10_errstr(rc) ); + goto leave; + } + } + + + leave: + if( rc ) + iobuf_cancel(out); + else + iobuf_close(out); + iobuf_close(inp); + rmd160_close( mfx.rmd160 ); + release_skc_list( skc_list ); + release_pkc_list( pkc_list ); + return rc; +} + + + +static void +show_fingerprint( PKT_public_cert *pkc ) +{ + byte *array, *p; + size_t i, n; + + p = array = fingerprint_from_pkc( pkc, &n ); + tty_printf(" Fingerprint:"); + if( n == 20 ) { + for(i=0; i < n ; i++, i++, p += 2 ) { + if( i == 10 ) + tty_printf(" "); + tty_printf(" %02X%02X", *p, p[1] ); + } + } + else { + for(i=0; i < n ; i++, p++ ) { + if( i && !(i%8) ) + tty_printf(" "); + tty_printf(" %02X", *p ); + } + } + tty_printf("\n"); + m_free(array); +} + + +/**************** + * Ask wether the user is willing to sign the key. Return true if so. + */ +static int +sign_it_p( PKT_public_cert *pkc, PKT_user_id *uid ) +{ + char *answer; + int yes; + + tty_printf("\nAre you really sure that you want so sign this key:\n\n" + "%4u%c/%08lX %s ", + nbits_from_pkc( pkc ), + pubkey_letter( pkc->pubkey_algo ), + (ulong)keyid_from_pkc( pkc, NULL ), + datestr_from_pkc( pkc ) ); + tty_print_string( uid->name, uid->len ); + tty_printf("\n"); + show_fingerprint(pkc); + tty_printf("\n"); + answer = tty_get("Sign this key? "); + tty_kill_prompt(); + yes = answer_is_yes(answer); + m_free(answer); + return yes; +} + + +static void +check_all_keysigs( KBNODE keyblock ) +{ + KBNODE kbctx; + KBNODE node; + int rc; + int inv_sigs = 0; + int no_key = 0; + int oth_err = 0; + + for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) { + if( node->pkt->pkttype == PKT_SIGNATURE + && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) { + PKT_signature *sig = node->pkt->pkt.signature; + int sigrc; + + tty_printf("sig"); + switch( (rc = check_key_signature( keyblock, node )) ) { + case 0: node->flag = 0; sigrc = '!'; break; + case G10ERR_BAD_SIGN: inv_sigs++; node->flag = 1; sigrc = '-'; break; + case G10ERR_NO_PUBKEY: no_key++; node->flag = 2; sigrc = '?'; break; + default: oth_err++; node->flag = 4; sigrc = '%'; break; + } + tty_printf("%c %08lX %s ", + sigrc, sig->keyid[1], datestr_from_sig(sig)); + if( sigrc == '%' ) + tty_printf("[%s] ", g10_errstr(rc) ); + else if( sigrc == '?' ) + ; + else { + size_t n; + char *p = get_user_id( sig->keyid, &n ); + tty_print_string( p, n > 40? 40 : n ); + m_free(p); + } + tty_printf("\n"); + } + } + if( inv_sigs ) + tty_printf("%d bad signatures\n", inv_sigs ); + if( no_key ) + tty_printf("No public key for %d signatures\n", no_key ); + if( oth_err ) + tty_printf("%d signatures not checked due to errors\n", oth_err ); +} + + +/**************** + * This functions signs the key of USERNAME with all users listed in + * LOCUSR. If LOCUSR is NULL the default secret certificate will + * be used. This works on all keyrings, so there is no armor or + * compress stuff here. + */ +int +sign_key( const char *username, STRLIST locusr ) +{ + md_filter_context_t mfx; + int rc = 0; + SKC_LIST skc_list = NULL; + SKC_LIST skc_rover = NULL; + KBNODE keyblock = NULL; + KBNODE kbctx, node; + KBPOS kbpos; + PKT_public_cert *pkc; + int any; + u32 pkc_keyid[2]; + + memset( &mfx, 0, sizeof mfx); + + /* search the userid */ + rc = search_keyblock_byname( &kbpos, username ); + if( rc ) { + log_error("user '%s' not found\n", username ); + goto leave; + } + + /* build a list of all signators */ + rc=build_skc_list( locusr, &skc_list, 0 ); + if( rc ) + goto leave; + + + /* read the keyblock */ + rc = read_keyblock( &kbpos, &keyblock ); + if( rc ) { + log_error("error reading the certificate: %s\n", g10_errstr(rc) ); + goto leave; + } + + /* get the keyid from the keyblock */ + for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) { + if( node->pkt->pkttype == PKT_PUBLIC_CERT ) + break; + } + if( !node ) { + log_error("Oops; public key not found anymore!\n"); + rc = G10ERR_GENERAL; + goto leave; + } + + pkc = node->pkt->pkt.public_cert; + keyid_from_pkc( pkc, pkc_keyid ); + log_info("Checking signatures of this public key certificate:\n"); + tty_printf("pub %4u%c/%08lX %s ", + nbits_from_pkc( pkc ), + pubkey_letter( pkc->pubkey_algo ), + pkc_keyid[1], datestr_from_pkc(pkc) ); + { + size_t n; + char *p = get_user_id( pkc_keyid, &n ); + tty_print_string( p, n > 40? 40 : n ); + m_free(p); + tty_printf("\n"); + } + + clear_kbnode_flags( keyblock ); + check_all_keysigs( keyblock ); + /* look wether we should ask to remove invalid keys */ + /*+ FIXME: */ + + /* check wether we have already signed it */ + for( skc_rover = skc_list; skc_rover; skc_rover = skc_rover->next ) { + u32 akeyid[2]; + + keyid_from_skc( skc_rover->skc, akeyid ); + for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) { + if( node->pkt->pkttype == PKT_SIGNATURE + && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) { + if( akeyid[0] == node->pkt->pkt.signature->keyid[0] + && akeyid[1] == node->pkt->pkt.signature->keyid[1] ) { + log_info("Already signed by keyid %08lX\n", akeyid[1] ); + skc_rover->mark = 1; + } + } + } + } + for( skc_rover = skc_list; skc_rover; skc_rover = skc_rover->next ) { + if( !skc_rover->mark ) + break; + } + if( !skc_rover ) { + log_info("Nothing to sign\n"); + goto leave; + } + + /* Loop over all signers and all user ids and sign */ + for( skc_rover = skc_list; skc_rover; skc_rover = skc_rover->next ) { + if( skc_rover->mark ) + continue; + for( kbctx=NULL; (node=walk_kbtree( keyblock, &kbctx)) ; ) { + if( node->pkt->pkttype == PKT_USER_ID ) { + if( sign_it_p( pkc, node->pkt->pkt.user_id ) ) { + PACKET *pkt; + PKT_signature *sig; + + rc = make_keysig_packet( &sig, pkc, + node->pkt->pkt.user_id, + skc_rover->skc, + 0x10, + DIGEST_ALGO_RMD160 ); + if( rc ) { + log_error("make_keysig_packet failed: %s\n", g10_errstr(rc)); + goto leave; + } + + pkt = m_alloc_clear( sizeof *pkt ); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = sig; + add_kbnode_as_child( node, new_kbnode( pkt ) ); + } + } + } + } + + rc = update_keyblock( &kbpos, keyblock ); + if( rc ) { + log_error("insert_keyblock failed: %s\n", g10_errstr(rc) ); + goto leave; + } + + leave: + release_kbnode( keyblock ); + release_skc_list( skc_list ); + rmd160_close( mfx.rmd160 ); + return rc; +} + + + +/**************** + * Create a signature packet for the given public key certificate + * and the user id and return it in ret_sig. User signature class SIGCLASS + */ +int +make_keysig_packet( PKT_signature **ret_sig, PKT_public_cert *pkc, + PKT_user_id *uid, PKT_secret_cert *skc, + int sigclass, int digest_algo ) +{ + PKT_signature *sig; + int rc=0; + MD_HANDLE *md; + + assert( sigclass >= 0x10 && sigclass <= 0x13 ); + md = md_open( digest_algo, 0 ); + /* hash the public key certificate */ + hash_public_cert( md, pkc ); + md_write( md, uid->name, uid->len ); + /* and make the signature packet */ + sig = m_alloc_clear( sizeof *sig ); + sig->pubkey_algo = skc->pubkey_algo; + sig->timestamp = make_timestamp(); + sig->sig_class = sigclass; + + md_putchar( md, sig->sig_class ); + { u32 a = sig->timestamp; + md_putchar( md, (a >> 24) & 0xff ); + md_putchar( md, (a >> 16) & 0xff ); + md_putchar( md, (a >> 8) & 0xff ); + md_putchar( md, a & 0xff ); + } + + rc = complete_sig( sig, skc, md ); + + md_close( md ); + if( rc ) + free_seckey_enc( sig ); + else + *ret_sig = sig; + return rc; +} + diff --git a/g10/skclist.c b/g10/skclist.c new file mode 100644 index 000000000..75e547ffb --- /dev/null +++ b/g10/skclist.c @@ -0,0 +1,112 @@ +/* skclist.c + * Copyright (c) 1997 by Werner Koch (dd9jn) + * + * This file is part of G10. + * + * G10 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. + * + * G10 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include + +#include "options.h" +#include "packet.h" +#include "errors.h" +#include "keydb.h" +#include "memory.h" +#include "util.h" + + +void +release_skc_list( SKC_LIST skc_list ) +{ + SKC_LIST skc_rover; + + for( ; skc_list; skc_list = skc_rover ) { + skc_rover = skc_list->next; + free_secret_cert( skc_list->skc ); + m_free( skc_list ); + } +} + +int +build_skc_list( STRLIST locusr, SKC_LIST *ret_skc_list, int unlock ) +{ + SKC_LIST skc_list = NULL; + SKC_LIST skc_rover = NULL; + int rc; + + if( !locusr ) { /* use the default one */ + PKT_secret_cert *skc; + + skc = m_alloc_clear( sizeof *skc ); + if( (rc = get_seckey_byname( skc, NULL, unlock )) ) { + free_secret_cert( skc ); skc = NULL; + log_error("no default secret key: %s\n", g10_errstr(rc) ); + } + else if( is_valid_pubkey_algo(skc->pubkey_algo) ) { + SKC_LIST r; + r = m_alloc( sizeof *r ); + r->skc = skc; skc = NULL; + r->next = skc_list; + r->mark = 0; + skc_list = r; + } + else { + free_secret_cert( skc ); skc = NULL; + log_error("invalid default secret key: %s\n", g10_errstr(rc) ); + } + } + else { + for(; locusr; locusr = locusr->next ) { + PKT_secret_cert *skc; + + skc = m_alloc_clear( sizeof *skc ); + if( (rc = get_seckey_byname( skc, locusr->d, unlock )) ) { + free_secret_cert( skc ); skc = NULL; + log_error("skipped '%s': %s\n", locusr->d, g10_errstr(rc) ); + } + else if ( is_valid_pubkey_algo(skc->pubkey_algo) ) { + SKC_LIST r; + r = m_alloc( sizeof *r ); + r->skc = skc; skc = NULL; + r->next = skc_list; + r->mark = 0; + skc_list = r; + } + else { + free_secret_cert( skc ); skc = NULL; + log_error("skipped '%s': %s\n", locusr->d, g10_errstr(rc) ); + } + } + } + + + if( !rc && !skc_list ) { + log_error("no valid signators\n"); + rc = G10ERR_NO_USER_ID; + } + + if( rc ) + release_skc_list( skc_list ); + else + *ret_skc_list = skc_list; + return rc; +} + diff --git a/util/ttyio.c b/util/ttyio.c index 74d31d1af..c8a4e8f68 100644 --- a/util/ttyio.c +++ b/util/ttyio.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include "util.h" @@ -32,17 +33,35 @@ static int last_prompt_len; static FILE * -open_tty(void) +open_tty(struct termios *termsave ) { + struct termios term; + FILE *tty = fopen("/dev/tty", "r"); if( !tty ) log_fatal("cannot open /dev/tty: %s\n", strerror(errno) ); + + if( termsave ) { /* hide input */ + if( tcgetattr(fileno(tty), termsave) ) + log_fatal("tcgetattr() failed: %s\n", strerror(errno) ); + term = *termsave; + term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + if( tcsetattr( fileno(tty), TCSAFLUSH, &term ) ) + log_fatal("tcsetattr() failed: %s\n", strerror(errno) ); + } + + return tty; } static void -close_tty( FILE *tty ) +close_tty( FILE *tty, struct termios *termsave ) { + if( termsave ) { + if( tcsetattr(fileno(tty), TCSAFLUSH, termsave) ) + log_error("tcsetattr() failed: %s\n", strerror(errno) ); + putc('\n', stderr); + } fclose(tty); } @@ -82,18 +101,21 @@ tty_print_string( byte *p, size_t n ) -char * -tty_get( const char *prompt ) + + +static char * +do_get( const char *prompt, int hidden ) { char *buf; int c, n, i; FILE *fp; + struct termios termsave; last_prompt_len = 0; tty_printf( prompt ); buf = m_alloc(n=50); i = 0; - fp = open_tty(); + fp = open_tty(hidden? &termsave: NULL); while( (c=getc(fp)) != EOF && c != '\n' ) { last_prompt_len++; if( c == '\t' ) @@ -106,15 +128,22 @@ tty_get( const char *prompt ) } buf[i++] = c; } - close_tty(fp); + close_tty(fp, hidden? &termsave: NULL); buf[i] = 0; return buf; } + +char * +tty_get( const char *prompt ) +{ + return do_get( prompt, 0 ); +} + char * tty_get_hidden( const char *prompt ) { - return tty_get( prompt ); /* fixme */ + return do_get( prompt, 1 ); }