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++ ) {