From 60e0b2ad923ca9942e01f0579664d2ae7471df99 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 17 Apr 2002 16:00:03 +0000 Subject: [PATCH] * parse-packet.c (parse_key): Support a SHA1 checksum as per draft-rfc2440-bis04. * packet.h (PKT_secret_key): Add field sha1chk. * seckey-cert.c (do_check): Check the SHA1 checksum (protect_secret_key): And create it. * build-packet.c (do_secret_key): Mark it as sha-1 protected. * g10.c, options.h: New option --simple-sk-checksum. --- doc/gpg.sgml | 15 +++++++ g10/ChangeLog | 13 ++++++ g10/build-packet.c | 2 +- g10/g10.c | 4 +- g10/options.h | 2 + g10/packet.h | 1 + g10/parse-packet.c | 8 +++- g10/seckey-cert.c | 102 ++++++++++++++++++++++++++++++++------------- 8 files changed, 113 insertions(+), 34 deletions(-) diff --git a/doc/gpg.sgml b/doc/gpg.sgml index d1dd56671..fefaeee58 100644 --- a/doc/gpg.sgml +++ b/doc/gpg.sgml @@ -581,6 +581,9 @@ The second form of the command has the special property to render the secret part of the primary key useless; this is a GNU extension to OpenPGP and other implementations can not be expected to successfully import such a key. + +See the option --simple-sk-checksum if you want to import such an +exported key with an older OpenPGP implementation. @@ -1324,6 +1327,18 @@ for conventional encryption. + +--simple-sk-checksum + +Secret keys are integrity protected by using a SHA-1 checksum. This +method will be part of an enhanced OpenPGP specification but GnuPG +already uses it as a countermeasure against certain attacks. Old +applications don't understand this new format, so this option may be +used to switch back to the old behaviour. Using this this option +bears a security risk. + + + --compress-algo &ParmN; diff --git a/g10/ChangeLog b/g10/ChangeLog index 5d39dc16b..f4118fa1d 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,16 @@ +2002-04-17 Werner Koch + + +2002-04-16 Werner Koch + + * parse-packet.c (parse_key): Support a SHA1 checksum as per + draft-rfc2440-bis04. + * packet.h (PKT_secret_key): Add field sha1chk. + * seckey-cert.c (do_check): Check the SHA1 checksum + (protect_secret_key): And create it. + * build-packet.c (do_secret_key): Mark it as sha-1 protected. + * g10.c, options.h: New option --simple-sk-checksum. + 2002-04-13 David Shaw * parse-packet.c (parse_signature): Minor fix - signatures should diff --git a/g10/build-packet.c b/g10/build-packet.c index 4bc06f138..4b692fc8c 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -389,7 +389,7 @@ do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) } else { /* OpenPGP protection according to rfc2440 */ - iobuf_put(a, 0xff ); + iobuf_put(a, sk->protect.sha1chk? 0xfe : 0xff ); iobuf_put(a, sk->protect.algo ); if( sk->protect.s2k.mode >= 1000 ) { /* These modes are not possible in OpenPGP, we use them diff --git a/g10/g10.c b/g10/g10.c index 23273a19c..a070ccb65 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -218,6 +218,7 @@ enum cmd_and_opt_values { aNull = 0, oS2KMode, oS2KDigest, oS2KCipher, + oSimpleSKChecksum, oCharset, oNotDashEscaped, oEscapeFrom, @@ -412,6 +413,7 @@ static ARGPARSE_OPTS opts[] = { N_("|NAME|use message digest algorithm NAME for passphrases")}, { oS2KCipher, "s2k-cipher-algo",2, N_("|NAME|use cipher algorithm NAME for passphrases")}, + { oSimpleSKChecksum, "simple-sk-checksum", 0, "@"}, { oCipherAlgo, "cipher-algo", 2 , N_("|NAME|use cipher algorithm NAME")}, { oDigestAlgo, "digest-algo", 2 , N_("|NAME|use message digest algorithm NAME")}, { oCompressAlgo, "compress-algo", 1 , N_("|N|use compress algorithm N")}, @@ -1132,7 +1134,7 @@ main( int argc, char **argv ) case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break; case oS2KDigest: s2k_digest_string = m_strdup(pargs.r.ret_str); break; case oS2KCipher: s2k_cipher_string = m_strdup(pargs.r.ret_str); break; - + case oSimpleSKChecksum: opt.simple_sk_checksum = 1; break; case oNoEncryptTo: opt.no_encrypt_to = 1; break; case oEncryptTo: /* store the recipient in the second list */ sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings ); diff --git a/g10/options.h b/g10/options.h index d565e09d9..0393e5fe6 100644 --- a/g10/options.h +++ b/g10/options.h @@ -94,6 +94,8 @@ struct { int s2k_mode; int s2k_digest_algo; int s2k_cipher_algo; + int simple_sk_checksum; /* create the deprecated rfc2440 secret + key protection*/ int not_dash_escaped; int escape_from; int lock_once; diff --git a/g10/packet.h b/g10/packet.h index b4efabb43..e98014782 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -232,6 +232,7 @@ typedef struct { /* and should never be passed to a mpi_xxx() */ struct { byte algo; /* cipher used to protect the secret information*/ + byte sha1chk; /* SHA1 is used instead of a 16 bit checksum */ STRING2KEY s2k; byte ivlen; /* used length of the iv */ byte iv[16]; /* initialization vector for CFB mode */ diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 405edc43e..a4c4403a6 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1494,14 +1494,16 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, if (rc) /* one of the MPIs were bad */ goto leave; sk->protect.algo = iobuf_get_noeof(inp); pktlen--; + sk->protect.sha1chk = 0; if( sk->protect.algo ) { sk->is_protected = 1; sk->protect.s2k.count = 0; - if( sk->protect.algo == 255 ) { + if( sk->protect.algo == 254 || sk->protect.algo == 255 ) { if( pktlen < 3 ) { rc = G10ERR_INVALID_PACKET; goto leave; } + sk->protect.sha1chk = (sk->protect.algo == 254); sk->protect.algo = iobuf_get_noeof(inp); pktlen--; sk->protect.s2k.mode = iobuf_get_noeof(inp); pktlen--; sk->protect.s2k.hash_algo = iobuf_get_noeof(inp); pktlen--; @@ -1550,8 +1552,10 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, } if( list_mode ) { - printf(", algo: %d, hash: %d", + printf(", algo: %d,%s hash: %d", sk->protect.algo, + sk->protect.sha1chk? "" + :" simple checksum,", sk->protect.s2k.hash_algo ); if( sk->protect.s2k.mode == 1 || sk->protect.s2k.mode == 3 ) { diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index aeb67d85a..77e27ae7b 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -90,31 +90,54 @@ do_check( PKT_secret_key *sk, const char *tryagain_text ) cipher_decrypt( cipher_hd, data, p, ndata ); mpi_free( sk->skey[i] ); sk->skey[i] = NULL ; p = data; - 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 */ + 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 { + MD_HANDLE h = md_open (DIGEST_ALGO_SHA1, 1); + if (!h) + BUG(); /* algo not available */ + md_write (h, data, ndata - 20); + md_final (h); + if (!memcmp (md_read (h, DIGEST_ALGO_SHA1), + data + ndata - 20, 20) ) + csum = 0; /* digest does match */ + md_close (h); } - } - - /* 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; - sk->skey[i] = mpi_read_from_buffer(p, &nbytes, 1 ); - ndata -= nbytes; - p += nbytes; - } - /* at this point ndata should be equal to 2 (the checksum) */ - } + } + 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; + sk->skey[i] = mpi_read_from_buffer(p, &nbytes, 1 ); + ndata -= nbytes; + p += nbytes; + } + /* Note: at this point ndata should be 2 for a simple + checksum or 20 for the sha1 digest */ + } m_free(data); } else { @@ -265,7 +288,7 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) } for( ; j < PUBKEY_MAX_NSKEY; j++ ) bufarr[j] = NULL; - ndata += 2; /* for checksum */ + ndata += opt.simple_sk_checksum? 2 : 20; /* for checksum */ data = m_alloc_secure( ndata ); p = data; @@ -277,11 +300,30 @@ protect_secret_key( PKT_secret_key *sk, DEK *dek ) p += narr[j]; m_free(bufarr[j]); } - csum = checksum( data, ndata-2); - sk->csum = csum; - *p++ = csum >> 8; - *p++ = csum; - assert( p == data+ndata ); + + 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 { + MD_HANDLE h = md_open (DIGEST_ALGO_SHA1, 1); + if (!h) + BUG(); /* algo not available */ + md_write (h, data, ndata - 20); + md_final (h); + memcpy (p, md_read (h, DIGEST_ALGO_SHA1), 20); + p += 20; + md_close (h); + sk->csum = csum = 0; + sk->protect.sha1chk = 1; + } + assert( p == data+ndata ); + cipher_encrypt( cipher_hd, data, data, ndata ); for(i = pubkey_get_npkey(sk->pubkey_algo); i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {