summaryrefslogtreecommitdiff
path: root/libextra/opencdk/cipher.c
diff options
context:
space:
mode:
Diffstat (limited to 'libextra/opencdk/cipher.c')
-rw-r--r--libextra/opencdk/cipher.c468
1 files changed, 468 insertions, 0 deletions
diff --git a/libextra/opencdk/cipher.c b/libextra/opencdk/cipher.c
new file mode 100644
index 0000000000..cc4c6ce5c7
--- /dev/null
+++ b/libextra/opencdk/cipher.c
@@ -0,0 +1,468 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * cipher.c - Cipher filters
+ * Copyright (C) 2002, 2003 Timo Schulz
+ * Copyright (C) 1998-2001 Free Software Foundation
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * OpenCDK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <assert.h>
+#include <sys/stat.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "filters.h"
+#include "cipher.h"
+
+
+static void (*progress_cb)( void * hd, unsigned off, unsigned len );
+static void * progress_cb_value = NULL;
+
+
+static off_t
+fp_get_length( FILE * fp )
+{
+ struct stat statbuf;
+
+ if( fstat( fileno( fp ), &statbuf ) )
+ return (off_t)-1;
+ return statbuf.st_size;
+}
+
+
+static int
+hash_encode( void * opaque, FILE * in, FILE * out )
+{
+ md_filter_t * mfx = opaque;
+ byte buf[8192];
+ int nread;
+
+ if( !mfx )
+ return CDK_Inv_Value;
+
+ _cdk_log_debug( "hash filter: encode (algo=%d)\n", mfx->digest_algo );
+
+ if( !mfx->md ) {
+ mfx->md = cdk_md_open( mfx->digest_algo, 0 );
+ if( !mfx->md )
+ return CDK_Inv_Algo;
+ }
+
+ while( !feof( in ) ) {
+ nread = fread( buf, 1, sizeof buf-1, in );
+ if( !nread )
+ break;
+ cdk_md_write( mfx->md, buf, nread );
+ }
+
+ wipemem( buf, sizeof buf );
+ return 0;
+}
+
+
+int
+_cdk_filter_hash( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ if( ctl == STREAMCTL_READ )
+ return hash_encode( opaque, in, out );
+ else if( ctl == STREAMCTL_FREE ) {
+ md_filter_t * mfx = opaque;
+ if( mfx ) {
+ _cdk_log_debug( "free hash filter\n" );
+ cdk_md_close( mfx->md );
+ mfx->md = NULL;
+ return 0;
+ }
+ }
+ return CDK_Inv_Mode;
+}
+
+
+static int
+write_header( cipher_filter_t * cfx, FILE * out )
+{
+ struct cdk_pkt_encrypted_s ed;
+ CDK_PACKET pkt;
+ cdk_dek_t dek = cfx->dek;
+ byte temp[18];
+ size_t blocksize = 0;
+ int use_mdc = 0, nprefix;
+ int rc = 0;
+
+ blocksize = cdk_cipher_get_algo_blklen( dek->algo );
+ if( blocksize < 8 || blocksize > 16 )
+ return CDK_Inv_Algo;
+
+ use_mdc = dek->use_mdc;
+ if( blocksize != 8 )
+ use_mdc = 1; /* enabled by default for all 128-bit block cipher */
+
+ if( use_mdc && cfx->datalen )
+ cfx->datalen += 22;
+
+ memset( &ed, 0, sizeof ed );
+ if( !cfx->blkmode.on ) {
+ ed.len = cfx->datalen;
+ ed.extralen = blocksize + 2;
+ }
+
+ if( use_mdc ) {
+ ed.mdc_method = CDK_MD_SHA1;
+ cfx->mdc = cdk_md_open( CDK_MD_SHA1, 0 );
+ if( !cfx->mdc )
+ return CDK_Inv_Algo;
+ }
+
+ cdk_pkt_init( &pkt );
+ pkt.old_ctb = cfx->dek->rfc1991 && !cfx->blkmode.on? 1 : 0;
+ pkt.pkttype = use_mdc ? CDK_PKT_ENCRYPTED_MDC : CDK_PKT_ENCRYPTED;
+ pkt.pkt.encrypted = &ed;
+ rc = _cdk_pkt_write_fp( out, &pkt );
+ if( rc )
+ return rc;
+ nprefix = blocksize;
+ gcry_randomize( temp, nprefix, GCRY_STRONG_RANDOM );
+ temp[nprefix] = temp[nprefix - 2];
+ temp[nprefix + 1] = temp[nprefix - 1];
+ cfx->hd = cdk_cipher_open( dek->algo, use_mdc==0? 1 : 0,
+ dek->key, dek->keylen, NULL, 0 );
+ if( !cfx->hd )
+ return CDK_Inv_Algo;
+ if( cfx->mdc )
+ cdk_md_write( cfx->mdc, temp, nprefix + 2 );
+ rc = cdk_cipher_encrypt( cfx->hd, temp, temp, nprefix + 2 );
+ cdk_cipher_sync( cfx->hd );
+ if( !rc )
+ fwrite( temp, 1, nprefix+2, out );
+ return rc;
+}
+
+
+static int
+write_mdc_packet( FILE * out, cipher_filter_t * cfx )
+{
+ byte pktdata[22];
+ int dlen = cdk_md_get_algo_dlen( CDK_MD_SHA1 );
+ int rc;
+
+ if( !out || !cfx )
+ return CDK_Inv_Value;
+
+ if( dlen != 20 )
+ return CDK_Inv_Algo;
+ /* we must hash the prefix of the MDC packet here */
+ pktdata[0] = 0xd3;
+ pktdata[1] = 0x14;
+ cdk_md_putc( cfx->mdc, pktdata[0] );
+ cdk_md_putc( cfx->mdc, pktdata[1] );
+ cdk_md_final( cfx->mdc );
+ memcpy( pktdata + 2, cdk_md_read( cfx->mdc, CDK_MD_SHA1 ), dlen );
+ rc = cdk_cipher_encrypt( cfx->hd, pktdata, pktdata, dlen+2 );
+ if( !rc )
+ fwrite( pktdata, 1, dlen+2, out );
+ wipemem( pktdata, sizeof pktdata );
+ return rc;
+}
+
+
+static int
+num2bits( size_t n )
+{
+ int i;
+ if( !n )
+ return -1;
+ for( i = 0; n > 1; i++ )
+ n >>= 1;
+ return i;
+}
+
+
+static size_t
+pow2( size_t y )
+{
+ size_t x = 1, i;
+ for( i = 0; i < y; i++ )
+ x <<= 1;
+ return x;
+}
+
+
+static int
+write_partial_block( FILE * in, FILE * out, size_t * r_len,
+ cipher_filter_t * cfx )
+{
+ size_t n, nbytes;
+ int nread, rc;
+ byte buf[8193];
+
+ if( !out || !cfx )
+ return CDK_Inv_Value;
+
+ if( *r_len > 512 ) {
+ n = num2bits( *r_len );
+ nbytes = pow2( n );
+ fputc( 0xe0 | n, out );
+ (*r_len) -= nbytes;
+ }
+ else {
+ size_t pktlen = nbytes = *r_len;
+ if( pktlen < 192 )
+ fputc( pktlen, out );
+ else if( pktlen < 8384 ) {
+ pktlen -= 192;
+ fputc( (pktlen/256) + 192, out );
+ fputc( pktlen % 256, out );
+ }
+ (*r_len) -= nbytes;
+ }
+ while( nbytes > 0 ) {
+ nread = fread( buf, 1, sizeof buf-1, in );
+ if( !nread )
+ break;
+ if( cfx->mdc )
+ cdk_md_write( cfx->mdc, buf, nread );
+ rc = cdk_cipher_encrypt( cfx->hd, buf, buf, nread );
+ if( rc )
+ break;
+ nbytes -= nread;
+ fwrite( buf, 1, nread, out );
+ }
+ return 0;
+}
+
+
+static int
+cipher_encode_file( void * opaque, FILE * in, FILE * out )
+{
+ struct stat statbuf;
+ cipher_filter_t * cfx = opaque;
+ byte buf[8192];
+ size_t len, len2;
+ int rc = 0, nread;
+
+ if( !cfx || !in || !out )
+ return CDK_Inv_Value;
+
+ if( fstat( fileno( in ), &statbuf ) )
+ return CDK_File_Error;
+ len = len2 = statbuf.st_size;
+ while( !feof( in ) ) {
+ if( cfx->blkmode.on ) {
+ rc = write_partial_block( in, out, &len2, cfx );
+ if( rc )
+ break;
+ continue;
+ }
+ nread = fread( buf, 1, sizeof buf -1, in );
+ if( !nread )
+ break;
+ if( cfx->mdc )
+ cdk_md_write( cfx->mdc, buf, nread );
+ rc = cdk_cipher_encrypt( cfx->hd, buf, buf, nread );
+ if( rc )
+ break;
+ if( progress_cb )
+ progress_cb( progress_cb_value, ftell( in ), len );
+ fwrite( buf, 1, nread, out );
+ }
+ wipemem( buf, sizeof buf );
+ if( !rc && cfx->mdc )
+ rc = write_mdc_packet( out, cfx );
+ return rc;
+}
+
+
+static int
+read_header( cipher_filter_t * cfx, FILE * in )
+{
+ cdk_dek_t dek;
+ byte temp[32];
+ int blocksize, nprefix;
+ int i = 0, c = 0, rc = 0;
+
+ if( !cfx || !in )
+ return CDK_Inv_Value;
+
+ dek = cfx->dek;
+ blocksize = cdk_cipher_get_algo_blklen( dek->algo );
+ if( blocksize < 8 || blocksize > 16 )
+ return CDK_Inv_Algo;
+
+ nprefix = blocksize;
+ if( cfx->datalen && cfx->datalen < (nprefix + 2) )
+ return CDK_Inv_Value;
+ if( cfx->mdc_method ) {
+ cfx->mdc = cdk_md_open( cfx->mdc_method, 0 );
+ if( !cfx->mdc )
+ return CDK_Inv_Algo;
+ }
+ cfx->hd = cdk_cipher_open( dek->algo, cfx->mdc_method==0? 1 : 0,
+ dek->key, dek->keylen, NULL, 0 );
+ if( !cfx->hd )
+ return CDK_Inv_Algo;
+ for( i = 0; i < (nprefix + 2); i++ ) {
+ c = fgetc( in );
+ if( c == EOF )
+ return CDK_File_Error;
+ temp[i] = c;
+ }
+ rc = cdk_cipher_decrypt( cfx->hd, temp, temp, nprefix + 2 );
+ if( rc )
+ return rc;
+ cdk_cipher_sync( cfx->hd );
+ i = nprefix;
+ if( temp[i - 2] != temp[i] || temp[i - 1] != temp[i + 1] )
+ rc = CDK_Chksum_Error;
+ if( cfx->mdc )
+ cdk_md_write( cfx->mdc, temp, nprefix + 2 );
+ if( cfx->blkmode.on )
+ cfx->blkmode.size -= (nprefix + 2);
+ return rc;
+}
+
+
+static int
+finalize_mdc( cdk_md_hd_t md, const byte * buf, size_t nread )
+{
+ byte mdcbuf[20];
+ int dlen = cdk_md_get_algo_dlen( CDK_MD_SHA1 );
+ int rc = 0;
+
+ if( cdk_md_get_algo( md ) != CDK_MD_SHA1 || dlen != 20 )
+ return CDK_Inv_Algo;
+
+ if( buf[nread - dlen - 2] == 0xd3 && buf[nread - dlen - 1] == 0x14 ) {
+ cdk_md_write( md, buf, nread - dlen );
+ cdk_md_final( md );
+ memcpy( mdcbuf, cdk_md_read( md, 0 ), dlen );
+ if( memcmp( mdcbuf, buf + nread - dlen, dlen ) )
+ rc = CDK_Bad_MDC;
+ return rc;
+ }
+ wipemem( mdcbuf, sizeof mdcbuf );
+ return CDK_Inv_Packet;
+}
+
+
+static int
+cipher_decode_file( void * opaque, FILE * in, FILE * out )
+{
+ cipher_filter_t * cfx = opaque;
+ byte buf[8192];
+ int rc = 0, nread, nreq;
+
+ if( !cfx || !in || !out )
+ return CDK_Inv_Value;
+
+ while( !feof( in ) ) {
+ _cdk_log_debug( "partial on=%d size=%lu\n",
+ cfx->blkmode.on, cfx->blkmode.size );
+ nreq = cfx->blkmode.on? cfx->blkmode.size: sizeof buf-1;
+ nread = fread( buf, 1, nreq, in );
+ if( !nread )
+ break;
+ rc = cdk_cipher_decrypt( cfx->hd, buf, buf, nread );
+ if( rc )
+ break;
+ if( feof( in ) && cfx->mdc )
+ rc = finalize_mdc( cfx->mdc, buf, nread );
+ else if( cfx->mdc )
+ cdk_md_write( cfx->mdc, buf, nread );
+ fwrite( buf, 1, nread, out );
+ if( cfx->blkmode.on ) {
+ cfx->blkmode.size = _cdk_pkt_read_len( in, &cfx->blkmode.on );
+ if( cfx->blkmode.size == (size_t)EOF )
+ return CDK_Inv_Packet;
+ }
+ }
+ wipemem( buf, sizeof buf );
+ return rc;
+}
+
+
+int
+cipher_decode( void * opaque, FILE * in, FILE * out )
+{
+ cipher_filter_t * cfx = opaque;
+ int rc;
+
+ _cdk_log_debug( "cipher filter: decode\n" );
+
+ if( !cfx || !in || !out )
+ return CDK_Inv_Value;
+
+ rc = read_header( cfx, in );
+ if( !rc )
+ rc = cipher_decode_file( cfx, in, out );
+ return rc;
+}
+
+
+int
+cipher_encode( void * opaque, FILE * in, FILE * out )
+{
+ cipher_filter_t * cfx = opaque;
+ int rc;
+
+ _cdk_log_debug( "cipher filter: encode\n" );
+
+ if( !cfx || !in || !out )
+ return CDK_Inv_Value;
+
+ cfx->datalen = fp_get_length( in );
+ if( cfx->datalen < 8192 && cfx->blkmode.on )
+ cfx->blkmode.on = 0;
+ rc = write_header( cfx, out );
+ if( !rc )
+ rc = cipher_encode_file( cfx, in, out );
+ return rc;
+}
+
+
+int
+_cdk_filter_cipher( void * opaque, int ctl, FILE * in, FILE * out )
+{
+ if( ctl == STREAMCTL_READ )
+ return cipher_decode( opaque, in, out );
+ else if( ctl == STREAMCTL_WRITE )
+ return cipher_encode( opaque, in, out );
+ else if( ctl == STREAMCTL_FREE ) {
+ cipher_filter_t * cfx = opaque;
+ if( cfx ) {
+ _cdk_log_debug( "free cipher filter\n" );
+ cdk_md_close( cfx->mdc );
+ cfx->mdc = NULL;
+ cdk_cipher_close( cfx->hd );
+ cfx->hd = NULL;
+ }
+ }
+ return CDK_Inv_Mode;
+}
+
+
+void
+cdk_set_progress_handler( void (*cb)(void * hd, unsigned off, unsigned size),
+ void * cb_value )
+{
+ progress_cb = cb;
+ progress_cb_value = cb_value;
+}
+