*** empty log message ***
[gnutls.git] / libextra / opencdk / sign.c
blob46e1a905096d8315d9fbe19a1a0a747dc4a49207
1 /* -*- Mode: C; c-file-style: "bsd" -*-
2 * sign.c - Signing routines
3 * Copyright (C) 2006 Free Software Foundation, Inc.
4 * Copyright (C) 2002, 2003 Timo Schulz
6 * This file is part of OpenCDK.
8 * OpenCDK is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * OpenCDK is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with OpenCDK; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26 #include <stdio.h>
27 #include <time.h>
28 #include <string.h>
30 #include "opencdk.h"
31 #include "main.h"
32 #include "packet.h"
33 #include "filters.h"
34 #include "stream.h"
37 static int file_clearsign( cdk_ctx_t, cdk_strlist_t,
38 const char *, const char * );
39 static int stream_clearsign( cdk_ctx_t, cdk_stream_t,
40 cdk_stream_t, cdk_strlist_t );
43 static void
44 calc_subpkt_size( cdk_pkt_signature_t sig )
46 size_t nbytes;
48 if( sig->hashed ) {
49 _cdk_subpkt_get_array( sig->hashed, 1, &nbytes );
50 sig->hashed_size = nbytes;
52 if( sig->unhashed ) {
53 _cdk_subpkt_get_array( sig->unhashed, 1, &nbytes );
54 sig->unhashed_size = nbytes;
59 int
60 _cdk_sig_hash_for( int pkalgo, int pktver )
62 if( is_DSA( pkalgo ) )
63 return CDK_MD_SHA1;
64 else if( is_RSA( pkalgo ) && pktver < 4 )
65 return CDK_MD_MD5;
66 return CDK_MD_SHA1; /* default message digest */
70 int
71 _cdk_sig_create( cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig )
73 cdk_subpkt_t node;
74 byte buf[8];
76 if( !sig )
77 return CDK_Inv_Value;
79 if( pk ) {
80 if( !sig->version )
81 sig->version = pk->version;
82 sig->pubkey_algo = pk->pubkey_algo;
83 sig->digest_algo = _cdk_sig_hash_for( pk->pubkey_algo, pk->version );
84 cdk_pk_get_keyid( pk, sig->keyid );
86 sig->timestamp = _cdk_timestamp( );
87 if( sig->version == 3 )
88 return 0;
90 sig->hashed = sig->unhashed = NULL;
92 _cdk_u32tobuf( sig->keyid[0], buf );
93 _cdk_u32tobuf( sig->keyid[1], buf + 4 );
94 node = cdk_subpkt_new( 8 );
95 if( node )
96 cdk_subpkt_init( node, CDK_SIGSUBPKT_ISSUER, buf, 8 );
97 sig->unhashed = node;
99 _cdk_u32tobuf( sig->timestamp, buf );
100 node = cdk_subpkt_new( 4 );
101 if( node ) {
102 cdk_subpkt_init( node, CDK_SIGSUBPKT_SIG_CREATED, buf, 4 );
103 sig->hashed = node;
106 if( sig->expiredate ) {
107 u32 u = sig->expiredate - sig->timestamp;
108 _cdk_u32tobuf( u, buf );
109 node = cdk_subpkt_new( 4 );
110 if( node ) {
111 cdk_subpkt_init( node, CDK_SIGSUBPKT_SIG_EXPIRE, buf, 4 );
112 cdk_subpkt_add( sig->hashed, node );
115 calc_subpkt_size( sig );
116 return 0;
121 _cdk_sig_complete( cdk_pkt_signature_t sig, cdk_pkt_seckey_t sk,
122 cdk_md_hd_t md )
124 byte digest[24];
126 if( !sig || !sk || !md )
127 return CDK_Inv_Value;
129 calc_subpkt_size( sig );
130 _cdk_hash_sig_data( sig, md );
131 cdk_md_final( md );
132 memcpy( digest, cdk_md_read( md, sig->digest_algo),
133 cdk_md_get_algo_dlen( sig->digest_algo ) );
134 return cdk_pk_sign( sk, sig, digest );
138 static int
139 write_literal( cdk_stream_t inp, cdk_stream_t out )
141 cdk_packet_t pkt;
142 cdk_pkt_literal_t pt;
143 const char * s = _cdk_stream_get_fname( inp );
144 int rc;
146 if( !inp || !out )
147 return CDK_Inv_Value;
149 pkt = cdk_calloc( 1, sizeof *pkt );
150 if( !pkt )
151 return CDK_Out_Of_Core;
152 cdk_stream_seek( inp, 0 );
153 if( !s )
154 s = "_CONSOLE";
155 pt = cdk_calloc( 1, sizeof *pt + strlen( s ) + 1 );
156 if( !pt )
157 return CDK_Out_Of_Core;
158 pt->len = cdk_stream_get_length( inp );
159 pt->mode = 'b';
160 pt->timestamp = _cdk_timestamp( );
161 pt->namelen = strlen( s );
162 pt->buf = inp;
163 strcpy( pt->name, s );
164 pkt->pkttype = CDK_PKT_LITERAL;
165 pkt->pkt.literal = pt;
166 rc = cdk_pkt_write( out, pkt );
167 cdk_free( pt );
168 cdk_free( pkt );
169 return rc;
173 static int
174 write_pubkey_enc_list( cdk_ctx_t hd, cdk_stream_t out, cdk_strlist_t remusr )
176 cdk_keylist_t pkl;
177 int rc;
179 if( !hd || !out )
180 return CDK_Inv_Value;
182 rc = cdk_pklist_build( &pkl, hd->db.pub, remusr, PK_USAGE_ENCR );
183 if( rc )
184 return rc;
186 cdk_dek_free( hd->dek );
187 rc = cdk_dek_new( &hd->dek );
188 if( !rc )
189 rc = cdk_dek_set_cipher( hd->dek, cdk_pklist_select_algo( pkl, 1 ) );
190 if( !rc )
191 rc = cdk_dek_set_key( hd->dek, NULL, 0 );
192 if( !rc ) {
193 cdk_dek_set_mdc_flag( hd->dek, cdk_pklist_use_mdc( pkl ) );
194 rc = cdk_pklist_encrypt( pkl, hd->dek, out );
196 cdk_pklist_release( pkl );
197 return rc;
201 static int
202 sig_get_version( cdk_ctx_t hd, cdk_keylist_t kl )
204 cdk_keylist_t l;
206 if( hd && hd->opt.compat )
207 return 3;
209 for( l = kl; l; l = l->next ) {
210 if( (l->type == CDK_PKT_PUBLIC_KEY && l->key.pk->version == 3)
211 || (l->type == CDK_PKT_SECRET_KEY && l->key.sk->version == 3))
212 return 3;
214 return 4;
219 * cdk_stream_sign:
220 * @hd: session handle
221 * @inp: input stream
222 * @out: output stream
223 * @locusr: local user list for signing
224 * @remusr: remote user list for encrypting
225 * @encryptflag: shall the output be encrypted? (1/0)
226 * @sigmode: signature mode
228 * Sign the data from the STREAM @inp.
230 cdk_error_t
231 cdk_stream_sign( cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out,
232 cdk_strlist_t locusr, cdk_strlist_t remusr,
233 int encryptflag, int sigmode )
235 cdk_keylist_t list;
236 cdk_pkt_seckey_t sk;
237 md_filter_t * mfx;
238 int sigver, digest_algo;
239 int rc, detached = sigmode == CDK_SIGMODE_DETACHED;
241 if( !hd )
242 return CDK_Inv_Value;
243 if( detached && encryptflag )
244 return CDK_Inv_Mode;
246 if( sigmode == CDK_SIGMODE_CLEAR )
247 return stream_clearsign( hd, inp, out, locusr );
249 rc = cdk_sklist_build( &list, hd->db.sec, hd, locusr, 1, PK_USAGE_SIGN );
250 if( rc )
251 return rc;
253 sk = list->key.sk;
254 digest_algo = _cdk_sig_hash_for( sk->pubkey_algo, sk->version );
255 if( cdk_handle_control( hd, CDK_CTLF_GET, CDK_CTL_FORCE_DIGEST ) )
256 digest_algo = hd->digest_algo;
258 if( hd->opt.armor )
259 cdk_stream_set_armor_flag( out, detached? CDK_ARMOR_SIGNATURE : 0 );
261 if( encryptflag ) {
262 cdk_stream_set_cache( out, 1 );
263 rc = write_pubkey_enc_list( hd, out, remusr );
264 if( rc ) {
265 cdk_sklist_release( list );
266 return rc;
268 cdk_stream_set_cipher_flag( out, hd->dek, hd->dek->use_mdc );
269 cdk_stream_set_cache( out, 0 );
272 cdk_stream_set_hash_flag( inp, digest_algo );
273 /* kick off the filter */
274 sigver = sig_get_version( hd, list );
275 cdk_stream_read( inp, NULL, 0 );
276 mfx = _cdk_stream_get_opaque( inp, fHASH );
277 if( mfx && mfx->md ) {
278 if( sigver == 3 ) {
279 rc = cdk_sklist_write( list, out, mfx->md, 0x00, 0x03 );
280 if( !rc && !detached )
281 rc = write_literal( inp, out );
283 else {
284 if( !detached ) {
285 rc = cdk_sklist_write_onepass( list, out, 0x00, digest_algo );
286 if( !rc )
287 rc = write_literal( inp, out );
289 if( !rc )
290 rc = cdk_sklist_write( list, out, mfx->md, 0x00, 0x04 );
293 cdk_sklist_release( list );
294 return rc;
299 * cdk_file_sign:
300 * @locusr: List of userid which should be used for signing
301 * @remusr: If encrypt is valid, the list of recipients
302 * @file: Name of the input file
303 * @output: Name of the output file
304 * @sigmode: Signature mode
305 * @encryptflag: enable sign and encrypt
307 * Sign a file.
309 cdk_error_t
310 cdk_file_sign( cdk_ctx_t hd, cdk_strlist_t locusr, cdk_strlist_t remusr,
311 const char * file, const char * output,
312 int sigmode, int encryptflag )
314 cdk_stream_t inp = NULL, out = NULL;
315 int rc = 0;
317 if( !file || !output )
318 return CDK_Inv_Value;
319 if( encryptflag && !remusr )
320 return CDK_Inv_Value;
321 if( (sigmode != CDK_SIGMODE_NORMAL) && encryptflag )
322 return CDK_Inv_Mode;
323 if( !remusr && !locusr )
324 return CDK_Inv_Value;
325 if( sigmode == CDK_SIGMODE_CLEAR )
326 return file_clearsign( hd, locusr, file, output );
328 rc = cdk_stream_open( file, &inp );
329 if( rc )
330 return rc;
332 if( hd->opt.armor || encryptflag )
333 rc = cdk_stream_new( output, &out );
334 else
335 rc = cdk_stream_create( output, &out );
336 if( rc ) {
337 cdk_stream_close( inp );
338 return rc;
340 rc = cdk_stream_sign( hd, inp, out, locusr, remusr, encryptflag, sigmode );
342 cdk_stream_close( inp );
343 cdk_stream_close( out );
344 return rc;
348 static void
349 put_hash_line( cdk_stream_t out, int digest_algo, int is_v4 )
351 const char * s = NULL;
353 if( !is_v4 ) {
354 cdk_stream_putc( out, '\n' );
355 return;
358 switch( digest_algo ) {
359 case CDK_MD_MD2 : s = "Hash: MD2\n\n"; break;
360 case CDK_MD_MD5 : s = "Hash: MD5\n\n"; break;
361 case CDK_MD_SHA1 : s = "Hash: SHA1\n\n"; break;
362 case CDK_MD_RMD160 : s = "Hash: RIPEMD160\n\n"; break;
363 case CDK_MD_SHA256 : s = "Hash: SHA256\n\n"; break;
364 default : s = "Hash: SHA1\n\n"; break;
366 _cdk_stream_puts( out, s );
370 void
371 _cdk_trim_string( char * s, int canon )
373 while( s && *s &&( s[strlen( s )-1] == '\t'
374 || s[strlen( s )-1] == '\r'
375 || s[strlen( s )-1] == '\n'
376 || s[strlen( s )-1] == ' '))
377 s[strlen( s ) -1] = '\0';
378 if( canon )
379 strcat( s, "\r\n" );
383 static int
384 stream_clearsign( cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out,
385 cdk_strlist_t locusr )
387 cdk_md_hd_t md = NULL;
388 cdk_keylist_t list;
389 cdk_stream_t tmp;
390 cdk_pkt_seckey_t sk;
391 const char * s;
392 char buf[1024+2];
393 int digest_algo, sigver;
394 int rc, nread;
396 rc = cdk_sklist_build( &list, hd->db.sec, hd, locusr, 1, PK_USAGE_SIGN );
397 if( rc )
398 return rc;
400 sk = list->key.sk;
401 digest_algo = _cdk_sig_hash_for( sk->pubkey_algo, sk->version );
402 md = cdk_md_open( digest_algo, 0 );
403 if( !md ) {
404 cdk_sklist_release( list );
405 return CDK_Gcry_Error;
408 s = _cdk_armor_get_lineend( );
409 strcpy( buf, "-----BEGIN PGP SIGNED MESSAGE-----" );
410 strcat( buf, s );
411 _cdk_stream_puts( out, buf );
412 put_hash_line( out, digest_algo, sk->version == 4 );
414 while( !cdk_stream_eof( inp ) ) {
415 nread = _cdk_stream_gets( inp, buf, sizeof buf-1 );
416 if( !nread )
417 break;
418 _cdk_trim_string( buf, 1 );
419 cdk_md_write( md, buf, strlen( buf ) );
420 if( buf[0] == '-' ) {
421 memmove( &buf[2], buf, nread + 1 );
422 buf[1] = ' ';
424 if( strlen( s ) == 1 ) {
425 buf[strlen( buf ) - 1] = '\0';
426 buf[strlen( buf ) - 1] = '\n';
428 _cdk_stream_puts( out, buf );
430 _cdk_stream_puts( out, s );
431 tmp = cdk_stream_tmp( );
432 if( !tmp ) {
433 rc = CDK_Out_Of_Core;
434 goto leave;
436 cdk_stream_tmp_set_mode( tmp, 1 );
437 cdk_stream_set_armor_flag( tmp, CDK_ARMOR_SIGNATURE );
439 sigver = sig_get_version( hd, list );
440 rc = cdk_sklist_write( list, tmp, md, 0x01, sigver );
441 if( rc ) {
442 cdk_stream_close( tmp );
443 goto leave;
446 rc = cdk_stream_flush( tmp );
447 if( rc )
448 goto leave;
450 while( !cdk_stream_eof( tmp ) ) {
451 nread = cdk_stream_read( tmp, buf, sizeof buf-1 );
452 if( !nread )
453 break;
454 cdk_stream_write( out, buf, nread );
456 cdk_stream_close( tmp );
458 leave:
459 cdk_md_close( md );
460 cdk_sklist_release( list );
461 return rc;
465 static int
466 file_clearsign( cdk_ctx_t hd, cdk_strlist_t locusr,
467 const char * file, const char * output )
469 cdk_stream_t inp = NULL, out = NULL;
470 int rc;
472 if( !locusr || !file || !output )
473 return CDK_Inv_Value;
474 if( !hd->opt.overwrite && _cdk_check_file( output ) )
475 return CDK_Inv_Mode;
477 rc = cdk_stream_open( file, &inp );
478 if( rc )
479 return rc;
481 rc = cdk_stream_create( output, &out );
482 if( rc ) {
483 cdk_stream_close( inp );
484 return rc;
487 rc = stream_clearsign( hd, inp, out, locusr );
489 cdk_stream_close( inp );
490 cdk_stream_close( out );
492 return rc;