summaryrefslogtreecommitdiffstats
path: root/g10/seskey.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/seskey.c')
-rw-r--r--g10/seskey.c242
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();