pgcrypto supports s2k-mode for key-stretching during symmetric encryption, and even defaults to s2k-mode=3, which means configurable iterations. But it doesn't support s2k-count to actually set those iterations to be anything other than the default. If you are interested in key-stretching, the default is not going to cut it. (You could argue that pgp's s2k doesn't cut it either even at the max, but at least we should offer the maximum that the pgp spec makes available.)
This patch implements s2k-count as an option to pgp_sym_encrypt. Demo (note the password is intentionally wrong in the last character): select pgp_sym_decrypt( pgp_sym_encrypt('foobar','acf86729b6b0289f4d1909db8c1aaf0c','s2k-mode=3'), 'acf86729b6b0289f4d1909db8c1aaf0d'); ERROR: Wrong key or corrupt data Time: 1.606 ms select pgp_sym_decrypt( pgp_sym_encrypt('foobar','acf86729b6b0289f4d1909db8c1aaf0c','s2k-mode=3,s2k-count=65000000'), 'acf86729b6b0289f4d1909db8c1aaf0d'); ERROR: Wrong key or corrupt data Time: 615.720 ms I did not bump the extension version. I realized the migration file would be empty, as there no change to SQL-level functionality (the new s2k-count is parsed out of a string down in the C code). Since only one version of contrib extensions binary object files are installed in any given postgres installation, people using the newer binary gets the feature even if they have not updated the extension version. So I don't know if it makes sense to bump the version if people inherently get the feature anyway. Cheers, Jeff
diff --git a/contrib/pgcrypto/expected/pgp-encrypt.out b/contrib/pgcrypto/expected/pgp-encrypt.out new file mode 100644 index b35de79..2bf999f *** a/contrib/pgcrypto/expected/pgp-encrypt.out --- b/contrib/pgcrypto/expected/pgp-encrypt.out *************** select pgp_sym_decrypt( *** 103,108 **** --- 103,126 ---- 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) + + select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-count=65000000'), + 'key', 'expect-s2k-count=65000000'); -- rounded up + 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'), diff --git a/contrib/pgcrypto/pgp-decrypt.c b/contrib/pgcrypto/pgp-decrypt.c new file mode 100644 index 5c69745..3d16033 *** a/contrib/pgcrypto/pgp-decrypt.c --- b/contrib/pgcrypto/pgp-decrypt.c *************** parse_symenc_sesskey(PGP_Context *ctx, P *** 643,648 **** --- 643,649 ---- if (res < 0) return res; ctx->s2k_mode = ctx->s2k.mode; + ctx->s2k_count = iter_to_count(ctx->s2k.iter); ctx->s2k_digest_algo = ctx->s2k.digest_algo; /* diff --git a/contrib/pgcrypto/pgp-encrypt.c b/contrib/pgcrypto/pgp-encrypt.c new file mode 100644 index 2320c75..c9148fd *** a/contrib/pgcrypto/pgp-encrypt.c --- b/contrib/pgcrypto/pgp-encrypt.c *************** init_s2k_key(PGP_Context *ctx) *** 567,573 **** 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); if (res < 0) return res; --- 567,573 ---- 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, ctx->s2k_count); if (res < 0) return res; diff --git a/contrib/pgcrypto/pgp-pgsql.c b/contrib/pgcrypto/pgp-pgsql.c new file mode 100644 index 1842985..1b55bc9 *** a/contrib/pgcrypto/pgp-pgsql.c --- b/contrib/pgcrypto/pgp-pgsql.c *************** struct debug_expect *** 181,186 **** --- 181,187 ---- int expect; int cipher_algo; int s2k_mode; + int s2k_count; int s2k_cipher_algo; int s2k_digest_algo; int compress_algo; *************** fill_expect(struct debug_expect * ex, in *** 196,201 **** --- 197,203 ---- 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; *************** check_expect(PGP_Context *ctx, struct de *** 218,223 **** --- 220,226 ---- { 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) *************** set_arg(PGP_Context *ctx, char *key, cha *** 247,252 **** --- 250,257 ---- 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) *************** set_arg(PGP_Context *ctx, char *key, cha *** 286,291 **** --- 291,301 ---- 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; diff --git a/contrib/pgcrypto/pgp-s2k.c b/contrib/pgcrypto/pgp-s2k.c new file mode 100644 index 193dd95..baa8e88 *** a/contrib/pgcrypto/pgp-s2k.c --- b/contrib/pgcrypto/pgp-s2k.c *************** calc_s2k_iter_salted(PGP_S2K *s2k, PX_MD *** 137,143 **** count; cval = s2k->iter; ! count = ((unsigned) 16 + (cval & 15)) << ((cval >> 4) + 6); md_rlen = px_md_result_size(md); --- 137,143 ---- count; cval = s2k->iter; ! count = iter_to_count(cval); md_rlen = px_md_result_size(md); *************** calc_s2k_iter_salted(PGP_S2K *s2k, PX_MD *** 200,215 **** * Too small: weak * Too big: slow * gpg defaults to 96 => 65536 iters ! * let it float a bit: 96 + 32 => 262144 iters */ static int ! decide_count(unsigned rand_byte) { ! return 96 + (rand_byte & 0x1F); } int ! pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo) { int res = 0; uint8 tmp; --- 200,223 ---- * Too small: weak * Too big: slow * gpg defaults to 96 => 65536 iters ! * For our default let it float a bit: 96 + 32 => 262144 iters ! * Otherwise, find the smallest iteration which provides ! * at least the specified count. */ static int ! decide_count(unsigned rand_byte,int count) { ! int iter; ! if (count == -1) ! return 96 + (rand_byte & 0x1F); ! for (iter=0; iter<=255; iter++) ! if (iter_to_count(iter) >= count) ! return iter; ! return 255; } int ! pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo, int count) { int res = 0; uint8 tmp; *************** pgp_s2k_fill(PGP_S2K *s2k, int mode, int *** 231,237 **** res = px_get_pseudo_random_bytes(&tmp, 1); if (res < 0) break; ! s2k->iter = decide_count(tmp); break; default: res = PXE_PGP_BAD_S2K_MODE; --- 239,245 ---- res = px_get_pseudo_random_bytes(&tmp, 1); if (res < 0) break; ! s2k->iter = decide_count(tmp,count); break; default: res = PXE_PGP_BAD_S2K_MODE; diff --git a/contrib/pgcrypto/pgp.c b/contrib/pgcrypto/pgp.c new file mode 100644 index 03fe48f..f0e016c *** a/contrib/pgcrypto/pgp.c --- b/contrib/pgcrypto/pgp.c *************** *** 40,45 **** --- 40,46 ---- 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; *************** pgp_init(PGP_Context **ctx_p) *** 206,211 **** --- 207,213 ---- 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; *************** pgp_set_s2k_mode(PGP_Context *ctx, int m *** 270,275 **** --- 272,288 ---- } 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) { switch (algo) diff --git a/contrib/pgcrypto/pgp.h b/contrib/pgcrypto/pgp.h new file mode 100644 index 62b8517..c3e86e3 *** a/contrib/pgcrypto/pgp.h --- b/contrib/pgcrypto/pgp.h *************** *** 34,39 **** --- 34,41 ---- #include "mbuf.h" #include "px.h" + #define iter_to_count(cval) (((unsigned) 16 + (cval & 15)) << ((cval >> 4) + 6)) + enum PGP_S2K_TYPE { PGP_S2K_SIMPLE = 0, *************** struct PGP_Context *** 138,143 **** --- 140,146 ---- */ PGP_S2K s2k; int s2k_mode; + int s2k_count; int s2k_digest_algo; int s2k_cipher_algo; int cipher_algo; *************** const char *pgp_get_cipher_name(int code *** 243,248 **** --- 246,252 ---- 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); *************** int pgp_load_cipher(int c, PX_Cipher * *** 267,273 **** 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_read(PullFilter *src, PGP_S2K *s2k); int pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int klen); --- 271,277 ---- 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 count); int pgp_s2k_read(PullFilter *src, PGP_S2K *s2k); int pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int klen); diff --git a/contrib/pgcrypto/sql/pgp-encrypt.sql b/contrib/pgcrypto/sql/pgp-encrypt.sql new file mode 100644 index a9ac0b9..1dd575d *** a/contrib/pgcrypto/sql/pgp-encrypt.sql --- b/contrib/pgcrypto/sql/pgp-encrypt.sql *************** select pgp_sym_decrypt( *** 55,60 **** --- 55,68 ---- 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'); + select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-count=65000000'), + 'key', 'expect-s2k-count=65000000'); -- rounded up + -- s2k digest change select pgp_sym_decrypt( pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'), diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml new file mode 100644 index bfcbe02..21a884b *** a/doc/src/sgml/pgcrypto.sgml --- b/doc/src/sgml/pgcrypto.sgml *************** Applies to: pgp_sym_encrypt *** 859,864 **** --- 859,877 ---- </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> <para>
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers