diff options
Diffstat (limited to 'g10/seskey.c')
-rw-r--r-- | g10/seskey.c | 242 |
1 files changed, 157 insertions, 85 deletions
diff --git a/g10/seskey.c b/g10/seskey.c index ee5584c66..1f3e8ab49 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -1,6 +1,6 @@ /* seskey.c - make sesssion keys etc. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, - * 2006, 2009 Free Software Foundation, Inc. + * 2006, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -27,6 +27,7 @@ #include "gpg.h" #include "util.h" #include "cipher.h" +#include "options.h" #include "main.h" #include "i18n.h" @@ -48,10 +49,10 @@ make_session_key( DEK *dek ) 0 : GCRY_CIPHER_ENABLE_SYNC))) ) BUG(); gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); - for (i=0; i < 16; i++ ) + for (i=0; i < 16; i++ ) { rc = gcry_cipher_setkey (chd, dek->key, dek->keylen); - if (!rc) + if (!rc) { gcry_cipher_close (chd); return; @@ -73,81 +74,127 @@ make_session_key( DEK *dek ) * returns: A mpi with the session key (caller must free) */ gcry_mpi_t -encode_session_key (DEK *dek, unsigned int nbits) +encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits) { - size_t nframe = (nbits+7) / 8; - byte *p; - byte *frame; - int i,n; - u16 csum; - gcry_mpi_t a; - - /* The current limitation is that we can only use a session key - * whose length is a multiple of BITS_PER_MPI_LIMB - * I think we can live with that. - */ - if( dek->keylen + 7 > nframe || !nframe ) - log_bug("can't encode a %d bit key in a %d bits frame\n", - dek->keylen*8, nbits ); - - /* We encode the session key in this way: - * - * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) - * - * (But how can we store the leading 0 - the external representaion - * of MPIs doesn't allow leading zeroes =:-) - * - * RND are non-zero random bytes. - * A is the cipher algorithm - * DEK is the encryption key (session key) length k depends on the - * cipher algorithm (20 is used with blowfish160). - * CSUM is the 16 bit checksum over the DEK - */ - csum = 0; - for( p = dek->key, i=0; i < dek->keylen; i++ ) - csum += *p++; + size_t nframe = (nbits+7) / 8; + byte *p; + byte *frame; + int i,n; + u16 csum; + gcry_mpi_t a; + + if (DBG_CIPHER) + log_debug ("encode_session_key: encoding %d byte DEK", dek->keylen); + + csum = 0; + for (p = dek->key, i=0; i < dek->keylen; i++) + csum += *p++; + + /* Shortcut for ECDH. It's padding is minimal to simply make the + output be a multiple of 8 bytes. */ + if (openpgp_pk_algo == PUBKEY_ALGO_ECDH) + { + /* Pad to 8 byte granulatiry; the padding byte is the number of + * padded bytes. + * + * A DEK(k bytes) CSUM(2 bytes) 0x 0x 0x 0x ... 0x + * +---- x times ---+ + */ + nframe = (( 1 + dek->keylen + 2 /* The value so far is always odd. */ + + 7 ) & (~7)); + + /* alg+key+csum fit and the size is congruent to 8. */ + assert (!(nframe%8) && nframe > 1 + dek->keylen + 2 ); + + frame = xmalloc_secure (nframe); + n = 0; + frame[n++] = dek->algo; + memcpy (frame+n, dek->key, dek->keylen); + n += dek->keylen; + frame[n++] = csum >> 8; + frame[n++] = csum; + i = nframe - n; /* Number of padded bytes. */ + memset (frame+n, i, i); /* Use it as the value of each padded byte. */ + assert (n+i == nframe); + + if (DBG_CIPHER) + log_debug ("encode_session_key: " + "[%d] %02x %02x %02x ... %02x %02x %02x\n", + nframe, frame[0], frame[1], frame[2], + frame[nframe-3], frame[nframe-2], frame[nframe-1]); + + if (gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, nframe, &nframe)) + BUG(); + xfree(frame); + return a; + } - frame = xmalloc_secure( nframe ); - n = 0; - frame[n++] = 0; - frame[n++] = 2; - i = nframe - 6 - dek->keylen; - assert( i > 0 ); - p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM); - /* Replace zero bytes by new values. */ - for(;;) { - int j, k; - byte *pp; - - /* count the zero bytes */ - for(j=k=0; j < i; j++ ) - if( !p[j] ) - k++; - if( !k ) - break; /* okay: no zero bytes */ - k += k/128 + 3; /* better get some more */ - pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM); - for(j=0; j < i && k ;) { - if( !p[j] ) - p[j] = pp[--k]; - if (p[j]) - j++; + /* The current limitation is that we can only use a session key + * whose length is a multiple of BITS_PER_MPI_LIMB + * I think we can live with that. + */ + if (dek->keylen + 7 > nframe || !nframe) + log_bug ("can't encode a %d bit key in a %d bits frame\n", + dek->keylen*8, nbits ); + + /* We encode the session key in this way: + * + * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes) + * + * (But how can we store the leading 0 - the external representaion + * of MPIs doesn't allow leading zeroes =:-) + * + * RND are non-zero random bytes. + * A is the cipher algorithm + * DEK is the encryption key (session key) length k depends on the + * cipher algorithm (20 is used with blowfish160). + * CSUM is the 16 bit checksum over the DEK + */ + + frame = xmalloc_secure( nframe ); + n = 0; + frame[n++] = 0; + frame[n++] = 2; + i = nframe - 6 - dek->keylen; + assert( i > 0 ); + p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM); + /* Replace zero bytes by new values. */ + for (;;) + { + int j, k; + byte *pp; + + /* Count the zero bytes. */ + for (j=k=0; j < i; j++ ) + if (!p[j]) + k++; + if (!k) + break; /* Okay: no zero bytes. */ + k += k/128 + 3; /* Better get some more. */ + pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM); + for (j=0; j < i && k ;) + { + if (!p[j]) + p[j] = pp[--k]; + if (p[j]) + j++; } - xfree(pp); + xfree (pp); } - memcpy( frame+n, p, i ); - xfree(p); - n += i; - frame[n++] = 0; - frame[n++] = dek->algo; - memcpy( frame+n, dek->key, dek->keylen ); n += dek->keylen; - frame[n++] = csum >>8; - frame[n++] = csum; - assert( n == nframe ); - if (gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, n, &nframe)) - BUG(); - xfree(frame); - return a; + memcpy (frame+n, p, i); + xfree (p); + n += i; + frame[n++] = 0; + frame[n++] = dek->algo; + memcpy (frame+n, dek->key, dek->keylen ); + n += dek->keylen; + frame[n++] = csum >>8; + frame[n++] = csum; + assert (n == nframe); + if (gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, n, &nframe)) + BUG(); + xfree (frame); + return a; } @@ -161,8 +208,8 @@ do_encode_md( gcry_md_hd_t md, int algo, size_t len, unsigned nbits, gcry_mpi_t a; if( len + asnlen + 4 > nframe ) - log_bug("can't encode a %d bit MD into a %d bits frame\n", - (int)(len*8), (int)nbits); + log_bug ("can't encode a %d bit MD into a %d bits frame, algo=%d\n", + (int)(len*8), (int)nbits, algo); /* We encode the MD in this way: * @@ -209,19 +256,27 @@ gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) { gcry_mpi_t frame; + int pkalgo; assert (hash_algo); assert (pk); - if (pk->pubkey_algo == GCRY_PK_DSA) + pkalgo = map_pk_openpgp_to_gcry (pk->pubkey_algo); + + if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA) { - /* It's a DSA signature, so find out the size of q. */ + /* It's a DSA signature, so find out the size of q. */ size_t qbytes = gcry_mpi_get_nbits (pk->pkey[1]); + /* pkey[1] is Q for ECDSA, which is an uncompressed point, + i.e. 04 <x> <y> */ + if (pkalgo == GCRY_PK_ECDSA) + qbytes = ecdsa_qbits_from_Q (qbytes); + /* Make sure it is a multiple of 8 bits. */ - if(qbytes%8) + if (qbytes%8) { log_error(_("DSA requires the hash length to be a" " multiple of 8 bits\n")); @@ -236,22 +291,39 @@ encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo) DSA. ;) */ if (qbytes < 160) { - log_error (_("DSA key %s uses an unsafe (%zu bit) hash\n"), - keystr_from_pk (pk), qbytes); + log_error (_("%s key %s uses an unsafe (%zu bit) hash\n"), + gcry_pk_algo_name (pkalgo), keystr_from_pk (pk), qbytes); return NULL; } qbytes /= 8; /* Check if we're too short. Too long is safe as we'll - automatically left-truncate. */ - if (gcry_md_get_algo_dlen (hash_algo) < qbytes) + automatically left-truncate. + + FIXME: Check against FIPS. + This checks would require the use of SHA512 with ECDSA 512. I + think this is overkill to fail in this case. Therefore, + relax the check, but only for ECDSA keys. We may need to + adjust it later for general case. (Note that the check will + never pass for ECDSA 521 anyway as the only hash that + intended to match it is SHA 512, but 512 < 521). */ + if (gcry_md_get_algo_dlen (hash_algo) + < ((pkalgo == GCRY_PK_ECDSA && qbytes > (521)/8) ? 512/8 : qbytes)) { - log_error (_("DSA key %s requires a %zu bit or larger hash\n"), - keystr_from_pk(pk), qbytes*8); + log_error (_("%s key %s requires a %zu bit or larger hash " + "(hash is %s\n"), + gcry_pk_algo_name (pkalgo), + keystr_from_pk(pk), qbytes*8, + gcry_md_algo_name (hash_algo)); return NULL; } + /* By passing QBYTES as length to mpi_scan, we do the truncation + of the hash. + + Note that in case of ECDSA 521 the hash is always smaller + than the key size. */ if (gcry_mpi_scan (&frame, GCRYMPI_FMT_USG, gcry_md_read (md, hash_algo), qbytes, &qbytes)) BUG(); |