Add iterated-and-salted string-to-key support to PGPPacket._string_to_key
authorW. Trevor King <wking@tremily.us>
Sun, 22 Dec 2013 00:43:35 +0000 (16:43 -0800)
committerW. Trevor King <wking@tremily.us>
Mon, 23 Dec 2013 21:41:06 +0000 (13:41 -0800)
From RFC 4880 [1]:

  Iterated-Salted S2K hashes the passphrase and salt data multiple
  times.  The total number of octets to be hashed is specified in the
  encoded count in the S2K specifier.  Note that the resulting count
  value is an octet count of how many octets will be hashed, not an
  iteration count.

  Initially, one or more hash contexts are set up as with the other
  S2K algorithms, depending on how many octets of key data are needed.
  Then the salt, followed by the passphrase data, is repeatedly hashed
  until the number of octets specified by the octet count has been
  hashed.  The one exception is that if the octet count is less than
  the size of the salt plus passphrase, the full salt plus passphrase
  will be hashed even though that is greater than the octet count.
  After the hashing is done, the data is unloaded from the hash
  context(s) as with the other S2K algorithms.

[1]: http://tools.ietf.org/search/rfc4880#section-3.7.1.3

gpg-migrate.py

index b5050daede5a8b4d0aee0ce1c64240a34ee21236..1dba8981414bd3ed3768638efd06dfc8afa56e83 100755 (executable)
@@ -878,8 +878,15 @@ class PGPPacket (dict):
         key = b''
         if self['string-to-key-type'] == 'simple':
             update_bytes = string
-        elif self['string-to-key-type'] == 'salted':
+        elif self['string-to-key-type'] in [
+                'salted',
+                'iterated and salted',
+                ]:
             update_bytes = self['string-to-key-salt'] + string
+            if self['string-to-key-type'] == 'iterated and salted':
+                count = self['string-to-key-count']
+                if count < len(update_bytes):
+                    count = len(update_bytes)
         else:
             raise NotImplementedError(
                 'key calculation for string-to-key type {}'.format(
@@ -892,6 +899,11 @@ class PGPPacket (dict):
                     'salted',
                     ]:
                 string_hash.update(update_bytes)
+            elif self['string-to-key-type'] == 'iterated and salted':
+                remaining = count
+                while remaining > 0:
+                    string_hash.update(update_bytes[:remaining])
+                    remaining -= len(update_bytes)
             key += string_hash.digest()
         key = key[:key_size_bytes]
         return key