diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2004-05-13 06:39:45 +0000 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2004-05-13 06:39:45 +0000 |
commit | b4eeab22afb5744b082248ed263281f7f603f323 (patch) | |
tree | 4e04a1afb086ed133bd207931786800fcf8a3095 /libextra/opencdk/encrypt.c | |
parent | cf3b1fbffaa20c40d645ba1192549cb1bf59d0e7 (diff) | |
download | gnutls-b4eeab22afb5744b082248ed263281f7f603f323.tar.gz |
Opencdk library is being included if not found.
Diffstat (limited to 'libextra/opencdk/encrypt.c')
-rw-r--r-- | libextra/opencdk/encrypt.c | 1020 |
1 files changed, 1020 insertions, 0 deletions
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; +} + + |