Fixed regression in OpenPGP secret key export.
authorWerner Koch <wk@gnupg.org>
Tue, 26 Apr 2011 18:33:46 +0000 (20:33 +0200)
committerWerner Koch <wk@gnupg.org>
Tue, 26 Apr 2011 18:39:09 +0000 (20:39 +0200)
The protection used in the exported key used a different iteration
count than given in the S2K field.  Thus all OpenPGP keys exported
from GnuPG 2.1-beta can't be imported again.  Given that the actual
secret key material is kept in private-keys-v1.d/ the can be
re-exported with this fixed version.

NEWS
agent/ChangeLog
agent/agent.h
agent/cvt-openpgp.c
agent/protect.c
g10/ChangeLog
g10/export.c

diff --git a/NEWS b/NEWS
index beadfc3..f37deb2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
 Noteworthy changes in version 2.1.0beta3
 -----------------------------------------------------
 
+ * Fixed regression in GPG'S secret key export function.
+
 
 Noteworthy changes in version 2.1.0beta2 (2011-03-08)
 -----------------------------------------------------
index 9a6134d..78ddf8d 100644 (file)
@@ -1,3 +1,10 @@
+2011-04-26  Werner Koch  <wk@g10code.com>
+
+       * cvt-openpgp.c (convert_to_openpgp): Use rfc4880 encoded S2K count.
+       * protect.c (get_standard_s2k_count_rfc4880): New.
+       (S2K_DECODE_COUNT): New.
+       (s2k_hash_passphrase): Use the new macro.
+
 2011-04-21  Werner Koch  <wk@g10code.com>
 
        * agent.h (server_control_s): Add field cache_ttl_opt_preset.
index 16c9aba..9aaf264 100644 (file)
@@ -330,6 +330,7 @@ gpg_error_t agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey,
 
 /*-- protect.c --*/
 unsigned long get_standard_s2k_count (void);
+unsigned char get_standard_s2k_count_rfc4880 (void);
 int agent_protect (const unsigned char *plainkey, const char *passphrase,
                    unsigned char **result, size_t *resultlen);
 int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
index 1595a32..0f31728 100644 (file)
@@ -1046,7 +1046,10 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
 
   gcry_create_nonce (protect_iv, sizeof protect_iv);
   gcry_create_nonce (salt, sizeof salt);
-  s2k_count = get_standard_s2k_count ();
+  /* We need to use the encoded S2k count.  It is not possible to
+     encode it after it has been used because the encoding procedure
+     may round the value up.  */
+  s2k_count = get_standard_s2k_count_rfc4880 ();
   err = apply_protection (array, npkey, nskey, passphrase,
                           GCRY_CIPHER_AES, protect_iv, sizeof protect_iv,
                           3, GCRY_MD_SHA1, salt, s2k_count);
index 0b8c9b4..7df82de 100644 (file)
@@ -41,6 +41,9 @@
 #define PROT_CIPHER_STRING "aes"
 #define PROT_CIPHER_KEYLEN (128/8)
 
+/* Decode an rfc4880 encoded S2K count.  */
+#define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6))
+
 
 /* A table containing the information needed to create a protected
    private key.  */
@@ -192,6 +195,33 @@ get_standard_s2k_count (void)
 }
 
 
+/* Same as get_standard_s2k_count but return the count in the encoding
+   as described by rfc4880.  */
+unsigned char
+get_standard_s2k_count_rfc4880 (void)
+{
+  unsigned long iterations;
+  unsigned int count;
+  unsigned char result;
+  unsigned char c=0;
+
+  iterations = get_standard_s2k_count ();
+  if (iterations >= 65011712)
+    return 255;
+
+  /* Need count to be in the range 16-31 */
+  for (count=iterations>>6; count>=32; count>>=1)
+    c++;
+
+  result = (c<<4)|(count-16);
+
+  if (S2K_DECODE_COUNT(result) < iterations)
+    result++;
+
+  return result;
+
+}
+
 
 \f
 /* Calculate the MIC for a private key or shared secret S-expression.
@@ -1041,7 +1071,7 @@ s2k_hash_passphrase (const char *passphrase, int hashalgo,
                      unsigned char *key, size_t keylen)
 {
   return hash_passphrase (passphrase, hashalgo, s2kmode, s2ksalt,
-                          (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6),
+                          S2K_DECODE_COUNT (s2kcount),
                           key, keylen);
 }
 
index 8b22df8..86c9b98 100644 (file)
@@ -1,3 +1,8 @@
+2011-04-26  Werner Koch  <wk@g10code.com>
+
+       * export.c (transfer_format_to_openpgp): Do not apply
+       encode_s2k_iterations to S2K_COUNT.
+
 2011-04-25  Werner Koch  <wk@g10code.com>
 
        * delkey.c (do_delete_key): Mark classify_user_id for use with
index 9f4959e..2e35eea 100644 (file)
@@ -626,10 +626,9 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
     }
 
   /* Do some sanity checks.  */
-  if (s2k_count <= 1024)
+  if (s2k_count > 255)
     {
-      /* The count must be larger so that encode_s2k_iterations does
-         not fall into a backward compatibility mode.  */
+      /* We expect an already encoded S2K count.  */
       err = gpg_error (GPG_ERR_INV_DATA);
       goto leave;
     }
@@ -682,7 +681,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
   ski->s2k.hash_algo = s2k_algo;
   assert (sizeof ski->s2k.salt == sizeof s2k_salt);
   memcpy (ski->s2k.salt, s2k_salt, sizeof s2k_salt);
-  ski->s2k.count = encode_s2k_iterations (s2k_count);
+  ski->s2k.count = s2k_count;
   assert (ivlen <= sizeof ski->iv);
   memcpy (ski->iv, iv, ivlen);
   ski->ivlen = ivlen;