a whole bunch of internal cleanups
[gnupg.git] / g10 / seckey-cert.c
1 /* seckey-cert.c -  secret key certifucate packet handling
2  *      Copyright (C) 1998 Free Software Foundation, Inc.
3  *
4  * This file is part of GNUPG.
5  *
6  * GNUPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GNUPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include "util.h"
27 #include "memory.h"
28 #include "packet.h"
29 #include "mpi.h"
30 #include "keydb.h"
31 #include "cipher.h"
32 #include "main.h"
33 #include "options.h"
34 #include "i18n.h"
35
36
37 static int
38 do_check( PKT_secret_cert *cert )
39 {
40     byte *buffer;
41     u16 csum=0;
42     int res;
43     unsigned nbytes;
44
45     if( cert->is_protected ) { /* remove the protection */
46         DEK *dek = NULL;
47         u32 keyid[2];
48         CIPHER_HANDLE cipher_hd=NULL;
49         PKT_secret_cert *save_cert;
50         char save_iv[8];
51
52         if( cert->protect.algo == CIPHER_ALGO_NONE )
53             BUG();
54         if( check_cipher_algo( cert->protect.algo ) )
55             return G10ERR_CIPHER_ALGO; /* unsupported protection algorithm */
56         keyid_from_skc( cert, keyid );
57         dek = passphrase_to_dek( keyid, cert->protect.algo,
58                                  &cert->protect.s2k, 0 );
59         cipher_hd = cipher_open( cert->protect.algo,
60                                  CIPHER_MODE_AUTO_CFB, 1);
61         cipher_setkey( cipher_hd, dek->key, dek->keylen );
62         cipher_setiv( cipher_hd, NULL );
63         m_free(dek); /* pw is in secure memory, so m_free() burns it */
64         save_cert = copy_secret_cert( NULL, cert );
65         memcpy(save_iv, cert->protect.iv, 8 );
66         cipher_decrypt( cipher_hd, cert->protect.iv, cert->protect.iv, 8 );
67         switch( cert->pubkey_algo ) {
68           case PUBKEY_ALGO_ELGAMAL:
69           case PUBKEY_ALGO_ELGAMAL_E:
70             /* FIXME: removed ELG knowledge from this function */
71             buffer = mpi_get_secure_buffer( cert->skey[3], &nbytes, NULL );
72             cipher_decrypt( cipher_hd, buffer, buffer, nbytes );
73             mpi_set_buffer( cert->skey[3], buffer, nbytes, 0 );
74             csum = checksum_mpi( cert->skey[3] );
75             m_free( buffer );
76             break;
77           case PUBKEY_ALGO_DSA:
78             buffer = mpi_get_secure_buffer( cert->skey[4], &nbytes, NULL );
79             cipher_decrypt( cipher_hd, buffer, buffer, nbytes );
80             mpi_set_buffer( cert->skey[4], buffer, nbytes, 0 );
81             csum = checksum_mpi( cert->skey[4] );
82             m_free( buffer );
83             break;
84         #ifdef HAVE_RSA_CIPHER
85           case PUBKEY_ALGO_RSA:
86           case PUBKEY_ALGO_RSA_E:
87           case PUBKEY_ALGO_RSA_S:
88             csum = 0;
89             #define X(a) do { \
90                 buffer = mpi_get_secure_buffer( cert->d.rsa.##a,     \
91                                                 &nbytes, NULL );     \
92                 csum += checksum_u16( nbytes*8 );                    \
93                 cipher_decrypt( cipher_hd, buffer, buffer, nbytes ); \
94                 csum += checksum( buffer, nbytes );                  \
95                 mpi_set_buffer(cert->d.rsa.##a, buffer, nbytes, 0 ); \
96                 m_free( buffer );                                    \
97                } while(0)
98             X(d);
99             X(p);
100             X(q);
101             X(u);
102             #undef X
103             break;
104         #endif /* HAVE_RSA_CIPHER */
105
106           default: BUG();
107         }
108         cipher_close( cipher_hd );
109         /* now let's see whether we have used the right passphrase */
110         if( csum != cert->csum ) {
111             if( csum != cert->csum ) {
112                 copy_secret_cert( cert, save_cert );
113                 free_secret_cert( save_cert );
114                 memcpy( cert->protect.iv, save_iv, 8 );
115                 return G10ERR_BAD_PASS;
116             }
117         }
118
119         res = pubkey_check_secret_key( cert->pubkey_algo, cert->skey );
120         if( res ) {
121             copy_secret_cert( cert, save_cert );
122             free_secret_cert( save_cert );
123             memcpy( cert->protect.iv, save_iv, 8 );
124             return G10ERR_BAD_PASS;
125         }
126         free_secret_cert( save_cert );
127         cert->is_protected = 0;
128     }
129     else { /* not protected */
130         switch( cert->pubkey_algo ) {
131           case PUBKEY_ALGO_ELGAMAL_E:
132           case PUBKEY_ALGO_ELGAMAL:
133             csum = checksum_mpi( cert->skey[3] );
134             break;
135           case PUBKEY_ALGO_DSA:
136             csum = checksum_mpi( cert->skey[4] );
137             break;
138         #ifdef HAVE_RSA_CIPHER
139           case PUBKEY_ALGO_RSA_E:
140           case PUBKEY_ALGO_RSA_S:
141           case PUBKEY_ALGO_RSA:
142             csum =0;
143             buffer = mpi_get_buffer( cert->d.rsa.rsa_d, &nbytes, NULL );
144             csum += checksum_u16( nbytes*8 );
145             csum += checksum( buffer, nbytes );
146             m_free( buffer );
147             buffer = mpi_get_buffer( cert->d.rsa.rsa_p, &nbytes, NULL );
148             csum += checksum_u16( nbytes*8 );
149             csum += checksum( buffer, nbytes );
150             m_free( buffer );
151             buffer = mpi_get_buffer( cert->d.rsa.rsa_q, &nbytes, NULL );
152             csum += checksum_u16( nbytes*8 );
153             csum += checksum( buffer, nbytes );
154             m_free( buffer );
155             buffer = mpi_get_buffer( cert->d.rsa.rsa_u, &nbytes, NULL );
156             csum += checksum_u16( nbytes*8 );
157             csum += checksum( buffer, nbytes );
158             m_free( buffer );
159             break;
160         #endif
161           default: BUG();
162         }
163         if( csum != cert->csum )
164             return G10ERR_CHECKSUM;
165     }
166
167     return 0;
168 }
169
170
171
172 /****************
173  * Check the secret key certificate
174  * Ask up to 3 times for a correct passphrase
175  */
176 int
177 check_secret_key( PKT_secret_cert *cert )
178 {
179     int rc = G10ERR_BAD_PASS;
180     int i;
181
182     for(i=0; i < 3 && rc == G10ERR_BAD_PASS; i++ ) {
183         if( i )
184             log_error(_("Invalid passphrase; please try again ...\n"));
185         switch( cert->pubkey_algo ) {
186           case PUBKEY_ALGO_ELGAMAL_E:
187           case PUBKEY_ALGO_ELGAMAL:
188           case PUBKEY_ALGO_DSA:
189             rc = do_check( cert );
190           #if 0 /* set to 1 to enable the workaround */
191             if( rc == G10ERR_BAD_PASS && cert->is_protected
192                 && cert->protect.algo == CIPHER_ALGO_BLOWFISH
193                 && cert->pubkey_algo != PUBKEY_ALGO_ELGAMAL ) {
194                 /* Workaround for a bug in 0.2.16 which still used
195                  * a 160 bit key for BLOWFISH. */
196      log_info("trying workaround for 0.2.16 passphrase bug ...\n");
197      log_info("If you don't need this, uncomment it in g10/seckey-cert.c\n\n");
198                 cert->protect.algo = CIPHER_ALGO_BLOWFISH160;
199                 rc = do_check( cert );
200                 if( rc )
201                     rc = G10ERR_BAD_PASS;
202                 cert->protect.algo = CIPHER_ALGO_BLOWFISH;
203             }
204           #endif
205             break;
206         #ifdef HAVE_RSA_CIPHER
207           case PUBKEY_ALGO_RSA:
208           case PUBKEY_ALGO_RSA_E:
209           case PUBKEY_ALGO_RSA_S:
210             rc = do_check( cert );
211             break;
212         #endif
213           default: rc = G10ERR_PUBKEY_ALGO;
214         }
215         if( get_passphrase_fd() != -1 )
216             break;
217     }
218
219     return rc;
220 }
221
222 /****************
223  * check whether the secret key is protected.
224  * Returns: 0 not protected, -1 on error or the protection algorithm
225  */
226 int
227 is_secret_key_protected( PKT_secret_cert *cert )
228 {
229     return cert->is_protected? cert->protect.algo : 0;
230 }
231
232
233 static int
234 do_protect( void (*fnc)(CIPHER_HANDLE, byte *, byte *, unsigned),
235             CIPHER_HANDLE fnc_hd, PKT_secret_cert *cert )
236 {
237     byte *buffer;
238     unsigned nbytes;
239
240     switch( cert->pubkey_algo ) {
241       case PUBKEY_ALGO_ELGAMAL_E:
242       case PUBKEY_ALGO_ELGAMAL:
243         buffer = mpi_get_buffer( cert->skey[3], &nbytes, NULL );
244         (*fnc)( fnc_hd, buffer, buffer, nbytes );
245         mpi_set_buffer( cert->skey[3], buffer, nbytes, 0 );
246         m_free( buffer );
247         break;
248
249       case PUBKEY_ALGO_DSA:
250         buffer = mpi_get_buffer( cert->skey[4], &nbytes, NULL );
251         (*fnc)( fnc_hd, buffer, buffer, nbytes );
252         mpi_set_buffer( cert->skey[4], buffer, nbytes, 0 );
253         m_free( buffer );
254         break;
255
256       default: return G10ERR_PUBKEY_ALGO;
257     }
258     return 0;
259 }
260
261
262 /****************
263  * Protect the secret key certificate with the passphrase from DEK
264  */
265 int
266 protect_secret_key( PKT_secret_cert *cert, DEK *dek )
267 {
268     int rc=0;
269
270     if( !dek )
271         return 0;
272
273     if( !cert->is_protected ) { /* okay, apply the protection */
274         CIPHER_HANDLE cipher_hd=NULL;
275
276         if( check_cipher_algo( cert->protect.algo ) )
277             rc = G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */
278         else {
279             cipher_hd = cipher_open( cert->protect.algo,
280                                      CIPHER_MODE_AUTO_CFB, 1 );
281             cipher_setkey( cipher_hd, dek->key, dek->keylen );
282             cipher_setiv( cipher_hd, NULL );
283             cipher_encrypt( cipher_hd, cert->protect.iv, cert->protect.iv, 8 );
284             if( !do_protect( &cipher_encrypt, cipher_hd, cert ) )
285                 cert->is_protected = 1;
286             cipher_close( cipher_hd );
287         }
288     }
289     return rc;
290 }
291