pgcrypto: support changing S2K iteration count
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 9 Mar 2016 17:31:07 +0000 (14:31 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Wed, 9 Mar 2016 17:31:07 +0000 (14:31 -0300)
pgcrypto already supports key-stretching during symmetric encryption,
including the salted-and-iterated method; but the number of iterations
was not configurable.  This commit implements a new s2k-count parameter
to pgp_sym_encrypt() which permits selecting a larger number of
iterations.

Author: Jeff Janes

contrib/pgcrypto/expected/pgp-encrypt.out
contrib/pgcrypto/pgp-decrypt.c
contrib/pgcrypto/pgp-encrypt.c
contrib/pgcrypto/pgp-pgsql.c
contrib/pgcrypto/pgp-s2k.c
contrib/pgcrypto/pgp.c
contrib/pgcrypto/pgp.h
contrib/pgcrypto/sql/pgp-encrypt.sql
doc/src/sgml/pgcrypto.sgml

index b35de79afa741173cb63e3f868884f1aebcf295b..8fc558c402bde4400746c05fed376e2b1f3849c9 100644 (file)
@@ -103,6 +103,25 @@ select pgp_sym_decrypt(
  Secret.
 (1 row)
 
+-- s2k count change
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-count=1024'),
+       'key', 'expect-s2k-count=1024');
+ pgp_sym_decrypt 
+-----------------
+ Secret.
+(1 row)
+
+-- s2k_count rounds up
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-count=65000000'),
+       'key', 'expect-s2k-count=65000000');
+NOTICE:  pgp_decrypt: unexpected s2k_count: expected 65000000 got 65011712
+ pgp_sym_decrypt 
+-----------------
+ Secret.
+(1 row)
+
 -- s2k digest change
 select pgp_sym_decrypt(
        pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'),
index 5c69745156ccba0884bd82d3f6863e9f56d85ccc..9ea60c4c47daf2201438f4dfb102ef342b27bb55 100644 (file)
@@ -643,6 +643,7 @@ parse_symenc_sesskey(PGP_Context *ctx, PullFilter *src)
        if (res < 0)
                return res;
        ctx->s2k_mode = ctx->s2k.mode;
+       ctx->s2k_count = s2k_decode_count(ctx->s2k.iter);
        ctx->s2k_digest_algo = ctx->s2k.digest_algo;
 
        /*
index 2320c7574b5cb585138fa09cb12e96b968da49da..c9148fd2fc6d0f630ba1116a5d753157791d9d85 100644 (file)
@@ -567,7 +567,7 @@ init_s2k_key(PGP_Context *ctx)
        if (ctx->s2k_cipher_algo < 0)
                ctx->s2k_cipher_algo = ctx->cipher_algo;
 
-       res = pgp_s2k_fill(&ctx->s2k, ctx->s2k_mode, ctx->s2k_digest_algo);
+       res = pgp_s2k_fill(&ctx->s2k, ctx->s2k_mode, ctx->s2k_digest_algo, ctx->s2k_count);
        if (res < 0)
                return res;
 
index 1842985e53db053a05701597d3a935ac6d959652..1f65b667cab3747cf2af2216b577284f31e3051c 100644 (file)
@@ -181,6 +181,7 @@ struct debug_expect
        int                     expect;
        int                     cipher_algo;
        int                     s2k_mode;
+       int                     s2k_count;
        int                     s2k_cipher_algo;
        int                     s2k_digest_algo;
        int                     compress_algo;
@@ -196,6 +197,7 @@ fill_expect(struct debug_expect * ex, int text_mode)
        ex->expect = 0;
        ex->cipher_algo = -1;
        ex->s2k_mode = -1;
+       ex->s2k_count = -1;
        ex->s2k_cipher_algo = -1;
        ex->s2k_digest_algo = -1;
        ex->compress_algo = -1;
@@ -218,6 +220,7 @@ check_expect(PGP_Context *ctx, struct debug_expect * ex)
 {
        EX_CHECK(cipher_algo);
        EX_CHECK(s2k_mode);
+       EX_CHECK(s2k_count);
        EX_CHECK(s2k_digest_algo);
        EX_CHECK(use_sess_key);
        if (ctx->use_sess_key)
@@ -247,6 +250,8 @@ set_arg(PGP_Context *ctx, char *key, char *val,
                res = pgp_set_sess_key(ctx, atoi(val));
        else if (strcmp(key, "s2k-mode") == 0)
                res = pgp_set_s2k_mode(ctx, atoi(val));
+       else if (strcmp(key, "s2k-count") == 0)
+               res = pgp_set_s2k_count(ctx, atoi(val));
        else if (strcmp(key, "s2k-digest-algo") == 0)
                res = pgp_set_s2k_digest_algo(ctx, val);
        else if (strcmp(key, "s2k-cipher-algo") == 0)
@@ -286,6 +291,11 @@ set_arg(PGP_Context *ctx, char *key, char *val,
                ex->expect = 1;
                ex->s2k_mode = atoi(val);
        }
+       else if (ex != NULL && strcmp(key, "expect-s2k-count") == 0)
+       {
+               ex->expect = 1;
+               ex->s2k_count = atoi(val);
+       }
        else if (ex != NULL && strcmp(key, "expect-s2k-digest-algo") == 0)
        {
                ex->expect = 1;
index 193dd95173ffed5b716961ec8faa4f24d02897b6..9937d154f275853b2e013f287d04a9b8dc79d993 100644 (file)
@@ -132,12 +132,10 @@ calc_s2k_iter_salted(PGP_S2K *s2k, PX_MD *md, const uint8 *key,
        unsigned        preload = 0;
        unsigned        remain,
                                c,
-                               cval,
                                curcnt,
                                count;
 
-       cval = s2k->iter;
-       count = ((unsigned) 16 + (cval & 15)) << ((cval >> 4) + 6);
+       count = s2k_decode_count(s2k->iter);
 
        md_rlen = px_md_result_size(md);
 
@@ -195,21 +193,34 @@ calc_s2k_iter_salted(PGP_S2K *s2k, PX_MD *md, const uint8 *key,
 }
 
 /*
- * Decide S2K_ISALTED iteration count
+ * Decide PGP_S2K_ISALTED iteration count (in OpenPGP one-byte representation)
  *
  * Too small: weak
  * Too big: slow
  * gpg defaults to 96 => 65536 iters
- * let it float a bit: 96 + 32 => 262144 iters
+ *
+ * For our default (count=-1) we let it float a bit: 96 + 32 => between 65536
+ * and 262144 iterations.
+ *
+ * Otherwise, find the smallest number which provides at least the specified
+ * iteration count.
  */
-static int
-decide_count(unsigned rand_byte)
+static uint8
+decide_s2k_iter(unsigned rand_byte, int count)
 {
-       return 96 + (rand_byte & 0x1F);
+       int                     iter;
+
+       if (count == -1)
+               return 96 + (rand_byte & 0x1F);
+       /* this is a bit brute-force, but should be quick enough */
+       for (iter = 0; iter <= 255; iter++)
+               if (s2k_decode_count(iter) >= count)
+                       return iter;
+       return 255;
 }
 
 int
-pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo)
+pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo, int count)
 {
        int                     res = 0;
        uint8           tmp;
@@ -219,19 +230,19 @@ pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo)
 
        switch (s2k->mode)
        {
-               case 0:
+               case PGP_S2K_SIMPLE:
                        break;
-               case 1:
+               case PGP_S2K_SALTED:
                        res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT);
                        break;
-               case 3:
+               case PGP_S2K_ISALTED:
                        res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT);
                        if (res < 0)
                                break;
                        res = px_get_pseudo_random_bytes(&tmp, 1);
                        if (res < 0)
                                break;
-                       s2k->iter = decide_count(tmp);
+                       s2k->iter = decide_s2k_iter(tmp, count);
                        break;
                default:
                        res = PXE_PGP_BAD_S2K_MODE;
index 03fe48fb64ce0025506aa2ecf7f62f6751b009a8..0800fc325d10b1ceefe90461adc3f82a927eca5b 100644 (file)
@@ -40,6 +40,7 @@
 static int     def_cipher_algo = PGP_SYM_AES_128;
 static int     def_s2k_cipher_algo = -1;
 static int     def_s2k_mode = PGP_S2K_ISALTED;
+static int     def_s2k_count = -1;
 static int     def_s2k_digest_algo = PGP_DIGEST_SHA1;
 static int     def_compress_algo = PGP_COMPR_NONE;
 static int     def_compress_level = 6;
@@ -206,6 +207,7 @@ pgp_init(PGP_Context **ctx_p)
        ctx->cipher_algo = def_cipher_algo;
        ctx->s2k_cipher_algo = def_s2k_cipher_algo;
        ctx->s2k_mode = def_s2k_mode;
+       ctx->s2k_count = def_s2k_count;
        ctx->s2k_digest_algo = def_s2k_digest_algo;
        ctx->compress_algo = def_compress_algo;
        ctx->compress_level = def_compress_level;
@@ -269,6 +271,17 @@ pgp_set_s2k_mode(PGP_Context *ctx, int mode)
        return err;
 }
 
+int
+pgp_set_s2k_count(PGP_Context *ctx, int count)
+{
+       if (ctx->s2k_mode == PGP_S2K_ISALTED && count >= 1024 && count <= 65011712)
+       {
+               ctx->s2k_count = count;
+               return PXE_OK;
+       }
+       return PXE_ARGUMENT_ERROR;
+}
+
 int
 pgp_set_compress_algo(PGP_Context *ctx, int algo)
 {
index 62b8517c27c682e50c0993d9b9d74c3d97934bbc..88f7f8dc48409796ffb386748ed40a46fc1e9fe4 100644 (file)
@@ -124,7 +124,7 @@ struct PGP_S2K
        uint8           mode;
        uint8           digest_algo;
        uint8           salt[8];
-       uint8           iter;
+       uint8           iter;           /* encoded (one-octet) count */
        /* calculated: */
        uint8           key[PGP_MAX_KEY];
        uint8           key_len;
@@ -138,6 +138,7 @@ struct PGP_Context
         */
        PGP_S2K         s2k;
        int                     s2k_mode;
+       int                     s2k_count;              /* 4-byte decoded count */
        int                     s2k_digest_algo;
        int                     s2k_cipher_algo;
        int                     cipher_algo;
@@ -171,6 +172,10 @@ struct PGP_Context
        unsigned        sess_key_len;
 };
 
+/* from RFC 4880 3.7.1.3 */
+#define s2k_decode_count(cval) \
+       (((unsigned) 16 + (cval & 15)) << ((cval >> 4) + 6))
+
 struct PGP_MPI
 {
        uint8      *data;
@@ -243,6 +248,7 @@ const char *pgp_get_cipher_name(int code);
 
 int                    pgp_set_cipher_algo(PGP_Context *ctx, const char *name);
 int                    pgp_set_s2k_mode(PGP_Context *ctx, int type);
+int                    pgp_set_s2k_count(PGP_Context *ctx, int count);
 int                    pgp_set_s2k_cipher_algo(PGP_Context *ctx, const char *name);
 int                    pgp_set_s2k_digest_algo(PGP_Context *ctx, const char *name);
 int                    pgp_set_convert_crlf(PGP_Context *ctx, int doit);
@@ -267,7 +273,7 @@ int                 pgp_load_cipher(int c, PX_Cipher **res);
 int                    pgp_get_cipher_key_size(int c);
 int                    pgp_get_cipher_block_size(int c);
 
-int                    pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo);
+int                    pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo, int count);
 int                    pgp_s2k_read(PullFilter *src, PGP_S2K *s2k);
 int                    pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int klen);
 
index a9ac0b924b7828c090f730013f859394f30c5f40..ed9d2c8127f8ae8682835c2700166c2398a9c8e6 100644 (file)
@@ -55,6 +55,15 @@ select pgp_sym_decrypt(
        pgp_sym_encrypt('Secret.', 'key', 's2k-mode=3'),
        'key', 'expect-s2k-mode=3');
 
+-- s2k count change
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-count=1024'),
+       'key', 'expect-s2k-count=1024');
+-- s2k_count rounds up
+select pgp_sym_decrypt(
+       pgp_sym_encrypt('Secret.', 'key', 's2k-count=65000000'),
+       'key', 'expect-s2k-count=65000000');
+
 -- s2k digest change
 select pgp_sym_decrypt(
        pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'),
index bfcbe02f8512c1a72f55d8369246a9875715f41b..21a884bf20cd8bdacda6ef16f5a16974ce1795aa 100644 (file)
@@ -858,6 +858,19 @@ Applies to: pgp_sym_encrypt
 </literallayout>
   </sect4>
 
+  <sect4>
+   <title>s2k-count</title>
+
+   <para>
+    The number of iterations of the S2K algorithm to use.  It must
+    be a value between 1024 and 65011712, inclusive.
+   </para>
+<literallayout>
+Default: A random value bewteen 65536 and 253952
+Applies to: pgp_sym_encrypt, only with s2k-mode=3
+</literallayout>
+  </sect4>
+
   <sect4>
    <title>s2k-digest-algo</title>