mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-09 12:54:23 +01:00
c0c2c58054
to libgcrypt functions, using shared error codes from libgpg-error, replacing the old functions we used to have in ../util by those in ../jnlib and ../common, renaming the malloc functions and a couple of types. Note, that not all changes are listed below becuause they are too similar and done at far too many places. As of today the code builds using the current libgcrypt from CVS but it is very unlikely that it actually works.
450 lines
15 KiB
C
450 lines
15 KiB
C
/* seckey-cert.c - secret key certificate packet handling
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002,
|
|
* 2003 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* GnuPG 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.
|
|
*
|
|
* GnuPG 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "gpg.h"
|
|
#include "util.h"
|
|
#include "memory.h"
|
|
#include "packet.h"
|
|
#include "mpi.h"
|
|
#include "keydb.h"
|
|
#include "cipher.h"
|
|
#include "main.h"
|
|
#include "options.h"
|
|
#include "i18n.h"
|
|
#include "status.h"
|
|
#include "pkglue.h"
|
|
|
|
static int
|
|
do_check( PKT_secret_key *sk, const char *tryagain_text, int mode,
|
|
int *canceled )
|
|
{
|
|
byte *buffer;
|
|
u16 csum=0;
|
|
int i, res;
|
|
unsigned nbytes;
|
|
gpg_error_t rc;
|
|
|
|
if( sk->is_protected ) { /* remove the protection */
|
|
DEK *dek = NULL;
|
|
u32 keyid[4]; /* 4! because we need two of them */
|
|
CIPHER_HANDLE cipher_hd=NULL;
|
|
PKT_secret_key *save_sk;
|
|
|
|
if( sk->protect.s2k.mode == 1001 ) {
|
|
log_info(_("secret key parts are not available\n"));
|
|
return GPG_ERR_GENERAL;
|
|
}
|
|
if( sk->protect.algo == CIPHER_ALGO_NONE )
|
|
BUG();
|
|
if( openpgp_cipher_test_algo( sk->protect.algo ) ) {
|
|
log_info(_("protection algorithm %d%s is not supported\n"),
|
|
sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" );
|
|
if (sk->protect.algo==CIPHER_ALGO_IDEA)
|
|
{
|
|
write_status (STATUS_RSA_OR_IDEA);
|
|
idea_cipher_warn (0);
|
|
}
|
|
return GPG_ERR_CIPHER_ALGO;
|
|
}
|
|
keyid_from_sk( sk, keyid );
|
|
keyid[2] = keyid[3] = 0;
|
|
if( !sk->is_primary ) {
|
|
keyid[2] = sk->main_keyid[0];
|
|
keyid[3] = sk->main_keyid[1];
|
|
}
|
|
dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo,
|
|
&sk->protect.s2k, mode,
|
|
tryagain_text, canceled );
|
|
if (!dek && canceled && *canceled)
|
|
return GPG_ERR_GENERAL;
|
|
|
|
rc = gcry_cipher_open (&cipher_hd, sk->protect.algo,
|
|
GCRY_CIPHER_MODE_CFB,
|
|
GCRY_CIPHER_SECURE
|
|
| (sk->protect.algo >= 100 ?
|
|
0 : GCRY_CIPHER_ENABLE_SYNC));
|
|
if (rc)
|
|
log_fatal ("cipher open failed: %s\n", gpg_strerror (rc) );
|
|
|
|
rc = gcry_cipher_setkey (cipher_hd, dek->key, dek->keylen);
|
|
if (rc)
|
|
log_fatal ("set key failed: %s\n", gpg_strerror (rc) );
|
|
|
|
xfree (dek);
|
|
save_sk = copy_secret_key( NULL, sk );
|
|
gcry_cipher_setiv (cipher_hd, sk->protect.iv, sk->protect.ivlen);
|
|
csum = 0;
|
|
if( sk->version >= 4 ) {
|
|
int ndata;
|
|
unsigned int ndatabits;
|
|
byte *p, *data;
|
|
u16 csumc = 0;
|
|
|
|
i = pubkey_get_npkey(sk->pubkey_algo);
|
|
assert( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE ));
|
|
p = gcry_mpi_get_opaque( sk->skey[i], &ndatabits );
|
|
ndata = (ndatabits+7)/8;
|
|
if ( ndata > 1 )
|
|
csumc = p[ndata-2] << 8 | p[ndata-1];
|
|
data = gcry_xmalloc_secure ( ndata );
|
|
gcry_cipher_decrypt( cipher_hd, data, ndata, p, ndata );
|
|
gcry_mpi_release ( sk->skey[i] ); sk->skey[i] = NULL ;
|
|
p = data;
|
|
if (sk->protect.sha1chk) {
|
|
/* This is the new SHA1 checksum method to detect
|
|
tampering with the key as used by the Klima/Rosa
|
|
attack */
|
|
sk->csum = 0;
|
|
csum = 1;
|
|
if( ndata < 20 )
|
|
log_error("not enough bytes for SHA-1 checksum\n");
|
|
else {
|
|
gcry_md_hd_t h;
|
|
|
|
if ( gcry_md_open (&h, DIGEST_ALGO_SHA1, 1))
|
|
BUG(); /* algo not available */
|
|
gcry_md_write (h, data, ndata - 20);
|
|
gcry_md_final (h);
|
|
if (!memcmp (gcry_md_read (h, DIGEST_ALGO_SHA1),
|
|
data + ndata - 20, 20) ) {
|
|
/* digest does match. We have to keep the old
|
|
style checksum in sk->csum, so that the
|
|
test used for unprotected keys does work.
|
|
This test gets used when we are adding new
|
|
keys. */
|
|
sk->csum = csum = checksum (data, ndata-20);
|
|
}
|
|
gcry_md_close (h);
|
|
}
|
|
}
|
|
else {
|
|
if( ndata < 2 ) {
|
|
log_error("not enough bytes for checksum\n");
|
|
sk->csum = 0;
|
|
csum = 1;
|
|
}
|
|
else {
|
|
csum = checksum( data, ndata-2);
|
|
sk->csum = data[ndata-2] << 8 | data[ndata-1];
|
|
if ( sk->csum != csum ) {
|
|
/* This is a PGP 7.0.0 workaround */
|
|
sk->csum = csumc; /* take the encrypted one */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* must check it here otherwise the mpi_read_xx would fail
|
|
because the length may have an arbitrary value */
|
|
if( sk->csum == csum ) {
|
|
for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
|
|
nbytes = ndata;
|
|
assert( gcry_is_secure( p ) );
|
|
res = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_PGP,
|
|
p, &nbytes);
|
|
if( res )
|
|
log_bug ("gcry_mpi_scan failed in do_check: %s\n",
|
|
gpg_strerror (res));
|
|
ndata -= nbytes;
|
|
p += nbytes;
|
|
}
|
|
/* Note: at this point ndata should be 2 for a simple
|
|
checksum or 20 for the sha1 digest */
|
|
}
|
|
xfree (data);
|
|
}
|
|
else {
|
|
for(i=pubkey_get_npkey(sk->pubkey_algo);
|
|
i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
|
|
byte *p;
|
|
int ndata;
|
|
unsigned int ndatabits;
|
|
|
|
assert( gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE));
|
|
p = gcry_mpi_get_opaque( sk->skey[i], &ndatabits );
|
|
ndata = (ndatabits+7)/8;
|
|
assert (ndata >= 2);
|
|
assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2);
|
|
buffer = gcry_xmalloc_secure (ndata);
|
|
gcry_cipher_sync (cipher_hd);
|
|
buffer[0] = p[0];
|
|
buffer[1] = p[1];
|
|
gcry_cipher_decrypt (cipher_hd, buffer+2, ndata-2,
|
|
p+2, ndata-2);
|
|
csum += checksum (buffer, ndata);
|
|
gcry_mpi_release (sk->skey[i]);
|
|
res = gcry_mpi_scan( &sk->skey[i], GCRYMPI_FMT_USG,
|
|
buffer, &ndata );
|
|
if( res )
|
|
log_bug ("gcry_mpi_scan failed in do_check: %s\n",
|
|
gpg_strerror (res));
|
|
|
|
assert (sk->skey[i]);
|
|
xfree (buffer);
|
|
/* csum += checksum_mpi (sk->skey[i]); */
|
|
}
|
|
}
|
|
gcry_cipher_close (cipher_hd);
|
|
/* now let's see whether we have used the right passphrase */
|
|
if( csum != sk->csum ) {
|
|
copy_secret_key( sk, save_sk );
|
|
passphrase_clear_cache ( keyid, sk->pubkey_algo );
|
|
free_secret_key( save_sk );
|
|
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
|
|
}
|
|
/* the checksum may fail, so we also check the key itself */
|
|
#warning fixme - we need to reenable this
|
|
/* res = pubkey_check_secret_key( sk->pubkey_algo, sk->skey ); */
|
|
/* if( res ) { */
|
|
/* copy_secret_key( sk, save_sk ); */
|
|
/* passphrase_clear_cache ( keyid, sk->pubkey_algo ); */
|
|
/* free_secret_key( save_sk ); */
|
|
/* return gpg_error (GPG_ERR_BAD_PASSPHRASE); */
|
|
/* } */
|
|
free_secret_key( save_sk );
|
|
sk->is_protected = 0;
|
|
}
|
|
else { /* not protected, assume it is okay if the checksum is okay */
|
|
csum = 0;
|
|
for(i=pubkey_get_npkey(sk->pubkey_algo);
|
|
i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
|
|
csum += checksum_mpi( sk->skey[i] );
|
|
}
|
|
if( csum != sk->csum )
|
|
return GPG_ERR_CHECKSUM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* Check the secret key
|
|
* Ask up to 3 (or n) times for a correct passphrase
|
|
* If n is negative, disable the key info prompt and make n=abs(n)
|
|
*/
|
|
int
|
|
check_secret_key( PKT_secret_key *sk, int n )
|
|
{
|
|
int rc = gpg_error (GPG_ERR_BAD_PASSPHRASE);
|
|
int i,mode;
|
|
|
|
if(n<0)
|
|
{
|
|
n=abs(n);
|
|
mode=1;
|
|
}
|
|
else
|
|
mode=0;
|
|
|
|
if( n < 1 )
|
|
n = (opt.batch && !opt.use_agent)? 1 : 3; /* use the default value */
|
|
|
|
for(i=0; i < n && gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE; i++ ) {
|
|
int canceled = 0;
|
|
const char *tryagain = NULL;
|
|
if (i) {
|
|
tryagain = N_("Invalid passphrase; please try again");
|
|
log_info (_("%s ...\n"), _(tryagain));
|
|
}
|
|
rc = do_check( sk, tryagain, mode, &canceled );
|
|
if( gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
|
|
&& is_status_enabled() ) {
|
|
u32 kid[2];
|
|
char buf[50];
|
|
|
|
keyid_from_sk( sk, kid );
|
|
sprintf(buf, "%08lX%08lX", (ulong)kid[0], (ulong)kid[1]);
|
|
write_status_text( STATUS_BAD_PASSPHRASE, buf );
|
|
}
|
|
if( have_static_passphrase() || canceled)
|
|
break;
|
|
}
|
|
|
|
if( !rc )
|
|
write_status( STATUS_GOOD_PASSPHRASE );
|
|
|
|
return rc;
|
|
}
|
|
|
|
/****************
|
|
* check whether the secret key is protected.
|
|
* Returns: 0 not protected, -1 on error or the protection algorithm
|
|
*/
|
|
int
|
|
is_secret_key_protected( PKT_secret_key *sk )
|
|
{
|
|
return sk->is_protected? sk->protect.algo : 0;
|
|
}
|
|
|
|
|
|
|
|
/****************
|
|
* Protect the secret key with the passphrase from DEK
|
|
*/
|
|
int
|
|
protect_secret_key( PKT_secret_key *sk, DEK *dek )
|
|
{
|
|
int i,j, rc = 0;
|
|
byte *buffer;
|
|
unsigned nbytes;
|
|
u16 csum;
|
|
|
|
if( !dek )
|
|
return 0;
|
|
|
|
if( !sk->is_protected ) { /* okay, apply the protection */
|
|
CIPHER_HANDLE cipher_hd=NULL;
|
|
|
|
if( openpgp_cipher_test_algo( sk->protect.algo ) )
|
|
{
|
|
rc = gpg_error (GPG_ERR_CIPHER_ALGO); /* unsupport
|
|
protection
|
|
algorithm */
|
|
}
|
|
else {
|
|
print_cipher_algo_note( sk->protect.algo );
|
|
rc = gcry_cipher_open (&cipher_hd, sk->protect.algo,
|
|
GCRY_CIPHER_MODE_CFB,
|
|
GCRY_CIPHER_SECURE
|
|
| (sk->protect.algo >= 100 ?
|
|
0 : GCRY_CIPHER_ENABLE_SYNC) );
|
|
if (rc)
|
|
BUG();
|
|
if( gcry_cipher_setkey( cipher_hd, dek->key, dek->keylen ) )
|
|
log_info(_("WARNING: Weak key detected"
|
|
" - please change passphrase again.\n"));
|
|
sk->protect.ivlen = gcry_cipher_get_algo_blklen(sk->protect.algo);
|
|
assert( sk->protect.ivlen <= DIM(sk->protect.iv) );
|
|
if( sk->protect.ivlen != 8 && sk->protect.ivlen != 16 )
|
|
BUG(); /* yes, we are very careful */
|
|
gcry_randomize (sk->protect.iv, sk->protect.ivlen,
|
|
GCRY_STRONG_RANDOM);
|
|
gcry_cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen );
|
|
if( sk->version >= 4 ) {
|
|
byte *bufarr[PUBKEY_MAX_NSKEY];
|
|
unsigned narr[PUBKEY_MAX_NSKEY];
|
|
unsigned nbits[PUBKEY_MAX_NSKEY];
|
|
int ndata=0;
|
|
byte *p, *data;
|
|
|
|
for(j=0, i = pubkey_get_npkey(sk->pubkey_algo);
|
|
i < pubkey_get_nskey(sk->pubkey_algo); i++, j++ ) {
|
|
assert( !gcry_mpi_get_flag( sk->skey[i],
|
|
GCRYMPI_FLAG_OPAQUE ));
|
|
|
|
if( gcry_mpi_aprint( GCRYMPI_FMT_USG, (void**)bufarr+j,
|
|
narr+j, sk->skey[i]))
|
|
BUG();
|
|
|
|
nbits[j] = gcry_mpi_get_nbits( sk->skey[i] );
|
|
ndata += narr[j] + 2;
|
|
}
|
|
for( ; j < PUBKEY_MAX_NSKEY; j++ )
|
|
bufarr[j] = NULL;
|
|
ndata += opt.simple_sk_checksum? 2 : 20; /* for checksum */
|
|
|
|
data = xmalloc_secure ( ndata );
|
|
p = data;
|
|
for(j=0; j < PUBKEY_MAX_NSKEY && bufarr[j]; j++ ) {
|
|
p[0] = nbits[j] >> 8 ;
|
|
p[1] = nbits[j];
|
|
p += 2;
|
|
memcpy(p, bufarr[j], narr[j] );
|
|
p += narr[j];
|
|
xfree (bufarr[j]);
|
|
}
|
|
|
|
if (opt.simple_sk_checksum) {
|
|
log_info (_("generating the deprecated 16-bit checksum"
|
|
" for secret key protection\n"));
|
|
csum = checksum( data, ndata-2);
|
|
sk->csum = csum;
|
|
*p++ = csum >> 8;
|
|
*p++ = csum;
|
|
sk->protect.sha1chk = 0;
|
|
}
|
|
else {
|
|
gcry_md_hd_t h;
|
|
|
|
if (gcry_md_open (&h, GCRY_MD_SHA1, 1))
|
|
BUG(); /* algo not available */
|
|
gcry_md_write (h, data, ndata - 20);
|
|
gcry_md_final (h);
|
|
memcpy (p, gcry_md_read (h, GCRY_MD_SHA1), 20);
|
|
p += 20;
|
|
gcry_md_close (h);
|
|
sk->csum = csum = 0;
|
|
sk->protect.sha1chk = 1;
|
|
}
|
|
assert( p == data+ndata );
|
|
|
|
gcry_cipher_encrypt( cipher_hd, data, ndata, NULL, 0 );
|
|
for(i = pubkey_get_npkey(sk->pubkey_algo);
|
|
i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
|
|
gcry_mpi_release ( sk->skey[i] );
|
|
sk->skey[i] = NULL;
|
|
}
|
|
i = pubkey_get_npkey(sk->pubkey_algo);
|
|
sk->skey[i] = gcry_mpi_set_opaque(NULL, data, ndata*8);
|
|
}
|
|
else {
|
|
csum = 0;
|
|
for(i=pubkey_get_npkey(sk->pubkey_algo);
|
|
i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
|
|
byte *data;
|
|
unsigned int nbits;
|
|
|
|
csum += checksum_mpi (sk->skey[i]);
|
|
if( gcry_mpi_aprint( GCRYMPI_FMT_USG, &buffer,
|
|
&nbytes, sk->skey[i] ) )
|
|
BUG();
|
|
gcry_cipher_sync (cipher_hd);
|
|
assert (!gcry_mpi_get_flag( sk->skey[i],
|
|
GCRYMPI_FLAG_OPAQUE ));
|
|
data = xmalloc (nbytes+2);
|
|
nbits = gcry_mpi_get_nbits (sk->skey[i]);
|
|
assert (nbytes == (nbits + 7)/8);
|
|
data[0] = nbits >> 8;
|
|
data[1] = nbits;
|
|
gcry_cipher_encrypt (cipher_hd, data+2, nbytes,
|
|
buffer, nbytes);
|
|
xfree ( buffer );
|
|
|
|
gcry_mpi_release (sk->skey[i]);
|
|
sk->skey[i] = gcry_mpi_set_opaque (NULL, data,
|
|
(nbytes+2)*8);
|
|
}
|
|
sk->csum = csum;
|
|
}
|
|
sk->is_protected = 1;
|
|
gcry_cipher_close( cipher_hd );
|
|
}
|
|
}
|
|
return rc;
|
|
}
|