diff options
Diffstat (limited to 'libextra/opencdk/sign.c')
-rw-r--r-- | libextra/opencdk/sign.c | 493 |
1 files changed, 493 insertions, 0 deletions
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; +} + |