summaryrefslogtreecommitdiffstats
path: root/contrib/pgcrypto
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
commit5e45211a64149b3c659b90ff2de6fa982a5a93ed (patch)
tree739caf8c461053357daa9f162bef34516c7bf452 /contrib/pgcrypto
parentInitial commit. (diff)
downloadpostgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.tar.xz
postgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.zip
Adding upstream version 15.5.upstream/15.5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/pgcrypto')
-rw-r--r--contrib/pgcrypto/.gitignore4
-rw-r--r--contrib/pgcrypto/Makefile69
-rw-r--r--contrib/pgcrypto/crypt-blowfish.c756
-rw-r--r--contrib/pgcrypto/crypt-des.c791
-rw-r--r--contrib/pgcrypto/crypt-gensalt.c187
-rw-r--r--contrib/pgcrypto/crypt-md5.c169
-rw-r--r--contrib/pgcrypto/expected/3des.out65
-rw-r--r--contrib/pgcrypto/expected/blowfish.out142
-rw-r--r--contrib/pgcrypto/expected/blowfish_1.out62
-rw-r--r--contrib/pgcrypto/expected/cast5.out73
-rw-r--r--contrib/pgcrypto/expected/cast5_1.out33
-rw-r--r--contrib/pgcrypto/expected/crypt-blowfish.out36
-rw-r--r--contrib/pgcrypto/expected/crypt-des.out31
-rw-r--r--contrib/pgcrypto/expected/crypt-md5.out27
-rw-r--r--contrib/pgcrypto/expected/crypt-xdes.out51
-rw-r--r--contrib/pgcrypto/expected/des.out58
-rw-r--r--contrib/pgcrypto/expected/des_1.out26
-rw-r--r--contrib/pgcrypto/expected/hmac-md5.out72
-rw-r--r--contrib/pgcrypto/expected/hmac-sha1.out72
-rw-r--r--contrib/pgcrypto/expected/init.out13
-rw-r--r--contrib/pgcrypto/expected/md5.out45
-rw-r--r--contrib/pgcrypto/expected/pgp-armor.out370
-rw-r--r--contrib/pgcrypto/expected/pgp-compression.out80
-rw-r--r--contrib/pgcrypto/expected/pgp-decrypt.out419
-rw-r--r--contrib/pgcrypto/expected/pgp-decrypt_1.out415
-rw-r--r--contrib/pgcrypto/expected/pgp-encrypt.out208
-rw-r--r--contrib/pgcrypto/expected/pgp-info.out79
-rw-r--r--contrib/pgcrypto/expected/pgp-pubkey-decrypt.out656
-rw-r--r--contrib/pgcrypto/expected/pgp-pubkey-decrypt_1.out652
-rw-r--r--contrib/pgcrypto/expected/pgp-pubkey-encrypt.out68
-rw-r--r--contrib/pgcrypto/expected/pgp-zlib-DISABLED.out1
-rw-r--r--contrib/pgcrypto/expected/rijndael.out137
-rw-r--r--contrib/pgcrypto/expected/sha1.out45
-rw-r--r--contrib/pgcrypto/expected/sha2.out139
-rw-r--r--contrib/pgcrypto/mbuf.c560
-rw-r--r--contrib/pgcrypto/mbuf.h124
-rw-r--r--contrib/pgcrypto/openssl.c829
-rw-r--r--contrib/pgcrypto/pgcrypto--1.0--1.1.sql9
-rw-r--r--contrib/pgcrypto/pgcrypto--1.1--1.2.sql14
-rw-r--r--contrib/pgcrypto/pgcrypto--1.2--1.3.sql41
-rw-r--r--contrib/pgcrypto/pgcrypto--1.3.sql217
-rw-r--r--contrib/pgcrypto/pgcrypto.c475
-rw-r--r--contrib/pgcrypto/pgcrypto.control6
-rw-r--r--contrib/pgcrypto/pgcrypto.h37
-rw-r--r--contrib/pgcrypto/pgp-armor.c488
-rw-r--r--contrib/pgcrypto/pgp-cfb.c265
-rw-r--r--contrib/pgcrypto/pgp-compress.c346
-rw-r--r--contrib/pgcrypto/pgp-decrypt.c1212
-rw-r--r--contrib/pgcrypto/pgp-encrypt.c704
-rw-r--r--contrib/pgcrypto/pgp-info.c235
-rw-r--r--contrib/pgcrypto/pgp-mpi-openssl.c284
-rw-r--r--contrib/pgcrypto/pgp-mpi.c142
-rw-r--r--contrib/pgcrypto/pgp-pgsql.c1006
-rw-r--r--contrib/pgcrypto/pgp-pubdec.c235
-rw-r--r--contrib/pgcrypto/pgp-pubenc.c244
-rw-r--r--contrib/pgcrypto/pgp-pubkey.c583
-rw-r--r--contrib/pgcrypto/pgp-s2k.c308
-rw-r--r--contrib/pgcrypto/pgp.c370
-rw-r--r--contrib/pgcrypto/pgp.h327
-rw-r--r--contrib/pgcrypto/px-crypt.c164
-rw-r--r--contrib/pgcrypto/px-crypt.h82
-rw-r--r--contrib/pgcrypto/px-hmac.c176
-rw-r--r--contrib/pgcrypto/px.c341
-rw-r--r--contrib/pgcrypto/px.h228
-rw-r--r--contrib/pgcrypto/sql/3des.sql25
-rw-r--r--contrib/pgcrypto/sql/blowfish.sql53
-rw-r--r--contrib/pgcrypto/sql/cast5.sql32
-rw-r--r--contrib/pgcrypto/sql/crypt-blowfish.sql26
-rw-r--r--contrib/pgcrypto/sql/crypt-des.sql21
-rw-r--r--contrib/pgcrypto/sql/crypt-md5.sql17
-rw-r--r--contrib/pgcrypto/sql/crypt-xdes.sql33
-rw-r--r--contrib/pgcrypto/sql/des.sql24
-rw-r--r--contrib/pgcrypto/sql/hmac-md5.sql44
-rw-r--r--contrib/pgcrypto/sql/hmac-sha1.sql44
-rw-r--r--contrib/pgcrypto/sql/init.sql11
-rw-r--r--contrib/pgcrypto/sql/md5.sql11
-rw-r--r--contrib/pgcrypto/sql/pgp-armor.sql214
-rw-r--r--contrib/pgcrypto/sql/pgp-compression.sql51
-rw-r--r--contrib/pgcrypto/sql/pgp-decrypt.sql309
-rw-r--r--contrib/pgcrypto/sql/pgp-encrypt.sql104
-rw-r--r--contrib/pgcrypto/sql/pgp-info.sql22
-rw-r--r--contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql647
-rw-r--r--contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql48
-rw-r--r--contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql1
-rw-r--r--contrib/pgcrypto/sql/rijndael.sql72
-rw-r--r--contrib/pgcrypto/sql/sha1.sql11
-rw-r--r--contrib/pgcrypto/sql/sha2.sql33
87 files changed, 17956 insertions, 0 deletions
diff --git a/contrib/pgcrypto/.gitignore b/contrib/pgcrypto/.gitignore
new file mode 100644
index 0000000..5dcb3ff
--- /dev/null
+++ b/contrib/pgcrypto/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/pgcrypto/Makefile b/contrib/pgcrypto/Makefile
new file mode 100644
index 0000000..7fb59f5
--- /dev/null
+++ b/contrib/pgcrypto/Makefile
@@ -0,0 +1,69 @@
+# contrib/pgcrypto/Makefile
+
+ZLIB_TST = pgp-compression
+ZLIB_OFF_TST = pgp-zlib-DISABLED
+
+CF_PGP_TESTS = $(if $(subst no,,$(with_zlib)), $(ZLIB_TST), $(ZLIB_OFF_TST))
+
+OBJS = \
+ $(WIN32RES) \
+ crypt-blowfish.o \
+ crypt-des.o \
+ crypt-gensalt.o \
+ crypt-md5.o \
+ mbuf.o \
+ openssl.o \
+ pgcrypto.o \
+ pgp-armor.o \
+ pgp-cfb.o \
+ pgp-compress.o \
+ pgp-decrypt.o \
+ pgp-encrypt.o \
+ pgp-info.o \
+ pgp-mpi.o \
+ pgp-mpi-openssl.o \
+ pgp-pgsql.o \
+ pgp-pubdec.o \
+ pgp-pubenc.o \
+ pgp-pubkey.o \
+ pgp-s2k.o \
+ pgp.o \
+ px-crypt.o \
+ px-hmac.o \
+ px.o
+
+MODULE_big = pgcrypto
+
+EXTENSION = pgcrypto
+DATA = pgcrypto--1.3.sql pgcrypto--1.2--1.3.sql pgcrypto--1.1--1.2.sql \
+ pgcrypto--1.0--1.1.sql
+PGFILEDESC = "pgcrypto - cryptographic functions"
+
+REGRESS = init md5 sha1 hmac-md5 hmac-sha1 blowfish rijndael \
+ sha2 des 3des cast5 \
+ crypt-des crypt-md5 crypt-blowfish crypt-xdes \
+ pgp-armor pgp-decrypt pgp-encrypt $(CF_PGP_TESTS) \
+ pgp-pubkey-decrypt pgp-pubkey-encrypt pgp-info
+
+EXTRA_CLEAN = gen-rtab
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/pgcrypto
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+# Add libraries that pgcrypto depends (or might depend) on into the
+# shared library link. (The order in which you list them here doesn't
+# matter.)
+SHLIB_LINK += $(filter -lcrypto -lz, $(LIBS))
+ifeq ($(PORTNAME), win32)
+SHLIB_LINK += $(filter -leay32, $(LIBS))
+# those must be at the end
+SHLIB_LINK += -lws2_32
+endif
diff --git a/contrib/pgcrypto/crypt-blowfish.c b/contrib/pgcrypto/crypt-blowfish.c
new file mode 100644
index 0000000..a663852
--- /dev/null
+++ b/contrib/pgcrypto/crypt-blowfish.c
@@ -0,0 +1,756 @@
+/*
+ * contrib/pgcrypto/crypt-blowfish.c
+ *
+ * This code comes from John the Ripper password cracker, with reentrant
+ * and crypt(3) interfaces added, but optimizations specific to password
+ * cracking removed.
+ *
+ * Written by Solar Designer <solar at openwall.com> in 1998-2002 and
+ * placed in the public domain.
+ *
+ * There's absolutely no warranty.
+ *
+ * It is my intent that you should be able to use this on your system,
+ * as a part of a software package, or anywhere else to improve security,
+ * ensure compatibility, or for any other purpose. I would appreciate
+ * it if you give credit where it is due and keep your modifications in
+ * the public domain as well, but I don't require that in order to let
+ * you place this code and any modifications you make under a license
+ * of your choice.
+ *
+ * This implementation is compatible with OpenBSD bcrypt.c (version 2a)
+ * by Niels Provos <provos at citi.umich.edu>, and uses some of his
+ * ideas. The password hashing algorithm was designed by David Mazieres
+ * <dm at lcs.mit.edu>.
+ *
+ * There's a paper on the algorithm that explains its design decisions:
+ *
+ * http://www.usenix.org/events/usenix99/provos.html
+ *
+ * Some of the tricks in BF_ROUND might be inspired by Eric Young's
+ * Blowfish library (I can't be sure if I would think of something if I
+ * hadn't seen his code).
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+
+#include "px-crypt.h"
+#include "px.h"
+
+#ifdef __i386__
+#define BF_ASM 0 /* 1 */
+#define BF_SCALE 1
+#elif defined(__x86_64__) || defined(__alpha__) || defined(__hppa__)
+#define BF_ASM 0
+#define BF_SCALE 1
+#else
+#define BF_ASM 0
+#define BF_SCALE 0
+#endif
+
+typedef unsigned int BF_word;
+typedef signed int BF_word_signed;
+
+/* Number of Blowfish rounds, this is also hardcoded into a few places */
+#define BF_N 16
+
+typedef BF_word BF_key[BF_N + 2];
+
+typedef struct
+{
+ BF_word S[4][0x100];
+ BF_key P;
+} BF_ctx;
+
+/*
+ * Magic IV for 64 Blowfish encryptions that we do at the end.
+ * The string is "OrpheanBeholderScryDoubt" on big-endian.
+ */
+static BF_word BF_magic_w[6] = {
+ 0x4F727068, 0x65616E42, 0x65686F6C,
+ 0x64657253, 0x63727944, 0x6F756274
+};
+
+/*
+ * P-box and S-box tables initialized with digits of Pi.
+ */
+static BF_ctx BF_init_state = {
+ {
+ {
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
+ }, {
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
+ }, {
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
+ }, {
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
+ }
+ }, {
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b
+ }
+};
+
+static unsigned char BF_itoa64[64 + 1] =
+"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+static unsigned char BF_atoi64[0x60] = {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64,
+ 64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64,
+ 64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64
+};
+
+#define BF_safe_atoi64(dst, src) \
+do { \
+ tmp = (unsigned char)(src); \
+ if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
+ tmp = BF_atoi64[tmp]; \
+ if (tmp > 63) return -1; \
+ (dst) = tmp; \
+} while (0)
+
+static int
+BF_decode(BF_word *dst, const char *src, int size)
+{
+ unsigned char *dptr = (unsigned char *) dst;
+ unsigned char *end = dptr + size;
+ const unsigned char *sptr = (const unsigned char *) src;
+ unsigned int tmp,
+ c1,
+ c2,
+ c3,
+ c4;
+
+ do
+ {
+ BF_safe_atoi64(c1, *sptr++);
+ BF_safe_atoi64(c2, *sptr++);
+ *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4);
+ if (dptr >= end)
+ break;
+
+ BF_safe_atoi64(c3, *sptr++);
+ *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2);
+ if (dptr >= end)
+ break;
+
+ BF_safe_atoi64(c4, *sptr++);
+ *dptr++ = ((c3 & 0x03) << 6) | c4;
+ } while (dptr < end);
+
+ return 0;
+}
+
+static void
+BF_encode(char *dst, const BF_word *src, int size)
+{
+ const unsigned char *sptr = (const unsigned char *) src;
+ const unsigned char *end = sptr + size;
+ unsigned char *dptr = (unsigned char *) dst;
+ unsigned int c1,
+ c2;
+
+ do
+ {
+ c1 = *sptr++;
+ *dptr++ = BF_itoa64[c1 >> 2];
+ c1 = (c1 & 0x03) << 4;
+ if (sptr >= end)
+ {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 4;
+ *dptr++ = BF_itoa64[c1];
+ c1 = (c2 & 0x0f) << 2;
+ if (sptr >= end)
+ {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 6;
+ *dptr++ = BF_itoa64[c1];
+ *dptr++ = BF_itoa64[c2 & 0x3f];
+ } while (sptr < end);
+}
+
+static void
+BF_swap(BF_word *x, int count)
+{
+ /* Swap on little-endian hardware, else do nothing */
+#ifndef WORDS_BIGENDIAN
+ BF_word tmp;
+
+ do
+ {
+ tmp = *x;
+ tmp = (tmp << 16) | (tmp >> 16);
+ *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF);
+ } while (--count);
+#endif
+}
+
+#if BF_SCALE
+/* Architectures which can shift addresses left by 2 bits with no extra cost */
+#define BF_ROUND(L, R, N) \
+ tmp1 = (L) & 0xFF; \
+ tmp2 = (L) >> 8; \
+ tmp2 &= 0xFF; \
+ tmp3 = (L) >> 16; \
+ tmp3 &= 0xFF; \
+ tmp4 = (L) >> 24; \
+ tmp1 = data.ctx.S[3][tmp1]; \
+ tmp2 = data.ctx.S[2][tmp2]; \
+ tmp3 = data.ctx.S[1][tmp3]; \
+ tmp3 += data.ctx.S[0][tmp4]; \
+ tmp3 ^= tmp2; \
+ (R) ^= data.ctx.P[(N) + 1]; \
+ tmp3 += tmp1; \
+ (R) ^= tmp3
+#else
+/* Architectures with no complicated addressing modes supported */
+#define BF_INDEX(S, i) \
+ (*((BF_word *)(((unsigned char *)(S)) + (i))))
+#define BF_ROUND(L, R, N) \
+ tmp1 = (L) & 0xFF; \
+ tmp1 <<= 2; \
+ tmp2 = (L) >> 6; \
+ tmp2 &= 0x3FC; \
+ tmp3 = (L) >> 14; \
+ tmp3 &= 0x3FC; \
+ tmp4 = (L) >> 22; \
+ tmp4 &= 0x3FC; \
+ tmp1 = BF_INDEX(data.ctx.S[3], tmp1); \
+ tmp2 = BF_INDEX(data.ctx.S[2], tmp2); \
+ tmp3 = BF_INDEX(data.ctx.S[1], tmp3); \
+ tmp3 += BF_INDEX(data.ctx.S[0], tmp4); \
+ tmp3 ^= tmp2; \
+ (R) ^= data.ctx.P[(N) + 1]; \
+ tmp3 += tmp1; \
+ (R) ^= tmp3
+#endif
+
+/*
+ * Encrypt one block, BF_N is hardcoded here.
+ */
+#define BF_ENCRYPT \
+ L ^= data.ctx.P[0]; \
+ BF_ROUND(L, R, 0); \
+ BF_ROUND(R, L, 1); \
+ BF_ROUND(L, R, 2); \
+ BF_ROUND(R, L, 3); \
+ BF_ROUND(L, R, 4); \
+ BF_ROUND(R, L, 5); \
+ BF_ROUND(L, R, 6); \
+ BF_ROUND(R, L, 7); \
+ BF_ROUND(L, R, 8); \
+ BF_ROUND(R, L, 9); \
+ BF_ROUND(L, R, 10); \
+ BF_ROUND(R, L, 11); \
+ BF_ROUND(L, R, 12); \
+ BF_ROUND(R, L, 13); \
+ BF_ROUND(L, R, 14); \
+ BF_ROUND(R, L, 15); \
+ tmp4 = R; \
+ R = L; \
+ L = tmp4 ^ data.ctx.P[BF_N + 1]
+
+#if BF_ASM
+
+extern void _BF_body_r(BF_ctx *ctx);
+
+#define BF_body() \
+ _BF_body_r(&data.ctx)
+#else
+
+#define BF_body() \
+do { \
+ L = R = 0; \
+ ptr = data.ctx.P; \
+ do { \
+ ptr += 2; \
+ BF_ENCRYPT; \
+ *(ptr - 2) = L; \
+ *(ptr - 1) = R; \
+ } while (ptr < &data.ctx.P[BF_N + 2]); \
+\
+ ptr = data.ctx.S[0]; \
+ do { \
+ ptr += 2; \
+ BF_ENCRYPT; \
+ *(ptr - 2) = L; \
+ *(ptr - 1) = R; \
+ } while (ptr < &data.ctx.S[3][0xFF]); \
+} while (0)
+#endif
+
+static void
+BF_set_key(const char *key, BF_key expanded, BF_key initial,
+ int sign_extension_bug)
+{
+ const char *ptr = key;
+ int i,
+ j;
+ BF_word tmp;
+
+ for (i = 0; i < BF_N + 2; i++)
+ {
+ tmp = 0;
+ for (j = 0; j < 4; j++)
+ {
+ tmp <<= 8;
+ if (sign_extension_bug)
+ tmp |= (BF_word_signed) (signed char) *ptr;
+ else
+ tmp |= (unsigned char) *ptr;
+
+ if (!*ptr)
+ ptr = key;
+ else
+ ptr++;
+ }
+
+ expanded[i] = tmp;
+ initial[i] = BF_init_state.P[i] ^ tmp;
+ }
+}
+
+char *
+_crypt_blowfish_rn(const char *key, const char *setting,
+ char *output, int size)
+{
+ struct
+ {
+ BF_ctx ctx;
+ BF_key expanded_key;
+ union
+ {
+ BF_word salt[4];
+ BF_word output[6];
+ } binary;
+ } data;
+ BF_word L,
+ R;
+ BF_word tmp1,
+ tmp2,
+ tmp3,
+ tmp4;
+ BF_word *ptr;
+ BF_word count;
+ int i;
+
+ if (size < 7 + 22 + 31 + 1)
+ return NULL;
+
+ /*
+ * Blowfish salt value must be formatted as follows: "$2a$" or "$2x$", a
+ * two digit cost parameter, "$", and 22 digits from the alphabet
+ * "./0-9A-Za-z". -- from the PHP crypt docs. Apparently we enforce a few
+ * more restrictions on the count in the salt as well.
+ */
+ if (strlen(setting) < 29)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid salt")));
+
+ if (setting[0] != '$' ||
+ setting[1] != '2' ||
+ (setting[2] != 'a' && setting[2] != 'x') ||
+ setting[3] != '$' ||
+ setting[4] < '0' || setting[4] > '3' ||
+ setting[5] < '0' || setting[5] > '9' ||
+ (setting[4] == '3' && setting[5] > '1') ||
+ setting[6] != '$')
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid salt")));
+ }
+
+ count = (BF_word) 1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
+ if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16))
+ {
+ px_memset(data.binary.salt, 0, sizeof(data.binary.salt));
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid salt")));
+ }
+ BF_swap(data.binary.salt, 4);
+
+ BF_set_key(key, data.expanded_key, data.ctx.P, setting[2] == 'x');
+
+ memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
+
+ L = R = 0;
+ for (i = 0; i < BF_N + 2; i += 2)
+ {
+ L ^= data.binary.salt[i & 2];
+ R ^= data.binary.salt[(i & 2) + 1];
+ BF_ENCRYPT;
+ data.ctx.P[i] = L;
+ data.ctx.P[i + 1] = R;
+ }
+
+ ptr = data.ctx.S[0];
+ do
+ {
+ ptr += 4;
+ L ^= data.binary.salt[(BF_N + 2) & 3];
+ R ^= data.binary.salt[(BF_N + 3) & 3];
+ BF_ENCRYPT;
+ *(ptr - 4) = L;
+ *(ptr - 3) = R;
+
+ L ^= data.binary.salt[(BF_N + 4) & 3];
+ R ^= data.binary.salt[(BF_N + 5) & 3];
+ BF_ENCRYPT;
+ *(ptr - 2) = L;
+ *(ptr - 1) = R;
+ } while (ptr < &data.ctx.S[3][0xFF]);
+
+ do
+ {
+ CHECK_FOR_INTERRUPTS();
+
+ data.ctx.P[0] ^= data.expanded_key[0];
+ data.ctx.P[1] ^= data.expanded_key[1];
+ data.ctx.P[2] ^= data.expanded_key[2];
+ data.ctx.P[3] ^= data.expanded_key[3];
+ data.ctx.P[4] ^= data.expanded_key[4];
+ data.ctx.P[5] ^= data.expanded_key[5];
+ data.ctx.P[6] ^= data.expanded_key[6];
+ data.ctx.P[7] ^= data.expanded_key[7];
+ data.ctx.P[8] ^= data.expanded_key[8];
+ data.ctx.P[9] ^= data.expanded_key[9];
+ data.ctx.P[10] ^= data.expanded_key[10];
+ data.ctx.P[11] ^= data.expanded_key[11];
+ data.ctx.P[12] ^= data.expanded_key[12];
+ data.ctx.P[13] ^= data.expanded_key[13];
+ data.ctx.P[14] ^= data.expanded_key[14];
+ data.ctx.P[15] ^= data.expanded_key[15];
+ data.ctx.P[16] ^= data.expanded_key[16];
+ data.ctx.P[17] ^= data.expanded_key[17];
+
+ BF_body();
+
+ tmp1 = data.binary.salt[0];
+ tmp2 = data.binary.salt[1];
+ tmp3 = data.binary.salt[2];
+ tmp4 = data.binary.salt[3];
+ data.ctx.P[0] ^= tmp1;
+ data.ctx.P[1] ^= tmp2;
+ data.ctx.P[2] ^= tmp3;
+ data.ctx.P[3] ^= tmp4;
+ data.ctx.P[4] ^= tmp1;
+ data.ctx.P[5] ^= tmp2;
+ data.ctx.P[6] ^= tmp3;
+ data.ctx.P[7] ^= tmp4;
+ data.ctx.P[8] ^= tmp1;
+ data.ctx.P[9] ^= tmp2;
+ data.ctx.P[10] ^= tmp3;
+ data.ctx.P[11] ^= tmp4;
+ data.ctx.P[12] ^= tmp1;
+ data.ctx.P[13] ^= tmp2;
+ data.ctx.P[14] ^= tmp3;
+ data.ctx.P[15] ^= tmp4;
+ data.ctx.P[16] ^= tmp1;
+ data.ctx.P[17] ^= tmp2;
+
+ BF_body();
+ } while (--count);
+
+ for (i = 0; i < 6; i += 2)
+ {
+ L = BF_magic_w[i];
+ R = BF_magic_w[i + 1];
+
+ count = 64;
+ do
+ {
+ BF_ENCRYPT;
+ } while (--count);
+
+ data.binary.output[i] = L;
+ data.binary.output[i + 1] = R;
+ }
+
+ memcpy(output, setting, 7 + 22 - 1);
+ output[7 + 22 - 1] = BF_itoa64[(int)
+ BF_atoi64[(int) setting[7 + 22 - 1] - 0x20] & 0x30];
+
+/* This has to be bug-compatible with the original implementation, so
+ * only encode 23 of the 24 bytes. :-) */
+ BF_swap(data.binary.output, 6);
+ BF_encode(&output[7 + 22], data.binary.output, 23);
+ output[7 + 22 + 31] = '\0';
+
+/* Overwrite the most obvious sensitive data we have on the stack. Note
+ * that this does not guarantee there's no sensitive data left on the
+ * stack and/or in registers; I'm not aware of portable code that does. */
+ px_memset(&data, 0, sizeof(data));
+
+ return output;
+}
diff --git a/contrib/pgcrypto/crypt-des.c b/contrib/pgcrypto/crypt-des.c
new file mode 100644
index 0000000..98c30ea
--- /dev/null
+++ b/contrib/pgcrypto/crypt-des.c
@@ -0,0 +1,791 @@
+/*
+ * FreeSec: libcrypt for NetBSD
+ *
+ * contrib/pgcrypto/crypt-des.c
+ *
+ * Copyright (c) 1994 David Burren
+ * All rights reserved.
+ *
+ * Adapted for FreeBSD-2.0 by Geoffrey M. Rehmet
+ * this file should now *only* export crypt(), in order to make
+ * binaries of libcrypt exportable from the USA
+ *
+ * Adapted for FreeBSD-4.0 by Mark R V Murray
+ * this file should now *only* export px_crypt_des(), in order to make
+ * a module that can be optionally included in libcrypt.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of other contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/secure/lib/libcrypt/crypt-des.c,v 1.12 1999/09/20 12:39:20 markm Exp $
+ *
+ * This is an original implementation of the DES and the crypt(3) interfaces
+ * by David Burren <davidb@werj.com.au>.
+ *
+ * An excellent reference on the underlying algorithm (and related
+ * algorithms) is:
+ *
+ * B. Schneier, Applied Cryptography: protocols, algorithms,
+ * and source code in C, John Wiley & Sons, 1994.
+ *
+ * Note that in that book's description of DES the lookups for the initial,
+ * pbox, and final permutations are inverted (this has been brought to the
+ * attention of the author). A list of errata for this book has been
+ * posted to the sci.crypt newsgroup by the author and is available for FTP.
+ *
+ * ARCHITECTURE ASSUMPTIONS:
+ * It is assumed that the 8-byte arrays passed by reference can be
+ * addressed as arrays of uint32's (ie. the CPU is not picky about
+ * alignment).
+ */
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "port/pg_bswap.h"
+
+#include "px-crypt.h"
+
+#define _PASSWORD_EFMT1 '_'
+
+static const char _crypt_a64[] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static uint8 IP[64] = {
+ 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
+};
+
+static uint8 inv_key_perm[64];
+static uint8 u_key_perm[56];
+static uint8 key_perm[56] = {
+ 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
+ 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
+ 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
+ 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
+};
+
+static uint8 key_shifts[16] = {
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+static uint8 inv_comp_perm[56];
+static uint8 comp_perm[48] = {
+ 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
+ 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
+ 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
+ 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+};
+
+/*
+ * No E box is used, as it's replaced by some ANDs, shifts, and ORs.
+ */
+
+static uint8 u_sbox[8][64];
+static uint8 sbox[8][64] = {
+ {
+ 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
+ },
+ {
+ 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
+ },
+ {
+ 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
+ },
+ {
+ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
+ },
+ {
+ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
+ },
+ {
+ 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
+ },
+ {
+ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
+ },
+ {
+ 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
+ }
+};
+
+static uint8 un_pbox[32];
+static uint8 pbox[32] = {
+ 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
+ 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
+};
+
+static uint32 _crypt_bits32[32] =
+{
+ 0x80000000, 0x40000000, 0x20000000, 0x10000000,
+ 0x08000000, 0x04000000, 0x02000000, 0x01000000,
+ 0x00800000, 0x00400000, 0x00200000, 0x00100000,
+ 0x00080000, 0x00040000, 0x00020000, 0x00010000,
+ 0x00008000, 0x00004000, 0x00002000, 0x00001000,
+ 0x00000800, 0x00000400, 0x00000200, 0x00000100,
+ 0x00000080, 0x00000040, 0x00000020, 0x00000010,
+ 0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+static uint8 _crypt_bits8[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
+
+static uint32 saltbits;
+static long old_salt;
+static uint32 *bits28,
+ *bits24;
+static uint8 init_perm[64],
+ final_perm[64];
+static uint32 en_keysl[16],
+ en_keysr[16];
+static uint32 de_keysl[16],
+ de_keysr[16];
+static int des_initialised = 0;
+static uint8 m_sbox[4][4096];
+static uint32 psbox[4][256];
+static uint32 ip_maskl[8][256],
+ ip_maskr[8][256];
+static uint32 fp_maskl[8][256],
+ fp_maskr[8][256];
+static uint32 key_perm_maskl[8][128],
+ key_perm_maskr[8][128];
+static uint32 comp_maskl[8][128],
+ comp_maskr[8][128];
+static uint32 old_rawkey0,
+ old_rawkey1;
+
+static inline int
+ascii_to_bin(char ch)
+{
+ if (ch > 'z')
+ return 0;
+ if (ch >= 'a')
+ return (ch - 'a' + 38);
+ if (ch > 'Z')
+ return 0;
+ if (ch >= 'A')
+ return (ch - 'A' + 12);
+ if (ch > '9')
+ return 0;
+ if (ch >= '.')
+ return (ch - '.');
+ return 0;
+}
+
+static void
+des_init(void)
+{
+ int i,
+ j,
+ b,
+ k,
+ inbit,
+ obit;
+ uint32 *p,
+ *il,
+ *ir,
+ *fl,
+ *fr;
+
+ old_rawkey0 = old_rawkey1 = 0L;
+ saltbits = 0L;
+ old_salt = 0L;
+ bits24 = (bits28 = _crypt_bits32 + 4) + 4;
+
+ /*
+ * Invert the S-boxes, reordering the input bits.
+ */
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 64; j++)
+ {
+ b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf);
+ u_sbox[i][j] = sbox[i][b];
+ }
+
+ /*
+ * Convert the inverted S-boxes into 4 arrays of 8 bits. Each will handle
+ * 12 bits of the S-box input.
+ */
+ for (b = 0; b < 4; b++)
+ for (i = 0; i < 64; i++)
+ for (j = 0; j < 64; j++)
+ m_sbox[b][(i << 6) | j] =
+ (u_sbox[(b << 1)][i] << 4) |
+ u_sbox[(b << 1) + 1][j];
+
+ /*
+ * Set up the initial & final permutations into a useful form, and
+ * initialise the inverted key permutation.
+ */
+ for (i = 0; i < 64; i++)
+ {
+ init_perm[final_perm[i] = IP[i] - 1] = i;
+ inv_key_perm[i] = 255;
+ }
+
+ /*
+ * Invert the key permutation and initialise the inverted key compression
+ * permutation.
+ */
+ for (i = 0; i < 56; i++)
+ {
+ u_key_perm[i] = key_perm[i] - 1;
+ inv_key_perm[key_perm[i] - 1] = i;
+ inv_comp_perm[i] = 255;
+ }
+
+ /*
+ * Invert the key compression permutation.
+ */
+ for (i = 0; i < 48; i++)
+ inv_comp_perm[comp_perm[i] - 1] = i;
+
+ /*
+ * Set up the OR-mask arrays for the initial and final permutations, and
+ * for the key initial and compression permutations.
+ */
+ for (k = 0; k < 8; k++)
+ {
+ for (i = 0; i < 256; i++)
+ {
+ *(il = &ip_maskl[k][i]) = 0L;
+ *(ir = &ip_maskr[k][i]) = 0L;
+ *(fl = &fp_maskl[k][i]) = 0L;
+ *(fr = &fp_maskr[k][i]) = 0L;
+ for (j = 0; j < 8; j++)
+ {
+ inbit = 8 * k + j;
+ if (i & _crypt_bits8[j])
+ {
+ if ((obit = init_perm[inbit]) < 32)
+ *il |= _crypt_bits32[obit];
+ else
+ *ir |= _crypt_bits32[obit - 32];
+ if ((obit = final_perm[inbit]) < 32)
+ *fl |= _crypt_bits32[obit];
+ else
+ *fr |= _crypt_bits32[obit - 32];
+ }
+ }
+ }
+ for (i = 0; i < 128; i++)
+ {
+ *(il = &key_perm_maskl[k][i]) = 0L;
+ *(ir = &key_perm_maskr[k][i]) = 0L;
+ for (j = 0; j < 7; j++)
+ {
+ inbit = 8 * k + j;
+ if (i & _crypt_bits8[j + 1])
+ {
+ if ((obit = inv_key_perm[inbit]) == 255)
+ continue;
+ if (obit < 28)
+ *il |= bits28[obit];
+ else
+ *ir |= bits28[obit - 28];
+ }
+ }
+ *(il = &comp_maskl[k][i]) = 0L;
+ *(ir = &comp_maskr[k][i]) = 0L;
+ for (j = 0; j < 7; j++)
+ {
+ inbit = 7 * k + j;
+ if (i & _crypt_bits8[j + 1])
+ {
+ if ((obit = inv_comp_perm[inbit]) == 255)
+ continue;
+ if (obit < 24)
+ *il |= bits24[obit];
+ else
+ *ir |= bits24[obit - 24];
+ }
+ }
+ }
+ }
+
+ /*
+ * Invert the P-box permutation, and convert into OR-masks for handling
+ * the output of the S-box arrays setup above.
+ */
+ for (i = 0; i < 32; i++)
+ un_pbox[pbox[i] - 1] = i;
+
+ for (b = 0; b < 4; b++)
+ for (i = 0; i < 256; i++)
+ {
+ *(p = &psbox[b][i]) = 0L;
+ for (j = 0; j < 8; j++)
+ {
+ if (i & _crypt_bits8[j])
+ *p |= _crypt_bits32[un_pbox[8 * b + j]];
+ }
+ }
+
+ des_initialised = 1;
+}
+
+static void
+setup_salt(long salt)
+{
+ uint32 obit,
+ saltbit;
+ int i;
+
+ if (salt == old_salt)
+ return;
+ old_salt = salt;
+
+ saltbits = 0L;
+ saltbit = 1;
+ obit = 0x800000;
+ for (i = 0; i < 24; i++)
+ {
+ if (salt & saltbit)
+ saltbits |= obit;
+ saltbit <<= 1;
+ obit >>= 1;
+ }
+}
+
+static int
+des_setkey(const char *key)
+{
+ uint32 k0,
+ k1,
+ rawkey0,
+ rawkey1;
+ int shifts,
+ round;
+
+ if (!des_initialised)
+ des_init();
+
+ rawkey0 = pg_ntoh32(*(const uint32 *) key);
+ rawkey1 = pg_ntoh32(*(const uint32 *) (key + 4));
+
+ if ((rawkey0 | rawkey1)
+ && rawkey0 == old_rawkey0
+ && rawkey1 == old_rawkey1)
+ {
+ /*
+ * Already setup for this key. This optimization fails on a zero key
+ * (which is weak and has bad parity anyway) in order to simplify the
+ * starting conditions.
+ */
+ return 0;
+ }
+ old_rawkey0 = rawkey0;
+ old_rawkey1 = rawkey1;
+
+ /*
+ * Do key permutation and split into two 28-bit subkeys.
+ */
+ k0 = key_perm_maskl[0][rawkey0 >> 25]
+ | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f]
+ | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f]
+ | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f]
+ | key_perm_maskl[4][rawkey1 >> 25]
+ | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f]
+ | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f]
+ | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f];
+ k1 = key_perm_maskr[0][rawkey0 >> 25]
+ | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f]
+ | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f]
+ | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f]
+ | key_perm_maskr[4][rawkey1 >> 25]
+ | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f]
+ | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f]
+ | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f];
+
+ /*
+ * Rotate subkeys and do compression permutation.
+ */
+ shifts = 0;
+ for (round = 0; round < 16; round++)
+ {
+ uint32 t0,
+ t1;
+
+ shifts += key_shifts[round];
+
+ t0 = (k0 << shifts) | (k0 >> (28 - shifts));
+ t1 = (k1 << shifts) | (k1 >> (28 - shifts));
+
+ de_keysl[15 - round] =
+ en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f]
+ | comp_maskl[1][(t0 >> 14) & 0x7f]
+ | comp_maskl[2][(t0 >> 7) & 0x7f]
+ | comp_maskl[3][t0 & 0x7f]
+ | comp_maskl[4][(t1 >> 21) & 0x7f]
+ | comp_maskl[5][(t1 >> 14) & 0x7f]
+ | comp_maskl[6][(t1 >> 7) & 0x7f]
+ | comp_maskl[7][t1 & 0x7f];
+
+ de_keysr[15 - round] =
+ en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f]
+ | comp_maskr[1][(t0 >> 14) & 0x7f]
+ | comp_maskr[2][(t0 >> 7) & 0x7f]
+ | comp_maskr[3][t0 & 0x7f]
+ | comp_maskr[4][(t1 >> 21) & 0x7f]
+ | comp_maskr[5][(t1 >> 14) & 0x7f]
+ | comp_maskr[6][(t1 >> 7) & 0x7f]
+ | comp_maskr[7][t1 & 0x7f];
+ }
+ return 0;
+}
+
+static int
+do_des(uint32 l_in, uint32 r_in, uint32 *l_out, uint32 *r_out, int count)
+{
+ /*
+ * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format.
+ */
+ uint32 l,
+ r,
+ *kl,
+ *kr,
+ *kl1,
+ *kr1;
+ uint32 f,
+ r48l,
+ r48r;
+ int round;
+
+ if (count == 0)
+ return 1;
+ else if (count > 0)
+ {
+ /*
+ * Encrypting
+ */
+ kl1 = en_keysl;
+ kr1 = en_keysr;
+ }
+ else
+ {
+ /*
+ * Decrypting
+ */
+ count = -count;
+ kl1 = de_keysl;
+ kr1 = de_keysr;
+ }
+
+ /*
+ * Do initial permutation (IP).
+ */
+ l = ip_maskl[0][l_in >> 24]
+ | ip_maskl[1][(l_in >> 16) & 0xff]
+ | ip_maskl[2][(l_in >> 8) & 0xff]
+ | ip_maskl[3][l_in & 0xff]
+ | ip_maskl[4][r_in >> 24]
+ | ip_maskl[5][(r_in >> 16) & 0xff]
+ | ip_maskl[6][(r_in >> 8) & 0xff]
+ | ip_maskl[7][r_in & 0xff];
+ r = ip_maskr[0][l_in >> 24]
+ | ip_maskr[1][(l_in >> 16) & 0xff]
+ | ip_maskr[2][(l_in >> 8) & 0xff]
+ | ip_maskr[3][l_in & 0xff]
+ | ip_maskr[4][r_in >> 24]
+ | ip_maskr[5][(r_in >> 16) & 0xff]
+ | ip_maskr[6][(r_in >> 8) & 0xff]
+ | ip_maskr[7][r_in & 0xff];
+
+ while (count--)
+ {
+ CHECK_FOR_INTERRUPTS();
+
+ /*
+ * Do each round.
+ */
+ kl = kl1;
+ kr = kr1;
+ round = 16;
+ while (round--)
+ {
+ /*
+ * Expand R to 48 bits (simulate the E-box).
+ */
+ r48l = ((r & 0x00000001) << 23)
+ | ((r & 0xf8000000) >> 9)
+ | ((r & 0x1f800000) >> 11)
+ | ((r & 0x01f80000) >> 13)
+ | ((r & 0x001f8000) >> 15);
+
+ r48r = ((r & 0x0001f800) << 7)
+ | ((r & 0x00001f80) << 5)
+ | ((r & 0x000001f8) << 3)
+ | ((r & 0x0000001f) << 1)
+ | ((r & 0x80000000) >> 31);
+
+ /*
+ * Do salting for crypt() and friends, and XOR with the permuted
+ * key.
+ */
+ f = (r48l ^ r48r) & saltbits;
+ r48l ^= f ^ *kl++;
+ r48r ^= f ^ *kr++;
+
+ /*
+ * Do sbox lookups (which shrink it back to 32 bits) and do the
+ * pbox permutation at the same time.
+ */
+ f = psbox[0][m_sbox[0][r48l >> 12]]
+ | psbox[1][m_sbox[1][r48l & 0xfff]]
+ | psbox[2][m_sbox[2][r48r >> 12]]
+ | psbox[3][m_sbox[3][r48r & 0xfff]];
+
+ /*
+ * Now that we've permuted things, complete f().
+ */
+ f ^= l;
+ l = r;
+ r = f;
+ }
+ r = l;
+ l = f;
+ }
+
+ /*
+ * Do final permutation (inverse of IP).
+ */
+ *l_out = fp_maskl[0][l >> 24]
+ | fp_maskl[1][(l >> 16) & 0xff]
+ | fp_maskl[2][(l >> 8) & 0xff]
+ | fp_maskl[3][l & 0xff]
+ | fp_maskl[4][r >> 24]
+ | fp_maskl[5][(r >> 16) & 0xff]
+ | fp_maskl[6][(r >> 8) & 0xff]
+ | fp_maskl[7][r & 0xff];
+ *r_out = fp_maskr[0][l >> 24]
+ | fp_maskr[1][(l >> 16) & 0xff]
+ | fp_maskr[2][(l >> 8) & 0xff]
+ | fp_maskr[3][l & 0xff]
+ | fp_maskr[4][r >> 24]
+ | fp_maskr[5][(r >> 16) & 0xff]
+ | fp_maskr[6][(r >> 8) & 0xff]
+ | fp_maskr[7][r & 0xff];
+ return 0;
+}
+
+static int
+des_cipher(const char *in, char *out, long salt, int count)
+{
+ uint32 buffer[2];
+ uint32 l_out,
+ r_out,
+ rawl,
+ rawr;
+ int retval;
+
+ if (!des_initialised)
+ des_init();
+
+ setup_salt(salt);
+
+ /* copy data to avoid assuming input is word-aligned */
+ memcpy(buffer, in, sizeof(buffer));
+
+ rawl = pg_ntoh32(buffer[0]);
+ rawr = pg_ntoh32(buffer[1]);
+
+ retval = do_des(rawl, rawr, &l_out, &r_out, count);
+ if (retval)
+ return retval;
+
+ buffer[0] = pg_hton32(l_out);
+ buffer[1] = pg_hton32(r_out);
+
+ /* copy data to avoid assuming output is word-aligned */
+ memcpy(out, buffer, sizeof(buffer));
+
+ return retval;
+}
+
+char *
+px_crypt_des(const char *key, const char *setting)
+{
+ int i;
+ uint32 count,
+ salt,
+ l,
+ r0,
+ r1,
+ keybuf[2];
+ char *p;
+ uint8 *q;
+ static char output[21];
+
+ if (!des_initialised)
+ des_init();
+
+
+ /*
+ * Copy the key, shifting each character up by one bit and padding with
+ * zeros.
+ */
+ q = (uint8 *) keybuf;
+ while (q - (uint8 *) keybuf - 8)
+ {
+ *q++ = *key << 1;
+ if (*key != '\0')
+ key++;
+ }
+ if (des_setkey((char *) keybuf))
+ return NULL;
+
+#ifndef DISABLE_XDES
+ if (*setting == _PASSWORD_EFMT1)
+ {
+ /*
+ * "new"-style: setting must be a 9-character (underscore, then 4
+ * bytes of count, then 4 bytes of salt) string. See CRYPT(3) under
+ * the "Extended crypt" heading for further details.
+ *
+ * Unlimited characters of the input key are used. This is known as
+ * the "Extended crypt" DES method.
+ *
+ */
+ if (strlen(setting) < 9)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid salt")));
+
+ for (i = 1, count = 0L; i < 5; i++)
+ count |= ascii_to_bin(setting[i]) << (i - 1) * 6;
+
+ for (i = 5, salt = 0L; i < 9; i++)
+ salt |= ascii_to_bin(setting[i]) << (i - 5) * 6;
+
+ while (*key)
+ {
+ /*
+ * Encrypt the key with itself.
+ */
+ if (des_cipher((char *) keybuf, (char *) keybuf, 0L, 1))
+ return NULL;
+
+ /*
+ * And XOR with the next 8 characters of the key.
+ */
+ q = (uint8 *) keybuf;
+ while (q - (uint8 *) keybuf - 8 && *key)
+ *q++ ^= *key++ << 1;
+
+ if (des_setkey((char *) keybuf))
+ return NULL;
+ }
+ strlcpy(output, setting, 10);
+
+ /*
+ * Double check that we weren't given a short setting. If we were, the
+ * above code will probably have created weird values for count and
+ * salt, but we don't really care. Just make sure the output string
+ * doesn't have an extra NUL in it.
+ */
+ p = output + strlen(output);
+ }
+ else
+#endif /* !DISABLE_XDES */
+ {
+ /*
+ * "old"-style: setting - 2 bytes of salt key - only up to the first 8
+ * characters of the input key are used.
+ */
+ count = 25;
+
+ if (strlen(setting) < 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid salt")));
+
+ salt = (ascii_to_bin(setting[1]) << 6)
+ | ascii_to_bin(setting[0]);
+
+ output[0] = setting[0];
+
+ /*
+ * If the encrypted password that the salt was extracted from is only
+ * 1 character long, the salt will be corrupted. We need to ensure
+ * that the output string doesn't have an extra NUL in it!
+ */
+ output[1] = setting[1] ? setting[1] : output[0];
+
+ p = output + 2;
+ }
+ setup_salt(salt);
+
+ /*
+ * Do it.
+ */
+ if (do_des(0L, 0L, &r0, &r1, count))
+ return NULL;
+
+ /*
+ * Now encode the result...
+ */
+ l = (r0 >> 8);
+ *p++ = _crypt_a64[(l >> 18) & 0x3f];
+ *p++ = _crypt_a64[(l >> 12) & 0x3f];
+ *p++ = _crypt_a64[(l >> 6) & 0x3f];
+ *p++ = _crypt_a64[l & 0x3f];
+
+ l = (r0 << 16) | ((r1 >> 16) & 0xffff);
+ *p++ = _crypt_a64[(l >> 18) & 0x3f];
+ *p++ = _crypt_a64[(l >> 12) & 0x3f];
+ *p++ = _crypt_a64[(l >> 6) & 0x3f];
+ *p++ = _crypt_a64[l & 0x3f];
+
+ l = r1 << 2;
+ *p++ = _crypt_a64[(l >> 12) & 0x3f];
+ *p++ = _crypt_a64[(l >> 6) & 0x3f];
+ *p++ = _crypt_a64[l & 0x3f];
+ *p = 0;
+
+ return output;
+}
diff --git a/contrib/pgcrypto/crypt-gensalt.c b/contrib/pgcrypto/crypt-gensalt.c
new file mode 100644
index 0000000..740f361
--- /dev/null
+++ b/contrib/pgcrypto/crypt-gensalt.c
@@ -0,0 +1,187 @@
+/*
+ * Written by Solar Designer and placed in the public domain.
+ * See crypt_blowfish.c for more information.
+ *
+ * contrib/pgcrypto/crypt-gensalt.c
+ *
+ * This file contains salt generation functions for the traditional and
+ * other common crypt(3) algorithms, except for bcrypt which is defined
+ * entirely in crypt_blowfish.c.
+ *
+ * Put bcrypt generator also here as crypt-blowfish.c
+ * may not be compiled always. -- marko
+ */
+
+#include "postgres.h"
+
+#include "px-crypt.h"
+
+typedef unsigned int BF_word;
+
+static unsigned char _crypt_itoa64[64 + 1] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+char *
+_crypt_gensalt_traditional_rn(unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ if (size < 2 || output_size < 2 + 1 || (count && count != 25))
+ {
+ if (output_size > 0)
+ output[0] = '\0';
+ return NULL;
+ }
+
+ output[0] = _crypt_itoa64[(unsigned int) input[0] & 0x3f];
+ output[1] = _crypt_itoa64[(unsigned int) input[1] & 0x3f];
+ output[2] = '\0';
+
+ return output;
+}
+
+char *
+_crypt_gensalt_extended_rn(unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ unsigned long value;
+
+/* Even iteration counts make it easier to detect weak DES keys from a look
+ * at the hash, so they should be avoided */
+ if (size < 3 || output_size < 1 + 4 + 4 + 1 ||
+ (count && (count > 0xffffff || !(count & 1))))
+ {
+ if (output_size > 0)
+ output[0] = '\0';
+ return NULL;
+ }
+
+ if (!count)
+ count = 725;
+
+ output[0] = '_';
+ output[1] = _crypt_itoa64[count & 0x3f];
+ output[2] = _crypt_itoa64[(count >> 6) & 0x3f];
+ output[3] = _crypt_itoa64[(count >> 12) & 0x3f];
+ output[4] = _crypt_itoa64[(count >> 18) & 0x3f];
+ value = (unsigned long) (unsigned char) input[0] |
+ ((unsigned long) (unsigned char) input[1] << 8) |
+ ((unsigned long) (unsigned char) input[2] << 16);
+ output[5] = _crypt_itoa64[value & 0x3f];
+ output[6] = _crypt_itoa64[(value >> 6) & 0x3f];
+ output[7] = _crypt_itoa64[(value >> 12) & 0x3f];
+ output[8] = _crypt_itoa64[(value >> 18) & 0x3f];
+ output[9] = '\0';
+
+ return output;
+}
+
+char *
+_crypt_gensalt_md5_rn(unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ unsigned long value;
+
+ if (size < 3 || output_size < 3 + 4 + 1 || (count && count != 1000))
+ {
+ if (output_size > 0)
+ output[0] = '\0';
+ return NULL;
+ }
+
+ output[0] = '$';
+ output[1] = '1';
+ output[2] = '$';
+ value = (unsigned long) (unsigned char) input[0] |
+ ((unsigned long) (unsigned char) input[1] << 8) |
+ ((unsigned long) (unsigned char) input[2] << 16);
+ output[3] = _crypt_itoa64[value & 0x3f];
+ output[4] = _crypt_itoa64[(value >> 6) & 0x3f];
+ output[5] = _crypt_itoa64[(value >> 12) & 0x3f];
+ output[6] = _crypt_itoa64[(value >> 18) & 0x3f];
+ output[7] = '\0';
+
+ if (size >= 6 && output_size >= 3 + 4 + 4 + 1)
+ {
+ value = (unsigned long) (unsigned char) input[3] |
+ ((unsigned long) (unsigned char) input[4] << 8) |
+ ((unsigned long) (unsigned char) input[5] << 16);
+ output[7] = _crypt_itoa64[value & 0x3f];
+ output[8] = _crypt_itoa64[(value >> 6) & 0x3f];
+ output[9] = _crypt_itoa64[(value >> 12) & 0x3f];
+ output[10] = _crypt_itoa64[(value >> 18) & 0x3f];
+ output[11] = '\0';
+ }
+
+ return output;
+}
+
+
+
+static unsigned char BF_itoa64[64 + 1] =
+"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+static void
+BF_encode(char *dst, const BF_word *src, int size)
+{
+ const unsigned char *sptr = (const unsigned char *) src;
+ const unsigned char *end = sptr + size;
+ unsigned char *dptr = (unsigned char *) dst;
+ unsigned int c1,
+ c2;
+
+ do
+ {
+ c1 = *sptr++;
+ *dptr++ = BF_itoa64[c1 >> 2];
+ c1 = (c1 & 0x03) << 4;
+ if (sptr >= end)
+ {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 4;
+ *dptr++ = BF_itoa64[c1];
+ c1 = (c2 & 0x0f) << 2;
+ if (sptr >= end)
+ {
+ *dptr++ = BF_itoa64[c1];
+ break;
+ }
+
+ c2 = *sptr++;
+ c1 |= c2 >> 6;
+ *dptr++ = BF_itoa64[c1];
+ *dptr++ = BF_itoa64[c2 & 0x3f];
+ } while (sptr < end);
+}
+
+char *
+_crypt_gensalt_blowfish_rn(unsigned long count,
+ const char *input, int size, char *output, int output_size)
+{
+ if (size < 16 || output_size < 7 + 22 + 1 ||
+ (count && (count < 4 || count > 31)))
+ {
+ if (output_size > 0)
+ output[0] = '\0';
+ return NULL;
+ }
+
+ if (!count)
+ count = 5;
+
+ output[0] = '$';
+ output[1] = '2';
+ output[2] = 'a';
+ output[3] = '$';
+ output[4] = '0' + count / 10;
+ output[5] = '0' + count % 10;
+ output[6] = '$';
+
+ BF_encode(&output[7], (const BF_word *) input, 16);
+ output[7 + 22] = '\0';
+
+ return output;
+}
diff --git a/contrib/pgcrypto/crypt-md5.c b/contrib/pgcrypto/crypt-md5.c
new file mode 100644
index 0000000..d38721a
--- /dev/null
+++ b/contrib/pgcrypto/crypt-md5.c
@@ -0,0 +1,169 @@
+/*
+ * File imported from FreeBSD, original by Poul-Henning Kamp.
+ *
+ * $FreeBSD: src/lib/libcrypt/crypt-md5.c,v 1.5 1999/12/17 20:21:45 peter Exp $
+ *
+ * contrib/pgcrypto/crypt-md5.c
+ */
+
+#include "postgres.h"
+
+#include "px-crypt.h"
+#include "px.h"
+
+#define MD5_SIZE 16
+
+static const char _crypt_a64[] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+static void
+_crypt_to64(char *s, unsigned long v, int n)
+{
+ while (--n >= 0)
+ {
+ *s++ = _crypt_a64[v & 0x3f];
+ v >>= 6;
+ }
+}
+
+/*
+ * UNIX password
+ */
+
+char *
+px_crypt_md5(const char *pw, const char *salt, char *passwd, unsigned dstlen)
+{
+ static char *magic = "$1$"; /* This string is magic for this algorithm.
+ * Having it this way, we can get better later
+ * on */
+ static char *p;
+ static const char *sp,
+ *ep;
+ unsigned char final[MD5_SIZE];
+ int sl,
+ pl,
+ i;
+ PX_MD *ctx,
+ *ctx1;
+ int err;
+ unsigned long l;
+
+ if (!passwd || dstlen < 120)
+ return NULL;
+
+ /* Refine the Salt first */
+ sp = salt;
+
+ /* If it starts with the magic string, then skip that */
+ if (strncmp(sp, magic, strlen(magic)) == 0)
+ sp += strlen(magic);
+
+ /* It stops at the first '$', max 8 chars */
+ for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
+ continue;
+
+ /* get the length of the true salt */
+ sl = ep - sp;
+
+ /* we need two PX_MD objects */
+ err = px_find_digest("md5", &ctx);
+ if (err)
+ return NULL;
+ err = px_find_digest("md5", &ctx1);
+ if (err)
+ {
+ /* this path is possible under low-memory circumstances */
+ px_md_free(ctx);
+ return NULL;
+ }
+
+ /* The password first, since that is what is most unknown */
+ px_md_update(ctx, (const uint8 *) pw, strlen(pw));
+
+ /* Then our magic string */
+ px_md_update(ctx, (uint8 *) magic, strlen(magic));
+
+ /* Then the raw salt */
+ px_md_update(ctx, (const uint8 *) sp, sl);
+
+ /* Then just as many characters of the MD5(pw,salt,pw) */
+ px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
+ px_md_update(ctx1, (const uint8 *) sp, sl);
+ px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
+ px_md_finish(ctx1, final);
+ for (pl = strlen(pw); pl > 0; pl -= MD5_SIZE)
+ px_md_update(ctx, final, pl > MD5_SIZE ? MD5_SIZE : pl);
+
+ /* Don't leave anything around in vm they could use. */
+ px_memset(final, 0, sizeof final);
+
+ /* Then something really weird... */
+ for (i = strlen(pw); i; i >>= 1)
+ if (i & 1)
+ px_md_update(ctx, final, 1);
+ else
+ px_md_update(ctx, (const uint8 *) pw, 1);
+
+ /* Now make the output string */
+ strcpy(passwd, magic);
+ strncat(passwd, sp, sl);
+ strcat(passwd, "$");
+
+ px_md_finish(ctx, final);
+
+ /*
+ * and now, just to make sure things don't run too fast On a 60 Mhz
+ * Pentium this takes 34 msec, so you would need 30 seconds to build a
+ * 1000 entry dictionary...
+ */
+ for (i = 0; i < 1000; i++)
+ {
+ px_md_reset(ctx1);
+ if (i & 1)
+ px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
+ else
+ px_md_update(ctx1, final, MD5_SIZE);
+
+ if (i % 3)
+ px_md_update(ctx1, (const uint8 *) sp, sl);
+
+ if (i % 7)
+ px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
+
+ if (i & 1)
+ px_md_update(ctx1, final, MD5_SIZE);
+ else
+ px_md_update(ctx1, (const uint8 *) pw, strlen(pw));
+ px_md_finish(ctx1, final);
+ }
+
+ p = passwd + strlen(passwd);
+
+ l = (final[0] << 16) | (final[6] << 8) | final[12];
+ _crypt_to64(p, l, 4);
+ p += 4;
+ l = (final[1] << 16) | (final[7] << 8) | final[13];
+ _crypt_to64(p, l, 4);
+ p += 4;
+ l = (final[2] << 16) | (final[8] << 8) | final[14];
+ _crypt_to64(p, l, 4);
+ p += 4;
+ l = (final[3] << 16) | (final[9] << 8) | final[15];
+ _crypt_to64(p, l, 4);
+ p += 4;
+ l = (final[4] << 16) | (final[10] << 8) | final[5];
+ _crypt_to64(p, l, 4);
+ p += 4;
+ l = final[11];
+ _crypt_to64(p, l, 2);
+ p += 2;
+ *p = '\0';
+
+ /* Don't leave anything around in vm they could use. */
+ px_memset(final, 0, sizeof final);
+
+ px_md_free(ctx1);
+ px_md_free(ctx);
+
+ return passwd;
+}
diff --git a/contrib/pgcrypto/expected/3des.out b/contrib/pgcrypto/expected/3des.out
new file mode 100644
index 0000000..ee15489
--- /dev/null
+++ b/contrib/pgcrypto/expected/3des.out
@@ -0,0 +1,65 @@
+--
+-- 3DES cipher
+--
+-- test vector from somewhere
+SELECT encrypt('\x8000000000000000',
+ '\x010101010101010101010101010101010101010101010101',
+ '3des-ecb/pad:none');
+ encrypt
+--------------------
+ \x95f8a5e5dd31d900
+(1 row)
+
+select encrypt('', 'foo', '3des');
+ encrypt
+--------------------
+ \x752111e37a2d7ac3
+(1 row)
+
+-- 10 bytes key
+select encrypt('foo', '0123456789', '3des');
+ encrypt
+--------------------
+ \xd2fb8baa1717cb02
+(1 row)
+
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', '3des');
+ encrypt
+--------------------
+ \xa44360e699269817
+(1 row)
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', '3des'), '0123456', '3des'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', '3des');
+ encrypt_iv
+--------------------
+ \x50735067b073bb93
+(1 row)
+
+select encode(decrypt_iv('\x50735067b073bb93', '0123456', 'abcd', '3des'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789012345678901', '3des');
+ encrypt
+--------------------------------------------------------------------
+ \xb71e3422269d0ded19468f33d65cd663c28e0871984792a7b3ba0ddcecec8d2c
+(1 row)
+
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789012345678901', '3des'), '0123456789012345678901', '3des'), 'escape');
+ encode
+----------------------------
+ Lets try a longer message.
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/blowfish.out b/contrib/pgcrypto/expected/blowfish.out
new file mode 100644
index 0000000..f0346a7
--- /dev/null
+++ b/contrib/pgcrypto/expected/blowfish.out
@@ -0,0 +1,142 @@
+--
+-- Blowfish cipher
+--
+-- some standard Blowfish testvalues
+SELECT encrypt('\x0000000000000000', '\x0000000000000000', 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \x4ef997456198dd78
+(1 row)
+
+SELECT encrypt('\xffffffffffffffff', '\xffffffffffffffff', 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \x51866fd5b85ecb8a
+(1 row)
+
+SELECT encrypt('\x1000000000000001', '\x3000000000000000', 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \x7d856f9a613063f2
+(1 row)
+
+SELECT encrypt('\x1111111111111111', '\x1111111111111111', 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \x2466dd878b963c9d
+(1 row)
+
+SELECT encrypt('\x0123456789abcdef', '\xfedcba9876543210', 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \x0aceab0fc6a0a28d
+(1 row)
+
+SELECT encrypt('\x01a1d6d039776742', '\xfedcba9876543210', 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \x3273b8badc9e9e15
+(1 row)
+
+SELECT encrypt('\xffffffffffffffff', '\x0000000000000000', 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \x014933e0cdaff6e4
+(1 row)
+
+-- setkey
+SELECT encrypt('\xfedcba9876543210', '\xf0e1d2c3b4a5968778695a4b3c2d1e0f', 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \x93142887ee3be15c
+(1 row)
+
+-- with padding
+SELECT encrypt('\x01234567890123456789', '\x33443344334433443344334433443344', 'bf-ecb');
+ encrypt
+------------------------------------
+ \x0d04a43a20456dee5ede6ed9e4dcaaa6
+(1 row)
+
+-- cbc
+-- 28 bytes key
+SELECT encrypt('\x6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5',
+ '\x37363534333231204e6f77206973207468652074696d6520666f7220',
+ 'bf-cbc');
+ encrypt
+--------------------------------------------------------------------
+ \x4f2beb748c4f689ec755edb9dc252a41b93a3786850b4c75d6a702b6a8e48825
+(1 row)
+
+-- 29 bytes key
+SELECT encrypt('\x6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5ff92cc',
+ '\x37363534333231204e6f77206973207468652074696d6520666f722000',
+ 'bf-cbc');
+ encrypt
+------------------------------------------------------------------------------------
+ \x3ea6357a0ee7fad6d0c4b63464f2aafa40c2e91b4b7e1bba8114932fd92b5c8f111e7e50e7b2e541
+(1 row)
+
+-- blowfish-448
+SELECT encrypt('\xfedcba9876543210',
+ '\xf0e1d2c3b4a5968778695a4b3c2d1e0f001122334455667704689104c2fd3b2f584023641aba61761f1f1f1f0e0e0e0effffffffffffffff',
+ 'bf-ecb/pad:none');
+ encrypt
+--------------------
+ \xc04504012e4e1f53
+(1 row)
+
+-- empty data
+select encrypt('', 'foo', 'bf');
+ encrypt
+--------------------
+ \x1871949bb2311c8e
+(1 row)
+
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'bf');
+ encrypt
+--------------------
+ \x42f58af3b2c03f46
+(1 row)
+
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', 'bf');
+ encrypt
+--------------------
+ \x86ab6f0bc72b5f22
+(1 row)
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'bf'), '0123456', 'bf'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'bf');
+ encrypt_iv
+--------------------
+ \x95c7e89322525d59
+(1 row)
+
+select encode(decrypt_iv('\x95c7e89322525d59', '0123456', 'abcd', 'bf'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'bf');
+ encrypt
+--------------------------------------------------------------------
+ \xa76059f7a1b627b5b84080d9beb337714c7a7f8b70300023e5feb6dfa6813536
+(1 row)
+
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'bf'), '0123456789', 'bf'), 'escape');
+ encode
+----------------------------
+ Lets try a longer message.
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/blowfish_1.out b/contrib/pgcrypto/expected/blowfish_1.out
new file mode 100644
index 0000000..e52abf1
--- /dev/null
+++ b/contrib/pgcrypto/expected/blowfish_1.out
@@ -0,0 +1,62 @@
+--
+-- Blowfish cipher
+--
+-- some standard Blowfish testvalues
+SELECT encrypt('\x0000000000000000', '\x0000000000000000', 'bf-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+SELECT encrypt('\xffffffffffffffff', '\xffffffffffffffff', 'bf-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+SELECT encrypt('\x1000000000000001', '\x3000000000000000', 'bf-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+SELECT encrypt('\x1111111111111111', '\x1111111111111111', 'bf-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+SELECT encrypt('\x0123456789abcdef', '\xfedcba9876543210', 'bf-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+SELECT encrypt('\x01a1d6d039776742', '\xfedcba9876543210', 'bf-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+SELECT encrypt('\xffffffffffffffff', '\x0000000000000000', 'bf-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+-- setkey
+SELECT encrypt('\xfedcba9876543210', '\xf0e1d2c3b4a5968778695a4b3c2d1e0f', 'bf-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+-- with padding
+SELECT encrypt('\x01234567890123456789', '\x33443344334433443344334433443344', 'bf-ecb');
+ERROR: encrypt error: Cipher cannot be initialized
+-- cbc
+-- 28 bytes key
+SELECT encrypt('\x6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5',
+ '\x37363534333231204e6f77206973207468652074696d6520666f7220',
+ 'bf-cbc');
+ERROR: encrypt error: Key was too big
+-- 29 bytes key
+SELECT encrypt('\x6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5ff92cc',
+ '\x37363534333231204e6f77206973207468652074696d6520666f722000',
+ 'bf-cbc');
+ERROR: encrypt error: Key was too big
+-- blowfish-448
+SELECT encrypt('\xfedcba9876543210',
+ '\xf0e1d2c3b4a5968778695a4b3c2d1e0f001122334455667704689104c2fd3b2f584023641aba61761f1f1f1f0e0e0e0effffffffffffffff',
+ 'bf-ecb/pad:none');
+ERROR: encrypt error: Key was too big
+-- empty data
+select encrypt('', 'foo', 'bf');
+ERROR: encrypt error: Cipher cannot be initialized
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'bf');
+ERROR: encrypt error: Cipher cannot be initialized
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', 'bf');
+ERROR: encrypt error: Key was too big
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'bf'), '0123456', 'bf'), 'escape');
+ERROR: encrypt error: Cipher cannot be initialized
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'bf');
+ERROR: encrypt_iv error: Cipher cannot be initialized
+select encode(decrypt_iv('\x95c7e89322525d59', '0123456', 'abcd', 'bf'), 'escape');
+ERROR: decrypt_iv error: Cipher cannot be initialized
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'bf');
+ERROR: encrypt error: Cipher cannot be initialized
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'bf'), '0123456789', 'bf'), 'escape');
+ERROR: encrypt error: Cipher cannot be initialized
diff --git a/contrib/pgcrypto/expected/cast5.out b/contrib/pgcrypto/expected/cast5.out
new file mode 100644
index 0000000..8a4da98
--- /dev/null
+++ b/contrib/pgcrypto/expected/cast5.out
@@ -0,0 +1,73 @@
+--
+-- Cast5 cipher
+--
+-- test vectors from RFC2144
+-- 128 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x0123456712345678234567893456789A', 'cast5-ecb/pad:none');
+ encrypt
+--------------------
+ \x238b4fe5847e44b2
+(1 row)
+
+-- 80 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x01234567123456782345', 'cast5-ecb/pad:none');
+ encrypt
+--------------------
+ \xeb6a711a2c02271b
+(1 row)
+
+-- 40 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x0123456712', 'cast5-ecb/pad:none');
+ encrypt
+--------------------
+ \x7ac816d16e9b302e
+(1 row)
+
+-- cbc
+-- empty data
+select encrypt('', 'foo', 'cast5');
+ encrypt
+--------------------
+ \xa48bd1aabde4de10
+(1 row)
+
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'cast5');
+ encrypt
+--------------------
+ \xb07f19255e60cb6d
+(1 row)
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'cast5'), '0123456', 'cast5'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'cast5');
+ encrypt_iv
+--------------------
+ \x384a970695ce016a
+(1 row)
+
+select encode(decrypt_iv('\x384a970695ce016a', '0123456', 'abcd', 'cast5'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'cast5');
+ encrypt
+--------------------------------------------------------------------
+ \x04fcffc91533e1505dadcb10766d9fed0937818e663e402384e049942ba60fff
+(1 row)
+
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'cast5'), '0123456789', 'cast5'), 'escape');
+ encode
+----------------------------
+ Lets try a longer message.
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/cast5_1.out b/contrib/pgcrypto/expected/cast5_1.out
new file mode 100644
index 0000000..175e0b0
--- /dev/null
+++ b/contrib/pgcrypto/expected/cast5_1.out
@@ -0,0 +1,33 @@
+--
+-- Cast5 cipher
+--
+-- test vectors from RFC2144
+-- 128 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x0123456712345678234567893456789A', 'cast5-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+-- 80 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x01234567123456782345', 'cast5-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+-- 40 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x0123456712', 'cast5-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+-- cbc
+-- empty data
+select encrypt('', 'foo', 'cast5');
+ERROR: encrypt error: Cipher cannot be initialized
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'cast5');
+ERROR: encrypt error: Cipher cannot be initialized
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'cast5'), '0123456', 'cast5'), 'escape');
+ERROR: encrypt error: Cipher cannot be initialized
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'cast5');
+ERROR: encrypt_iv error: Cipher cannot be initialized
+select encode(decrypt_iv('\x384a970695ce016a', '0123456', 'abcd', 'cast5'), 'escape');
+ERROR: decrypt_iv error: Cipher cannot be initialized
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'cast5');
+ERROR: encrypt error: Cipher cannot be initialized
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'cast5'), '0123456789', 'cast5'), 'escape');
+ERROR: encrypt error: Cipher cannot be initialized
diff --git a/contrib/pgcrypto/expected/crypt-blowfish.out b/contrib/pgcrypto/expected/crypt-blowfish.out
new file mode 100644
index 0000000..d79b0c0
--- /dev/null
+++ b/contrib/pgcrypto/expected/crypt-blowfish.out
@@ -0,0 +1,36 @@
+--
+-- crypt() and gen_salt(): bcrypt
+--
+SELECT crypt('', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
+ crypt
+--------------------------------------------------------------
+ $2a$06$RQiOJ.3ELirrXwxIZY8q0OlGbBEpDmx7IRZlNYvGJ1SHXwNi2cEKK
+(1 row)
+
+SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
+ crypt
+--------------------------------------------------------------
+ $2a$06$RQiOJ.3ELirrXwxIZY8q0OR3CVJrAfda1z26CCHPnB6mmVZD8p0/C
+(1 row)
+
+-- error, salt too short:
+SELECT crypt('foox', '$2a$');
+ERROR: invalid salt
+-- error, first digit of count in salt invalid
+SELECT crypt('foox', '$2a$40$RQiOJ.3ELirrXwxIZY8q0O');
+ERROR: invalid salt
+-- error, count in salt too small
+SELECT crypt('foox', '$2a$00$RQiOJ.3ELirrXwxIZY8q0O');
+ERROR: invalid salt
+CREATE TABLE ctest (data text, res text, salt text);
+INSERT INTO ctest VALUES ('password', '', '');
+UPDATE ctest SET salt = gen_salt('bf', 8);
+UPDATE ctest SET res = crypt(data, salt);
+SELECT res = crypt(data, res) AS "worked"
+FROM ctest;
+ worked
+--------
+ t
+(1 row)
+
+DROP TABLE ctest;
diff --git a/contrib/pgcrypto/expected/crypt-des.out b/contrib/pgcrypto/expected/crypt-des.out
new file mode 100644
index 0000000..a462dcd
--- /dev/null
+++ b/contrib/pgcrypto/expected/crypt-des.out
@@ -0,0 +1,31 @@
+--
+-- crypt() and gen_salt(): crypt-des
+--
+SELECT crypt('', 'NB');
+ crypt
+---------------
+ NBPx/38Y48kHg
+(1 row)
+
+SELECT crypt('foox', 'NB');
+ crypt
+---------------
+ NB53EGGqrrb5E
+(1 row)
+
+-- We are supposed to pass in a 2-character salt.
+-- error since salt is too short:
+SELECT crypt('password', 'a');
+ERROR: invalid salt
+CREATE TABLE ctest (data text, res text, salt text);
+INSERT INTO ctest VALUES ('password', '', '');
+UPDATE ctest SET salt = gen_salt('des');
+UPDATE ctest SET res = crypt(data, salt);
+SELECT res = crypt(data, res) AS "worked"
+FROM ctest;
+ worked
+--------
+ t
+(1 row)
+
+DROP TABLE ctest;
diff --git a/contrib/pgcrypto/expected/crypt-md5.out b/contrib/pgcrypto/expected/crypt-md5.out
new file mode 100644
index 0000000..a1c8304
--- /dev/null
+++ b/contrib/pgcrypto/expected/crypt-md5.out
@@ -0,0 +1,27 @@
+--
+-- crypt() and gen_salt(): md5
+--
+SELECT crypt('', '$1$Szzz0yzz');
+ crypt
+------------------------------------
+ $1$Szzz0yzz$To38XrR3BsbXQW2ZpfKjF1
+(1 row)
+
+SELECT crypt('foox', '$1$Szzz0yzz');
+ crypt
+------------------------------------
+ $1$Szzz0yzz$IYL49cd3t9bllsA7Jmz1M1
+(1 row)
+
+CREATE TABLE ctest (data text, res text, salt text);
+INSERT INTO ctest VALUES ('password', '', '');
+UPDATE ctest SET salt = gen_salt('md5');
+UPDATE ctest SET res = crypt(data, salt);
+SELECT res = crypt(data, res) AS "worked"
+FROM ctest;
+ worked
+--------
+ t
+(1 row)
+
+DROP TABLE ctest;
diff --git a/contrib/pgcrypto/expected/crypt-xdes.out b/contrib/pgcrypto/expected/crypt-xdes.out
new file mode 100644
index 0000000..8cf9075
--- /dev/null
+++ b/contrib/pgcrypto/expected/crypt-xdes.out
@@ -0,0 +1,51 @@
+--
+-- crypt() and gen_salt(): extended des
+--
+SELECT crypt('', '_J9..j2zz');
+ crypt
+----------------------
+ _J9..j2zzR/nIRDK3pPc
+(1 row)
+
+SELECT crypt('foox', '_J9..j2zz');
+ crypt
+----------------------
+ _J9..j2zzAYKMvO2BYRY
+(1 row)
+
+-- check XDES handling of keys longer than 8 chars
+SELECT crypt('longlongpassword', '_J9..j2zz');
+ crypt
+----------------------
+ _J9..j2zz4BeseiQNwUg
+(1 row)
+
+-- error, salt too short
+SELECT crypt('foox', '_J9..BWH');
+ERROR: invalid salt
+-- error, count specified in the second argument is 0
+SELECT crypt('password', '_........');
+ERROR: crypt(3) returned NULL
+-- error, count will wind up still being 0 due to invalid encoding
+-- of the count: only chars ``./0-9A-Za-z' are valid
+SELECT crypt('password', '_..!!!!!!');
+ERROR: crypt(3) returned NULL
+-- count should be non-zero here, will work
+SELECT crypt('password', '_/!!!!!!!');
+ crypt
+----------------------
+ _/!!!!!!!zqM49hRzxko
+(1 row)
+
+CREATE TABLE ctest (data text, res text, salt text);
+INSERT INTO ctest VALUES ('password', '', '');
+UPDATE ctest SET salt = gen_salt('xdes', 1001);
+UPDATE ctest SET res = crypt(data, salt);
+SELECT res = crypt(data, res) AS "worked"
+FROM ctest;
+ worked
+--------
+ t
+(1 row)
+
+DROP TABLE ctest;
diff --git a/contrib/pgcrypto/expected/des.out b/contrib/pgcrypto/expected/des.out
new file mode 100644
index 0000000..fdbaea2
--- /dev/null
+++ b/contrib/pgcrypto/expected/des.out
@@ -0,0 +1,58 @@
+--
+-- DES cipher
+--
+-- no official test vectors atm
+-- from blowfish.sql
+SELECT encrypt('\x0123456789abcdef', '\xfedcba9876543210', 'des-ecb/pad:none');
+ encrypt
+--------------------
+ \xed39d950fa74bcc4
+(1 row)
+
+-- empty data
+select encrypt('', 'foo', 'des');
+ encrypt
+--------------------
+ \x752111e37a2d7ac3
+(1 row)
+
+-- 8 bytes key
+select encrypt('foo', '01234589', 'des');
+ encrypt
+--------------------
+ \xdec0f9c602b647a8
+(1 row)
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'des'), '0123456', 'des'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'des');
+ encrypt_iv
+--------------------
+ \x50735067b073bb93
+(1 row)
+
+select encode(decrypt_iv('\x50735067b073bb93', '0123456', 'abcd', 'des'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- long message
+select encrypt('Lets try a longer message.', '01234567', 'des');
+ encrypt
+--------------------------------------------------------------------
+ \x5ad146043e5f30967e06a0fcbae602daf4ff2a5fd0ed12d6c5913cf85f1e36ca
+(1 row)
+
+select encode(decrypt(encrypt('Lets try a longer message.', '01234567', 'des'), '01234567', 'des'), 'escape');
+ encode
+----------------------------
+ Lets try a longer message.
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/des_1.out b/contrib/pgcrypto/expected/des_1.out
new file mode 100644
index 0000000..5a76154
--- /dev/null
+++ b/contrib/pgcrypto/expected/des_1.out
@@ -0,0 +1,26 @@
+--
+-- DES cipher
+--
+-- no official test vectors atm
+-- from blowfish.sql
+SELECT encrypt('\x0123456789abcdef', '\xfedcba9876543210', 'des-ecb/pad:none');
+ERROR: encrypt error: Cipher cannot be initialized
+-- empty data
+select encrypt('', 'foo', 'des');
+ERROR: encrypt error: Cipher cannot be initialized
+-- 8 bytes key
+select encrypt('foo', '01234589', 'des');
+ERROR: encrypt error: Cipher cannot be initialized
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'des'), '0123456', 'des'), 'escape');
+ERROR: encrypt error: Cipher cannot be initialized
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'des');
+ERROR: encrypt_iv error: Cipher cannot be initialized
+select encode(decrypt_iv('\x50735067b073bb93', '0123456', 'abcd', 'des'), 'escape');
+ERROR: decrypt_iv error: Cipher cannot be initialized
+-- long message
+select encrypt('Lets try a longer message.', '01234567', 'des');
+ERROR: encrypt error: Cipher cannot be initialized
+select encode(decrypt(encrypt('Lets try a longer message.', '01234567', 'des'), '01234567', 'des'), 'escape');
+ERROR: encrypt error: Cipher cannot be initialized
diff --git a/contrib/pgcrypto/expected/hmac-md5.out b/contrib/pgcrypto/expected/hmac-md5.out
new file mode 100644
index 0000000..0d8d761
--- /dev/null
+++ b/contrib/pgcrypto/expected/hmac-md5.out
@@ -0,0 +1,72 @@
+--
+-- HMAC-MD5
+--
+SELECT hmac(
+'Hi There',
+'\x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'::bytea,
+'md5');
+ hmac
+------------------------------------
+ \x9294727a3638bb1c13f48ef8158bfc9d
+(1 row)
+
+-- 2
+SELECT hmac(
+'Jefe',
+'what do ya want for nothing?',
+'md5');
+ hmac
+------------------------------------
+ \x813aead7c4a34bff01a16d61368e7c13
+(1 row)
+
+-- 3
+SELECT hmac(
+'\xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'::bytea,
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'md5');
+ hmac
+------------------------------------
+ \x56be34521d144c88dbb8c733f0e8b3f6
+(1 row)
+
+-- 4
+SELECT hmac(
+'\xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd'::bytea,
+'\x0102030405060708090a0b0c0d0e0f10111213141516171819'::bytea,
+'md5');
+ hmac
+------------------------------------
+ \x697eaf0aca3a3aea3a75164746ffaa79
+(1 row)
+
+-- 5
+SELECT hmac(
+'Test With Truncation',
+'\x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c'::bytea,
+'md5');
+ hmac
+------------------------------------
+ \x56461ef2342edc00f9bab995690efd4c
+(1 row)
+
+-- 6
+SELECT hmac(
+'Test Using Larger Than Block-Size Key - Hash Key First',
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'md5');
+ hmac
+------------------------------------
+ \x6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd
+(1 row)
+
+-- 7
+SELECT hmac(
+'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data',
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'md5');
+ hmac
+------------------------------------
+ \x6f630fad67cda0ee1fb1f562db3aa53e
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/hmac-sha1.out b/contrib/pgcrypto/expected/hmac-sha1.out
new file mode 100644
index 0000000..a7d4c38
--- /dev/null
+++ b/contrib/pgcrypto/expected/hmac-sha1.out
@@ -0,0 +1,72 @@
+--
+-- HMAC-SHA1
+--
+SELECT hmac(
+'Hi There',
+'\x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'::bytea,
+'sha1');
+ hmac
+--------------------------------------------
+ \x675b0b3a1b4ddf4e124872da6c2f632bfed957e9
+(1 row)
+
+-- 2
+SELECT hmac(
+'Jefe',
+'what do ya want for nothing?',
+'sha1');
+ hmac
+--------------------------------------------
+ \x156d4c35468a0339f3fa57a067bf47f814eb7a57
+(1 row)
+
+-- 3
+SELECT hmac(
+'\xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'::bytea,
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'sha1');
+ hmac
+--------------------------------------------
+ \xd730594d167e35d5956fd8003d0db3d3f46dc7bb
+(1 row)
+
+-- 4
+SELECT hmac(
+'\xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd'::bytea,
+'\x0102030405060708090a0b0c0d0e0f10111213141516171819'::bytea,
+'sha1');
+ hmac
+--------------------------------------------
+ \x4c9007f4026250c6bc8414f9bf50c86c2d7235da
+(1 row)
+
+-- 5
+SELECT hmac(
+'Test With Truncation',
+'\x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c'::bytea,
+'sha1');
+ hmac
+--------------------------------------------
+ \x37268b7e21e84da5720c53c4ba03ad1104039fa7
+(1 row)
+
+-- 6
+SELECT hmac(
+'Test Using Larger Than Block-Size Key - Hash Key First',
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'sha1');
+ hmac
+--------------------------------------------
+ \xaa4ae5e15272d00e95705637ce8a3b55ed402112
+(1 row)
+
+-- 7
+SELECT hmac(
+'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data',
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'sha1');
+ hmac
+--------------------------------------------
+ \xe8e99d0f45237d786d6bbaa7965c7808bbff1a91
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/init.out b/contrib/pgcrypto/expected/init.out
new file mode 100644
index 0000000..d1341a6
--- /dev/null
+++ b/contrib/pgcrypto/expected/init.out
@@ -0,0 +1,13 @@
+--
+-- init pgcrypto
+--
+CREATE EXTENSION pgcrypto;
+-- check error handling
+select gen_salt('foo');
+ERROR: gen_salt: Unknown salt algorithm
+select digest('foo', 'foo');
+ERROR: Cannot use "foo": No such hash algorithm
+select hmac('foo', 'foo', 'foo');
+ERROR: Cannot use "foo": No such hash algorithm
+select encrypt('foo', 'foo', 'foo');
+ERROR: Cannot use "foo": No such cipher algorithm
diff --git a/contrib/pgcrypto/expected/md5.out b/contrib/pgcrypto/expected/md5.out
new file mode 100644
index 0000000..1790594
--- /dev/null
+++ b/contrib/pgcrypto/expected/md5.out
@@ -0,0 +1,45 @@
+--
+-- MD5 message digest
+--
+SELECT digest('', 'md5');
+ digest
+------------------------------------
+ \xd41d8cd98f00b204e9800998ecf8427e
+(1 row)
+
+SELECT digest('a', 'md5');
+ digest
+------------------------------------
+ \x0cc175b9c0f1b6a831c399e269772661
+(1 row)
+
+SELECT digest('abc', 'md5');
+ digest
+------------------------------------
+ \x900150983cd24fb0d6963f7d28e17f72
+(1 row)
+
+SELECT digest('message digest', 'md5');
+ digest
+------------------------------------
+ \xf96b697d7cb7938d525a2f31aaf161d0
+(1 row)
+
+SELECT digest('abcdefghijklmnopqrstuvwxyz', 'md5');
+ digest
+------------------------------------
+ \xc3fcd3d76192e4007dfb496cca67e13b
+(1 row)
+
+SELECT digest('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'md5');
+ digest
+------------------------------------
+ \xd174ab98d277d9f5a5611c2c9f419d9f
+(1 row)
+
+SELECT digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'md5');
+ digest
+------------------------------------
+ \x57edf4a22be3c955ac49da2e2107b67a
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/pgp-armor.out b/contrib/pgcrypto/expected/pgp-armor.out
new file mode 100644
index 0000000..0f5ff46
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-armor.out
@@ -0,0 +1,370 @@
+--
+-- PGP Armor
+--
+select armor('');
+ armor
+-----------------------------
+ -----BEGIN PGP MESSAGE-----+
+ +
+ =twTO +
+ -----END PGP MESSAGE----- +
+
+(1 row)
+
+select armor('test');
+ armor
+-----------------------------
+ -----BEGIN PGP MESSAGE-----+
+ +
+ dGVzdA== +
+ =+G7Q +
+ -----END PGP MESSAGE----- +
+
+(1 row)
+
+select encode(dearmor(armor('')), 'escape');
+ encode
+--------
+
+(1 row)
+
+select encode(dearmor(armor('zooka')), 'escape');
+ encode
+--------
+ zooka
+(1 row)
+
+select armor('0123456789abcdef0123456789abcdef0123456789abcdef
+0123456789abcdef0123456789abcdef0123456789abcdef');
+ armor
+------------------------------------------------------------------------------
+ -----BEGIN PGP MESSAGE----- +
+ +
+ MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmCjAxMjM0NTY3+
+ ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZg== +
+ =JFw5 +
+ -----END PGP MESSAGE----- +
+
+(1 row)
+
+-- lots formatting
+select encode(dearmor(' a pgp msg:
+
+-----BEGIN PGP MESSAGE-----
+Comment: Some junk
+
+em9va2E=
+
+ =D5cR
+
+-----END PGP MESSAGE-----'), 'escape');
+ encode
+--------
+ zooka
+(1 row)
+
+-- lots messages
+select encode(dearmor('
+wrong packet:
+ -----BEGIN PGP MESSAGE-----
+
+ d3Jvbmc=
+ =vCYP
+ -----END PGP MESSAGE-----
+
+right packet:
+-----BEGIN PGP MESSAGE-----
+
+cmlnaHQ=
+=nbpj
+-----END PGP MESSAGE-----
+
+use only first packet
+-----BEGIN PGP MESSAGE-----
+
+d3Jvbmc=
+=vCYP
+-----END PGP MESSAGE-----
+'), 'escape');
+ encode
+--------
+ right
+(1 row)
+
+-- bad crc
+select dearmor('
+-----BEGIN PGP MESSAGE-----
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ERROR: Corrupt ascii-armor
+-- corrupt (no space after the colon)
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+foo:
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ERROR: Corrupt ascii-armor
+-- corrupt (no empty line)
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ERROR: Corrupt ascii-armor
+-- no headers
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+-----+-------
+(0 rows)
+
+-- header with empty value
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+foo:
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+-----+-------
+ foo |
+(1 row)
+
+-- simple
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+fookey: foovalue
+barkey: barvalue
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+--------+----------
+ fookey | foovalue
+ barkey | barvalue
+(2 rows)
+
+-- insane keys, part 1
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+insane:key :
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+-------------+-------
+ insane:key |
+(1 row)
+
+-- insane keys, part 2
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+insane:key : text value here
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+-------------+-----------------
+ insane:key | text value here
+(1 row)
+
+-- long value
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+long: this value is more than 76 characters long, but it should still parse correctly as that''s permitted by RFC 4880
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+------+-----------------------------------------------------------------------------------------------------------------
+ long | this value is more than 76 characters long, but it should still parse correctly as that's permitted by RFC 4880
+(1 row)
+
+-- long value, split up
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+long: this value is more than 76 characters long, but it should still
+long: parse correctly as that''s permitted by RFC 4880
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+------+------------------------------------------------------------------
+ long | this value is more than 76 characters long, but it should still
+ long | parse correctly as that's permitted by RFC 4880
+(2 rows)
+
+-- long value, split up, part 2
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+long: this value is more than
+long: 76 characters long, but it should still
+long: parse correctly as that''s permitted by RFC 4880
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+------+-------------------------------------------------
+ long | this value is more than
+ long | 76 characters long, but it should still
+ long | parse correctly as that's permitted by RFC 4880
+(3 rows)
+
+-- long value, split up, part 3
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+emptykey:
+long: this value is more than
+emptykey:
+long: 76 characters long, but it should still
+emptykey:
+long: parse correctly as that''s permitted by RFC 4880
+emptykey:
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ key | value
+----------+-------------------------------------------------
+ emptykey |
+ long | this value is more than
+ emptykey |
+ long | 76 characters long, but it should still
+ emptykey |
+ long | parse correctly as that's permitted by RFC 4880
+ emptykey |
+(7 rows)
+
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.blowfish.sha1.mdc.s2k3.z0
+
+jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
+yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
+=JcP+
+-----END PGP MESSAGE-----
+');
+ key | value
+---------+--------------------------------
+ Comment | dat1.blowfish.sha1.mdc.s2k3.z0
+(1 row)
+
+-- test CR+LF line endings
+select * from pgp_armor_headers(replace('
+-----BEGIN PGP MESSAGE-----
+fookey: foovalue
+barkey: barvalue
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+', E'\n', E'\r\n'));
+ key | value
+--------+----------
+ fookey | foovalue
+ barkey | barvalue
+(2 rows)
+
+-- test header generation
+select armor('zooka', array['foo'], array['bar']);
+ armor
+-----------------------------
+ -----BEGIN PGP MESSAGE-----+
+ foo: bar +
+ +
+ em9va2E= +
+ =D5cR +
+ -----END PGP MESSAGE----- +
+
+(1 row)
+
+select armor('zooka', array['Version', 'Comment'], array['Created by pgcrypto', 'PostgreSQL, the world''s most advanced open source database']);
+ armor
+---------------------------------------------------------------------
+ -----BEGIN PGP MESSAGE----- +
+ Version: Created by pgcrypto +
+ Comment: PostgreSQL, the world's most advanced open source database+
+ +
+ em9va2E= +
+ =D5cR +
+ -----END PGP MESSAGE----- +
+
+(1 row)
+
+select * from pgp_armor_headers(
+ armor('zooka', array['Version', 'Comment'],
+ array['Created by pgcrypto', 'PostgreSQL, the world''s most advanced open source database']));
+ key | value
+---------+------------------------------------------------------------
+ Version | Created by pgcrypto
+ Comment | PostgreSQL, the world's most advanced open source database
+(2 rows)
+
+-- error/corner cases
+select armor('', array['foo'], array['too', 'many']);
+ERROR: mismatched array dimensions
+select armor('', array['too', 'many'], array['foo']);
+ERROR: mismatched array dimensions
+select armor('', array[['']], array['foo']);
+ERROR: wrong number of array subscripts
+select armor('', array['foo'], array[['']]);
+ERROR: wrong number of array subscripts
+select armor('', array[null], array['foo']);
+ERROR: null value not allowed for header key
+select armor('', array['foo'], array[null]);
+ERROR: null value not allowed for header value
+select armor('', '[0:0]={"foo"}', array['foo']);
+ armor
+-----------------------------
+ -----BEGIN PGP MESSAGE-----+
+ foo: foo +
+ +
+ =twTO +
+ -----END PGP MESSAGE----- +
+
+(1 row)
+
+select armor('', array['foo'], '[0:0]={"foo"}');
+ armor
+-----------------------------
+ -----BEGIN PGP MESSAGE-----+
+ foo: foo +
+ +
+ =twTO +
+ -----END PGP MESSAGE----- +
+
+(1 row)
+
+select armor('', array[E'embedded\nnewline'], array['foo']);
+ERROR: header key must not contain newlines
+select armor('', array['foo'], array[E'embedded\nnewline']);
+ERROR: header value must not contain newlines
+select armor('', array['embedded: colon+space'], array['foo']);
+ERROR: header key must not contain ": "
diff --git a/contrib/pgcrypto/expected/pgp-compression.out b/contrib/pgcrypto/expected/pgp-compression.out
new file mode 100644
index 0000000..d4c57fe
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-compression.out
@@ -0,0 +1,80 @@
+--
+-- PGP compression support
+--
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0ECQMCsci6AdHnELlh0kQB4jFcVwHMJg0Bulop7m3Mi36s15TAhBo0AnzIrRFrdLVCkKohsS6+
+DMcmR53SXfLoDJOv/M8uKj3QSq7oWNIp95pxfA==
+=tbSn
+-----END PGP MESSAGE-----
+'), 'key', 'expect-compress-algo=1');
+ pgp_sym_decrypt
+-----------------
+ Secret message
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret message', 'key', 'compress-algo=0'),
+ 'key', 'expect-compress-algo=0');
+ pgp_sym_decrypt
+-----------------
+ Secret message
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret message', 'key', 'compress-algo=1'),
+ 'key', 'expect-compress-algo=1');
+ pgp_sym_decrypt
+-----------------
+ Secret message
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret message', 'key', 'compress-algo=2'),
+ 'key', 'expect-compress-algo=2');
+ pgp_sym_decrypt
+-----------------
+ Secret message
+(1 row)
+
+-- level=0 should turn compression off
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret message', 'key',
+ 'compress-algo=2, compress-level=0'),
+ 'key', 'expect-compress-algo=0');
+ pgp_sym_decrypt
+-----------------
+ Secret message
+(1 row)
+
+-- check corner case involving an input string of 16kB, as per bug #16476.
+SELECT setseed(0);
+ setseed
+---------
+
+(1 row)
+
+WITH random_string AS
+(
+ -- This generates a random string of 16366 bytes. This is chosen
+ -- as random so that it does not get compressed, and the decompression
+ -- would work on a string with the same length as the origin, making the
+ -- test behavior more predictible. lpad() ensures that the generated
+ -- hexadecimal value is completed by extra zero characters if random()
+ -- has generated a value strictly lower than 16.
+ SELECT string_agg(decode(lpad(to_hex((random()*256)::int), 2, '0'), 'hex'), '') as bytes
+ FROM generate_series(0, 16365)
+)
+SELECT bytes =
+ pgp_sym_decrypt_bytea(
+ pgp_sym_encrypt_bytea(bytes, 'key',
+ 'compress-algo=1,compress-level=1'),
+ 'key', 'expect-compress-algo=1')
+ AS is_same
+ FROM random_string;
+ is_same
+---------
+ t
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/pgp-decrypt.out b/contrib/pgcrypto/expected/pgp-decrypt.out
new file mode 100644
index 0000000..eb049ba
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-decrypt.out
@@ -0,0 +1,419 @@
+--
+-- pgp decrypt tests
+--
+-- Checking ciphers
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.blowfish.sha1.mdc.s2k3.z0
+
+jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
+yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
+=JcP+
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCci97v0Q6Z0Zg0kQBsVf5Oe3iC+FBzUmuMV9KxmAyOMyjCc/5i8f1Eest
+UTAsG35A1vYs02VARKzGz6xI2UHwFUirP+brPBg3Ee7muOx8pA==
+=XtrP
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k3.z0
+
+jA0ECAMCI7YQpWqp3D1g0kQBCjB7GlX7+SQeXNleXeXQ78ZAPNliquGDq9u378zI
+5FPTqAhIB2/2fjY8QEIs1ai00qphjX2NitxV/3Wn+6dufB4Q4g==
+=rCZt
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k3.z0
+
+jA0ECQMC4f/5djqCC1Rg0kQBTHEPsD+Sw7biBsM2er3vKyGPAQkuTBGKC5ie7hT/
+lceMfQdbAg6oTFyJpk/wH18GzRDphCofg0X8uLgkAKMrpcmgog==
+=fB6S
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking MDC modes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.nomdc.s2k3.z0
+
+jA0EBwMCnv07rlXqWctgyS2Dm2JfOKCRL4sLSLJUC8RS2cH7cIhKSuLitOtyquB+
+u9YkgfJfsuRJmgQ9tmo=
+=60ui
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCEeP3idNjQ1Bg0kQBf4G0wX+2QNzLh2YNwYkQgQkfYhn/hLXjV4nK9nsE
+8Ex1Dsdt5UPvOz8W8VKQRS6loOfOe+yyXil8W3IYFwUpdDUi+Q==
+=moGf
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking hashes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.md5.mdc.s2k3.z0
+
+jA0EBwMClrXXtOXetohg0kQBn0Kl1ymevQZRHkdoYRHgzCwSQEiss7zYff2UNzgO
+KyRrHf7zEBuZiZ2AG34jNVMOLToj1jJUg5zTSdecUzQVCykWTA==
+=NyLk
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCApbdlrURoWJg0kQBzHM/E0o7djY82bNuspjxjAcPFrrtp0uvDdMQ4z2m
+/PM8jhgI5vxFYfNQjLl8y3fHYIomk9YflN9K/Q13iq8A8sjeTw==
+=FxbQ
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking S2K modes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k0.z0
+
+jAQEBwAC0kQBKTaLAKE3xzps+QIZowqRNb2eAdzBw2LxEW2YD5PgNlbhJdGg+dvw
+Ah9GXjGS1TVALzTImJbz1uHUZRfhJlFbc5yGQw==
+=YvkV
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k1.z0
+
+jAwEBwEC/QTByBLI3b/SRAHPxKzI6SZBo5lAEOD+EsvKQWO4adL9tDY+++Iqy1xK
+4IaWXVKEj9R2Lr2xntWWMGZtcKtjD2lFFRXXd9dZp1ZThNDz
+=dbXm
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCEq4Su3ZqNEJg0kQB4QG5jBTKF0i04xtH+avzmLhstBNRxvV3nsmB3cwl
+z+9ZaA/XdSx5ZiFnMym8P6r8uY9rLjjNptvvRHlxIReF+p9MNg==
+=VJKg
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k0.z0
+
+jAQECAAC0kQBBDnQWkgsx9YFaqDfWmpsiyAJ6y2xG/sBvap1dySYEMuZ+wJTXQ9E
+Cr3i2M7TgVZ0M4jp4QL0adG1lpN5iK7aQeOwMw==
+=cg+i
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k1.z0
+
+jAwECAECruOfyNDFiTnSRAEVoGXm4A9UZKkWljdzjEO/iaE7mIraltIpQMkiqCh9
+7h8uZ2u9uRBOv222fZodGvc6bvq/4R4hAa/6qSHtm8mdmvGt
+=aHmC
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k3.z0
+
+jA0ECAMCjFn6SRi3SONg0kQBqtSHPaD0m7rXfDAhCWU/ypAsI93GuHGRyM99cvMv
+q6eF6859ZVnli3BFSDSk3a4e/pXhglxmDYCfjAXkozKNYLo6yw==
+=K0LS
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k0.z0
+
+jAQECQAC0kQB4L1eMbani07XF2ZYiXNK9LW3v8w41oUPl7dStmrJPQFwsdxmrDHu
+rQr3WbdKdY9ufjOE5+mXI+EFkSPrF9rL9NCq6w==
+=RGts
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k1.z0
+
+jAwECQECKHhrou7ZOIXSRAHWIVP+xjVQcjAVBTt+qh9SNzYe248xFTwozkwev3mO
++KVJW0qhk0An+Y2KF99/bYFl9cL5D3Tl43fC8fXGl3x3m7pR
+=SUrU
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k3.z0
+
+jA0ECQMCjc8lwZu8Fz1g0kQBkEzjImi21liep5jj+3dAJ2aZFfUkohi8b3n9z+7+
+4+NRzL7cMW2RLAFnJbiqXDlRHMwleeuLN1up2WIxsxtYYuaBjA==
+=XZrG
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking longer passwords
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCx6dBiuqrYNRg0kQBEo63AvA1SCslxP7ayanLf1H0/hlk2nONVhTwVEWi
+tTGup1mMz6Cfh1uDRErUuXpx9A0gdMu7zX0o5XjrL7WGDAZdSw==
+=XKKG
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCBDvYuS990iFg0kQBW31UK5OiCjWf5x6KJ8qNNT2HZWQCjCBZMU0XsOC6
+CMxFKadf144H/vpoV9GA0f22keQgCl0EsTE4V4lweVOPTKCMJg==
+=gWDh
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij2jk4h5g2j54khg23h54g2kh54g2khj54g23hj54');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCqXbFafC+ofVg0kQBejyiPqH0QMERVGfmPOjtAxvyG5KDIJPYojTgVSDt
+FwsDabdQUz5O7bgNSnxfmyw1OifGF+W2bIn/8W+0rDf8u3+O+Q==
+=OxOF
+-----END PGP MESSAGE-----
+'), 'x');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking various data
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCGJ+SpuOysINg0kQBJfSjzsW0x4OVcAyr17O7FBvMTwIGeGcJd99oTQU8
+Xtx3kDqnhUq9Z1fS3qPbi5iNP2A9NxOBxPWz2JzxhydANlgbxg==
+=W/ik
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+ digest
+--------------------------------------------
+ \x0225e3ede6f2587b076d021a189ff60aad67e066
+(1 row)
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat2.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCvdpDvidNzMxg0jUBvj8eS2+1t/9/zgemxvhtc0fvdKGGbjH7dleaTJRB
+SaV9L04ky1qECNDx3XjnoKLC+H7IOQ==
+=Fxen
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+ digest
+--------------------------------------------
+ \xda39a3ee5e6b4b0d3255bfef95601890afd80709
+(1 row)
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat3.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCxQvxJZ3G/HRg0lgBeYmTa7/uDAjPyFwSX4CYBgpZWVn/JS8JzILrcWF8
+gFnkUKIE0PSaYFp+Yi1VlRfUtRQ/X/LYNGa7tWZS+4VQajz2Xtz4vUeAEiYFYPXk
+73Hb8m1yRhQK
+=ivrD
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+ digest
+--------------------------------------------
+ \x5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
+(1 row)
+
+-- Checking CRLF
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: crlf mess
+
+ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
+a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
+=mBP9
+-----END PGP MESSAGE-----
+'), 'key', 'convert-crlf=0'), 'sha1');
+ digest
+--------------------------------------------
+ \x9353062be7720f1446d30b9e75573a4833886784
+(1 row)
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: crlf mess
+
+ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
+a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
+=mBP9
+-----END PGP MESSAGE-----
+'), 'key', 'convert-crlf=1'), 'sha1');
+ digest
+--------------------------------------------
+ \x7efefcab38467f7484d6fa43dc86cf5281bd78e2
+(1 row)
+
+-- check BUG #11905, problem with messages 6 less than a power of 2.
+select pgp_sym_decrypt(pgp_sym_encrypt(repeat('x',65530),'1'),'1') = repeat('x',65530);
+ ?column?
+----------
+ t
+(1 row)
+
+-- Negative tests
+-- Decryption with a certain incorrect key yields an apparent Literal Data
+-- packet reporting its content to be binary data. Ciphertext source:
+-- iterative pgp_sym_encrypt('secret', 'key') until the random prefix gave
+-- rise to that property.
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0EBwMCxf8PTrQBmJdl0jcB6y2joE7GSLKRv7trbNsF5Z8ou5NISLUg31llVH/S0B2wl4bvzZjV
+VsxxqLSPzNLAeIspJk5G
+=mSd/
+-----END PGP MESSAGE-----
+'), 'wrong-key', 'debug=1');
+NOTICE: dbg: prefix_init: corrupt prefix
+NOTICE: dbg: parse_literal_data: data type=b
+NOTICE: dbg: mdcbuf_finish: bad MDC pkt hdr
+ERROR: Wrong key or corrupt data
+-- Routine text/binary mismatch.
+select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1');
+NOTICE: dbg: parse_literal_data: data type=b
+ERROR: Not text data
+-- Decryption with a certain incorrect key yields an apparent BZip2-compressed
+-- plaintext. Ciphertext source: iterative pgp_sym_encrypt('secret', 'key')
+-- until the random prefix gave rise to that property.
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0EBwMC9rK/dMkF5Zlt0jcBlzAQ1mQY2qYbKYbw8h3EZ5Jk0K2IiY92R82TRhWzBIF/8cmXDPtP
+GXsd65oYJZp3Khz0qfyn
+=Nmpq
+-----END PGP MESSAGE-----
+'), 'wrong-key', 'debug=1');
+NOTICE: dbg: prefix_init: corrupt prefix
+NOTICE: dbg: parse_compressed_data: bzip2 unsupported
+NOTICE: dbg: mdcbuf_finish: bad MDC pkt hdr
+ERROR: Wrong key or corrupt data
+-- Routine use of BZip2 compression. Ciphertext source:
+-- echo x | gpg --homedir /nonexistent --personal-compress-preferences bzip2 \
+-- --personal-cipher-preferences aes --no-emit-version --batch \
+-- --symmetric --passphrase key --armor
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+jA0EBwMCRhFrAKNcLVJg0mMBLJG1cCASNk/x/3dt1zJ+2eo7jHfjgg3N6wpB3XIe
+QCwkWJwlBG5pzbO5gu7xuPQN+TbPJ7aQ2sLx3bAHhtYb0i3vV9RO10Gw++yUyd4R
+UCAAw2JRIISttRHMfDpDuZJpvYo=
+=AZ9M
+-----END PGP MESSAGE-----
+'), 'key', 'debug=1');
+NOTICE: dbg: parse_compressed_data: bzip2 unsupported
+ERROR: Unsupported compression algorithm
diff --git a/contrib/pgcrypto/expected/pgp-decrypt_1.out b/contrib/pgcrypto/expected/pgp-decrypt_1.out
new file mode 100644
index 0000000..80a4c48
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-decrypt_1.out
@@ -0,0 +1,415 @@
+--
+-- pgp decrypt tests
+--
+-- Checking ciphers
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.blowfish.sha1.mdc.s2k3.z0
+
+jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
+yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
+=JcP+
+-----END PGP MESSAGE-----
+'), 'foobar');
+ERROR: Wrong key or corrupt data
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCci97v0Q6Z0Zg0kQBsVf5Oe3iC+FBzUmuMV9KxmAyOMyjCc/5i8f1Eest
+UTAsG35A1vYs02VARKzGz6xI2UHwFUirP+brPBg3Ee7muOx8pA==
+=XtrP
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k3.z0
+
+jA0ECAMCI7YQpWqp3D1g0kQBCjB7GlX7+SQeXNleXeXQ78ZAPNliquGDq9u378zI
+5FPTqAhIB2/2fjY8QEIs1ai00qphjX2NitxV/3Wn+6dufB4Q4g==
+=rCZt
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k3.z0
+
+jA0ECQMC4f/5djqCC1Rg0kQBTHEPsD+Sw7biBsM2er3vKyGPAQkuTBGKC5ie7hT/
+lceMfQdbAg6oTFyJpk/wH18GzRDphCofg0X8uLgkAKMrpcmgog==
+=fB6S
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking MDC modes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.nomdc.s2k3.z0
+
+jA0EBwMCnv07rlXqWctgyS2Dm2JfOKCRL4sLSLJUC8RS2cH7cIhKSuLitOtyquB+
+u9YkgfJfsuRJmgQ9tmo=
+=60ui
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCEeP3idNjQ1Bg0kQBf4G0wX+2QNzLh2YNwYkQgQkfYhn/hLXjV4nK9nsE
+8Ex1Dsdt5UPvOz8W8VKQRS6loOfOe+yyXil8W3IYFwUpdDUi+Q==
+=moGf
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking hashes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.md5.mdc.s2k3.z0
+
+jA0EBwMClrXXtOXetohg0kQBn0Kl1ymevQZRHkdoYRHgzCwSQEiss7zYff2UNzgO
+KyRrHf7zEBuZiZ2AG34jNVMOLToj1jJUg5zTSdecUzQVCykWTA==
+=NyLk
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCApbdlrURoWJg0kQBzHM/E0o7djY82bNuspjxjAcPFrrtp0uvDdMQ4z2m
+/PM8jhgI5vxFYfNQjLl8y3fHYIomk9YflN9K/Q13iq8A8sjeTw==
+=FxbQ
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking S2K modes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k0.z0
+
+jAQEBwAC0kQBKTaLAKE3xzps+QIZowqRNb2eAdzBw2LxEW2YD5PgNlbhJdGg+dvw
+Ah9GXjGS1TVALzTImJbz1uHUZRfhJlFbc5yGQw==
+=YvkV
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k1.z0
+
+jAwEBwEC/QTByBLI3b/SRAHPxKzI6SZBo5lAEOD+EsvKQWO4adL9tDY+++Iqy1xK
+4IaWXVKEj9R2Lr2xntWWMGZtcKtjD2lFFRXXd9dZp1ZThNDz
+=dbXm
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCEq4Su3ZqNEJg0kQB4QG5jBTKF0i04xtH+avzmLhstBNRxvV3nsmB3cwl
+z+9ZaA/XdSx5ZiFnMym8P6r8uY9rLjjNptvvRHlxIReF+p9MNg==
+=VJKg
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k0.z0
+
+jAQECAAC0kQBBDnQWkgsx9YFaqDfWmpsiyAJ6y2xG/sBvap1dySYEMuZ+wJTXQ9E
+Cr3i2M7TgVZ0M4jp4QL0adG1lpN5iK7aQeOwMw==
+=cg+i
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k1.z0
+
+jAwECAECruOfyNDFiTnSRAEVoGXm4A9UZKkWljdzjEO/iaE7mIraltIpQMkiqCh9
+7h8uZ2u9uRBOv222fZodGvc6bvq/4R4hAa/6qSHtm8mdmvGt
+=aHmC
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k3.z0
+
+jA0ECAMCjFn6SRi3SONg0kQBqtSHPaD0m7rXfDAhCWU/ypAsI93GuHGRyM99cvMv
+q6eF6859ZVnli3BFSDSk3a4e/pXhglxmDYCfjAXkozKNYLo6yw==
+=K0LS
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k0.z0
+
+jAQECQAC0kQB4L1eMbani07XF2ZYiXNK9LW3v8w41oUPl7dStmrJPQFwsdxmrDHu
+rQr3WbdKdY9ufjOE5+mXI+EFkSPrF9rL9NCq6w==
+=RGts
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k1.z0
+
+jAwECQECKHhrou7ZOIXSRAHWIVP+xjVQcjAVBTt+qh9SNzYe248xFTwozkwev3mO
++KVJW0qhk0An+Y2KF99/bYFl9cL5D3Tl43fC8fXGl3x3m7pR
+=SUrU
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k3.z0
+
+jA0ECQMCjc8lwZu8Fz1g0kQBkEzjImi21liep5jj+3dAJ2aZFfUkohi8b3n9z+7+
+4+NRzL7cMW2RLAFnJbiqXDlRHMwleeuLN1up2WIxsxtYYuaBjA==
+=XZrG
+-----END PGP MESSAGE-----
+'), 'foobar');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking longer passwords
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCx6dBiuqrYNRg0kQBEo63AvA1SCslxP7ayanLf1H0/hlk2nONVhTwVEWi
+tTGup1mMz6Cfh1uDRErUuXpx9A0gdMu7zX0o5XjrL7WGDAZdSw==
+=XKKG
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCBDvYuS990iFg0kQBW31UK5OiCjWf5x6KJ8qNNT2HZWQCjCBZMU0XsOC6
+CMxFKadf144H/vpoV9GA0f22keQgCl0EsTE4V4lweVOPTKCMJg==
+=gWDh
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij2jk4h5g2j54khg23h54g2kh54g2khj54g23hj54');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCqXbFafC+ofVg0kQBejyiPqH0QMERVGfmPOjtAxvyG5KDIJPYojTgVSDt
+FwsDabdQUz5O7bgNSnxfmyw1OifGF+W2bIn/8W+0rDf8u3+O+Q==
+=OxOF
+-----END PGP MESSAGE-----
+'), 'x');
+ pgp_sym_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- Checking various data
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCGJ+SpuOysINg0kQBJfSjzsW0x4OVcAyr17O7FBvMTwIGeGcJd99oTQU8
+Xtx3kDqnhUq9Z1fS3qPbi5iNP2A9NxOBxPWz2JzxhydANlgbxg==
+=W/ik
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+ digest
+--------------------------------------------
+ \x0225e3ede6f2587b076d021a189ff60aad67e066
+(1 row)
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat2.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCvdpDvidNzMxg0jUBvj8eS2+1t/9/zgemxvhtc0fvdKGGbjH7dleaTJRB
+SaV9L04ky1qECNDx3XjnoKLC+H7IOQ==
+=Fxen
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+ digest
+--------------------------------------------
+ \xda39a3ee5e6b4b0d3255bfef95601890afd80709
+(1 row)
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat3.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCxQvxJZ3G/HRg0lgBeYmTa7/uDAjPyFwSX4CYBgpZWVn/JS8JzILrcWF8
+gFnkUKIE0PSaYFp+Yi1VlRfUtRQ/X/LYNGa7tWZS+4VQajz2Xtz4vUeAEiYFYPXk
+73Hb8m1yRhQK
+=ivrD
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+ digest
+--------------------------------------------
+ \x5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
+(1 row)
+
+-- Checking CRLF
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: crlf mess
+
+ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
+a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
+=mBP9
+-----END PGP MESSAGE-----
+'), 'key', 'convert-crlf=0'), 'sha1');
+ digest
+--------------------------------------------
+ \x9353062be7720f1446d30b9e75573a4833886784
+(1 row)
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: crlf mess
+
+ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
+a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
+=mBP9
+-----END PGP MESSAGE-----
+'), 'key', 'convert-crlf=1'), 'sha1');
+ digest
+--------------------------------------------
+ \x7efefcab38467f7484d6fa43dc86cf5281bd78e2
+(1 row)
+
+-- check BUG #11905, problem with messages 6 less than a power of 2.
+select pgp_sym_decrypt(pgp_sym_encrypt(repeat('x',65530),'1'),'1') = repeat('x',65530);
+ ?column?
+----------
+ t
+(1 row)
+
+-- Negative tests
+-- Decryption with a certain incorrect key yields an apparent Literal Data
+-- packet reporting its content to be binary data. Ciphertext source:
+-- iterative pgp_sym_encrypt('secret', 'key') until the random prefix gave
+-- rise to that property.
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0EBwMCxf8PTrQBmJdl0jcB6y2joE7GSLKRv7trbNsF5Z8ou5NISLUg31llVH/S0B2wl4bvzZjV
+VsxxqLSPzNLAeIspJk5G
+=mSd/
+-----END PGP MESSAGE-----
+'), 'wrong-key', 'debug=1');
+NOTICE: dbg: prefix_init: corrupt prefix
+NOTICE: dbg: parse_literal_data: data type=b
+NOTICE: dbg: mdcbuf_finish: bad MDC pkt hdr
+ERROR: Wrong key or corrupt data
+-- Routine text/binary mismatch.
+select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1');
+NOTICE: dbg: parse_literal_data: data type=b
+ERROR: Not text data
+-- Decryption with a certain incorrect key yields an apparent BZip2-compressed
+-- plaintext. Ciphertext source: iterative pgp_sym_encrypt('secret', 'key')
+-- until the random prefix gave rise to that property.
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0EBwMC9rK/dMkF5Zlt0jcBlzAQ1mQY2qYbKYbw8h3EZ5Jk0K2IiY92R82TRhWzBIF/8cmXDPtP
+GXsd65oYJZp3Khz0qfyn
+=Nmpq
+-----END PGP MESSAGE-----
+'), 'wrong-key', 'debug=1');
+NOTICE: dbg: prefix_init: corrupt prefix
+NOTICE: dbg: parse_compressed_data: bzip2 unsupported
+NOTICE: dbg: mdcbuf_finish: bad MDC pkt hdr
+ERROR: Wrong key or corrupt data
+-- Routine use of BZip2 compression. Ciphertext source:
+-- echo x | gpg --homedir /nonexistent --personal-compress-preferences bzip2 \
+-- --personal-cipher-preferences aes --no-emit-version --batch \
+-- --symmetric --passphrase key --armor
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+jA0EBwMCRhFrAKNcLVJg0mMBLJG1cCASNk/x/3dt1zJ+2eo7jHfjgg3N6wpB3XIe
+QCwkWJwlBG5pzbO5gu7xuPQN+TbPJ7aQ2sLx3bAHhtYb0i3vV9RO10Gw++yUyd4R
+UCAAw2JRIISttRHMfDpDuZJpvYo=
+=AZ9M
+-----END PGP MESSAGE-----
+'), 'key', 'debug=1');
+NOTICE: dbg: parse_compressed_data: bzip2 unsupported
+ERROR: Unsupported compression algorithm
diff --git a/contrib/pgcrypto/expected/pgp-encrypt.out b/contrib/pgcrypto/expected/pgp-encrypt.out
new file mode 100644
index 0000000..77e45ab
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-encrypt.out
@@ -0,0 +1,208 @@
+--
+-- PGP encrypt
+--
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), 'key');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+-- check whether the defaults are ok
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
+ 'key', 'expect-cipher-algo=aes128,
+ expect-disable-mdc=0,
+ expect-sess-key=0,
+ expect-s2k-mode=3,
+ expect-s2k-digest-algo=sha1,
+ expect-compress-algo=0
+ ');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+-- maybe the expect- stuff simply does not work
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
+ 'key', 'expect-cipher-algo=bf,
+ expect-disable-mdc=1,
+ expect-sess-key=1,
+ expect-s2k-mode=0,
+ expect-s2k-digest-algo=md5,
+ expect-compress-algo=1
+ ');
+NOTICE: pgp_decrypt: unexpected cipher_algo: expected 4 got 7
+NOTICE: pgp_decrypt: unexpected s2k_mode: expected 0 got 3
+NOTICE: pgp_decrypt: unexpected s2k_digest_algo: expected 1 got 2
+NOTICE: pgp_decrypt: unexpected use_sess_key: expected 1 got 0
+NOTICE: pgp_decrypt: unexpected disable_mdc: expected 1 got 0
+NOTICE: pgp_decrypt: unexpected compress_algo: expected 1 got 0
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+-- bytea as text
+select pgp_sym_decrypt(pgp_sym_encrypt_bytea('Binary', 'baz'), 'baz');
+ERROR: Not text data
+-- text as bytea
+select encode(pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz'), 'escape');
+ encode
+--------
+ Text
+(1 row)
+
+-- algorithm change
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=bf'),
+ 'key', 'expect-cipher-algo=bf');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes'),
+ 'key', 'expect-cipher-algo=aes128');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes192'),
+ 'key', 'expect-cipher-algo=aes192');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+-- s2k change
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 's2k-mode=0'),
+ 'key', 'expect-s2k-mode=0');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 's2k-mode=1'),
+ 'key', 'expect-s2k-mode=1');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 's2k-mode=3'),
+ 'key', 'expect-s2k-mode=3');
+ 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'),
+ 'key', 'expect-s2k-digest-algo=md5');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=sha1'),
+ 'key', 'expect-s2k-digest-algo=sha1');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+-- sess key
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=0'),
+ 'key', 'expect-sess-key=0');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=1'),
+ 'key', 'expect-sess-key=1');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=bf'),
+ 'key', 'expect-sess-key=1, expect-cipher-algo=bf');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes192'),
+ 'key', 'expect-sess-key=1, expect-cipher-algo=aes192');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes256'),
+ 'key', 'expect-sess-key=1, expect-cipher-algo=aes256');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+-- no mdc
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'disable-mdc=1'),
+ 'key', 'expect-disable-mdc=1');
+ pgp_sym_decrypt
+-----------------
+ Secret.
+(1 row)
+
+-- crlf
+select pgp_sym_decrypt_bytea(
+ pgp_sym_encrypt(E'1\n2\n3\r\n', 'key', 'convert-crlf=1'),
+ 'key');
+ pgp_sym_decrypt_bytea
+------------------------
+ \x310d0a320d0a330d0d0a
+(1 row)
+
+-- conversion should be lossless
+select digest(pgp_sym_decrypt(
+ pgp_sym_encrypt(E'\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'),
+ 'key', 'convert-crlf=1'), 'sha1') as result,
+ digest(E'\r\n0\n1\r\r\n\n2\r', 'sha1') as expect;
+ result | expect
+--------------------------------------------+--------------------------------------------
+ \x47bde5d88d6ef8770572b9cbb4278b402aa69966 | \x47bde5d88d6ef8770572b9cbb4278b402aa69966
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/pgp-info.out b/contrib/pgcrypto/expected/pgp-info.out
new file mode 100644
index 0000000..9064838
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-info.out
@@ -0,0 +1,79 @@
+--
+-- PGP info functions
+--
+-- pgp_key_id
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=1;
+ pgp_key_id
+------------------
+ D936CF64BB73F466
+(1 row)
+
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=2;
+ pgp_key_id
+------------------
+ 2C226E1FFE5CC7D4
+(1 row)
+
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=3;
+ pgp_key_id
+------------------
+ B68504FD128E1FF9
+(1 row)
+
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=4; -- should fail
+ERROR: No encryption key found
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=5;
+ pgp_key_id
+------------------
+ D936CF64BB73F466
+(1 row)
+
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=6;
+ pgp_key_id
+------------------
+ FD0206C409B74875
+(1 row)
+
+select pgp_key_id(dearmor(seckey)) from keytbl where id=1;
+ pgp_key_id
+------------------
+ D936CF64BB73F466
+(1 row)
+
+select pgp_key_id(dearmor(seckey)) from keytbl where id=2;
+ pgp_key_id
+------------------
+ 2C226E1FFE5CC7D4
+(1 row)
+
+select pgp_key_id(dearmor(seckey)) from keytbl where id=3;
+ pgp_key_id
+------------------
+ B68504FD128E1FF9
+(1 row)
+
+select pgp_key_id(dearmor(seckey)) from keytbl where id=4; -- should fail
+ERROR: No encryption key found
+select pgp_key_id(dearmor(seckey)) from keytbl where id=5;
+ pgp_key_id
+------------------
+ D936CF64BB73F466
+(1 row)
+
+select pgp_key_id(dearmor(seckey)) from keytbl where id=6;
+ pgp_key_id
+------------------
+ FD0206C409B74875
+(1 row)
+
+select pgp_key_id(dearmor(data)) as data_key_id
+from encdata order by id;
+ data_key_id
+------------------
+ D936CF64BB73F466
+ 2C226E1FFE5CC7D4
+ B68504FD128E1FF9
+ FD0206C409B74875
+ FD0206C409B74875
+(5 rows)
+
diff --git a/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out b/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out
new file mode 100644
index 0000000..b4b6810
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out
@@ -0,0 +1,656 @@
+--
+-- PGP Public Key Encryption
+--
+-- As most of the low-level stuff is tested in symmetric key
+-- tests, here's only public-key specific tests
+create table keytbl (
+ id int4,
+ name text,
+ pubkey text,
+ seckey text
+);
+create table encdata (
+ id int4,
+ data text
+);
+insert into keytbl (id, name, pubkey, seckey)
+values (1, 'elg1024', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx
+MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc
+AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd
+ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P
+sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI
++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9
+6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF
+k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v
+iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F
+ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80
+=RWci
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQAAAnj4i4st+s+C6
+WKTIDcL1Iy0Saq8lCp60H0VsZ2FtYWwgMTAyNCA8dGVzdEBleGFtcGxlLm9yZz6I
+XgQTEQIAHgUCQsghSAIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRAcKbwNGBdz
+ZDrbAJ9cp6AsjOhiLxwznsMJheGf4xkH8wCfUPjMCLm4tAEnyYn2hDNt7CB8B6Kd
+ATEEQsghShAEAIeUjW2yTALCfrEG3FhM3ZLvAHWAec2O0Mn/RDr59IN/W8wDYcZp
+m+oG0ZUDdIqMppQ8K2kylAH7gmYDXIP9D7MRRm/Zw3L4yFfKnVaZ6tT7szBbgW5h
+iOsHoOz49NXZT4jtMLdZS1/krm5Lam2MSPod9XN0Q2asY/igIMUfGDRjAAMGA/sE
+LNh3tWefqeDkoDBEYjcxdGnGVGJnNHvv/eoHy9H7dyD/kkhaOoRAa5ClYWSqD0kk
+a+SqTWhKG4XcbJyo1GsP6sqGhXDTM2+LBZPMKuVJQpEfoe9ruob/BbpXglfEiVE9
+VNiY7ZVyUdj3svYn4fK2X7ue1G3cHR2tL4lnOA4pYQAA9030E4u2ZKOfJBpUM+EM
+m9VmsGjaQZV4teB0R/q3W8sRIYhJBBgRAgAJBQJCyCFKAhsMAAoJEBwpvA0YF3Nk
+7a8AniFFotw1x2X+oryu3Q3nNtmxoKHpAJ9HU7jw7ydg33dI9J8gVkrmsSZ2/w==
+=nvqq
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (2, 'elg2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n
+vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke
+5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO
+RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij
+8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl
+Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt
+J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/
+T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5
+0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6a7QjRWxnYW1hbCAy
+MDQ4IDx0ZXN0MjA0OEBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgiCgIbAwYLCQgH
+AwIDFQIDAxYCAQIeAQIXgAAKCRBI6c1W/qZo29PDAKCG724enIxRog1j+aeCp/uq
+or6mbwCePuKy2/1kD1FvnhkZ/R5fpm+pdm25Ag0EQsgiIhAIAJI3Gb2Ehtz1taQ9
+AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMX
+MhoWyw8ZF5Zs1mLIjFGVorePrm94N3MNPWM7x9M36bHUjx0vCZKFIhcGY1g+htE/
+QweaJzNVeA5z4qZmik41FbQyQSyHa3bOkTZu++/U6ghP+iDp5UDBjMTkVyqITUVN
+gC+MR+da/I60irBVhue7younh4ovF+CrVDQJC06HZl6CAJJyA81SmRfi+dmKbbjZ
+LF6rhz0norPjISJvkIqvdtM4VPBKI5wpgwCzpEqjuiKrAVujRT68zvBvJ4aVqb11
+k5QdJscAAwUH/jVJh0HbWAoiFTe+NvohfrA8vPcD0rtU3Y+siiqrabotnxJd2NuC
+bxghJYGfNtnx0KDjFbCRKJVeTFok4UnuVYhXdH/c6i0/rCTNdeW2D6pmR4GfBozR
+Pw/ARf+jONawGLyUj7uq13iquwMSE7VyNuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0R
+QsetMq/iNBWraayKZnWUd+eQqNzE+NUo7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiF
+Z1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qunfGW00ZMMTCWabg0ZgxPzMfMeIcm6525A
+Yn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/RdWaISQQYEQIACQUCQsgiIgIbDAAKCRBI
+6c1W/qZo25ZSAJ98WTrtl2HiX8ZqZq95v1+9cHtZPQCfZDoWQPybkNescLmXC7q5
+1kNTmEU=
+=8QM5
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n
+vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke
+5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO
+RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij
+8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl
+Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt
+J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/
+T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5
+0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6awAAn2F+iNBElfJS
+8azqO/kEiIfpqu6/DQG0I0VsZ2FtYWwgMjA0OCA8dGVzdDIwNDhAZXhhbXBsZS5v
+cmc+iF0EExECAB4FAkLIIgoCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQSOnN
+Vv6maNvTwwCYkpcJmpl3aHCQdGomz7dFohDgjgCgiThZt2xTEi6GhBB1vuhk+f55
+n3+dAj0EQsgiIhAIAJI3Gb2Ehtz1taQ9AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D
+29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMXMhoWyw8ZF5Zs1mLIjFGVorePrm94N3MN
+PWM7x9M36bHUjx0vCZKFIhcGY1g+htE/QweaJzNVeA5z4qZmik41FbQyQSyHa3bO
+kTZu++/U6ghP+iDp5UDBjMTkVyqITUVNgC+MR+da/I60irBVhue7younh4ovF+Cr
+VDQJC06HZl6CAJJyA81SmRfi+dmKbbjZLF6rhz0norPjISJvkIqvdtM4VPBKI5wp
+gwCzpEqjuiKrAVujRT68zvBvJ4aVqb11k5QdJscAAwUH/jVJh0HbWAoiFTe+Nvoh
+frA8vPcD0rtU3Y+siiqrabotnxJd2NuCbxghJYGfNtnx0KDjFbCRKJVeTFok4Unu
+VYhXdH/c6i0/rCTNdeW2D6pmR4GfBozRPw/ARf+jONawGLyUj7uq13iquwMSE7Vy
+NuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0RQsetMq/iNBWraayKZnWUd+eQqNzE+NUo
+7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiFZ1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qun
+fGW00ZMMTCWabg0ZgxPzMfMeIcm6525AYn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/R
+dWYAAVQKFPXbRaxbdArwRVXMzSD3qj/+VwwhwEDt8zmBGnlBfwVdkjQQrDUMmV1S
+EwyISQQYEQIACQUCQsgiIgIbDAAKCRBI6c1W/qZo25ZSAJ4sgUfHTVsG/x3p3fcM
+3b5R86qKEACggYKSwPWCs0YVRHOWqZY0pnHtLH8=
+=3Dgk
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (3, 'elg4096', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=kRxT
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=Gjq6
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (4, 'rsa2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQELBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP
+ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2
+55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx
+5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K
+MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz
+R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYptB5SU0EgMjA0OCA8cnNhMjA0
+OEBleGFtcGxlLm9yZz6JATQEEwECAB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgEC
+HgECF4AACgkQnc+OnJvTHyQqHwf8DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liR
+nrLuVlLBpdO6CWmMUzfKRvyZlx54GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzw
+bLZyM5Gb3lsE/FEmE7Dxw/0Utf59uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDP
+D3dnU4uzKPhMcjnSN00pzjusP7C9NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv
+9bgGWopumlOkt8Zu5YG6+CtTbJXprPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhv
+S3NZKoJ/1DrGgoDAu1mGkM4KvLAxfDs/qQ9dZhtEmDbKPLTVEA==
+=lR4n
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQOWBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP
+ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2
+55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx
+5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K
+MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz
+R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYpAAf/QZsrrz0c7dgWwGqMIpw6
+fP+/lLa74+fa2CFRWtYowEiKsfDg/wN7Ua07036dNhPa8aZPsU6SRzm5PybKOURe
+D9pNt0FxJkX0j5pCWfjSJgTbc1rCdqZ/oyBk/U6pQtf//zfw3PbDl7I8TC6GOt2w
+5NgcXdsWHP7LAmPctOVUyzFsenevR0MFTHkMbmKI1HpFm8XN/e1Fl+qIAD+OagTF
+5B32VvpoJtkh5nxnIuToNJsa9Iy7F9MM2CeFOyTMihMcjXKBBUaAYoF115irBvqu
+7N/qWmzqLg8yxBZ56mh6meCF3+67VA2y7fL8rhw2QuqgLg1JFlKAVL+9crCSrn//
+GQQA1kT7FytW6BNOffblFYZkrJer3icoRDqa/ljgH/yVaWoVT1igy0E9XzYO7MwP
+2usj/resLy0NC1qCthk51cZ/wthooMl88e5Wb4l5FYwBEac7muSBTo4W8cAH1hFj
+TWL6XAGvEzGX3Mt9pn8uYGlQLZAhJoNCAU2EOCbN1PchDvsEAOWNKYesuUVk8+sQ
+St0NDNhd9BWtTWTHkCZb1dKC3JTfr9PqkTBLrWFbYjkOtvdPAW7FDaXXXZfdH1jH
+WfwP3Q+I6sqgSaWpCS4dBAns3/RVtO7czVgyIwma04iIvJqderYrfvkUq95KfwP2
+V8wXkhrPPPxyrg5y3wQlpY2jb5RBBAC17SK1ms+DBtck4vpdjp3SJ32SbyC/DU30
+89Q12j74S7Zdu1qZlKnvy3kWPYX/hMuSzGZ+mLVJNFEqH2X01aFzppYz0hdI9PGB
+9tTFEqZWQL9ZkXfjc79Cgnt12pNukRbtw0N/kyutOdIFHVT79wVAd+powqziXJsC
+Kc+4xjwSCkZitB5SU0EgMjA0OCA8cnNhMjA0OEBleGFtcGxlLm9yZz6JATQEEwEC
+AB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQnc+OnJvTHyQqHwf8
+DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liRnrLuVlLBpdO6CWmMUzfKRvyZlx54
+GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzwbLZyM5Gb3lsE/FEmE7Dxw/0Utf59
+uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDPD3dnU4uzKPhMcjnSN00pzjusP7C9
+NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv9bgGWopumlOkt8Zu5YG6+CtTbJXp
+rPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhvS3NZKoJ/1DrGgoDAu1mGkM4KvLAx
+fDs/qQ9dZhtEmDbKPLTVEA==
+=WKAv
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (5, 'psw-elg1024', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx
+MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc
+AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd
+ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P
+sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI
++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9
+6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF
+k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v
+iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F
+ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80
+=RWci
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQHpBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQP4HAwImKZ5q2QwT
+D2DDAY/IQBjes7WgqZeacfLPDoB8ecD/KLoSCH6Z3etvbPHSOKiazxoJ962Ix74H
+ZAE6ZbMTtl5dZW1ptB9FbGdhbWFsIDEwMjQgPHRlc3RAZXhhbXBsZS5vcmc+iF4E
+ExECAB4FAkLIIUgCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQHCm8DRgXc2Q6
+2wCfXKegLIzoYi8cM57DCYXhn+MZB/MAn1D4zAi5uLQBJ8mJ9oQzbewgfAeinQFf
+BELIIUoQBACHlI1tskwCwn6xBtxYTN2S7wB1gHnNjtDJ/0Q6+fSDf1vMA2HGaZvq
+BtGVA3SKjKaUPCtpMpQB+4JmA1yD/Q+zEUZv2cNy+MhXyp1WmerU+7MwW4FuYYjr
+B6Ds+PTV2U+I7TC3WUtf5K5uS2ptjEj6HfVzdENmrGP4oCDFHxg0YwADBgP7BCzY
+d7Vnn6ng5KAwRGI3MXRpxlRiZzR77/3qB8vR+3cg/5JIWjqEQGuQpWFkqg9JJGvk
+qk1oShuF3GycqNRrD+rKhoVw0zNviwWTzCrlSUKRH6Hva7qG/wW6V4JXxIlRPVTY
+mO2VclHY97L2J+Hytl+7ntRt3B0drS+JZzgOKWH+BwMCJimeatkMEw9gRkFjt4Xa
+9rX8awMBE5+vVcGKv/DNiCvJnlYvSdCj8VfuHsYFliiJo6u17NJon+K43e3yvDNk
+f631VOVanGEz7TyqOkWQiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCe
+IUWi3DXHZf6ivK7dDec22bGgoekAn0dTuPDvJ2Dfd0j0nyBWSuaxJnb/
+=SNvr
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (6, 'rsaenc2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQELBELr2m0BCADOrnknlnXI0EzRExf/TgoHvK7Xx/E0keWqV3KrOyC3/tY2KOrj
+UVxaAX5pkFX9wdQObGPIJm06u6D16CH6CildX/vxG7YgvvKzK8JGAbwrXAfk7OIW
+czO2zRaZGDynoK3mAxHRBReyTKtNv8rDQhuZs6AOozJNARdbyUO/yqUnqNNygWuT
+4htFDEuLPIJwAbMSD0BvFW6YQaPdxzaAZm3EWVNbwDzjgbBUdBiUUwRdZIFUhsjJ
+dirFdy5+uuZru6y6CNC1OERkJ7P8EyoFiZckAIE5gshVZzNuyLOZjc5DhWBvLbX4
+NZElAnfiv+4nA6y8wQLSIbmHA3nqJaBklj85AAYptCVSU0EgMjA0OCBFbmMgPHJz
+YTIwNDhlbmNAZXhhbXBsZS5vcmc+iQE0BBMBAgAeBQJC69ptAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEMiZ6pNEGVVZHMkIAJtGHHZ9iM8Yq1rr0zl1L6SvlQP8
+JCaxHa31wH3PKqGtq2M+cpb2rXf7gAY/doHJPXggfVzkyFrysmQ1gPbDGYLyOutw
++IkhihEb5bWxQBNj+3zAFs1YX6v2HXWbSUSmyY1V9/+NTtKk03olDc/swd3lXzku
+UOhcgfpBgIt3Q+MpT6M2+OIF7lVfSb1rWdpwTfGhZzW9szQOeoS4gPvxCCRyuabQ
+RJ6DWH61F8fFIDJg1z+A/Obx4fqX6GOA69RzgZ3oukFBIXxNwV9PZNnAmHtZVYO8
+0g/oVYBbuvOYedffDBeQarhERZ5W2TnIE+nqY61YOLBqosliygdZTXULzNi5AQsE
+QuvaugEIAOuCJZdkzORA6e1lr81Lnr4JzMsVBFA+X/yIkBbV6qX/A4nVSLAZKNPX
+z1YIrMTu+1rMIiy10IWbA6zgMTpzPhJRfgePONgdnCYyK5Ksh5/C5ntzKwwGwxfK
+lAXIxJurCHXTbEa+YvPdn76vJ3HsXOXVEL+fLb4U3l3Ng87YM202Lh1Ha2MeS2zE
+FZcAoKbFqAAjDLEai64SoOFh0W3CsD1DL4zmfp+YZrUPHTtZadsi53i4KKW/ws9U
+rHlolqYNhYze/uRLyfnUx9PN4r/GhEzauyDMV0smo91uB3aewPft+eCpmeWnu0PF
+JVK4xyRmhIq2rVCw16a1pBJirvGM+y0ABimJAR8EGAECAAkFAkLr2roCGwwACgkQ
+yJnqk0QZVVku1wgAg1bLSjPkhw+ldG5HzumpqR84+JKyozdJaJzefu2+1iqYE0B0
+WLz2PJVIiK41xiEkKhBvTOQYuXmtWqAWXptD91P5SoXoNJWLQO3TNwarANhHxkWg
+w/TOUxQqoctlRUej5NDD+4eW5G9lcS1FEGuKDWtX096u80vO+TbyJjvx2eVM1k+X
+dmeYsGOiNgDimCreJGYc14G7eY9jt24gw10n1sMAKI1qm6lcoHqZ9OOyla+wJdro
+PYZGO7R8+1O9R22WrK6BYDT5j/1JwMZqbOESjNvDEVT0yOHClCHRN4CChbt6LhKh
+CLUNdz/udIt0JAC6c/HdPLSW3HnmM3+iNj+Kug==
+=pwU2
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=UKh3
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (7, 'rsaenc2048-psw', '
+same key with password
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+lQPEBELr2m0BCADOrnknlnXI0EzRExf/TgoHvK7Xx/E0keWqV3KrOyC3/tY2KOrj
+UVxaAX5pkFX9wdQObGPIJm06u6D16CH6CildX/vxG7YgvvKzK8JGAbwrXAfk7OIW
+czO2zRaZGDynoK3mAxHRBReyTKtNv8rDQhuZs6AOozJNARdbyUO/yqUnqNNygWuT
+4htFDEuLPIJwAbMSD0BvFW6YQaPdxzaAZm3EWVNbwDzjgbBUdBiUUwRdZIFUhsjJ
+dirFdy5+uuZru6y6CNC1OERkJ7P8EyoFiZckAIE5gshVZzNuyLOZjc5DhWBvLbX4
+NZElAnfiv+4nA6y8wQLSIbmHA3nqJaBklj85AAYp/gcDCNnoEKwFo86JYCE1J92R
+HRQ7DoyAZpW1O0dTXL8Epk0sKsKDrCJOrIkDymsjfyBexADIeqOkioy/50wD2Mku
+CVHKWO2duAiJN5t/FoRgpR1/Q11K6QdfqOG0HxwfIXLcPv7eSIso8kWorj+I01BP
+Fn/atGEbIjdWaz/q2XHbu0Q3x6Et2gIsbLRVMhiYz1UG9uzGJ0TYCdBa2SFhs184
+52akMpD+XVdM0Sq9/Cx40Seo8hzERB96+GXnQ48q2OhlvcEXiFyD6M6wYCWbEV+6
+XQVMymbl22FPP/bD9ReQX2kjrkQlFAtmhr+0y8reMCbcxwLuQfA3173lSPo7jrbH
+oLrGhkRpqd2bYCelqdy/XMmRFso0+7uytHfTFrUNfDWfmHVrygoVrNnarCbxMMI0
+I8Q+tKHMThWgf0rIOSh0+w38kOXFCEqEWF8YkAqCrMZIlJIed78rOCFgG4aHajZR
+D8rpXdUOIr/WeUddK25Tu8IuNJb0kFf12IMgNh0nS+mzlqWiofS5kA0TeB8wBV6t
+RotaeyDNSsMoowfN8cf1yHMTxli+K1Tasg003WVUoWgUc+EsJ5+KTNwaX5uGv0Cs
+j6dg6/FVeVRL9UsyF+2kt7euX3mABuUtcVGx/ZKTq/MNGEh6/r3B5U37qt+FDRbw
+ppKPc2AP+yBUWsQskyrxFgv4eSpcLEg+lgdz/zLyG4qW4lrFUoO790Cm/J6C7/WQ
+Z+E8kcS8aINJkg1skahH31d59ZkbW9PVeJMFGzNb0Z2LowngNP/BMrJ0LT2CQyLs
+UxbT16S/gwAyUpJnbhWYr3nDdlwtC0rVopVTPD7khPRppcsq1f8D70rdIxI4Ouuw
+vbjNZ1EWRJ9f2Ywb++k/xgSXwJkGodUlrUr+3i8cv8mPx+fWvif9q7Y5Ex1wCRa8
+8FAj/o+hEbQlUlNBIDIwNDggRW5jIDxyc2EyMDQ4ZW5jQGV4YW1wbGUub3JnPokB
+NAQTAQIAHgUCQuvabQIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRDImeqTRBlV
+WRzJCACbRhx2fYjPGKta69M5dS+kr5UD/CQmsR2t9cB9zyqhratjPnKW9q13+4AG
+P3aByT14IH1c5Mha8rJkNYD2wxmC8jrrcPiJIYoRG+W1sUATY/t8wBbNWF+r9h11
+m0lEpsmNVff/jU7SpNN6JQ3P7MHd5V85LlDoXIH6QYCLd0PjKU+jNvjiBe5VX0m9
+a1nacE3xoWc1vbM0DnqEuID78Qgkcrmm0ESeg1h+tRfHxSAyYNc/gPzm8eH6l+hj
+gOvUc4Gd6LpBQSF8TcFfT2TZwJh7WVWDvNIP6FWAW7rzmHnX3wwXkGq4REWeVtk5
+yBPp6mOtWDiwaqLJYsoHWU11C8zYnQPEBELr2roBCADrgiWXZMzkQOntZa/NS56+
+CczLFQRQPl/8iJAW1eql/wOJ1UiwGSjT189WCKzE7vtazCIstdCFmwOs4DE6cz4S
+UX4HjzjYHZwmMiuSrIefwuZ7cysMBsMXypQFyMSbqwh102xGvmLz3Z++rydx7Fzl
+1RC/ny2+FN5dzYPO2DNtNi4dR2tjHktsxBWXAKCmxagAIwyxGouuEqDhYdFtwrA9
+Qy+M5n6fmGa1Dx07WWnbIud4uCilv8LPVKx5aJamDYWM3v7kS8n51MfTzeK/xoRM
+2rsgzFdLJqPdbgd2nsD37fngqZnlp7tDxSVSuMckZoSKtq1QsNemtaQSYq7xjPst
+AAYp/gcDCNnoEKwFo86JYAsxoD+wQ0zBi5RBM5EphXTpM1qKxmigsKOvBSaMmr0y
+VjHtGY3poyV3t6VboOGCsFcaKm0tIdDL7vrxxwyYESETpF29b7QrYcoaLKMG7fsy
+t9SUI3UV2H9uUquHgqHtsqz0jYOgm9tYnpesgQ/kOAWI/tej1ZJXUIWEmZMH/W6d
+ATNvZ3ivwApfC0qF5G3oPgBSoIuQ/8I+pN/kmuyNAnJWNgagFhA/2VFBvh5XgztV
+NW7G//KpR1scsn140SO/wpGBM3Kr4m8ztl9w9U6a7NlQZ2ub3/pIUTpSzyLBxJZ/
+RfuZI7ROdgDMKmEgCYrN2kfp0LIxnYL6ZJu3FDcS4V098lyf5rHvB3PAEdL6Zyhd
+qYp3Sx68r0F4vzk5iAIWf6pG2YdfoP2Z48Pmq9xW8qD9iwFcoz9oAzDEMENn6dfq
+6MzfoaXEoYp8cR/o+aeEaGUtYBHiaxQcJYx35B9IhsXXA49yRORK8qdwhSHxB3NQ
+H3pUWkfw368f/A207hQVs9yYXlEvMZikxl58gldCd3BAPqHm/XzgknRRNQZBPPKJ
+BMZebZ22Dm0qDuIqW4GXLB4sLf0+UXydVINIUOlzg+S4jrwx7eZqb6UkRXTIWVo5
+psTsD14wzWBRdUQHZOZD33+M8ugmewvLY/0Uix+2RorkmB7/jqoZvx/MehDwmCZd
+VH8sb2wpZ55sj7gCXxvrfieQD/VeH54OwjjbtK56iYq56RVD0h1az8xDY2GZXeT7
+J0c3BGpuoca5xOFWr1SylAr/miEPxOBfnfk8oZQJvZrjSBGjsTbALep2vDJk8ROD
+sdQCJuU1RHDrwKHlbUL0NbGRO2juJGsatdWnuVKsFbaFW2pHHkezKuwOcaAJv7Xt
+8LRF17czAJ1uaLKwV8Paqx6UIv+089GbWZi7HIkBHwQYAQIACQUCQuvaugIbDAAK
+CRDImeqTRBlVWS7XCACDVstKM+SHD6V0bkfO6ampHzj4krKjN0lonN5+7b7WKpgT
+QHRYvPY8lUiIrjXGISQqEG9M5Bi5ea1aoBZem0P3U/lKheg0lYtA7dM3BqsA2EfG
+RaDD9M5TFCqhy2VFR6Pk0MP7h5bkb2VxLUUQa4oNa1fT3q7zS875NvImO/HZ5UzW
+T5d2Z5iwY6I2AOKYKt4kZhzXgbt5j2O3biDDXSfWwwAojWqbqVygepn047KVr7Al
+2ug9hkY7tHz7U71HbZasroFgNPmP/UnAxmps4RKM28MRVPTI4cKUIdE3gIKFu3ou
+EqEItQ13P+50i3QkALpz8d08tJbceeYzf6I2P4q6
+=QFm5
+-----END PGP PRIVATE KEY BLOCK-----
+');
+-- elg1024 / aes128
+insert into encdata (id, data) values (1, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQEOA9k2z2S7c/RmEAQAgVWW0DeLrZ+1thWJGBPp2WRFL9HeNqqWHbKJCXJbz1Uy
+faUY7yxVvG5Eutmo+JMiY3mg23/DgVVXHQZsTWpGvGM6djgUNGKUjZDbW6Nog7Mr
+e78IywattCOmgUP9vIwwg3OVjuDCN/nVirGQFnXpJBc8DzWqDMWRWDy1M0ZsK7AD
+/2JTosSFxUdpON0DKtIY3GLzmh6Nk3iV0g8VgJKUBT1rhCXuMDj3snm//EMm7hTY
+PlnObq4mIhgz8NqprmhooxnU0Kapofb3P3wCHPpU14zxhXY8iKO/3JhBq2uFcx4X
+uBMwkW4AdNxY/mzJZELteTL8Tr0s7PISk+owb4URpG3n0jsBc0CVULxrjh5Ejkdw
+wCM195J6+KbQxOOFQ0b3uOVvv4dEgd/hRERCOq5EPaFhlHegyYJ7YO842vnSDA==
+=PABx
+-----END PGP MESSAGE-----
+');
+-- elg2048 / blowfish
+insert into encdata (id, data) values (2, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQIOAywibh/+XMfUEAf+OINhBngEsw4a/IJIeJvUgv1gTQzBwOdQEuc/runr4Oa8
+Skw/Bj0X/zgABVZLem1a35NHaNwaQaCFwMQ41YyWCu+jTdsiyX/Nw0w8LKKz0rNC
+vVpG6YuV7Turtsf8a5lXy1K0SHkLlgxQ6c76GS4gtSl5+bsL2+5R1gSRJ9NXqCQP
+OHRipEiYwBPqr5R21ZG0FXXNKGOGkj6jt/M/wh3WVtAhYuBI+HPKRfAEjd/Pu/eD
+e1zYtkH1dKKFmp44+nF0tTI274xpuso7ShfKYrOK3saFWrl0DWiWteUinjSA1YBY
+m7dG7NZ8PW+g1SZWhEoPjEEEHz3kWMvlKheMRDudnQf/dDyX6kZVIAQF/5B012hq
+QyVewgTGysowFIDn01uIewoEA9cASw699jw9IoJp+k5WZXnU+INllBLzQxniQCSu
+iEcr0x3fYqNtj9QBfbIqyRcY6HTWcmzyOUeGaSyX76j+tRAvtVtXpraFFFnaHB70
+YpXTjLkp8EBafzMghFaKDeXlr2TG/T7rbwcwWrFIwPqEAUKWN5m97Q3eyo8/ioMd
+YoFD64J9ovSsgbuU5IpIGAsjxK+NKzg/2STH7zZFEVCtgcIXsTHTZfiwS98/+1H9
+p1DIDaXIcUFV2ztmcKxh9gt2sXRz1W+x6D8O0k3nanU5yGG4miLKaq18fbcA0BD1
++NIzAfelq6nvvxYKcGcamBMgLo5JkZOBHvyr6RsAKIT5QYc0QTjysTk9l0Am3gYc
+G2pAE+3k
+=TBHV
+-----END PGP MESSAGE-----
+');
+-- elg4096 / aes256
+insert into encdata (id, data) values (3, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQQOA7aFBP0Sjh/5EA/+JCgncc8IZmmRjPStWnGf9tVJhgHTn+smIclibGzs0deS
+SPSCitzpblwbUDvu964+/5e5Q1l7rRuNN+AgETlEd4eppv7Swn2ChdgOXxRwukcT
+Nh3G+PTFvD4ayi7w1db3qvXIt0MwN4Alt436wJmK1oz2Ka9IcyO+wHWrDy1nSGSx
+z5x7YEj+EZPgWc/YAvudqE8Jpzd/OT5zSHN09UFkIAk6NxisKaIstbEGFgpqtoDZ
+1SJM84XAdL2IcaJ3YY7k/yzwlawhsakKd4GSd5vWmAwvyzzbSiBMfKsDE16ePLNU
+ZBF7CzmlCBPZ7YrFAHLpXBXXkCQvzD2BEYOjse50ZEfJ036T7950Ozcdy1EQbGon
+nyQ4Gh0PBpnMcBuiXOceWuYzhlzFOzDtlVKdNTxFRDcbEyW2jo9xQYvCCLnYy8EH
+2M7S8jCtVYJBbn63a82ELv+3+kWYcsvBJv2ZVBh4ncrBu9o0P+OYS7ApoOU+j6p2
++t0RXHksqXS1YiUwYF5KSw09EbYMgNZ9G04Px/PxLU6fSC9iDrGX7Xt3kOUP0mku
+C518fPckT0zzRXqfFruJNRzDytW50KxkOQZzU1/Az1YlYN9QzWeU4EtLPb2fftZo
+D0qH/ln+f9Op5t6sD2fcxZVECU1b/bFtZsxvwH406YL+UQ7hU/XnZrzVVzODal8P
+/j1hg7v7BdJqu1DTp9nFWUuwMFcYAczuXn29IG183NZ7Ts4whDeYEhS8eNoLPX4j
+txY12ILD/w/3Q4LoW/hPa6OdfEzsn0U5GLf1WiGmJE1H6ft2U/xUnerc/u0kt+FU
+WAisArd4MuKtf7B5Vu/VF3kUdrR0hTniUKUivmC4o1jSId31Dufxj4aadVyldXAr
+6TNBcdyragZjxEZ6hsBCYzA0Rd1a8atd6OaQoIEEfAzCu5Ks29pydHErStYGjWJ1
+KA5KPLVvjbHpDmRhlCcm8vgpYQsBYEB5gE9fx5yCTlsVhCB6y23h7hfdMqerDqkO
+ZOPsO5h+tiHCdIrQ36sMjuINy1/K2rYcXd+Crh2iHcfidpU9fvDz2ihTRNQlhjuT
+0cQZM5JhctEx4VXF4LDctRhit7Hn0iqsk604woQfJVvP8O673xSXT/kBY0A/v9C0
+3C4YoFNeSaKwbfZQ/4u1ZFPJxK2IIJa8UGpyAUewLMlzGVVagljybv/f4Z9ERAhy
+huq5sMmw8UPsrJF2TUGHz5WSIwoh0J/qovoQI09I9sdEnFczDvRavMO2Mldy3E5i
+exz9oewtel6GOmsZQSYWT/vJzbYMmvHNmNpVwwoKrLV6oI3kyQ80GHBwI1WlwHoK
+2iRB0w8q4VVvJeYAz8ZIp380cqC3pfO0uZsrOx4g3k4X0jsB5y7rF5xXcZfnVbvG
+DYKcOy60/OHMWVvpw6trAoA+iP+cVWPtrbRvLglTVTfYmi1ToZDDipkALBhndQ==
+=L/M/
+-----END PGP MESSAGE-----
+');
+-- rsaenc2048 / aes128
+insert into encdata (id, data) values (4, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQEMA/0CBsQJt0h1AQf+JyYnCiortj26P11zk28MKOGfWpWyAhuIgwbJXsdQ+e6r
+pEyyqs9GC6gI7SNF6+J8B/gsMwvkAL4FHAQCvA4ZZ6eeXR1Of4YG22JQGmpWVWZg
+DTyfhA2vkczuqfAD2tgUpMT6sdyGkQ/fnQ0lknlfHgC5GRx7aavOoAKtMqiZW5PR
+yae/qR48mjX7Mb+mLvbagv9mHEgQSmHwFpaq2k456BbcZ23bvCmBnCvqV/90Ggfb
+VP6gkSoFVsJ19RHsOhW1dk9ehbl51WB3zUOO5FZWwUTY9DJvKblRK/frF0+CXjE4
+HfcZXHSpSjx4haGGTsMvEJ85qFjZpr0eTGOdY5cFhNJAAVP8MZfji7OhPRAoOOIK
+eRGOCkao12pvPyFTFnPd5vqmyBbdNpK4Q0hS82ljugMJvM0p3vJZVzW402Kz6iBL
+GQ==
+=XHkF
+-----END PGP MESSAGE-----
+');
+-- rsaenc2048 / aes128 (not from gnupg)
+insert into encdata (id, data) values (5, '
+-----BEGIN PGP MESSAGE-----
+
+wcBMA/0CBsQJt0h1AQgAzxZ8j+OTeZ8IlLxfZ/mVd28/gUsCY+xigWBk/anZlK3T
+p2tNU2idHzKdAttH2Hu/PWbZp4kwjl9spezYxMqCeBZqtfGED88Y+rqK0n/ul30A
+7jjFHaw0XUOqFNlST1v6H2i7UXndnp+kcLfHPhnO5BIYWxB2CYBehItqtrn75eqr
+C7trGzU/cr74efcWagbCDSNjiAV7GlEptlzmgVMmNikyI6w0ojEUx8lCLc/OsFz9
+pJUAX8xuwjxDVv+W7xk6c96grQiQlm+FLDYGiGNXoAzx3Wi/howu3uV40dXfY+jx
+3WBrhEew5Pkpt1SsWoFnJWOfJ8GLd0ec8vfRCqAIVdLgAeS7NyawQYtd6wuVrEAj
+5SMg4Thb4d+g45RksuGLHUUr4qO9tiXglODa4InhmJfgNuLk+RGz4LXjq8wepEmW
+vRbgFOG54+Cf4C/gC+HkreDm5JKSKjvvw4B/jC6CDxq+JoziEe2Z1uEjCuEcr+Es
+/eGzeOi36BejXPMHeKxXejj5qBBHKV0pHVhZSgffR0TtlXdB967Yl/5agV0R89hI
+7Gw52emfnH4Z0Y4V0au2H0k1dR/2IxXdJEWSTG7Be1JHT59p9ei2gSEOrdBMIOjP
+tbYYUlmmbvD49bHfThkDiC+oc9947LgQsk3kOOLbNHcjkbrjH8R5kjII4m/SEZA1
+g09T+338SzevBcVXh/cFrQ6/Et+lyyO2LJRUMs69g/HyzJOVWT2Iu8E0eS9MWevY
+Qtrkrhrpkl3Y02qEp/j6M03Yu2t6ZF7dp51aJ5VhO2mmmtHaTnCyCc8Fcf72LmD8
+blH2nKZC9d6fi4YzSYMepZpMOFR65M80MCMiDUGnZBB8sEADu2/iVtqDUeG8mAA=
+=PHJ1
+-----END PGP MESSAGE-----
+');
+-- successful decrypt
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=1 and encdata.id=1;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=2 and encdata.id=2;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=3 and encdata.id=3;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=6 and encdata.id=4;
+ pgp_pub_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- wrong key
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=2 and encdata.id=1;
+ERROR: Wrong key
+-- sign-only key
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=4 and encdata.id=1;
+ERROR: No encryption key found
+-- rsa: password-protected secret key, wrong password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '123')
+from keytbl, encdata where keytbl.id=7 and encdata.id=4;
+ERROR: Wrong key or corrupt data
+-- rsa: password-protected secret key, right password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'parool')
+from keytbl, encdata where keytbl.id=7 and encdata.id=4;
+ pgp_pub_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- password-protected secret key, no password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+ERROR: Need password for secret key
+-- password-protected secret key, wrong password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'foo')
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+ERROR: Wrong key or corrupt data
+-- password-protected secret key, right password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'parool')
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+-- test for a short read from prefix_init
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=6 and encdata.id=5;
+ERROR: Wrong key or corrupt data
diff --git a/contrib/pgcrypto/expected/pgp-pubkey-decrypt_1.out b/contrib/pgcrypto/expected/pgp-pubkey-decrypt_1.out
new file mode 100644
index 0000000..f41c6c9
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-pubkey-decrypt_1.out
@@ -0,0 +1,652 @@
+--
+-- PGP Public Key Encryption
+--
+-- As most of the low-level stuff is tested in symmetric key
+-- tests, here's only public-key specific tests
+create table keytbl (
+ id int4,
+ name text,
+ pubkey text,
+ seckey text
+);
+create table encdata (
+ id int4,
+ data text
+);
+insert into keytbl (id, name, pubkey, seckey)
+values (1, 'elg1024', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx
+MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc
+AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd
+ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P
+sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI
++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9
+6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF
+k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v
+iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F
+ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80
+=RWci
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQAAAnj4i4st+s+C6
+WKTIDcL1Iy0Saq8lCp60H0VsZ2FtYWwgMTAyNCA8dGVzdEBleGFtcGxlLm9yZz6I
+XgQTEQIAHgUCQsghSAIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRAcKbwNGBdz
+ZDrbAJ9cp6AsjOhiLxwznsMJheGf4xkH8wCfUPjMCLm4tAEnyYn2hDNt7CB8B6Kd
+ATEEQsghShAEAIeUjW2yTALCfrEG3FhM3ZLvAHWAec2O0Mn/RDr59IN/W8wDYcZp
+m+oG0ZUDdIqMppQ8K2kylAH7gmYDXIP9D7MRRm/Zw3L4yFfKnVaZ6tT7szBbgW5h
+iOsHoOz49NXZT4jtMLdZS1/krm5Lam2MSPod9XN0Q2asY/igIMUfGDRjAAMGA/sE
+LNh3tWefqeDkoDBEYjcxdGnGVGJnNHvv/eoHy9H7dyD/kkhaOoRAa5ClYWSqD0kk
+a+SqTWhKG4XcbJyo1GsP6sqGhXDTM2+LBZPMKuVJQpEfoe9ruob/BbpXglfEiVE9
+VNiY7ZVyUdj3svYn4fK2X7ue1G3cHR2tL4lnOA4pYQAA9030E4u2ZKOfJBpUM+EM
+m9VmsGjaQZV4teB0R/q3W8sRIYhJBBgRAgAJBQJCyCFKAhsMAAoJEBwpvA0YF3Nk
+7a8AniFFotw1x2X+oryu3Q3nNtmxoKHpAJ9HU7jw7ydg33dI9J8gVkrmsSZ2/w==
+=nvqq
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (2, 'elg2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n
+vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke
+5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO
+RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij
+8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl
+Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt
+J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/
+T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5
+0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6a7QjRWxnYW1hbCAy
+MDQ4IDx0ZXN0MjA0OEBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgiCgIbAwYLCQgH
+AwIDFQIDAxYCAQIeAQIXgAAKCRBI6c1W/qZo29PDAKCG724enIxRog1j+aeCp/uq
+or6mbwCePuKy2/1kD1FvnhkZ/R5fpm+pdm25Ag0EQsgiIhAIAJI3Gb2Ehtz1taQ9
+AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMX
+MhoWyw8ZF5Zs1mLIjFGVorePrm94N3MNPWM7x9M36bHUjx0vCZKFIhcGY1g+htE/
+QweaJzNVeA5z4qZmik41FbQyQSyHa3bOkTZu++/U6ghP+iDp5UDBjMTkVyqITUVN
+gC+MR+da/I60irBVhue7younh4ovF+CrVDQJC06HZl6CAJJyA81SmRfi+dmKbbjZ
+LF6rhz0norPjISJvkIqvdtM4VPBKI5wpgwCzpEqjuiKrAVujRT68zvBvJ4aVqb11
+k5QdJscAAwUH/jVJh0HbWAoiFTe+NvohfrA8vPcD0rtU3Y+siiqrabotnxJd2NuC
+bxghJYGfNtnx0KDjFbCRKJVeTFok4UnuVYhXdH/c6i0/rCTNdeW2D6pmR4GfBozR
+Pw/ARf+jONawGLyUj7uq13iquwMSE7VyNuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0R
+QsetMq/iNBWraayKZnWUd+eQqNzE+NUo7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiF
+Z1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qunfGW00ZMMTCWabg0ZgxPzMfMeIcm6525A
+Yn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/RdWaISQQYEQIACQUCQsgiIgIbDAAKCRBI
+6c1W/qZo25ZSAJ98WTrtl2HiX8ZqZq95v1+9cHtZPQCfZDoWQPybkNescLmXC7q5
+1kNTmEU=
+=8QM5
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n
+vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke
+5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO
+RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij
+8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl
+Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt
+J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/
+T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5
+0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6awAAn2F+iNBElfJS
+8azqO/kEiIfpqu6/DQG0I0VsZ2FtYWwgMjA0OCA8dGVzdDIwNDhAZXhhbXBsZS5v
+cmc+iF0EExECAB4FAkLIIgoCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQSOnN
+Vv6maNvTwwCYkpcJmpl3aHCQdGomz7dFohDgjgCgiThZt2xTEi6GhBB1vuhk+f55
+n3+dAj0EQsgiIhAIAJI3Gb2Ehtz1taQ9AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D
+29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMXMhoWyw8ZF5Zs1mLIjFGVorePrm94N3MN
+PWM7x9M36bHUjx0vCZKFIhcGY1g+htE/QweaJzNVeA5z4qZmik41FbQyQSyHa3bO
+kTZu++/U6ghP+iDp5UDBjMTkVyqITUVNgC+MR+da/I60irBVhue7younh4ovF+Cr
+VDQJC06HZl6CAJJyA81SmRfi+dmKbbjZLF6rhz0norPjISJvkIqvdtM4VPBKI5wp
+gwCzpEqjuiKrAVujRT68zvBvJ4aVqb11k5QdJscAAwUH/jVJh0HbWAoiFTe+Nvoh
+frA8vPcD0rtU3Y+siiqrabotnxJd2NuCbxghJYGfNtnx0KDjFbCRKJVeTFok4Unu
+VYhXdH/c6i0/rCTNdeW2D6pmR4GfBozRPw/ARf+jONawGLyUj7uq13iquwMSE7Vy
+NuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0RQsetMq/iNBWraayKZnWUd+eQqNzE+NUo
+7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiFZ1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qun
+fGW00ZMMTCWabg0ZgxPzMfMeIcm6525AYn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/R
+dWYAAVQKFPXbRaxbdArwRVXMzSD3qj/+VwwhwEDt8zmBGnlBfwVdkjQQrDUMmV1S
+EwyISQQYEQIACQUCQsgiIgIbDAAKCRBI6c1W/qZo25ZSAJ4sgUfHTVsG/x3p3fcM
+3b5R86qKEACggYKSwPWCs0YVRHOWqZY0pnHtLH8=
+=3Dgk
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (3, 'elg4096', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=kRxT
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=Gjq6
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (4, 'rsa2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQELBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP
+ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2
+55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx
+5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K
+MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz
+R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYptB5SU0EgMjA0OCA8cnNhMjA0
+OEBleGFtcGxlLm9yZz6JATQEEwECAB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgEC
+HgECF4AACgkQnc+OnJvTHyQqHwf8DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liR
+nrLuVlLBpdO6CWmMUzfKRvyZlx54GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzw
+bLZyM5Gb3lsE/FEmE7Dxw/0Utf59uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDP
+D3dnU4uzKPhMcjnSN00pzjusP7C9NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv
+9bgGWopumlOkt8Zu5YG6+CtTbJXprPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhv
+S3NZKoJ/1DrGgoDAu1mGkM4KvLAxfDs/qQ9dZhtEmDbKPLTVEA==
+=lR4n
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQOWBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP
+ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2
+55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx
+5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K
+MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz
+R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYpAAf/QZsrrz0c7dgWwGqMIpw6
+fP+/lLa74+fa2CFRWtYowEiKsfDg/wN7Ua07036dNhPa8aZPsU6SRzm5PybKOURe
+D9pNt0FxJkX0j5pCWfjSJgTbc1rCdqZ/oyBk/U6pQtf//zfw3PbDl7I8TC6GOt2w
+5NgcXdsWHP7LAmPctOVUyzFsenevR0MFTHkMbmKI1HpFm8XN/e1Fl+qIAD+OagTF
+5B32VvpoJtkh5nxnIuToNJsa9Iy7F9MM2CeFOyTMihMcjXKBBUaAYoF115irBvqu
+7N/qWmzqLg8yxBZ56mh6meCF3+67VA2y7fL8rhw2QuqgLg1JFlKAVL+9crCSrn//
+GQQA1kT7FytW6BNOffblFYZkrJer3icoRDqa/ljgH/yVaWoVT1igy0E9XzYO7MwP
+2usj/resLy0NC1qCthk51cZ/wthooMl88e5Wb4l5FYwBEac7muSBTo4W8cAH1hFj
+TWL6XAGvEzGX3Mt9pn8uYGlQLZAhJoNCAU2EOCbN1PchDvsEAOWNKYesuUVk8+sQ
+St0NDNhd9BWtTWTHkCZb1dKC3JTfr9PqkTBLrWFbYjkOtvdPAW7FDaXXXZfdH1jH
+WfwP3Q+I6sqgSaWpCS4dBAns3/RVtO7czVgyIwma04iIvJqderYrfvkUq95KfwP2
+V8wXkhrPPPxyrg5y3wQlpY2jb5RBBAC17SK1ms+DBtck4vpdjp3SJ32SbyC/DU30
+89Q12j74S7Zdu1qZlKnvy3kWPYX/hMuSzGZ+mLVJNFEqH2X01aFzppYz0hdI9PGB
+9tTFEqZWQL9ZkXfjc79Cgnt12pNukRbtw0N/kyutOdIFHVT79wVAd+powqziXJsC
+Kc+4xjwSCkZitB5SU0EgMjA0OCA8cnNhMjA0OEBleGFtcGxlLm9yZz6JATQEEwEC
+AB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQnc+OnJvTHyQqHwf8
+DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liRnrLuVlLBpdO6CWmMUzfKRvyZlx54
+GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzwbLZyM5Gb3lsE/FEmE7Dxw/0Utf59
+uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDPD3dnU4uzKPhMcjnSN00pzjusP7C9
+NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv9bgGWopumlOkt8Zu5YG6+CtTbJXp
+rPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhvS3NZKoJ/1DrGgoDAu1mGkM4KvLAx
+fDs/qQ9dZhtEmDbKPLTVEA==
+=WKAv
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (5, 'psw-elg1024', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx
+MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc
+AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd
+ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P
+sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI
++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9
+6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF
+k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v
+iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F
+ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80
+=RWci
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQHpBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQP4HAwImKZ5q2QwT
+D2DDAY/IQBjes7WgqZeacfLPDoB8ecD/KLoSCH6Z3etvbPHSOKiazxoJ962Ix74H
+ZAE6ZbMTtl5dZW1ptB9FbGdhbWFsIDEwMjQgPHRlc3RAZXhhbXBsZS5vcmc+iF4E
+ExECAB4FAkLIIUgCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQHCm8DRgXc2Q6
+2wCfXKegLIzoYi8cM57DCYXhn+MZB/MAn1D4zAi5uLQBJ8mJ9oQzbewgfAeinQFf
+BELIIUoQBACHlI1tskwCwn6xBtxYTN2S7wB1gHnNjtDJ/0Q6+fSDf1vMA2HGaZvq
+BtGVA3SKjKaUPCtpMpQB+4JmA1yD/Q+zEUZv2cNy+MhXyp1WmerU+7MwW4FuYYjr
+B6Ds+PTV2U+I7TC3WUtf5K5uS2ptjEj6HfVzdENmrGP4oCDFHxg0YwADBgP7BCzY
+d7Vnn6ng5KAwRGI3MXRpxlRiZzR77/3qB8vR+3cg/5JIWjqEQGuQpWFkqg9JJGvk
+qk1oShuF3GycqNRrD+rKhoVw0zNviwWTzCrlSUKRH6Hva7qG/wW6V4JXxIlRPVTY
+mO2VclHY97L2J+Hytl+7ntRt3B0drS+JZzgOKWH+BwMCJimeatkMEw9gRkFjt4Xa
+9rX8awMBE5+vVcGKv/DNiCvJnlYvSdCj8VfuHsYFliiJo6u17NJon+K43e3yvDNk
+f631VOVanGEz7TyqOkWQiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCe
+IUWi3DXHZf6ivK7dDec22bGgoekAn0dTuPDvJ2Dfd0j0nyBWSuaxJnb/
+=SNvr
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (6, 'rsaenc2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQELBELr2m0BCADOrnknlnXI0EzRExf/TgoHvK7Xx/E0keWqV3KrOyC3/tY2KOrj
+UVxaAX5pkFX9wdQObGPIJm06u6D16CH6CildX/vxG7YgvvKzK8JGAbwrXAfk7OIW
+czO2zRaZGDynoK3mAxHRBReyTKtNv8rDQhuZs6AOozJNARdbyUO/yqUnqNNygWuT
+4htFDEuLPIJwAbMSD0BvFW6YQaPdxzaAZm3EWVNbwDzjgbBUdBiUUwRdZIFUhsjJ
+dirFdy5+uuZru6y6CNC1OERkJ7P8EyoFiZckAIE5gshVZzNuyLOZjc5DhWBvLbX4
+NZElAnfiv+4nA6y8wQLSIbmHA3nqJaBklj85AAYptCVSU0EgMjA0OCBFbmMgPHJz
+YTIwNDhlbmNAZXhhbXBsZS5vcmc+iQE0BBMBAgAeBQJC69ptAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEMiZ6pNEGVVZHMkIAJtGHHZ9iM8Yq1rr0zl1L6SvlQP8
+JCaxHa31wH3PKqGtq2M+cpb2rXf7gAY/doHJPXggfVzkyFrysmQ1gPbDGYLyOutw
++IkhihEb5bWxQBNj+3zAFs1YX6v2HXWbSUSmyY1V9/+NTtKk03olDc/swd3lXzku
+UOhcgfpBgIt3Q+MpT6M2+OIF7lVfSb1rWdpwTfGhZzW9szQOeoS4gPvxCCRyuabQ
+RJ6DWH61F8fFIDJg1z+A/Obx4fqX6GOA69RzgZ3oukFBIXxNwV9PZNnAmHtZVYO8
+0g/oVYBbuvOYedffDBeQarhERZ5W2TnIE+nqY61YOLBqosliygdZTXULzNi5AQsE
+QuvaugEIAOuCJZdkzORA6e1lr81Lnr4JzMsVBFA+X/yIkBbV6qX/A4nVSLAZKNPX
+z1YIrMTu+1rMIiy10IWbA6zgMTpzPhJRfgePONgdnCYyK5Ksh5/C5ntzKwwGwxfK
+lAXIxJurCHXTbEa+YvPdn76vJ3HsXOXVEL+fLb4U3l3Ng87YM202Lh1Ha2MeS2zE
+FZcAoKbFqAAjDLEai64SoOFh0W3CsD1DL4zmfp+YZrUPHTtZadsi53i4KKW/ws9U
+rHlolqYNhYze/uRLyfnUx9PN4r/GhEzauyDMV0smo91uB3aewPft+eCpmeWnu0PF
+JVK4xyRmhIq2rVCw16a1pBJirvGM+y0ABimJAR8EGAECAAkFAkLr2roCGwwACgkQ
+yJnqk0QZVVku1wgAg1bLSjPkhw+ldG5HzumpqR84+JKyozdJaJzefu2+1iqYE0B0
+WLz2PJVIiK41xiEkKhBvTOQYuXmtWqAWXptD91P5SoXoNJWLQO3TNwarANhHxkWg
+w/TOUxQqoctlRUej5NDD+4eW5G9lcS1FEGuKDWtX096u80vO+TbyJjvx2eVM1k+X
+dmeYsGOiNgDimCreJGYc14G7eY9jt24gw10n1sMAKI1qm6lcoHqZ9OOyla+wJdro
+PYZGO7R8+1O9R22WrK6BYDT5j/1JwMZqbOESjNvDEVT0yOHClCHRN4CChbt6LhKh
+CLUNdz/udIt0JAC6c/HdPLSW3HnmM3+iNj+Kug==
+=pwU2
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=UKh3
+-----END PGP PRIVATE KEY BLOCK-----
+');
+insert into keytbl (id, name, pubkey, seckey)
+values (7, 'rsaenc2048-psw', '
+same key with password
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+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
+=QFm5
+-----END PGP PRIVATE KEY BLOCK-----
+');
+-- elg1024 / aes128
+insert into encdata (id, data) values (1, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQEOA9k2z2S7c/RmEAQAgVWW0DeLrZ+1thWJGBPp2WRFL9HeNqqWHbKJCXJbz1Uy
+faUY7yxVvG5Eutmo+JMiY3mg23/DgVVXHQZsTWpGvGM6djgUNGKUjZDbW6Nog7Mr
+e78IywattCOmgUP9vIwwg3OVjuDCN/nVirGQFnXpJBc8DzWqDMWRWDy1M0ZsK7AD
+/2JTosSFxUdpON0DKtIY3GLzmh6Nk3iV0g8VgJKUBT1rhCXuMDj3snm//EMm7hTY
+PlnObq4mIhgz8NqprmhooxnU0Kapofb3P3wCHPpU14zxhXY8iKO/3JhBq2uFcx4X
+uBMwkW4AdNxY/mzJZELteTL8Tr0s7PISk+owb4URpG3n0jsBc0CVULxrjh5Ejkdw
+wCM195J6+KbQxOOFQ0b3uOVvv4dEgd/hRERCOq5EPaFhlHegyYJ7YO842vnSDA==
+=PABx
+-----END PGP MESSAGE-----
+');
+-- elg2048 / blowfish
+insert into encdata (id, data) values (2, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQIOAywibh/+XMfUEAf+OINhBngEsw4a/IJIeJvUgv1gTQzBwOdQEuc/runr4Oa8
+Skw/Bj0X/zgABVZLem1a35NHaNwaQaCFwMQ41YyWCu+jTdsiyX/Nw0w8LKKz0rNC
+vVpG6YuV7Turtsf8a5lXy1K0SHkLlgxQ6c76GS4gtSl5+bsL2+5R1gSRJ9NXqCQP
+OHRipEiYwBPqr5R21ZG0FXXNKGOGkj6jt/M/wh3WVtAhYuBI+HPKRfAEjd/Pu/eD
+e1zYtkH1dKKFmp44+nF0tTI274xpuso7ShfKYrOK3saFWrl0DWiWteUinjSA1YBY
+m7dG7NZ8PW+g1SZWhEoPjEEEHz3kWMvlKheMRDudnQf/dDyX6kZVIAQF/5B012hq
+QyVewgTGysowFIDn01uIewoEA9cASw699jw9IoJp+k5WZXnU+INllBLzQxniQCSu
+iEcr0x3fYqNtj9QBfbIqyRcY6HTWcmzyOUeGaSyX76j+tRAvtVtXpraFFFnaHB70
+YpXTjLkp8EBafzMghFaKDeXlr2TG/T7rbwcwWrFIwPqEAUKWN5m97Q3eyo8/ioMd
+YoFD64J9ovSsgbuU5IpIGAsjxK+NKzg/2STH7zZFEVCtgcIXsTHTZfiwS98/+1H9
+p1DIDaXIcUFV2ztmcKxh9gt2sXRz1W+x6D8O0k3nanU5yGG4miLKaq18fbcA0BD1
++NIzAfelq6nvvxYKcGcamBMgLo5JkZOBHvyr6RsAKIT5QYc0QTjysTk9l0Am3gYc
+G2pAE+3k
+=TBHV
+-----END PGP MESSAGE-----
+');
+-- elg4096 / aes256
+insert into encdata (id, data) values (3, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQQOA7aFBP0Sjh/5EA/+JCgncc8IZmmRjPStWnGf9tVJhgHTn+smIclibGzs0deS
+SPSCitzpblwbUDvu964+/5e5Q1l7rRuNN+AgETlEd4eppv7Swn2ChdgOXxRwukcT
+Nh3G+PTFvD4ayi7w1db3qvXIt0MwN4Alt436wJmK1oz2Ka9IcyO+wHWrDy1nSGSx
+z5x7YEj+EZPgWc/YAvudqE8Jpzd/OT5zSHN09UFkIAk6NxisKaIstbEGFgpqtoDZ
+1SJM84XAdL2IcaJ3YY7k/yzwlawhsakKd4GSd5vWmAwvyzzbSiBMfKsDE16ePLNU
+ZBF7CzmlCBPZ7YrFAHLpXBXXkCQvzD2BEYOjse50ZEfJ036T7950Ozcdy1EQbGon
+nyQ4Gh0PBpnMcBuiXOceWuYzhlzFOzDtlVKdNTxFRDcbEyW2jo9xQYvCCLnYy8EH
+2M7S8jCtVYJBbn63a82ELv+3+kWYcsvBJv2ZVBh4ncrBu9o0P+OYS7ApoOU+j6p2
++t0RXHksqXS1YiUwYF5KSw09EbYMgNZ9G04Px/PxLU6fSC9iDrGX7Xt3kOUP0mku
+C518fPckT0zzRXqfFruJNRzDytW50KxkOQZzU1/Az1YlYN9QzWeU4EtLPb2fftZo
+D0qH/ln+f9Op5t6sD2fcxZVECU1b/bFtZsxvwH406YL+UQ7hU/XnZrzVVzODal8P
+/j1hg7v7BdJqu1DTp9nFWUuwMFcYAczuXn29IG183NZ7Ts4whDeYEhS8eNoLPX4j
+txY12ILD/w/3Q4LoW/hPa6OdfEzsn0U5GLf1WiGmJE1H6ft2U/xUnerc/u0kt+FU
+WAisArd4MuKtf7B5Vu/VF3kUdrR0hTniUKUivmC4o1jSId31Dufxj4aadVyldXAr
+6TNBcdyragZjxEZ6hsBCYzA0Rd1a8atd6OaQoIEEfAzCu5Ks29pydHErStYGjWJ1
+KA5KPLVvjbHpDmRhlCcm8vgpYQsBYEB5gE9fx5yCTlsVhCB6y23h7hfdMqerDqkO
+ZOPsO5h+tiHCdIrQ36sMjuINy1/K2rYcXd+Crh2iHcfidpU9fvDz2ihTRNQlhjuT
+0cQZM5JhctEx4VXF4LDctRhit7Hn0iqsk604woQfJVvP8O673xSXT/kBY0A/v9C0
+3C4YoFNeSaKwbfZQ/4u1ZFPJxK2IIJa8UGpyAUewLMlzGVVagljybv/f4Z9ERAhy
+huq5sMmw8UPsrJF2TUGHz5WSIwoh0J/qovoQI09I9sdEnFczDvRavMO2Mldy3E5i
+exz9oewtel6GOmsZQSYWT/vJzbYMmvHNmNpVwwoKrLV6oI3kyQ80GHBwI1WlwHoK
+2iRB0w8q4VVvJeYAz8ZIp380cqC3pfO0uZsrOx4g3k4X0jsB5y7rF5xXcZfnVbvG
+DYKcOy60/OHMWVvpw6trAoA+iP+cVWPtrbRvLglTVTfYmi1ToZDDipkALBhndQ==
+=L/M/
+-----END PGP MESSAGE-----
+');
+-- rsaenc2048 / aes128
+insert into encdata (id, data) values (4, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQEMA/0CBsQJt0h1AQf+JyYnCiortj26P11zk28MKOGfWpWyAhuIgwbJXsdQ+e6r
+pEyyqs9GC6gI7SNF6+J8B/gsMwvkAL4FHAQCvA4ZZ6eeXR1Of4YG22JQGmpWVWZg
+DTyfhA2vkczuqfAD2tgUpMT6sdyGkQ/fnQ0lknlfHgC5GRx7aavOoAKtMqiZW5PR
+yae/qR48mjX7Mb+mLvbagv9mHEgQSmHwFpaq2k456BbcZ23bvCmBnCvqV/90Ggfb
+VP6gkSoFVsJ19RHsOhW1dk9ehbl51WB3zUOO5FZWwUTY9DJvKblRK/frF0+CXjE4
+HfcZXHSpSjx4haGGTsMvEJ85qFjZpr0eTGOdY5cFhNJAAVP8MZfji7OhPRAoOOIK
+eRGOCkao12pvPyFTFnPd5vqmyBbdNpK4Q0hS82ljugMJvM0p3vJZVzW402Kz6iBL
+GQ==
+=XHkF
+-----END PGP MESSAGE-----
+');
+-- rsaenc2048 / aes128 (not from gnupg)
+insert into encdata (id, data) values (5, '
+-----BEGIN PGP MESSAGE-----
+
+wcBMA/0CBsQJt0h1AQgAzxZ8j+OTeZ8IlLxfZ/mVd28/gUsCY+xigWBk/anZlK3T
+p2tNU2idHzKdAttH2Hu/PWbZp4kwjl9spezYxMqCeBZqtfGED88Y+rqK0n/ul30A
+7jjFHaw0XUOqFNlST1v6H2i7UXndnp+kcLfHPhnO5BIYWxB2CYBehItqtrn75eqr
+C7trGzU/cr74efcWagbCDSNjiAV7GlEptlzmgVMmNikyI6w0ojEUx8lCLc/OsFz9
+pJUAX8xuwjxDVv+W7xk6c96grQiQlm+FLDYGiGNXoAzx3Wi/howu3uV40dXfY+jx
+3WBrhEew5Pkpt1SsWoFnJWOfJ8GLd0ec8vfRCqAIVdLgAeS7NyawQYtd6wuVrEAj
+5SMg4Thb4d+g45RksuGLHUUr4qO9tiXglODa4InhmJfgNuLk+RGz4LXjq8wepEmW
+vRbgFOG54+Cf4C/gC+HkreDm5JKSKjvvw4B/jC6CDxq+JoziEe2Z1uEjCuEcr+Es
+/eGzeOi36BejXPMHeKxXejj5qBBHKV0pHVhZSgffR0TtlXdB967Yl/5agV0R89hI
+7Gw52emfnH4Z0Y4V0au2H0k1dR/2IxXdJEWSTG7Be1JHT59p9ei2gSEOrdBMIOjP
+tbYYUlmmbvD49bHfThkDiC+oc9947LgQsk3kOOLbNHcjkbrjH8R5kjII4m/SEZA1
+g09T+338SzevBcVXh/cFrQ6/Et+lyyO2LJRUMs69g/HyzJOVWT2Iu8E0eS9MWevY
+Qtrkrhrpkl3Y02qEp/j6M03Yu2t6ZF7dp51aJ5VhO2mmmtHaTnCyCc8Fcf72LmD8
+blH2nKZC9d6fi4YzSYMepZpMOFR65M80MCMiDUGnZBB8sEADu2/iVtqDUeG8mAA=
+=PHJ1
+-----END PGP MESSAGE-----
+');
+-- successful decrypt
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=1 and encdata.id=1;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=2 and encdata.id=2;
+ERROR: Wrong key or corrupt data
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=3 and encdata.id=3;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=6 and encdata.id=4;
+ pgp_pub_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- wrong key
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=2 and encdata.id=1;
+ERROR: Wrong key
+-- sign-only key
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=4 and encdata.id=1;
+ERROR: No encryption key found
+-- rsa: password-protected secret key, wrong password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '123')
+from keytbl, encdata where keytbl.id=7 and encdata.id=4;
+ERROR: Wrong key or corrupt data
+-- rsa: password-protected secret key, right password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'parool')
+from keytbl, encdata where keytbl.id=7 and encdata.id=4;
+ pgp_pub_decrypt
+-----------------
+ Secret message.
+(1 row)
+
+-- password-protected secret key, no password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+ERROR: Need password for secret key
+-- password-protected secret key, wrong password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'foo')
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+ERROR: Wrong key or corrupt data
+-- password-protected secret key, right password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'parool')
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+-- test for a short read from prefix_init
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=6 and encdata.id=5;
+ERROR: Wrong key or corrupt data
diff --git a/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out b/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out
new file mode 100644
index 0000000..7f90554
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out
@@ -0,0 +1,68 @@
+--
+-- PGP Public Key Encryption
+--
+-- successful encrypt/decrypt
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=1;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=2;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=3;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=6;
+ pgp_pub_decrypt
+-----------------
+ Secret msg
+(1 row)
+
+-- try with rsa-sign only
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=4;
+ERROR: No encryption key found
+-- try with secret key
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(seckey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=1;
+ERROR: Refusing to encrypt with secret key
+-- does text-to-bytea works
+select encode(pgp_pub_decrypt_bytea(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey)), 'escape')
+from keytbl where keytbl.id=1;
+ encode
+------------
+ Secret msg
+(1 row)
+
+-- and bytea-to-text?
+select pgp_pub_decrypt(
+ pgp_pub_encrypt_bytea('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=1;
+ERROR: Not text data
diff --git a/contrib/pgcrypto/expected/pgp-zlib-DISABLED.out b/contrib/pgcrypto/expected/pgp-zlib-DISABLED.out
new file mode 100644
index 0000000..6f4eccd
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-zlib-DISABLED.out
@@ -0,0 +1 @@
+-- zlib is disabled
diff --git a/contrib/pgcrypto/expected/rijndael.out b/contrib/pgcrypto/expected/rijndael.out
new file mode 100644
index 0000000..015ba44
--- /dev/null
+++ b/contrib/pgcrypto/expected/rijndael.out
@@ -0,0 +1,137 @@
+--
+-- AES cipher (aka Rijndael-128, -192, or -256)
+--
+-- some standard Rijndael testvalues
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f',
+'aes-ecb/pad:none');
+ encrypt
+------------------------------------
+ \x69c4e0d86a7b0430d8cdb78070b4c55a
+(1 row)
+
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f1011121314151617',
+'aes-ecb/pad:none');
+ encrypt
+------------------------------------
+ \xdda97ca4864cdfe06eaf70a0ec0d7191
+(1 row)
+
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-ecb/pad:none');
+ encrypt
+------------------------------------
+ \x8ea2b7ca516745bfeafc49904b496089
+(1 row)
+
+-- cbc
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-cbc/pad:none');
+ encrypt
+------------------------------------
+ \x8ea2b7ca516745bfeafc49904b496089
+(1 row)
+
+-- without padding, input not multiple of block size
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff00',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-cbc/pad:none');
+ERROR: encrypt error: Encryption failed
+-- key padding
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405',
+'aes-cbc');
+ encrypt
+------------------------------------
+ \x189a28932213f017b246678dbc28655f
+(1 row)
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405060708090a0b0c0d0e0f10111213',
+'aes-cbc');
+ encrypt
+------------------------------------
+ \x3b02279162d15580e069d3a71407a556
+(1 row)
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b',
+'aes-cbc');
+ encrypt
+------------------------------------
+ \x4facb6a041d53e0a5a73289170901fe7
+(1 row)
+
+-- empty data
+select encrypt('', 'foo', 'aes');
+ encrypt
+------------------------------------
+ \xb48cc3338a2eb293b6007ef72c360d48
+(1 row)
+
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'aes');
+ encrypt
+------------------------------------
+ \xf397f03d2819b7172b68d0706fda4693
+(1 row)
+
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', 'aes');
+ encrypt
+------------------------------------
+ \x5c9db77af02b4678117bcd8a71ae7f53
+(1 row)
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'aes'), '0123456', 'aes'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- data not multiple of block size
+select encode(decrypt(encrypt('foo', '0123456', 'aes') || '\x00'::bytea, '0123456', 'aes'), 'escape');
+ERROR: decrypt error: Decryption failed
+-- bad padding
+-- (The input value is the result of encrypt_iv('abcdefghijklmnopqrstuvwxyz', '0123456', 'abcd', 'aes')
+-- with the 16th byte changed (s/db/eb/) to corrupt the padding of the last block.)
+select encode(decrypt_iv('\xa21a9c15231465964e3396d32095e67eb52bab05f556a581621dee1b85385789', '0123456', 'abcd', 'aes'), 'escape');
+ERROR: decrypt_iv error: Decryption failed
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'aes');
+ encrypt_iv
+------------------------------------
+ \x2c24cb7da91d6d5699801268b0f5adad
+(1 row)
+
+select encode(decrypt_iv('\x2c24cb7da91d6d5699801268b0f5adad', '0123456', 'abcd', 'aes'), 'escape');
+ encode
+--------
+ foo
+(1 row)
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'aes');
+ encrypt
+--------------------------------------------------------------------
+ \xd9beb785dd5403ed02f66b755bb191b93ed93ca54930153f2c3b9ec7785056ad
+(1 row)
+
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes'), '0123456789', 'aes'), 'escape');
+ encode
+----------------------------
+ Lets try a longer message.
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/sha1.out b/contrib/pgcrypto/expected/sha1.out
new file mode 100644
index 0000000..b8eaed1
--- /dev/null
+++ b/contrib/pgcrypto/expected/sha1.out
@@ -0,0 +1,45 @@
+--
+-- SHA1 message digest
+--
+SELECT digest('', 'sha1');
+ digest
+--------------------------------------------
+ \xda39a3ee5e6b4b0d3255bfef95601890afd80709
+(1 row)
+
+SELECT digest('a', 'sha1');
+ digest
+--------------------------------------------
+ \x86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
+(1 row)
+
+SELECT digest('abc', 'sha1');
+ digest
+--------------------------------------------
+ \xa9993e364706816aba3e25717850c26c9cd0d89d
+(1 row)
+
+SELECT digest('message digest', 'sha1');
+ digest
+--------------------------------------------
+ \xc12252ceda8be8994d5fa0290a47231c1d16aae3
+(1 row)
+
+SELECT digest('abcdefghijklmnopqrstuvwxyz', 'sha1');
+ digest
+--------------------------------------------
+ \x32d10c7b8cf96570ca04ce37f2a19d84240d3a89
+(1 row)
+
+SELECT digest('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'sha1');
+ digest
+--------------------------------------------
+ \x761c457bf73b14d27e9e9265c46f4b4dda11f940
+(1 row)
+
+SELECT digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha1');
+ digest
+--------------------------------------------
+ \x50abf5706a150990a08b2c5ea40fa0e585554732
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/sha2.out b/contrib/pgcrypto/expected/sha2.out
new file mode 100644
index 0000000..6f67fe6
--- /dev/null
+++ b/contrib/pgcrypto/expected/sha2.out
@@ -0,0 +1,139 @@
+--
+-- SHA2 family
+--
+-- SHA224
+SELECT digest('', 'sha224');
+ digest
+------------------------------------------------------------
+ \xd14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f
+(1 row)
+
+SELECT digest('a', 'sha224');
+ digest
+------------------------------------------------------------
+ \xabd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5
+(1 row)
+
+SELECT digest('abc', 'sha224');
+ digest
+------------------------------------------------------------
+ \x23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7
+(1 row)
+
+SELECT digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha224');
+ digest
+------------------------------------------------------------
+ \x75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525
+(1 row)
+
+SELECT digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha224');
+ digest
+------------------------------------------------------------
+ \xb50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e
+(1 row)
+
+-- SHA256
+SELECT digest('', 'sha256');
+ digest
+--------------------------------------------------------------------
+ \xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+(1 row)
+
+SELECT digest('a', 'sha256');
+ digest
+--------------------------------------------------------------------
+ \xca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
+(1 row)
+
+SELECT digest('abc', 'sha256');
+ digest
+--------------------------------------------------------------------
+ \xba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
+(1 row)
+
+SELECT digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha256');
+ digest
+--------------------------------------------------------------------
+ \x248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1
+(1 row)
+
+SELECT digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha256');
+ digest
+--------------------------------------------------------------------
+ \xf371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e
+(1 row)
+
+-- SHA384
+SELECT digest('', 'sha384');
+ digest
+----------------------------------------------------------------------------------------------------
+ \x38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b
+(1 row)
+
+SELECT digest('a', 'sha384');
+ digest
+----------------------------------------------------------------------------------------------------
+ \x54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31
+(1 row)
+
+SELECT digest('abc', 'sha384');
+ digest
+----------------------------------------------------------------------------------------------------
+ \xcb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7
+(1 row)
+
+SELECT digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha384');
+ digest
+----------------------------------------------------------------------------------------------------
+ \x3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b
+(1 row)
+
+SELECT digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha384');
+ digest
+----------------------------------------------------------------------------------------------------
+ \x09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039
+(1 row)
+
+SELECT digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha384');
+ digest
+----------------------------------------------------------------------------------------------------
+ \x3d208973ab3508dbbd7e2c2862ba290ad3010e4978c198dc4d8fd014e582823a89e16f9b2a7bbc1ac938e2d199e8bea4
+(1 row)
+
+-- SHA512
+SELECT digest('', 'sha512');
+ digest
+------------------------------------------------------------------------------------------------------------------------------------
+ \xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
+(1 row)
+
+SELECT digest('a', 'sha512');
+ digest
+------------------------------------------------------------------------------------------------------------------------------------
+ \x1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75
+(1 row)
+
+SELECT digest('abc', 'sha512');
+ digest
+------------------------------------------------------------------------------------------------------------------------------------
+ \xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
+(1 row)
+
+SELECT digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha512');
+ digest
+------------------------------------------------------------------------------------------------------------------------------------
+ \x204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445
+(1 row)
+
+SELECT digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha512');
+ digest
+------------------------------------------------------------------------------------------------------------------------------------
+ \x8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909
+(1 row)
+
+SELECT digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha512');
+ digest
+------------------------------------------------------------------------------------------------------------------------------------
+ \x930d0cefcb30ff1133b6898121f1cf3d27578afcafe8677c5257cf069911f75d8f5831b56ebfda67b278e66dff8b84fe2b2870f742a580d8edb41987232850c9
+(1 row)
+
diff --git a/contrib/pgcrypto/mbuf.c b/contrib/pgcrypto/mbuf.c
new file mode 100644
index 0000000..bc668a0
--- /dev/null
+++ b/contrib/pgcrypto/mbuf.c
@@ -0,0 +1,560 @@
+/*
+ * mbuf.c
+ * Memory buffer operations.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/mbuf.c
+ */
+
+#include "postgres.h"
+
+#include "mbuf.h"
+#include "px.h"
+
+#define STEP (16*1024)
+
+struct MBuf
+{
+ uint8 *data;
+ uint8 *data_end;
+ uint8 *read_pos;
+ uint8 *buf_end;
+ bool no_write;
+ bool own_data;
+};
+
+int
+mbuf_avail(MBuf *mbuf)
+{
+ return mbuf->data_end - mbuf->read_pos;
+}
+
+int
+mbuf_size(MBuf *mbuf)
+{
+ return mbuf->data_end - mbuf->data;
+}
+
+int
+mbuf_tell(MBuf *mbuf)
+{
+ return mbuf->read_pos - mbuf->data;
+}
+
+int
+mbuf_free(MBuf *mbuf)
+{
+ if (mbuf->own_data)
+ {
+ px_memset(mbuf->data, 0, mbuf->buf_end - mbuf->data);
+ pfree(mbuf->data);
+ }
+ pfree(mbuf);
+ return 0;
+}
+
+static void
+prepare_room(MBuf *mbuf, int block_len)
+{
+ uint8 *newbuf;
+ unsigned newlen;
+
+ if (mbuf->data_end + block_len <= mbuf->buf_end)
+ return;
+
+ newlen = (mbuf->buf_end - mbuf->data)
+ + ((block_len + STEP + STEP - 1) & -STEP);
+
+ newbuf = repalloc(mbuf->data, newlen);
+
+ mbuf->buf_end = newbuf + newlen;
+ mbuf->data_end = newbuf + (mbuf->data_end - mbuf->data);
+ mbuf->read_pos = newbuf + (mbuf->read_pos - mbuf->data);
+ mbuf->data = newbuf;
+}
+
+int
+mbuf_append(MBuf *dst, const uint8 *buf, int len)
+{
+ if (dst->no_write)
+ {
+ px_debug("mbuf_append: no_write");
+ return PXE_BUG;
+ }
+
+ prepare_room(dst, len);
+
+ memcpy(dst->data_end, buf, len);
+ dst->data_end += len;
+
+ return 0;
+}
+
+MBuf *
+mbuf_create(int len)
+{
+ MBuf *mbuf;
+
+ if (!len)
+ len = 8192;
+
+ mbuf = palloc(sizeof *mbuf);
+ mbuf->data = palloc(len);
+ mbuf->buf_end = mbuf->data + len;
+ mbuf->data_end = mbuf->data;
+ mbuf->read_pos = mbuf->data;
+
+ mbuf->no_write = false;
+ mbuf->own_data = true;
+
+ return mbuf;
+}
+
+MBuf *
+mbuf_create_from_data(uint8 *data, int len)
+{
+ MBuf *mbuf;
+
+ mbuf = palloc(sizeof *mbuf);
+ mbuf->data = (uint8 *) data;
+ mbuf->buf_end = mbuf->data + len;
+ mbuf->data_end = mbuf->data + len;
+ mbuf->read_pos = mbuf->data;
+
+ mbuf->no_write = true;
+ mbuf->own_data = false;
+
+ return mbuf;
+}
+
+
+int
+mbuf_grab(MBuf *mbuf, int len, uint8 **data_p)
+{
+ if (len > mbuf_avail(mbuf))
+ len = mbuf_avail(mbuf);
+
+ mbuf->no_write = true;
+
+ *data_p = mbuf->read_pos;
+ mbuf->read_pos += len;
+ return len;
+}
+
+int
+mbuf_rewind(MBuf *mbuf)
+{
+ mbuf->read_pos = mbuf->data;
+ return 0;
+}
+
+int
+mbuf_steal_data(MBuf *mbuf, uint8 **data_p)
+{
+ int len = mbuf_size(mbuf);
+
+ mbuf->no_write = true;
+ mbuf->own_data = false;
+
+ *data_p = mbuf->data;
+
+ mbuf->data = mbuf->data_end = mbuf->read_pos = mbuf->buf_end = NULL;
+
+ return len;
+}
+
+/*
+ * PullFilter
+ */
+
+struct PullFilter
+{
+ PullFilter *src;
+ const PullFilterOps *op;
+ int buflen;
+ uint8 *buf;
+ int pos;
+ void *priv;
+};
+
+int
+pullf_create(PullFilter **pf_p, const PullFilterOps *op, void *init_arg, PullFilter *src)
+{
+ PullFilter *pf;
+ void *priv;
+ int res;
+
+ if (op->init != NULL)
+ {
+ res = op->init(&priv, init_arg, src);
+ if (res < 0)
+ return res;
+ }
+ else
+ {
+ priv = init_arg;
+ res = 0;
+ }
+
+ pf = palloc0(sizeof(*pf));
+ pf->buflen = res;
+ pf->op = op;
+ pf->priv = priv;
+ pf->src = src;
+ if (pf->buflen > 0)
+ {
+ pf->buf = palloc(pf->buflen);
+ pf->pos = 0;
+ }
+ else
+ {
+ pf->buf = NULL;
+ pf->pos = 0;
+ }
+ *pf_p = pf;
+ return 0;
+}
+
+void
+pullf_free(PullFilter *pf)
+{
+ if (pf->op->free)
+ pf->op->free(pf->priv);
+
+ if (pf->buf)
+ {
+ px_memset(pf->buf, 0, pf->buflen);
+ pfree(pf->buf);
+ }
+
+ px_memset(pf, 0, sizeof(*pf));
+ pfree(pf);
+}
+
+/* may return less data than asked, 0 means eof */
+int
+pullf_read(PullFilter *pf, int len, uint8 **data_p)
+{
+ int res;
+
+ if (pf->op->pull)
+ {
+ if (pf->buflen && len > pf->buflen)
+ len = pf->buflen;
+ res = pf->op->pull(pf->priv, pf->src, len, data_p,
+ pf->buf, pf->buflen);
+ }
+ else
+ res = pullf_read(pf->src, len, data_p);
+ return res;
+}
+
+int
+pullf_read_max(PullFilter *pf, int len, uint8 **data_p, uint8 *tmpbuf)
+{
+ int res,
+ total;
+ uint8 *tmp;
+
+ res = pullf_read(pf, len, data_p);
+ if (res <= 0 || res == len)
+ return res;
+
+ /* read was shorter, use tmpbuf */
+ memcpy(tmpbuf, *data_p, res);
+ *data_p = tmpbuf;
+ len -= res;
+ total = res;
+
+ while (len > 0)
+ {
+ res = pullf_read(pf, len, &tmp);
+ if (res < 0)
+ {
+ /* so the caller must clear only on success */
+ px_memset(tmpbuf, 0, total);
+ return res;
+ }
+ if (res == 0)
+ break;
+ memcpy(tmpbuf + total, tmp, res);
+ total += res;
+ len -= res;
+ }
+ return total;
+}
+
+/*
+ * caller wants exactly len bytes and don't bother with references
+ */
+int
+pullf_read_fixed(PullFilter *src, int len, uint8 *dst)
+{
+ int res;
+ uint8 *p;
+
+ res = pullf_read_max(src, len, &p, dst);
+ if (res < 0)
+ return res;
+ if (res != len)
+ {
+ px_debug("pullf_read_fixed: need=%d got=%d", len, res);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ if (p != dst)
+ memcpy(dst, p, len);
+ return 0;
+}
+
+/*
+ * read from MBuf
+ */
+static int
+pull_from_mbuf(void *arg, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ MBuf *mbuf = arg;
+
+ return mbuf_grab(mbuf, len, data_p);
+}
+
+static const struct PullFilterOps mbuf_reader = {
+ NULL, pull_from_mbuf, NULL
+};
+
+int
+pullf_create_mbuf_reader(PullFilter **mp_p, MBuf *src)
+{
+ return pullf_create(mp_p, &mbuf_reader, src, NULL);
+}
+
+
+/*
+ * PushFilter
+ */
+
+struct PushFilter
+{
+ PushFilter *next;
+ const PushFilterOps *op;
+ int block_size;
+ uint8 *buf;
+ int pos;
+ void *priv;
+};
+
+int
+pushf_create(PushFilter **mp_p, const PushFilterOps *op, void *init_arg, PushFilter *next)
+{
+ PushFilter *mp;
+ void *priv;
+ int res;
+
+ if (op->init != NULL)
+ {
+ res = op->init(next, init_arg, &priv);
+ if (res < 0)
+ return res;
+ }
+ else
+ {
+ priv = init_arg;
+ res = 0;
+ }
+
+ mp = palloc0(sizeof(*mp));
+ mp->block_size = res;
+ mp->op = op;
+ mp->priv = priv;
+ mp->next = next;
+ if (mp->block_size > 0)
+ {
+ mp->buf = palloc(mp->block_size);
+ mp->pos = 0;
+ }
+ else
+ {
+ mp->buf = NULL;
+ mp->pos = 0;
+ }
+ *mp_p = mp;
+ return 0;
+}
+
+void
+pushf_free(PushFilter *mp)
+{
+ if (mp->op->free)
+ mp->op->free(mp->priv);
+
+ if (mp->buf)
+ {
+ px_memset(mp->buf, 0, mp->block_size);
+ pfree(mp->buf);
+ }
+
+ px_memset(mp, 0, sizeof(*mp));
+ pfree(mp);
+}
+
+void
+pushf_free_all(PushFilter *mp)
+{
+ PushFilter *tmp;
+
+ while (mp)
+ {
+ tmp = mp->next;
+ pushf_free(mp);
+ mp = tmp;
+ }
+}
+
+static int
+wrap_process(PushFilter *mp, const uint8 *data, int len)
+{
+ int res;
+
+ if (mp->op->push != NULL)
+ res = mp->op->push(mp->next, mp->priv, data, len);
+ else
+ res = pushf_write(mp->next, data, len);
+ if (res > 0)
+ return PXE_BUG;
+ return res;
+}
+
+/* consumes all data, returns len on success */
+int
+pushf_write(PushFilter *mp, const uint8 *data, int len)
+{
+ int need,
+ res;
+
+ /*
+ * no buffering
+ */
+ if (mp->block_size <= 0)
+ return wrap_process(mp, data, len);
+
+ /*
+ * try to empty buffer
+ */
+ need = mp->block_size - mp->pos;
+ if (need > 0)
+ {
+ if (len < need)
+ {
+ memcpy(mp->buf + mp->pos, data, len);
+ mp->pos += len;
+ return 0;
+ }
+ memcpy(mp->buf + mp->pos, data, need);
+ len -= need;
+ data += need;
+ }
+
+ /*
+ * buffer full, process
+ */
+ res = wrap_process(mp, mp->buf, mp->block_size);
+ if (res < 0)
+ return res;
+ mp->pos = 0;
+
+ /*
+ * now process directly from data
+ */
+ while (len > 0)
+ {
+ if (len > mp->block_size)
+ {
+ res = wrap_process(mp, data, mp->block_size);
+ if (res < 0)
+ return res;
+ data += mp->block_size;
+ len -= mp->block_size;
+ }
+ else
+ {
+ memcpy(mp->buf, data, len);
+ mp->pos += len;
+ break;
+ }
+ }
+ return 0;
+}
+
+int
+pushf_flush(PushFilter *mp)
+{
+ int res;
+
+ while (mp)
+ {
+ if (mp->block_size > 0)
+ {
+ res = wrap_process(mp, mp->buf, mp->pos);
+ if (res < 0)
+ return res;
+ }
+
+ if (mp->op->flush)
+ {
+ res = mp->op->flush(mp->next, mp->priv);
+ if (res < 0)
+ return res;
+ }
+
+ mp = mp->next;
+ }
+ return 0;
+}
+
+
+/*
+ * write to MBuf
+ */
+static int
+push_into_mbuf(PushFilter *next, void *arg, const uint8 *data, int len)
+{
+ int res = 0;
+ MBuf *mbuf = arg;
+
+ if (len > 0)
+ res = mbuf_append(mbuf, data, len);
+ return res < 0 ? res : 0;
+}
+
+static const struct PushFilterOps mbuf_filter = {
+ NULL, push_into_mbuf, NULL, NULL
+};
+
+int
+pushf_create_mbuf_writer(PushFilter **res, MBuf *dst)
+{
+ return pushf_create(res, &mbuf_filter, dst, NULL);
+}
diff --git a/contrib/pgcrypto/mbuf.h b/contrib/pgcrypto/mbuf.h
new file mode 100644
index 0000000..e6d754e
--- /dev/null
+++ b/contrib/pgcrypto/mbuf.h
@@ -0,0 +1,124 @@
+/*
+ * mbuf.h
+ * Memory buffer operations.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/mbuf.h
+ */
+
+#ifndef __PX_MBUF_H
+#define __PX_MBUF_H
+
+typedef struct MBuf MBuf;
+typedef struct PushFilter PushFilter;
+typedef struct PullFilter PullFilter;
+typedef struct PushFilterOps PushFilterOps;
+typedef struct PullFilterOps PullFilterOps;
+
+struct PushFilterOps
+{
+ /*
+ * should return needed buffer size, 0- no buffering, <0 on error if NULL,
+ * no buffering, and priv=init_arg
+ */
+ int (*init) (PushFilter *next, void *init_arg, void **priv_p);
+
+ /*
+ * send data to next. should consume all? if null, it will be simply
+ * copied (in-place) returns 0 on error
+ */
+ int (*push) (PushFilter *next, void *priv,
+ const uint8 *src, int len);
+ int (*flush) (PushFilter *next, void *priv);
+ void (*free) (void *priv);
+};
+
+struct PullFilterOps
+{
+ /*
+ * should return needed buffer size, 0- no buffering, <0 on error if NULL,
+ * no buffering, and priv=init_arg
+ */
+ int (*init) (void **priv_p, void *init_arg, PullFilter *src);
+
+ /*
+ * request data from src, put result ptr to data_p can use ptr from src or
+ * use buf as work area if NULL in-place copy
+ */
+ int (*pull) (void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen);
+ void (*free) (void *priv);
+};
+
+/*
+ * Memory buffer
+ */
+MBuf *mbuf_create(int len);
+MBuf *mbuf_create_from_data(uint8 *data, int len);
+int mbuf_tell(MBuf *mbuf);
+int mbuf_avail(MBuf *mbuf);
+int mbuf_size(MBuf *mbuf);
+int mbuf_grab(MBuf *mbuf, int len, uint8 **data_p);
+int mbuf_steal_data(MBuf *mbuf, uint8 **data_p);
+int mbuf_append(MBuf *dst, const uint8 *buf, int cnt);
+int mbuf_rewind(MBuf *mbuf);
+int mbuf_free(MBuf *mbuf);
+
+/*
+ * Push filter
+ */
+int pushf_create(PushFilter **res, const PushFilterOps *ops, void *init_arg,
+ PushFilter *next);
+int pushf_write(PushFilter *mp, const uint8 *data, int len);
+void pushf_free_all(PushFilter *mp);
+void pushf_free(PushFilter *mp);
+int pushf_flush(PushFilter *mp);
+
+int pushf_create_mbuf_writer(PushFilter **mp_p, MBuf *mbuf);
+
+/*
+ * Pull filter
+ */
+int pullf_create(PullFilter **res, const PullFilterOps *ops,
+ void *init_arg, PullFilter *src);
+int pullf_read(PullFilter *mp, int len, uint8 **data_p);
+int pullf_read_max(PullFilter *mp, int len,
+ uint8 **data_p, uint8 *tmpbuf);
+void pullf_free(PullFilter *mp);
+int pullf_read_fixed(PullFilter *src, int len, uint8 *dst);
+
+int pullf_create_mbuf_reader(PullFilter **pf_p, MBuf *mbuf);
+
+#define GETBYTE(pf, dst) \
+ do { \
+ uint8 __b; \
+ int __res = pullf_read_fixed(pf, 1, &__b); \
+ if (__res < 0) \
+ return __res; \
+ (dst) = __b; \
+ } while (0)
+
+#endif /* __PX_MBUF_H */
diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c
new file mode 100644
index 0000000..cf31551
--- /dev/null
+++ b/contrib/pgcrypto/openssl.c
@@ -0,0 +1,829 @@
+/*
+ * openssl.c
+ * Wrapper for OpenSSL library.
+ *
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/openssl.c
+ */
+
+#include "postgres.h"
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+#include "px.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+/*
+ * Max lengths we might want to handle.
+ */
+#define MAX_KEY (512/8)
+#define MAX_IV (128/8)
+
+/*
+ * Hashes
+ */
+
+/*
+ * To make sure we don't leak OpenSSL handles on abort, we keep OSSLDigest
+ * objects in a linked list, allocated in TopMemoryContext. We use the
+ * ResourceOwner mechanism to free them on abort.
+ */
+typedef struct OSSLDigest
+{
+ const EVP_MD *algo;
+ EVP_MD_CTX *ctx;
+
+ ResourceOwner owner;
+ struct OSSLDigest *next;
+ struct OSSLDigest *prev;
+} OSSLDigest;
+
+static OSSLDigest *open_digests = NULL;
+static bool digest_resowner_callback_registered = false;
+
+static void
+free_openssl_digest(OSSLDigest *digest)
+{
+ EVP_MD_CTX_destroy(digest->ctx);
+ if (digest->prev)
+ digest->prev->next = digest->next;
+ else
+ open_digests = digest->next;
+ if (digest->next)
+ digest->next->prev = digest->prev;
+ pfree(digest);
+}
+
+/*
+ * Close any open OpenSSL handles on abort.
+ */
+static void
+digest_free_callback(ResourceReleasePhase phase,
+ bool isCommit,
+ bool isTopLevel,
+ void *arg)
+{
+ OSSLDigest *curr;
+ OSSLDigest *next;
+
+ if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
+ return;
+
+ next = open_digests;
+ while (next)
+ {
+ curr = next;
+ next = curr->next;
+
+ if (curr->owner == CurrentResourceOwner)
+ {
+ if (isCommit)
+ elog(WARNING, "pgcrypto digest reference leak: digest %p still referenced", curr);
+ free_openssl_digest(curr);
+ }
+ }
+}
+
+static unsigned
+digest_result_size(PX_MD *h)
+{
+ OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
+ int result = EVP_MD_CTX_size(digest->ctx);
+
+ if (result < 0)
+ elog(ERROR, "EVP_MD_CTX_size() failed");
+
+ return result;
+}
+
+static unsigned
+digest_block_size(PX_MD *h)
+{
+ OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
+ int result = EVP_MD_CTX_block_size(digest->ctx);
+
+ if (result < 0)
+ elog(ERROR, "EVP_MD_CTX_block_size() failed");
+
+ return result;
+}
+
+static void
+digest_reset(PX_MD *h)
+{
+ OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
+
+ if (!EVP_DigestInit_ex(digest->ctx, digest->algo, NULL))
+ elog(ERROR, "EVP_DigestInit_ex() failed");
+}
+
+static void
+digest_update(PX_MD *h, const uint8 *data, unsigned dlen)
+{
+ OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
+
+ if (!EVP_DigestUpdate(digest->ctx, data, dlen))
+ elog(ERROR, "EVP_DigestUpdate() failed");
+}
+
+static void
+digest_finish(PX_MD *h, uint8 *dst)
+{
+ OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
+
+ if (!EVP_DigestFinal_ex(digest->ctx, dst, NULL))
+ elog(ERROR, "EVP_DigestFinal_ex() failed");
+}
+
+static void
+digest_free(PX_MD *h)
+{
+ OSSLDigest *digest = (OSSLDigest *) h->p.ptr;
+
+ free_openssl_digest(digest);
+ pfree(h);
+}
+
+static int px_openssl_initialized = 0;
+
+/* PUBLIC functions */
+
+int
+px_find_digest(const char *name, PX_MD **res)
+{
+ const EVP_MD *md;
+ EVP_MD_CTX *ctx;
+ PX_MD *h;
+ OSSLDigest *digest;
+
+ if (!px_openssl_initialized)
+ {
+ px_openssl_initialized = 1;
+ OpenSSL_add_all_algorithms();
+ }
+
+ if (!digest_resowner_callback_registered)
+ {
+ RegisterResourceReleaseCallback(digest_free_callback, NULL);
+ digest_resowner_callback_registered = true;
+ }
+
+ md = EVP_get_digestbyname(name);
+ if (md == NULL)
+ return PXE_NO_HASH;
+
+ /*
+ * Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object.
+ * The order is crucial, to make sure we don't leak anything on
+ * out-of-memory or other error.
+ */
+ digest = MemoryContextAlloc(TopMemoryContext, sizeof(*digest));
+
+ ctx = EVP_MD_CTX_create();
+ if (!ctx)
+ {
+ pfree(digest);
+ return PXE_CIPHER_INIT;
+ }
+ if (EVP_DigestInit_ex(ctx, md, NULL) == 0)
+ {
+ EVP_MD_CTX_destroy(ctx);
+ pfree(digest);
+ return PXE_CIPHER_INIT;
+ }
+
+ digest->algo = md;
+ digest->ctx = ctx;
+ digest->owner = CurrentResourceOwner;
+ digest->next = open_digests;
+ digest->prev = NULL;
+ open_digests = digest;
+
+ /* The PX_MD object is allocated in the current memory context. */
+ h = palloc(sizeof(*h));
+ h->result_size = digest_result_size;
+ h->block_size = digest_block_size;
+ h->reset = digest_reset;
+ h->update = digest_update;
+ h->finish = digest_finish;
+ h->free = digest_free;
+ h->p.ptr = (void *) digest;
+
+ *res = h;
+ return 0;
+}
+
+/*
+ * Ciphers
+ *
+ * We use OpenSSL's EVP* family of functions for these.
+ */
+
+/*
+ * prototype for the EVP functions that return an algorithm, e.g.
+ * EVP_aes_128_cbc().
+ */
+typedef const EVP_CIPHER *(*ossl_EVP_cipher_func) (void);
+
+/*
+ * ossl_cipher contains the static information about each cipher.
+ */
+struct ossl_cipher
+{
+ int (*init) (PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv);
+ ossl_EVP_cipher_func cipher_func;
+ int block_size;
+ int max_key_size;
+};
+
+/*
+ * OSSLCipher contains the state for using a cipher. A separate OSSLCipher
+ * object is allocated in each px_find_cipher() call.
+ *
+ * To make sure we don't leak OpenSSL handles on abort, we keep OSSLCipher
+ * objects in a linked list, allocated in TopMemoryContext. We use the
+ * ResourceOwner mechanism to free them on abort.
+ */
+typedef struct OSSLCipher
+{
+ EVP_CIPHER_CTX *evp_ctx;
+ const EVP_CIPHER *evp_ciph;
+ uint8 key[MAX_KEY];
+ uint8 iv[MAX_IV];
+ unsigned klen;
+ unsigned init;
+ const struct ossl_cipher *ciph;
+
+ ResourceOwner owner;
+ struct OSSLCipher *next;
+ struct OSSLCipher *prev;
+} OSSLCipher;
+
+static OSSLCipher *open_ciphers = NULL;
+static bool cipher_resowner_callback_registered = false;
+
+static void
+free_openssl_cipher(OSSLCipher *od)
+{
+ EVP_CIPHER_CTX_free(od->evp_ctx);
+ if (od->prev)
+ od->prev->next = od->next;
+ else
+ open_ciphers = od->next;
+ if (od->next)
+ od->next->prev = od->prev;
+ pfree(od);
+}
+
+/*
+ * Close any open OpenSSL cipher handles on abort.
+ */
+static void
+cipher_free_callback(ResourceReleasePhase phase,
+ bool isCommit,
+ bool isTopLevel,
+ void *arg)
+{
+ OSSLCipher *curr;
+ OSSLCipher *next;
+
+ if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
+ return;
+
+ next = open_ciphers;
+ while (next)
+ {
+ curr = next;
+ next = curr->next;
+
+ if (curr->owner == CurrentResourceOwner)
+ {
+ if (isCommit)
+ elog(WARNING, "pgcrypto cipher reference leak: cipher %p still referenced", curr);
+ free_openssl_cipher(curr);
+ }
+ }
+}
+
+/* Common routines for all algorithms */
+
+static unsigned
+gen_ossl_block_size(PX_Cipher *c)
+{
+ OSSLCipher *od = (OSSLCipher *) c->ptr;
+
+ return od->ciph->block_size;
+}
+
+static unsigned
+gen_ossl_key_size(PX_Cipher *c)
+{
+ OSSLCipher *od = (OSSLCipher *) c->ptr;
+
+ return od->ciph->max_key_size;
+}
+
+static unsigned
+gen_ossl_iv_size(PX_Cipher *c)
+{
+ unsigned ivlen;
+ OSSLCipher *od = (OSSLCipher *) c->ptr;
+
+ ivlen = od->ciph->block_size;
+ return ivlen;
+}
+
+static void
+gen_ossl_free(PX_Cipher *c)
+{
+ OSSLCipher *od = (OSSLCipher *) c->ptr;
+
+ free_openssl_cipher(od);
+ pfree(c);
+}
+
+static int
+gen_ossl_decrypt(PX_Cipher *c, int padding, const uint8 *data, unsigned dlen,
+ uint8 *res, unsigned *rlen)
+{
+ OSSLCipher *od = c->ptr;
+ int outlen,
+ outlen2;
+
+ if (!od->init)
+ {
+ if (!EVP_DecryptInit_ex(od->evp_ctx, od->evp_ciph, NULL, NULL, NULL))
+ return PXE_CIPHER_INIT;
+ if (!EVP_CIPHER_CTX_set_padding(od->evp_ctx, padding))
+ return PXE_CIPHER_INIT;
+ if (!EVP_CIPHER_CTX_set_key_length(od->evp_ctx, od->klen))
+ return PXE_CIPHER_INIT;
+ if (!EVP_DecryptInit_ex(od->evp_ctx, NULL, NULL, od->key, od->iv))
+ return PXE_CIPHER_INIT;
+ od->init = true;
+ }
+
+ if (!EVP_DecryptUpdate(od->evp_ctx, res, &outlen, data, dlen))
+ return PXE_DECRYPT_FAILED;
+ if (!EVP_DecryptFinal_ex(od->evp_ctx, res + outlen, &outlen2))
+ return PXE_DECRYPT_FAILED;
+ *rlen = outlen + outlen2;
+
+ return 0;
+}
+
+static int
+gen_ossl_encrypt(PX_Cipher *c, int padding, const uint8 *data, unsigned dlen,
+ uint8 *res, unsigned *rlen)
+{
+ OSSLCipher *od = c->ptr;
+ int outlen,
+ outlen2;
+
+ if (!od->init)
+ {
+ if (!EVP_EncryptInit_ex(od->evp_ctx, od->evp_ciph, NULL, NULL, NULL))
+ return PXE_CIPHER_INIT;
+ if (!EVP_CIPHER_CTX_set_padding(od->evp_ctx, padding))
+ return PXE_CIPHER_INIT;
+ if (!EVP_CIPHER_CTX_set_key_length(od->evp_ctx, od->klen))
+ return PXE_CIPHER_INIT;
+ if (!EVP_EncryptInit_ex(od->evp_ctx, NULL, NULL, od->key, od->iv))
+ return PXE_CIPHER_INIT;
+ od->init = true;
+ }
+
+ if (!EVP_EncryptUpdate(od->evp_ctx, res, &outlen, data, dlen))
+ return PXE_ENCRYPT_FAILED;
+ if (!EVP_EncryptFinal_ex(od->evp_ctx, res + outlen, &outlen2))
+ return PXE_ENCRYPT_FAILED;
+ *rlen = outlen + outlen2;
+
+ return 0;
+}
+
+/* Blowfish */
+
+/*
+ * Check if strong crypto is supported. Some OpenSSL installations
+ * support only short keys and unfortunately BF_set_key does not return any
+ * error value. This function tests if is possible to use strong key.
+ */
+static int
+bf_check_supported_key_len(void)
+{
+ static const uint8 key[56] = {
+ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69,
+ 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f, 0x00, 0x11, 0x22, 0x33,
+ 0x44, 0x55, 0x66, 0x77, 0x04, 0x68, 0x91, 0x04, 0xc2, 0xfd,
+ 0x3b, 0x2f, 0x58, 0x40, 0x23, 0x64, 0x1a, 0xba, 0x61, 0x76,
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x0e, 0x0e, 0x0e, 0x0e, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ static const uint8 data[8] = {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10};
+ static const uint8 res[8] = {0xc0, 0x45, 0x04, 0x01, 0x2e, 0x4e, 0x1f, 0x53};
+ uint8 out[8];
+ EVP_CIPHER_CTX *evp_ctx;
+ int outlen;
+ int status = 0;
+
+ /* encrypt with 448bits key and verify output */
+ evp_ctx = EVP_CIPHER_CTX_new();
+ if (!evp_ctx)
+ return 0;
+ if (!EVP_EncryptInit_ex(evp_ctx, EVP_bf_ecb(), NULL, NULL, NULL))
+ goto leave;
+ if (!EVP_CIPHER_CTX_set_key_length(evp_ctx, 56))
+ goto leave;
+ if (!EVP_EncryptInit_ex(evp_ctx, NULL, NULL, key, NULL))
+ goto leave;
+
+ if (!EVP_EncryptUpdate(evp_ctx, out, &outlen, data, 8))
+ goto leave;
+
+ if (memcmp(out, res, 8) != 0)
+ goto leave; /* Output does not match -> strong cipher is
+ * not supported */
+ status = 1;
+
+leave:
+ EVP_CIPHER_CTX_free(evp_ctx);
+ return status;
+}
+
+static int
+bf_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+ OSSLCipher *od = c->ptr;
+ unsigned bs = gen_ossl_block_size(c);
+ static int bf_is_strong = -1;
+
+ /*
+ * Test if key len is supported. BF_set_key silently cut large keys and it
+ * could be a problem when user transfer crypted data from one server to
+ * another.
+ */
+
+ if (bf_is_strong == -1)
+ bf_is_strong = bf_check_supported_key_len();
+
+ if (!bf_is_strong && klen > 16)
+ return PXE_KEY_TOO_BIG;
+
+ /* Key len is supported. We can use it. */
+ od->klen = klen;
+ memcpy(od->key, key, klen);
+
+ if (iv)
+ memcpy(od->iv, iv, bs);
+ else
+ memset(od->iv, 0, bs);
+ return 0;
+}
+
+/* DES */
+
+static int
+ossl_des_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+ OSSLCipher *od = c->ptr;
+ unsigned bs = gen_ossl_block_size(c);
+
+ od->klen = 8;
+ memset(od->key, 0, 8);
+ memcpy(od->key, key, klen > 8 ? 8 : klen);
+
+ if (iv)
+ memcpy(od->iv, iv, bs);
+ else
+ memset(od->iv, 0, bs);
+ return 0;
+}
+
+/* DES3 */
+
+static int
+ossl_des3_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+ OSSLCipher *od = c->ptr;
+ unsigned bs = gen_ossl_block_size(c);
+
+ od->klen = 24;
+ memset(od->key, 0, 24);
+ memcpy(od->key, key, klen > 24 ? 24 : klen);
+
+ if (iv)
+ memcpy(od->iv, iv, bs);
+ else
+ memset(od->iv, 0, bs);
+ return 0;
+}
+
+/* CAST5 */
+
+static int
+ossl_cast_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+ OSSLCipher *od = c->ptr;
+ unsigned bs = gen_ossl_block_size(c);
+
+ od->klen = klen;
+ memcpy(od->key, key, klen);
+
+ if (iv)
+ memcpy(od->iv, iv, bs);
+ else
+ memset(od->iv, 0, bs);
+ return 0;
+}
+
+/* AES */
+
+static int
+ossl_aes_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+ OSSLCipher *od = c->ptr;
+ unsigned bs = gen_ossl_block_size(c);
+
+ if (klen <= 128 / 8)
+ od->klen = 128 / 8;
+ else if (klen <= 192 / 8)
+ od->klen = 192 / 8;
+ else if (klen <= 256 / 8)
+ od->klen = 256 / 8;
+ else
+ return PXE_KEY_TOO_BIG;
+
+ memcpy(od->key, key, klen);
+
+ if (iv)
+ memcpy(od->iv, iv, bs);
+ else
+ memset(od->iv, 0, bs);
+
+ return 0;
+}
+
+static int
+ossl_aes_ecb_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+ OSSLCipher *od = c->ptr;
+ int err;
+
+ err = ossl_aes_init(c, key, klen, iv);
+ if (err)
+ return err;
+
+ switch (od->klen)
+ {
+ case 128 / 8:
+ od->evp_ciph = EVP_aes_128_ecb();
+ break;
+ case 192 / 8:
+ od->evp_ciph = EVP_aes_192_ecb();
+ break;
+ case 256 / 8:
+ od->evp_ciph = EVP_aes_256_ecb();
+ break;
+ default:
+ /* shouldn't happen */
+ err = PXE_CIPHER_INIT;
+ break;
+ }
+
+ return err;
+}
+
+static int
+ossl_aes_cbc_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+ OSSLCipher *od = c->ptr;
+ int err;
+
+ err = ossl_aes_init(c, key, klen, iv);
+ if (err)
+ return err;
+
+ switch (od->klen)
+ {
+ case 128 / 8:
+ od->evp_ciph = EVP_aes_128_cbc();
+ break;
+ case 192 / 8:
+ od->evp_ciph = EVP_aes_192_cbc();
+ break;
+ case 256 / 8:
+ od->evp_ciph = EVP_aes_256_cbc();
+ break;
+ default:
+ /* shouldn't happen */
+ err = PXE_CIPHER_INIT;
+ break;
+ }
+
+ return err;
+}
+
+/*
+ * aliases
+ */
+
+static PX_Alias ossl_aliases[] = {
+ {"bf", "bf-cbc"},
+ {"blowfish", "bf-cbc"},
+ {"blowfish-cbc", "bf-cbc"},
+ {"blowfish-ecb", "bf-ecb"},
+ {"blowfish-cfb", "bf-cfb"},
+ {"des", "des-cbc"},
+ {"3des", "des3-cbc"},
+ {"3des-ecb", "des3-ecb"},
+ {"3des-cbc", "des3-cbc"},
+ {"cast5", "cast5-cbc"},
+ {"aes", "aes-cbc"},
+ {"rijndael", "aes-cbc"},
+ {"rijndael-cbc", "aes-cbc"},
+ {"rijndael-ecb", "aes-ecb"},
+ {NULL}
+};
+
+static const struct ossl_cipher ossl_bf_cbc = {
+ bf_init,
+ EVP_bf_cbc,
+ 64 / 8, 448 / 8
+};
+
+static const struct ossl_cipher ossl_bf_ecb = {
+ bf_init,
+ EVP_bf_ecb,
+ 64 / 8, 448 / 8
+};
+
+static const struct ossl_cipher ossl_bf_cfb = {
+ bf_init,
+ EVP_bf_cfb,
+ 64 / 8, 448 / 8
+};
+
+static const struct ossl_cipher ossl_des_ecb = {
+ ossl_des_init,
+ EVP_des_ecb,
+ 64 / 8, 64 / 8
+};
+
+static const struct ossl_cipher ossl_des_cbc = {
+ ossl_des_init,
+ EVP_des_cbc,
+ 64 / 8, 64 / 8
+};
+
+static const struct ossl_cipher ossl_des3_ecb = {
+ ossl_des3_init,
+ EVP_des_ede3_ecb,
+ 64 / 8, 192 / 8
+};
+
+static const struct ossl_cipher ossl_des3_cbc = {
+ ossl_des3_init,
+ EVP_des_ede3_cbc,
+ 64 / 8, 192 / 8
+};
+
+static const struct ossl_cipher ossl_cast_ecb = {
+ ossl_cast_init,
+ EVP_cast5_ecb,
+ 64 / 8, 128 / 8
+};
+
+static const struct ossl_cipher ossl_cast_cbc = {
+ ossl_cast_init,
+ EVP_cast5_cbc,
+ 64 / 8, 128 / 8
+};
+
+static const struct ossl_cipher ossl_aes_ecb = {
+ ossl_aes_ecb_init,
+ NULL, /* EVP_aes_XXX_ecb(), determined in init
+ * function */
+ 128 / 8, 256 / 8
+};
+
+static const struct ossl_cipher ossl_aes_cbc = {
+ ossl_aes_cbc_init,
+ NULL, /* EVP_aes_XXX_cbc(), determined in init
+ * function */
+ 128 / 8, 256 / 8
+};
+
+/*
+ * Special handlers
+ */
+struct ossl_cipher_lookup
+{
+ const char *name;
+ const struct ossl_cipher *ciph;
+};
+
+static const struct ossl_cipher_lookup ossl_cipher_types[] = {
+ {"bf-cbc", &ossl_bf_cbc},
+ {"bf-ecb", &ossl_bf_ecb},
+ {"bf-cfb", &ossl_bf_cfb},
+ {"des-ecb", &ossl_des_ecb},
+ {"des-cbc", &ossl_des_cbc},
+ {"des3-ecb", &ossl_des3_ecb},
+ {"des3-cbc", &ossl_des3_cbc},
+ {"cast5-ecb", &ossl_cast_ecb},
+ {"cast5-cbc", &ossl_cast_cbc},
+ {"aes-ecb", &ossl_aes_ecb},
+ {"aes-cbc", &ossl_aes_cbc},
+ {NULL}
+};
+
+/* PUBLIC functions */
+
+int
+px_find_cipher(const char *name, PX_Cipher **res)
+{
+ const struct ossl_cipher_lookup *i;
+ PX_Cipher *c = NULL;
+ EVP_CIPHER_CTX *ctx;
+ OSSLCipher *od;
+
+ name = px_resolve_alias(ossl_aliases, name);
+ for (i = ossl_cipher_types; i->name; i++)
+ if (strcmp(i->name, name) == 0)
+ break;
+ if (i->name == NULL)
+ return PXE_NO_CIPHER;
+
+ if (!cipher_resowner_callback_registered)
+ {
+ RegisterResourceReleaseCallback(cipher_free_callback, NULL);
+ cipher_resowner_callback_registered = true;
+ }
+
+ /*
+ * Create an OSSLCipher object, an EVP_CIPHER_CTX object and a PX_Cipher.
+ * The order is crucial, to make sure we don't leak anything on
+ * out-of-memory or other error.
+ */
+ od = MemoryContextAllocZero(TopMemoryContext, sizeof(*od));
+ od->ciph = i->ciph;
+
+ /* Allocate an EVP_CIPHER_CTX object. */
+ ctx = EVP_CIPHER_CTX_new();
+ if (!ctx)
+ {
+ pfree(od);
+ return PXE_CIPHER_INIT;
+ }
+
+ od->evp_ctx = ctx;
+ od->owner = CurrentResourceOwner;
+ od->next = open_ciphers;
+ od->prev = NULL;
+ open_ciphers = od;
+
+ if (i->ciph->cipher_func)
+ od->evp_ciph = i->ciph->cipher_func();
+
+ /* The PX_Cipher is allocated in current memory context */
+ c = palloc(sizeof(*c));
+ c->block_size = gen_ossl_block_size;
+ c->key_size = gen_ossl_key_size;
+ c->iv_size = gen_ossl_iv_size;
+ c->free = gen_ossl_free;
+ c->init = od->ciph->init;
+ c->encrypt = gen_ossl_encrypt;
+ c->decrypt = gen_ossl_decrypt;
+ c->ptr = od;
+
+ *res = c;
+ return 0;
+}
diff --git a/contrib/pgcrypto/pgcrypto--1.0--1.1.sql b/contrib/pgcrypto/pgcrypto--1.0--1.1.sql
new file mode 100644
index 0000000..42e0c7f
--- /dev/null
+++ b/contrib/pgcrypto/pgcrypto--1.0--1.1.sql
@@ -0,0 +1,9 @@
+/* contrib/pgcrypto/pgcrypto--1.0--1.1.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pgcrypto UPDATE TO '1.1'" to load this file. \quit
+
+CREATE FUNCTION gen_random_uuid()
+RETURNS uuid
+AS 'MODULE_PATHNAME', 'pg_random_uuid'
+LANGUAGE C VOLATILE;
diff --git a/contrib/pgcrypto/pgcrypto--1.1--1.2.sql b/contrib/pgcrypto/pgcrypto--1.1--1.2.sql
new file mode 100644
index 0000000..753e169
--- /dev/null
+++ b/contrib/pgcrypto/pgcrypto--1.1--1.2.sql
@@ -0,0 +1,14 @@
+/* contrib/pgcrypto/pgcrypto--1.1--1.2.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pgcrypto UPDATE TO '1.2'" to load this file. \quit
+
+CREATE FUNCTION armor(bytea, text[], text[])
+RETURNS text
+AS 'MODULE_PATHNAME', 'pg_armor'
+LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION pgp_armor_headers(text, key OUT text, value OUT text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pgp_armor_headers'
+LANGUAGE C IMMUTABLE STRICT;
diff --git a/contrib/pgcrypto/pgcrypto--1.2--1.3.sql b/contrib/pgcrypto/pgcrypto--1.2--1.3.sql
new file mode 100644
index 0000000..525a037
--- /dev/null
+++ b/contrib/pgcrypto/pgcrypto--1.2--1.3.sql
@@ -0,0 +1,41 @@
+/* contrib/pgcrypto/pgcrypto--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pgcrypto UPDATE TO '1.3'" to load this file. \quit
+
+ALTER FUNCTION digest(text, text) PARALLEL SAFE;
+ALTER FUNCTION digest(bytea, text) PARALLEL SAFE;
+ALTER FUNCTION hmac(text, text, text) PARALLEL SAFE;
+ALTER FUNCTION hmac(bytea, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION crypt(text, text) PARALLEL SAFE;
+ALTER FUNCTION gen_salt(text) PARALLEL SAFE;
+ALTER FUNCTION gen_salt(text, int4) PARALLEL SAFE;
+ALTER FUNCTION encrypt(bytea, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION decrypt(bytea, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION encrypt_iv(bytea, bytea, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION decrypt_iv(bytea, bytea, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION gen_random_bytes(int4) PARALLEL SAFE;
+ALTER FUNCTION gen_random_uuid() PARALLEL SAFE;
+ALTER FUNCTION pgp_sym_encrypt(text, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_sym_encrypt_bytea(bytea, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_sym_encrypt(text, text, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_sym_encrypt_bytea(bytea, text, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_sym_decrypt(bytea, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_sym_decrypt_bytea(bytea, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_sym_decrypt(bytea, text, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_sym_decrypt_bytea(bytea, text, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_encrypt(text, bytea) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_encrypt_bytea(bytea, bytea) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_encrypt(text, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_decrypt(bytea, bytea) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_decrypt_bytea(bytea, bytea) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_decrypt(bytea, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_decrypt(bytea, bytea, text, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text) PARALLEL SAFE;
+ALTER FUNCTION pgp_key_id(bytea) PARALLEL SAFE;
+ALTER FUNCTION armor(bytea) PARALLEL SAFE;
+ALTER FUNCTION armor(bytea, text[], text[]) PARALLEL SAFE;
+ALTER FUNCTION dearmor(text) PARALLEL SAFE;
+ALTER FUNCTION pgp_armor_headers(text) PARALLEL SAFE;
diff --git a/contrib/pgcrypto/pgcrypto--1.3.sql b/contrib/pgcrypto/pgcrypto--1.3.sql
new file mode 100644
index 0000000..c2628ca
--- /dev/null
+++ b/contrib/pgcrypto/pgcrypto--1.3.sql
@@ -0,0 +1,217 @@
+/* contrib/pgcrypto/pgcrypto--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION pgcrypto" to load this file. \quit
+
+CREATE FUNCTION digest(text, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_digest'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION digest(bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_digest'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION hmac(text, text, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_hmac'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION hmac(bytea, bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_hmac'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION crypt(text, text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pg_crypt'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION gen_salt(text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pg_gen_salt'
+LANGUAGE C VOLATILE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION gen_salt(text, int4)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pg_gen_salt_rounds'
+LANGUAGE C VOLATILE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION encrypt(bytea, bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_encrypt'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION decrypt(bytea, bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_decrypt'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION encrypt_iv(bytea, bytea, bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_encrypt_iv'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION decrypt_iv(bytea, bytea, bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_decrypt_iv'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION gen_random_bytes(int4)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_random_bytes'
+LANGUAGE C VOLATILE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION gen_random_uuid()
+RETURNS uuid
+AS 'MODULE_PATHNAME', 'pg_random_uuid'
+LANGUAGE C VOLATILE PARALLEL SAFE;
+
+--
+-- pgp_sym_encrypt(data, key)
+--
+CREATE FUNCTION pgp_sym_encrypt(text, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_text'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_sym_encrypt_bytea(bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_bytea'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- pgp_sym_encrypt(data, key, args)
+--
+CREATE FUNCTION pgp_sym_encrypt(text, text, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_text'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_sym_encrypt_bytea(bytea, text, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_bytea'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- pgp_sym_decrypt(data, key)
+--
+CREATE FUNCTION pgp_sym_decrypt(bytea, text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_text'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_sym_decrypt_bytea(bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_bytea'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- pgp_sym_decrypt(data, key, args)
+--
+CREATE FUNCTION pgp_sym_decrypt(bytea, text, text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_text'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_sym_decrypt_bytea(bytea, text, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_bytea'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- pgp_pub_encrypt(data, key)
+--
+CREATE FUNCTION pgp_pub_encrypt(text, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_text'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_pub_encrypt_bytea(bytea, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_bytea'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- pgp_pub_encrypt(data, key, args)
+--
+CREATE FUNCTION pgp_pub_encrypt(text, bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_text'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_bytea'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- pgp_pub_decrypt(data, key)
+--
+CREATE FUNCTION pgp_pub_decrypt(bytea, bytea)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- pgp_pub_decrypt(data, key, psw)
+--
+CREATE FUNCTION pgp_pub_decrypt(bytea, bytea, text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- pgp_pub_decrypt(data, key, psw, arg)
+--
+CREATE FUNCTION pgp_pub_decrypt(bytea, bytea, text, text)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- PGP key ID
+--
+CREATE FUNCTION pgp_key_id(bytea)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pgp_key_id_w'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+--
+-- pgp armor
+--
+CREATE FUNCTION armor(bytea)
+RETURNS text
+AS 'MODULE_PATHNAME', 'pg_armor'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION armor(bytea, text[], text[])
+RETURNS text
+AS 'MODULE_PATHNAME', 'pg_armor'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION dearmor(text)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'pg_dearmor'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
+
+CREATE FUNCTION pgp_armor_headers(text, key OUT text, value OUT text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'pgp_armor_headers'
+LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
diff --git a/contrib/pgcrypto/pgcrypto.c b/contrib/pgcrypto/pgcrypto.c
new file mode 100644
index 0000000..f0ac625
--- /dev/null
+++ b/contrib/pgcrypto/pgcrypto.c
@@ -0,0 +1,475 @@
+/*
+ * pgcrypto.c
+ * Various cryptographic stuff for PostgreSQL.
+ *
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgcrypto.c
+ */
+
+#include "postgres.h"
+
+#include <ctype.h>
+
+#include "parser/scansup.h"
+#include "pgcrypto.h"
+#include "px-crypt.h"
+#include "px.h"
+#include "utils/builtins.h"
+#include "utils/uuid.h"
+
+PG_MODULE_MAGIC;
+
+/* private stuff */
+
+typedef int (*PFN) (const char *name, void **res);
+static void *find_provider(text *name, PFN pf, const char *desc, int silent);
+
+/* SQL function: hash(bytea, text) returns bytea */
+PG_FUNCTION_INFO_V1(pg_digest);
+
+Datum
+pg_digest(PG_FUNCTION_ARGS)
+{
+ bytea *arg;
+ text *name;
+ unsigned len,
+ hlen;
+ PX_MD *md;
+ bytea *res;
+
+ name = PG_GETARG_TEXT_PP(1);
+
+ /* will give error if fails */
+ md = find_provider(name, (PFN) px_find_digest, "Digest", 0);
+
+ hlen = px_md_result_size(md);
+
+ res = (text *) palloc(hlen + VARHDRSZ);
+ SET_VARSIZE(res, hlen + VARHDRSZ);
+
+ arg = PG_GETARG_BYTEA_PP(0);
+ len = VARSIZE_ANY_EXHDR(arg);
+
+ px_md_update(md, (uint8 *) VARDATA_ANY(arg), len);
+ px_md_finish(md, (uint8 *) VARDATA(res));
+ px_md_free(md);
+
+ PG_FREE_IF_COPY(arg, 0);
+ PG_FREE_IF_COPY(name, 1);
+
+ PG_RETURN_BYTEA_P(res);
+}
+
+/* SQL function: hmac(data:bytea, key:bytea, type:text) returns bytea */
+PG_FUNCTION_INFO_V1(pg_hmac);
+
+Datum
+pg_hmac(PG_FUNCTION_ARGS)
+{
+ bytea *arg;
+ bytea *key;
+ text *name;
+ unsigned len,
+ hlen,
+ klen;
+ PX_HMAC *h;
+ bytea *res;
+
+ name = PG_GETARG_TEXT_PP(2);
+
+ /* will give error if fails */
+ h = find_provider(name, (PFN) px_find_hmac, "HMAC", 0);
+
+ hlen = px_hmac_result_size(h);
+
+ res = (text *) palloc(hlen + VARHDRSZ);
+ SET_VARSIZE(res, hlen + VARHDRSZ);
+
+ arg = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ len = VARSIZE_ANY_EXHDR(arg);
+ klen = VARSIZE_ANY_EXHDR(key);
+
+ px_hmac_init(h, (uint8 *) VARDATA_ANY(key), klen);
+ px_hmac_update(h, (uint8 *) VARDATA_ANY(arg), len);
+ px_hmac_finish(h, (uint8 *) VARDATA(res));
+ px_hmac_free(h);
+
+ PG_FREE_IF_COPY(arg, 0);
+ PG_FREE_IF_COPY(key, 1);
+ PG_FREE_IF_COPY(name, 2);
+
+ PG_RETURN_BYTEA_P(res);
+}
+
+
+/* SQL function: pg_gen_salt(text) returns text */
+PG_FUNCTION_INFO_V1(pg_gen_salt);
+
+Datum
+pg_gen_salt(PG_FUNCTION_ARGS)
+{
+ text *arg0 = PG_GETARG_TEXT_PP(0);
+ int len;
+ char buf[PX_MAX_SALT_LEN + 1];
+
+ text_to_cstring_buffer(arg0, buf, sizeof(buf));
+ len = px_gen_salt(buf, buf, 0);
+ if (len < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("gen_salt: %s", px_strerror(len))));
+
+ PG_FREE_IF_COPY(arg0, 0);
+
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
+}
+
+/* SQL function: pg_gen_salt(text, int4) returns text */
+PG_FUNCTION_INFO_V1(pg_gen_salt_rounds);
+
+Datum
+pg_gen_salt_rounds(PG_FUNCTION_ARGS)
+{
+ text *arg0 = PG_GETARG_TEXT_PP(0);
+ int rounds = PG_GETARG_INT32(1);
+ int len;
+ char buf[PX_MAX_SALT_LEN + 1];
+
+ text_to_cstring_buffer(arg0, buf, sizeof(buf));
+ len = px_gen_salt(buf, buf, rounds);
+ if (len < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("gen_salt: %s", px_strerror(len))));
+
+ PG_FREE_IF_COPY(arg0, 0);
+
+ PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
+}
+
+/* SQL function: pg_crypt(psw:text, salt:text) returns text */
+PG_FUNCTION_INFO_V1(pg_crypt);
+
+Datum
+pg_crypt(PG_FUNCTION_ARGS)
+{
+ text *arg0 = PG_GETARG_TEXT_PP(0);
+ text *arg1 = PG_GETARG_TEXT_PP(1);
+ char *buf0,
+ *buf1,
+ *cres,
+ *resbuf;
+ text *res;
+
+ buf0 = text_to_cstring(arg0);
+ buf1 = text_to_cstring(arg1);
+
+ resbuf = palloc0(PX_MAX_CRYPT);
+
+ cres = px_crypt(buf0, buf1, resbuf, PX_MAX_CRYPT);
+
+ pfree(buf0);
+ pfree(buf1);
+
+ if (cres == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("crypt(3) returned NULL")));
+
+ res = cstring_to_text(cres);
+
+ pfree(resbuf);
+
+ PG_FREE_IF_COPY(arg0, 0);
+ PG_FREE_IF_COPY(arg1, 1);
+
+ PG_RETURN_TEXT_P(res);
+}
+
+/* SQL function: pg_encrypt(bytea, bytea, text) returns bytea */
+PG_FUNCTION_INFO_V1(pg_encrypt);
+
+Datum
+pg_encrypt(PG_FUNCTION_ARGS)
+{
+ int err;
+ bytea *data,
+ *key,
+ *res;
+ text *type;
+ PX_Combo *c;
+ unsigned dlen,
+ klen,
+ rlen;
+
+ type = PG_GETARG_TEXT_PP(2);
+ c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ dlen = VARSIZE_ANY_EXHDR(data);
+ klen = VARSIZE_ANY_EXHDR(key);
+
+ rlen = px_combo_encrypt_len(c, dlen);
+ res = palloc(VARHDRSZ + rlen);
+
+ err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, NULL, 0);
+ if (!err)
+ err = px_combo_encrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
+ (uint8 *) VARDATA(res), &rlen);
+ px_combo_free(c);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ PG_FREE_IF_COPY(type, 2);
+
+ if (err)
+ {
+ pfree(res);
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("encrypt error: %s", px_strerror(err))));
+ }
+
+ SET_VARSIZE(res, VARHDRSZ + rlen);
+ PG_RETURN_BYTEA_P(res);
+}
+
+/* SQL function: pg_decrypt(bytea, bytea, text) returns bytea */
+PG_FUNCTION_INFO_V1(pg_decrypt);
+
+Datum
+pg_decrypt(PG_FUNCTION_ARGS)
+{
+ int err;
+ bytea *data,
+ *key,
+ *res;
+ text *type;
+ PX_Combo *c;
+ unsigned dlen,
+ klen,
+ rlen;
+
+ type = PG_GETARG_TEXT_PP(2);
+ c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ dlen = VARSIZE_ANY_EXHDR(data);
+ klen = VARSIZE_ANY_EXHDR(key);
+
+ rlen = px_combo_decrypt_len(c, dlen);
+ res = palloc(VARHDRSZ + rlen);
+
+ err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, NULL, 0);
+ if (!err)
+ err = px_combo_decrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
+ (uint8 *) VARDATA(res), &rlen);
+
+ px_combo_free(c);
+
+ if (err)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("decrypt error: %s", px_strerror(err))));
+
+ SET_VARSIZE(res, VARHDRSZ + rlen);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ PG_FREE_IF_COPY(type, 2);
+
+ PG_RETURN_BYTEA_P(res);
+}
+
+/* SQL function: pg_encrypt_iv(bytea, bytea, bytea, text) returns bytea */
+PG_FUNCTION_INFO_V1(pg_encrypt_iv);
+
+Datum
+pg_encrypt_iv(PG_FUNCTION_ARGS)
+{
+ int err;
+ bytea *data,
+ *key,
+ *iv,
+ *res;
+ text *type;
+ PX_Combo *c;
+ unsigned dlen,
+ klen,
+ ivlen,
+ rlen;
+
+ type = PG_GETARG_TEXT_PP(3);
+ c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ iv = PG_GETARG_BYTEA_PP(2);
+ dlen = VARSIZE_ANY_EXHDR(data);
+ klen = VARSIZE_ANY_EXHDR(key);
+ ivlen = VARSIZE_ANY_EXHDR(iv);
+
+ rlen = px_combo_encrypt_len(c, dlen);
+ res = palloc(VARHDRSZ + rlen);
+
+ err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen,
+ (uint8 *) VARDATA_ANY(iv), ivlen);
+ if (!err)
+ err = px_combo_encrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
+ (uint8 *) VARDATA(res), &rlen);
+
+ px_combo_free(c);
+
+ if (err)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("encrypt_iv error: %s", px_strerror(err))));
+
+ SET_VARSIZE(res, VARHDRSZ + rlen);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ PG_FREE_IF_COPY(iv, 2);
+ PG_FREE_IF_COPY(type, 3);
+
+ PG_RETURN_BYTEA_P(res);
+}
+
+/* SQL function: pg_decrypt_iv(bytea, bytea, bytea, text) returns bytea */
+PG_FUNCTION_INFO_V1(pg_decrypt_iv);
+
+Datum
+pg_decrypt_iv(PG_FUNCTION_ARGS)
+{
+ int err;
+ bytea *data,
+ *key,
+ *iv,
+ *res;
+ text *type;
+ PX_Combo *c;
+ unsigned dlen,
+ klen,
+ rlen,
+ ivlen;
+
+ type = PG_GETARG_TEXT_PP(3);
+ c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ iv = PG_GETARG_BYTEA_PP(2);
+ dlen = VARSIZE_ANY_EXHDR(data);
+ klen = VARSIZE_ANY_EXHDR(key);
+ ivlen = VARSIZE_ANY_EXHDR(iv);
+
+ rlen = px_combo_decrypt_len(c, dlen);
+ res = palloc(VARHDRSZ + rlen);
+
+ err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen,
+ (uint8 *) VARDATA_ANY(iv), ivlen);
+ if (!err)
+ err = px_combo_decrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
+ (uint8 *) VARDATA(res), &rlen);
+
+ px_combo_free(c);
+
+ if (err)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("decrypt_iv error: %s", px_strerror(err))));
+
+ SET_VARSIZE(res, VARHDRSZ + rlen);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ PG_FREE_IF_COPY(iv, 2);
+ PG_FREE_IF_COPY(type, 3);
+
+ PG_RETURN_BYTEA_P(res);
+}
+
+/* SQL function: pg_random_bytes(int4) returns bytea */
+PG_FUNCTION_INFO_V1(pg_random_bytes);
+
+Datum
+pg_random_bytes(PG_FUNCTION_ARGS)
+{
+ int len = PG_GETARG_INT32(0);
+ bytea *res;
+
+ if (len < 1 || len > 1024)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("Length not in range")));
+
+ res = palloc(VARHDRSZ + len);
+ SET_VARSIZE(res, VARHDRSZ + len);
+
+ /* generate result */
+ if (!pg_strong_random(VARDATA(res), len))
+ px_THROW_ERROR(PXE_NO_RANDOM);
+
+ PG_RETURN_BYTEA_P(res);
+}
+
+/* SQL function: gen_random_uuid() returns uuid */
+PG_FUNCTION_INFO_V1(pg_random_uuid);
+
+Datum
+pg_random_uuid(PG_FUNCTION_ARGS)
+{
+ /* redirect to built-in function */
+ return gen_random_uuid(fcinfo);
+}
+
+static void *
+find_provider(text *name,
+ PFN provider_lookup,
+ const char *desc, int silent)
+{
+ void *res;
+ char *buf;
+ int err;
+
+ buf = downcase_truncate_identifier(VARDATA_ANY(name),
+ VARSIZE_ANY_EXHDR(name),
+ false);
+
+ err = provider_lookup(buf, &res);
+
+ if (err && !silent)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("Cannot use \"%s\": %s", buf, px_strerror(err))));
+
+ pfree(buf);
+
+ return err ? NULL : res;
+}
diff --git a/contrib/pgcrypto/pgcrypto.control b/contrib/pgcrypto/pgcrypto.control
new file mode 100644
index 0000000..d2151d3
--- /dev/null
+++ b/contrib/pgcrypto/pgcrypto.control
@@ -0,0 +1,6 @@
+# pgcrypto extension
+comment = 'cryptographic functions'
+default_version = '1.3'
+module_pathname = '$libdir/pgcrypto'
+relocatable = true
+trusted = true
diff --git a/contrib/pgcrypto/pgcrypto.h b/contrib/pgcrypto/pgcrypto.h
new file mode 100644
index 0000000..65a1ed3
--- /dev/null
+++ b/contrib/pgcrypto/pgcrypto.h
@@ -0,0 +1,37 @@
+/*
+ * pgcrypto.h
+ * Header file for pgcrypto.
+ *
+ * Copyright (c) 2000 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgcrypto.h
+ */
+
+#ifndef _PG_CRYPTO_H
+#define _PG_CRYPTO_H
+
+#include "fmgr.h"
+
+#endif
diff --git a/contrib/pgcrypto/pgp-armor.c b/contrib/pgcrypto/pgp-armor.c
new file mode 100644
index 0000000..679779a
--- /dev/null
+++ b/contrib/pgcrypto/pgp-armor.c
@@ -0,0 +1,488 @@
+/*
+ * pgp-armor.c
+ * PGP ascii-armor.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-armor.c
+ */
+
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+/*
+ * BASE64 - duplicated :(
+ */
+
+static const unsigned char _base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int
+pg_base64_encode(const uint8 *src, unsigned len, uint8 *dst)
+{
+ uint8 *p,
+ *lend = dst + 76;
+ const uint8 *s,
+ *end = src + len;
+ int pos = 2;
+ unsigned long buf = 0;
+
+ s = src;
+ p = dst;
+
+ while (s < end)
+ {
+ buf |= *s << (pos << 3);
+ pos--;
+ s++;
+
+ /*
+ * write it out
+ */
+ if (pos < 0)
+ {
+ *p++ = _base64[(buf >> 18) & 0x3f];
+ *p++ = _base64[(buf >> 12) & 0x3f];
+ *p++ = _base64[(buf >> 6) & 0x3f];
+ *p++ = _base64[buf & 0x3f];
+
+ pos = 2;
+ buf = 0;
+ }
+ if (p >= lend)
+ {
+ *p++ = '\n';
+ lend = p + 76;
+ }
+ }
+ if (pos != 2)
+ {
+ *p++ = _base64[(buf >> 18) & 0x3f];
+ *p++ = _base64[(buf >> 12) & 0x3f];
+ *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
+ *p++ = '=';
+ }
+
+ return p - dst;
+}
+
+/* probably should use lookup table */
+static int
+pg_base64_decode(const uint8 *src, unsigned len, uint8 *dst)
+{
+ const uint8 *srcend = src + len,
+ *s = src;
+ uint8 *p = dst;
+ char c;
+ unsigned b = 0;
+ unsigned long buf = 0;
+ int pos = 0,
+ end = 0;
+
+ while (s < srcend)
+ {
+ c = *s++;
+ if (c >= 'A' && c <= 'Z')
+ b = c - 'A';
+ else if (c >= 'a' && c <= 'z')
+ b = c - 'a' + 26;
+ else if (c >= '0' && c <= '9')
+ b = c - '0' + 52;
+ else if (c == '+')
+ b = 62;
+ else if (c == '/')
+ b = 63;
+ else if (c == '=')
+ {
+ /*
+ * end sequence
+ */
+ if (!end)
+ {
+ if (pos == 2)
+ end = 1;
+ else if (pos == 3)
+ end = 2;
+ else
+ return PXE_PGP_CORRUPT_ARMOR;
+ }
+ b = 0;
+ }
+ else if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ continue;
+ else
+ return PXE_PGP_CORRUPT_ARMOR;
+
+ /*
+ * add it to buffer
+ */
+ buf = (buf << 6) + b;
+ pos++;
+ if (pos == 4)
+ {
+ *p++ = (buf >> 16) & 255;
+ if (end == 0 || end > 1)
+ *p++ = (buf >> 8) & 255;
+ if (end == 0 || end > 2)
+ *p++ = buf & 255;
+ buf = 0;
+ pos = 0;
+ }
+ }
+
+ if (pos != 0)
+ return PXE_PGP_CORRUPT_ARMOR;
+ return p - dst;
+}
+
+static unsigned
+pg_base64_enc_len(unsigned srclen)
+{
+ /*
+ * 3 bytes will be converted to 4, linefeed after 76 chars
+ */
+ return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4);
+}
+
+static unsigned
+pg_base64_dec_len(unsigned srclen)
+{
+ return (srclen * 3) >> 2;
+}
+
+/*
+ * PGP armor
+ */
+
+static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n";
+static const char *armor_footer = "\n-----END PGP MESSAGE-----\n";
+
+/* CRC24 implementation from rfc2440 */
+#define CRC24_INIT 0x00b704ceL
+#define CRC24_POLY 0x01864cfbL
+static long
+crc24(const uint8 *data, unsigned len)
+{
+ unsigned crc = CRC24_INIT;
+ int i;
+
+ while (len--)
+ {
+ crc ^= (*data++) << 16;
+ for (i = 0; i < 8; i++)
+ {
+ crc <<= 1;
+ if (crc & 0x1000000)
+ crc ^= CRC24_POLY;
+ }
+ }
+ return crc & 0xffffffL;
+}
+
+void
+pgp_armor_encode(const uint8 *src, unsigned len, StringInfo dst,
+ int num_headers, char **keys, char **values)
+{
+ int n;
+ int res;
+ unsigned b64len;
+ unsigned crc = crc24(src, len);
+
+ appendStringInfoString(dst, armor_header);
+
+ for (n = 0; n < num_headers; n++)
+ appendStringInfo(dst, "%s: %s\n", keys[n], values[n]);
+ appendStringInfoChar(dst, '\n');
+
+ /* make sure we have enough room to pg_base64_encode() */
+ b64len = pg_base64_enc_len(len);
+ enlargeStringInfo(dst, (int) b64len);
+
+ res = pg_base64_encode(src, len, (uint8 *) dst->data + dst->len);
+ if (res > b64len)
+ elog(FATAL, "overflow - encode estimate too small");
+ dst->len += res;
+
+ if (*(dst->data + dst->len - 1) != '\n')
+ appendStringInfoChar(dst, '\n');
+
+ appendStringInfoChar(dst, '=');
+ appendStringInfoChar(dst, _base64[(crc >> 18) & 0x3f]);
+ appendStringInfoChar(dst, _base64[(crc >> 12) & 0x3f]);
+ appendStringInfoChar(dst, _base64[(crc >> 6) & 0x3f]);
+ appendStringInfoChar(dst, _base64[crc & 0x3f]);
+
+ appendStringInfoString(dst, armor_footer);
+}
+
+static const uint8 *
+find_str(const uint8 *data, const uint8 *data_end, const char *str, int strlen)
+{
+ const uint8 *p = data;
+
+ if (!strlen)
+ return NULL;
+ if (data_end - data < strlen)
+ return NULL;
+ while (p < data_end)
+ {
+ p = memchr(p, str[0], data_end - p);
+ if (p == NULL)
+ return NULL;
+ if (p + strlen > data_end)
+ return NULL;
+ if (memcmp(p, str, strlen) == 0)
+ return p;
+ p++;
+ }
+ return NULL;
+}
+
+static int
+find_header(const uint8 *data, const uint8 *datend,
+ const uint8 **start_p, int is_end)
+{
+ const uint8 *p = data;
+ static const char *start_sep = "-----BEGIN";
+ static const char *end_sep = "-----END";
+ const char *sep = is_end ? end_sep : start_sep;
+
+ /* find header line */
+ while (1)
+ {
+ p = find_str(p, datend, sep, strlen(sep));
+ if (p == NULL)
+ return PXE_PGP_CORRUPT_ARMOR;
+ /* it must start at beginning of line */
+ if (p == data || *(p - 1) == '\n')
+ break;
+ p += strlen(sep);
+ }
+ *start_p = p;
+ p += strlen(sep);
+
+ /* check if header text ok */
+ for (; p < datend && *p != '-'; p++)
+ {
+ /* various junk can be there, but definitely not line-feed */
+ if (*p >= ' ')
+ continue;
+ return PXE_PGP_CORRUPT_ARMOR;
+ }
+ if (datend - p < 5 || memcmp(p, sep, 5) != 0)
+ return PXE_PGP_CORRUPT_ARMOR;
+ p += 5;
+
+ /* check if at end of line */
+ if (p < datend)
+ {
+ if (*p != '\n' && *p != '\r')
+ return PXE_PGP_CORRUPT_ARMOR;
+ if (*p == '\r')
+ p++;
+ if (p < datend && *p == '\n')
+ p++;
+ }
+ return p - *start_p;
+}
+
+int
+pgp_armor_decode(const uint8 *src, int len, StringInfo dst)
+{
+ const uint8 *p = src;
+ const uint8 *data_end = src + len;
+ long crc;
+ const uint8 *base64_start,
+ *armor_end;
+ const uint8 *base64_end = NULL;
+ uint8 buf[4];
+ int hlen;
+ int blen;
+ int res = PXE_PGP_CORRUPT_ARMOR;
+
+ /* armor start */
+ hlen = find_header(src, data_end, &p, 0);
+ if (hlen <= 0)
+ goto out;
+ p += hlen;
+
+ /* armor end */
+ hlen = find_header(p, data_end, &armor_end, 1);
+ if (hlen <= 0)
+ goto out;
+
+ /* skip comments - find empty line */
+ while (p < armor_end && *p != '\n' && *p != '\r')
+ {
+ p = memchr(p, '\n', armor_end - p);
+ if (!p)
+ goto out;
+
+ /* step to start of next line */
+ p++;
+ }
+ base64_start = p;
+
+ /* find crc pos */
+ for (p = armor_end; p >= base64_start; p--)
+ if (*p == '=')
+ {
+ base64_end = p - 1;
+ break;
+ }
+ if (base64_end == NULL)
+ goto out;
+
+ /* decode crc */
+ if (pg_base64_decode(p + 1, 4, buf) != 3)
+ goto out;
+ crc = (((long) buf[0]) << 16) + (((long) buf[1]) << 8) + (long) buf[2];
+
+ /* decode data */
+ blen = (int) pg_base64_dec_len(len);
+ enlargeStringInfo(dst, blen);
+ res = pg_base64_decode(base64_start, base64_end - base64_start, (uint8 *) dst->data);
+ if (res > blen)
+ elog(FATAL, "overflow - decode estimate too small");
+ if (res >= 0)
+ {
+ if (crc24((uint8 *) dst->data, res) == crc)
+ dst->len += res;
+ else
+ res = PXE_PGP_CORRUPT_ARMOR;
+ }
+out:
+ return res;
+}
+
+/*
+ * Extracts all armor headers from an ASCII-armored input.
+ *
+ * Returns 0 on success, or PXE_* error code on error. On success, the
+ * number of headers and their keys and values are returned in *nheaders,
+ * *nkeys and *nvalues.
+ */
+int
+pgp_extract_armor_headers(const uint8 *src, unsigned len,
+ int *nheaders, char ***keys, char ***values)
+{
+ const uint8 *data_end = src + len;
+ const uint8 *p;
+ const uint8 *base64_start;
+ const uint8 *armor_start;
+ const uint8 *armor_end;
+ Size armor_len;
+ char *line;
+ char *nextline;
+ char *eol,
+ *colon;
+ int hlen;
+ char *buf;
+ int hdrlines;
+ int n;
+
+ /* armor start */
+ hlen = find_header(src, data_end, &armor_start, 0);
+ if (hlen <= 0)
+ return PXE_PGP_CORRUPT_ARMOR;
+ armor_start += hlen;
+
+ /* armor end */
+ hlen = find_header(armor_start, data_end, &armor_end, 1);
+ if (hlen <= 0)
+ return PXE_PGP_CORRUPT_ARMOR;
+
+ /* Count the number of armor header lines. */
+ hdrlines = 0;
+ p = armor_start;
+ while (p < armor_end && *p != '\n' && *p != '\r')
+ {
+ p = memchr(p, '\n', armor_end - p);
+ if (!p)
+ return PXE_PGP_CORRUPT_ARMOR;
+
+ /* step to start of next line */
+ p++;
+ hdrlines++;
+ }
+ base64_start = p;
+
+ /*
+ * Make a modifiable copy of the part of the input that contains the
+ * headers. The returned key/value pointers will point inside the buffer.
+ */
+ armor_len = base64_start - armor_start;
+ buf = palloc(armor_len + 1);
+ memcpy(buf, armor_start, armor_len);
+ buf[armor_len] = '\0';
+
+ /* Allocate return arrays */
+ *keys = (char **) palloc(hdrlines * sizeof(char *));
+ *values = (char **) palloc(hdrlines * sizeof(char *));
+
+ /*
+ * Split the header lines at newlines and ": " separators, and collect
+ * pointers to the keys and values in the return arrays.
+ */
+ n = 0;
+ line = buf;
+ for (;;)
+ {
+ /* find end of line */
+ eol = strchr(line, '\n');
+ if (!eol)
+ break;
+ nextline = eol + 1;
+ /* if the line ends in CR + LF, strip the CR */
+ if (eol > line && *(eol - 1) == '\r')
+ eol--;
+ *eol = '\0';
+
+ /* find colon+space separating the key and value */
+ colon = strstr(line, ": ");
+ if (!colon)
+ return PXE_PGP_CORRUPT_ARMOR;
+ *colon = '\0';
+
+ /* shouldn't happen, we counted the number of lines beforehand */
+ if (n >= hdrlines)
+ elog(ERROR, "unexpected number of armor header lines");
+
+ (*keys)[n] = line;
+ (*values)[n] = colon + 2;
+ n++;
+
+ /* step to start of next line */
+ line = nextline;
+ }
+
+ if (n != hdrlines)
+ elog(ERROR, "unexpected number of armor header lines");
+
+ *nheaders = n;
+ return 0;
+}
diff --git a/contrib/pgcrypto/pgp-cfb.c b/contrib/pgcrypto/pgp-cfb.c
new file mode 100644
index 0000000..de41e82
--- /dev/null
+++ b/contrib/pgcrypto/pgp-cfb.c
@@ -0,0 +1,265 @@
+/*
+ * pgp-cfb.c
+ * Implements both normal and PGP-specific CFB mode.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-cfb.c
+ */
+
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+typedef int (*mix_data_t) (PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
+
+struct PGP_CFB
+{
+ PX_Cipher *ciph;
+ int block_size;
+ int pos;
+ int block_no;
+ int resync;
+ uint8 fr[PGP_MAX_BLOCK];
+ uint8 fre[PGP_MAX_BLOCK];
+ uint8 encbuf[PGP_MAX_BLOCK];
+};
+
+int
+pgp_cfb_create(PGP_CFB **ctx_p, int algo, const uint8 *key, int key_len,
+ int resync, uint8 *iv)
+{
+ int res;
+ PX_Cipher *ciph;
+ PGP_CFB *ctx;
+
+ res = pgp_load_cipher(algo, &ciph);
+ if (res < 0)
+ return res;
+
+ res = px_cipher_init(ciph, key, key_len, NULL);
+ if (res < 0)
+ {
+ px_cipher_free(ciph);
+ return res;
+ }
+
+ ctx = palloc0(sizeof(*ctx));
+ ctx->ciph = ciph;
+ ctx->block_size = px_cipher_block_size(ciph);
+ ctx->resync = resync;
+
+ if (iv)
+ memcpy(ctx->fr, iv, ctx->block_size);
+
+ *ctx_p = ctx;
+ return 0;
+}
+
+void
+pgp_cfb_free(PGP_CFB *ctx)
+{
+ px_cipher_free(ctx->ciph);
+ px_memset(ctx, 0, sizeof(*ctx));
+ pfree(ctx);
+}
+
+/*
+ * Data processing for normal CFB. (PGP_PKT_SYMENCRYPTED_DATA_MDC)
+ */
+static int
+mix_encrypt_normal(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
+{
+ int i;
+
+ for (i = ctx->pos; i < ctx->pos + len; i++)
+ *dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++);
+ ctx->pos += len;
+ return len;
+}
+
+static int
+mix_decrypt_normal(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
+{
+ int i;
+
+ for (i = ctx->pos; i < ctx->pos + len; i++)
+ {
+ ctx->encbuf[i] = *data++;
+ *dst++ = ctx->fre[i] ^ ctx->encbuf[i];
+ }
+ ctx->pos += len;
+ return len;
+}
+
+/*
+ * Data processing for old PGP CFB mode. (PGP_PKT_SYMENCRYPTED_DATA)
+ *
+ * The goal is to hide the horror from the rest of the code,
+ * thus its all concentrated here.
+ */
+static int
+mix_encrypt_resync(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
+{
+ int i,
+ n;
+
+ /* block #2 is 2 bytes long */
+ if (ctx->block_no == 2)
+ {
+ n = 2 - ctx->pos;
+ if (len < n)
+ n = len;
+ for (i = ctx->pos; i < ctx->pos + n; i++)
+ *dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++);
+
+ ctx->pos += n;
+ len -= n;
+
+ if (ctx->pos == 2)
+ {
+ memcpy(ctx->fr, ctx->encbuf + 2, ctx->block_size - 2);
+ memcpy(ctx->fr + ctx->block_size - 2, ctx->encbuf, 2);
+ ctx->pos = 0;
+ return n;
+ }
+ }
+ for (i = ctx->pos; i < ctx->pos + len; i++)
+ *dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++);
+ ctx->pos += len;
+ return len;
+}
+
+static int
+mix_decrypt_resync(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
+{
+ int i,
+ n;
+
+ /* block #2 is 2 bytes long */
+ if (ctx->block_no == 2)
+ {
+ n = 2 - ctx->pos;
+ if (len < n)
+ n = len;
+ for (i = ctx->pos; i < ctx->pos + n; i++)
+ {
+ ctx->encbuf[i] = *data++;
+ *dst++ = ctx->fre[i] ^ ctx->encbuf[i];
+ }
+ ctx->pos += n;
+ len -= n;
+
+ if (ctx->pos == 2)
+ {
+ memcpy(ctx->fr, ctx->encbuf + 2, ctx->block_size - 2);
+ memcpy(ctx->fr + ctx->block_size - 2, ctx->encbuf, 2);
+ ctx->pos = 0;
+ return n;
+ }
+ }
+ for (i = ctx->pos; i < ctx->pos + len; i++)
+ {
+ ctx->encbuf[i] = *data++;
+ *dst++ = ctx->fre[i] ^ ctx->encbuf[i];
+ }
+ ctx->pos += len;
+ return len;
+}
+
+/*
+ * common code for both encrypt and decrypt.
+ */
+static int
+cfb_process(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst,
+ mix_data_t mix_data)
+{
+ int n;
+ int res;
+
+ while (len > 0 && ctx->pos > 0)
+ {
+ n = ctx->block_size - ctx->pos;
+ if (len < n)
+ n = len;
+
+ n = mix_data(ctx, data, n, dst);
+ data += n;
+ dst += n;
+ len -= n;
+
+ if (ctx->pos == ctx->block_size)
+ {
+ memcpy(ctx->fr, ctx->encbuf, ctx->block_size);
+ ctx->pos = 0;
+ }
+ }
+
+ while (len > 0)
+ {
+ unsigned rlen;
+
+ px_cipher_encrypt(ctx->ciph, 0, ctx->fr, ctx->block_size, ctx->fre, &rlen);
+ if (ctx->block_no < 5)
+ ctx->block_no++;
+
+ n = ctx->block_size;
+ if (len < n)
+ n = len;
+
+ res = mix_data(ctx, data, n, dst);
+ data += res;
+ dst += res;
+ len -= res;
+
+ if (ctx->pos == ctx->block_size)
+ {
+ memcpy(ctx->fr, ctx->encbuf, ctx->block_size);
+ ctx->pos = 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ * public interface
+ */
+
+int
+pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
+{
+ mix_data_t mix = ctx->resync ? mix_encrypt_resync : mix_encrypt_normal;
+
+ return cfb_process(ctx, data, len, dst, mix);
+}
+
+int
+pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst)
+{
+ mix_data_t mix = ctx->resync ? mix_decrypt_resync : mix_decrypt_normal;
+
+ return cfb_process(ctx, data, len, dst, mix);
+}
diff --git a/contrib/pgcrypto/pgp-compress.c b/contrib/pgcrypto/pgp-compress.c
new file mode 100644
index 0000000..086bec3
--- /dev/null
+++ b/contrib/pgcrypto/pgp-compress.c
@@ -0,0 +1,346 @@
+/*
+ * pgp-compress.c
+ * ZIP and ZLIB compression via zlib.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-compress.c
+ */
+
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+/*
+ * Compressed pkt writer
+ */
+
+#ifdef HAVE_LIBZ
+
+#include <zlib.h>
+
+#define ZIP_OUT_BUF 8192
+#define ZIP_IN_BLOCK 8192
+
+struct ZipStat
+{
+ uint8 type;
+ int buf_len;
+ int hdr_done;
+ z_stream stream;
+ uint8 buf[ZIP_OUT_BUF];
+};
+
+static void *
+z_alloc(void *priv, unsigned n_items, unsigned item_len)
+{
+ return palloc(n_items * item_len);
+}
+
+static void
+z_free(void *priv, void *addr)
+{
+ pfree(addr);
+}
+
+static int
+compress_init(PushFilter *next, void *init_arg, void **priv_p)
+{
+ int res;
+ struct ZipStat *st;
+ PGP_Context *ctx = init_arg;
+ uint8 type = ctx->compress_algo;
+
+ if (type != PGP_COMPR_ZLIB && type != PGP_COMPR_ZIP)
+ return PXE_PGP_UNSUPPORTED_COMPR;
+
+ /*
+ * init
+ */
+ st = palloc0(sizeof(*st));
+ st->buf_len = ZIP_OUT_BUF;
+ st->stream.zalloc = z_alloc;
+ st->stream.zfree = z_free;
+
+ if (type == PGP_COMPR_ZIP)
+ res = deflateInit2(&st->stream, ctx->compress_level,
+ Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
+ else
+ res = deflateInit(&st->stream, ctx->compress_level);
+ if (res != Z_OK)
+ {
+ pfree(st);
+ return PXE_PGP_COMPRESSION_ERROR;
+ }
+ *priv_p = st;
+
+ return ZIP_IN_BLOCK;
+}
+
+/* writes compressed data packet */
+
+/* can handle zero-len incoming data, but shouldn't */
+static int
+compress_process(PushFilter *next, void *priv, const uint8 *data, int len)
+{
+ int res,
+ n_out;
+ struct ZipStat *st = priv;
+
+ /*
+ * process data
+ */
+ st->stream.next_in = unconstify(uint8 *, data);
+ st->stream.avail_in = len;
+ while (st->stream.avail_in > 0)
+ {
+ st->stream.next_out = st->buf;
+ st->stream.avail_out = st->buf_len;
+ res = deflate(&st->stream, Z_NO_FLUSH);
+ if (res != Z_OK)
+ return PXE_PGP_COMPRESSION_ERROR;
+
+ n_out = st->buf_len - st->stream.avail_out;
+ if (n_out > 0)
+ {
+ res = pushf_write(next, st->buf, n_out);
+ if (res < 0)
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+static int
+compress_flush(PushFilter *next, void *priv)
+{
+ int res,
+ zres,
+ n_out;
+ struct ZipStat *st = priv;
+
+ st->stream.next_in = NULL;
+ st->stream.avail_in = 0;
+ while (1)
+ {
+ st->stream.next_out = st->buf;
+ st->stream.avail_out = st->buf_len;
+ zres = deflate(&st->stream, Z_FINISH);
+ if (zres != Z_STREAM_END && zres != Z_OK)
+ return PXE_PGP_COMPRESSION_ERROR;
+
+ n_out = st->buf_len - st->stream.avail_out;
+ if (n_out > 0)
+ {
+ res = pushf_write(next, st->buf, n_out);
+ if (res < 0)
+ return res;
+ }
+ if (zres == Z_STREAM_END)
+ break;
+ }
+ return 0;
+}
+
+static void
+compress_free(void *priv)
+{
+ struct ZipStat *st = priv;
+
+ deflateEnd(&st->stream);
+ px_memset(st, 0, sizeof(*st));
+ pfree(st);
+}
+
+static const PushFilterOps
+ compress_filter = {
+ compress_init, compress_process, compress_flush, compress_free
+};
+
+int
+pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
+{
+ return pushf_create(res, &compress_filter, ctx, dst);
+}
+
+/*
+ * Decompress
+ */
+struct DecomprData
+{
+ int buf_len; /* = ZIP_OUT_BUF */
+ int buf_data; /* available data */
+ uint8 *pos;
+ z_stream stream;
+ int eof;
+ uint8 buf[ZIP_OUT_BUF];
+};
+
+static int
+decompress_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_Context *ctx = arg;
+ struct DecomprData *dec;
+ int res;
+
+ if (ctx->compress_algo != PGP_COMPR_ZLIB
+ && ctx->compress_algo != PGP_COMPR_ZIP)
+ return PXE_PGP_UNSUPPORTED_COMPR;
+
+ dec = palloc0(sizeof(*dec));
+ dec->buf_len = ZIP_OUT_BUF;
+ *priv_p = dec;
+
+ dec->stream.zalloc = z_alloc;
+ dec->stream.zfree = z_free;
+
+ if (ctx->compress_algo == PGP_COMPR_ZIP)
+ res = inflateInit2(&dec->stream, -15);
+ else
+ res = inflateInit(&dec->stream);
+ if (res != Z_OK)
+ {
+ pfree(dec);
+ px_debug("decompress_init: inflateInit error");
+ return PXE_PGP_COMPRESSION_ERROR;
+ }
+
+ return 0;
+}
+
+static int
+decompress_read(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ int res;
+ int flush;
+ struct DecomprData *dec = priv;
+
+restart:
+ if (dec->buf_data > 0)
+ {
+ if (len > dec->buf_data)
+ len = dec->buf_data;
+ *data_p = dec->pos;
+ dec->pos += len;
+ dec->buf_data -= len;
+ return len;
+ }
+
+ if (dec->eof)
+ return 0;
+
+ if (dec->stream.avail_in == 0)
+ {
+ uint8 *tmp;
+
+ res = pullf_read(src, 8192, &tmp);
+ if (res < 0)
+ return res;
+ dec->stream.next_in = tmp;
+ dec->stream.avail_in = res;
+ }
+
+ dec->stream.next_out = dec->buf;
+ dec->stream.avail_out = dec->buf_len;
+ dec->pos = dec->buf;
+
+ /*
+ * Z_SYNC_FLUSH is tell zlib to output as much as possible. It should do
+ * it anyway (Z_NO_FLUSH), but seems to reserve the right not to. So lets
+ * follow the API.
+ */
+ flush = dec->stream.avail_in ? Z_SYNC_FLUSH : Z_FINISH;
+ res = inflate(&dec->stream, flush);
+ if (res != Z_OK && res != Z_STREAM_END)
+ {
+ px_debug("decompress_read: inflate error: %d", res);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ dec->buf_data = dec->buf_len - dec->stream.avail_out;
+ if (res == Z_STREAM_END)
+ {
+ uint8 *tmp;
+
+ /*
+ * A stream must be terminated by a normal packet. If the last stream
+ * packet in the source stream is a full packet, a normal empty packet
+ * must follow. Since the underlying packet reader doesn't know that
+ * the compressed stream has been ended, we need to consume the
+ * terminating packet here. This read does not harm even if the
+ * stream has already ended.
+ */
+ res = pullf_read(src, 1, &tmp);
+
+ if (res < 0)
+ return res;
+ else if (res > 0)
+ {
+ px_debug("decompress_read: extra bytes after end of stream");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ dec->eof = 1;
+ }
+ goto restart;
+}
+
+static void
+decompress_free(void *priv)
+{
+ struct DecomprData *dec = priv;
+
+ inflateEnd(&dec->stream);
+ px_memset(dec, 0, sizeof(*dec));
+ pfree(dec);
+}
+
+static const PullFilterOps
+ decompress_filter = {
+ decompress_init, decompress_read, decompress_free
+};
+
+int
+pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
+{
+ return pullf_create(res, &decompress_filter, ctx, src);
+}
+#else /* !HAVE_LIBZ */
+
+int
+pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst)
+{
+ return PXE_PGP_UNSUPPORTED_COMPR;
+}
+
+int
+pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src)
+{
+ return PXE_PGP_UNSUPPORTED_COMPR;
+}
+
+#endif
diff --git a/contrib/pgcrypto/pgp-decrypt.c b/contrib/pgcrypto/pgp-decrypt.c
new file mode 100644
index 0000000..d12dcad
--- /dev/null
+++ b/contrib/pgcrypto/pgp-decrypt.c
@@ -0,0 +1,1212 @@
+/*
+ * pgp-decrypt.c
+ * OpenPGP decrypt.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-decrypt.c
+ */
+
+#include "postgres.h"
+
+#include "mbuf.h"
+#include "pgp.h"
+#include "px.h"
+
+#define NO_CTX_SIZE 0
+#define ALLOW_CTX_SIZE 1
+#define NO_COMPR 0
+#define ALLOW_COMPR 1
+#define NO_MDC 0
+#define NEED_MDC 1
+
+#define PKT_NORMAL 1
+#define PKT_STREAM 2
+#define PKT_CONTEXT 3
+
+#define MAX_CHUNK (16*1024*1024)
+
+static int
+parse_new_len(PullFilter *src, int *len_p)
+{
+ uint8 b;
+ int len;
+ int pkttype = PKT_NORMAL;
+
+ GETBYTE(src, b);
+ if (b <= 191)
+ len = b;
+ else if (b >= 192 && b <= 223)
+ {
+ len = ((unsigned) (b) - 192) << 8;
+ GETBYTE(src, b);
+ len += 192 + b;
+ }
+ else if (b == 255)
+ {
+ GETBYTE(src, b);
+ len = b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ }
+ else
+ {
+ len = 1 << (b & 0x1F);
+ pkttype = PKT_STREAM;
+ }
+
+ if (len < 0 || len > MAX_CHUNK)
+ {
+ px_debug("parse_new_len: weird length");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ *len_p = len;
+ return pkttype;
+}
+
+static int
+parse_old_len(PullFilter *src, int *len_p, int lentype)
+{
+ uint8 b;
+ int len;
+
+ GETBYTE(src, b);
+ len = b;
+
+ if (lentype == 1)
+ {
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ }
+ else if (lentype == 2)
+ {
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ GETBYTE(src, b);
+ len = (len << 8) | b;
+ }
+
+ if (len < 0 || len > MAX_CHUNK)
+ {
+ px_debug("parse_old_len: weird length");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ *len_p = len;
+ return PKT_NORMAL;
+}
+
+/* returns pkttype or 0 on eof */
+int
+pgp_parse_pkt_hdr(PullFilter *src, uint8 *tag, int *len_p, int allow_ctx)
+{
+ int lentype;
+ int res;
+ uint8 *p;
+
+ /* EOF is normal here, thus we don't use GETBYTE */
+ res = pullf_read(src, 1, &p);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ return 0;
+
+ if ((*p & 0x80) == 0)
+ {
+ px_debug("pgp_parse_pkt_hdr: not pkt hdr");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ if (*p & 0x40)
+ {
+ *tag = *p & 0x3f;
+ res = parse_new_len(src, len_p);
+ }
+ else
+ {
+ lentype = *p & 3;
+ *tag = (*p >> 2) & 0x0F;
+ if (lentype == 3)
+ res = allow_ctx ? PKT_CONTEXT : PXE_PGP_CORRUPT_DATA;
+ else
+ res = parse_old_len(src, len_p, lentype);
+ }
+ return res;
+}
+
+/*
+ * Packet reader
+ */
+struct PktData
+{
+ int type;
+ int len;
+};
+
+static int
+pktreader_pull(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ int res;
+ struct PktData *pkt = priv;
+
+ /* PKT_CONTEXT means: whatever there is */
+ if (pkt->type == PKT_CONTEXT)
+ return pullf_read(src, len, data_p);
+
+ while (pkt->len == 0)
+ {
+ /* this was last chunk in stream */
+ if (pkt->type == PKT_NORMAL)
+ return 0;
+
+ /* next chunk in stream */
+ res = parse_new_len(src, &pkt->len);
+ if (res < 0)
+ return res;
+ pkt->type = res;
+ }
+
+ if (len > pkt->len)
+ len = pkt->len;
+
+ res = pullf_read(src, len, data_p);
+ if (res > 0)
+ pkt->len -= res;
+
+ return res;
+}
+
+static void
+pktreader_free(void *priv)
+{
+ struct PktData *pkt = priv;
+
+ px_memset(pkt, 0, sizeof(*pkt));
+ pfree(pkt);
+}
+
+static struct PullFilterOps pktreader_filter = {
+ NULL, pktreader_pull, pktreader_free
+};
+
+/* needs helper function to pass several parameters */
+int
+pgp_create_pkt_reader(PullFilter **pf_p, PullFilter *src, int len,
+ int pkttype, PGP_Context *ctx)
+{
+ int res;
+ struct PktData *pkt = palloc(sizeof(*pkt));
+
+ pkt->type = pkttype;
+ pkt->len = len;
+ res = pullf_create(pf_p, &pktreader_filter, pkt, src);
+ if (res < 0)
+ pfree(pkt);
+ return res;
+}
+
+/*
+ * Prefix check filter
+ * https://tools.ietf.org/html/rfc4880#section-5.7
+ * https://tools.ietf.org/html/rfc4880#section-5.13
+ */
+
+static int
+prefix_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_Context *ctx = arg;
+ int len;
+ int res;
+ uint8 *buf;
+ uint8 tmpbuf[PGP_MAX_BLOCK + 2];
+
+ len = pgp_get_cipher_block_size(ctx->cipher_algo);
+ if (len > sizeof(tmpbuf))
+ return PXE_BUG;
+
+ res = pullf_read_max(src, len + 2, &buf, tmpbuf);
+ if (res < 0)
+ return res;
+ if (res != len + 2)
+ {
+ px_debug("prefix_init: short read");
+ px_memset(tmpbuf, 0, sizeof(tmpbuf));
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ if (buf[len - 2] != buf[len] || buf[len - 1] != buf[len + 1])
+ {
+ px_debug("prefix_init: corrupt prefix");
+ /* report error in pgp_decrypt() */
+ ctx->corrupt_prefix = 1;
+ }
+ px_memset(tmpbuf, 0, sizeof(tmpbuf));
+ return 0;
+}
+
+static struct PullFilterOps prefix_filter = {
+ prefix_init, NULL, NULL
+};
+
+
+/*
+ * Decrypt filter
+ */
+
+static int
+decrypt_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_CFB *cfb = arg;
+
+ *priv_p = cfb;
+
+ /* we need to write somewhere, so ask for a buffer */
+ return 4096;
+}
+
+static int
+decrypt_read(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ PGP_CFB *cfb = priv;
+ uint8 *tmp;
+ int res;
+
+ res = pullf_read(src, len, &tmp);
+ if (res > 0)
+ {
+ pgp_cfb_decrypt(cfb, tmp, res, buf);
+ *data_p = buf;
+ }
+ return res;
+}
+
+struct PullFilterOps pgp_decrypt_filter = {
+ decrypt_init, decrypt_read, NULL
+};
+
+
+/*
+ * MDC hasher filter
+ */
+
+static int
+mdc_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_Context *ctx = arg;
+
+ *priv_p = ctx;
+ return pgp_load_digest(PGP_DIGEST_SHA1, &ctx->mdc_ctx);
+}
+
+static void
+mdc_free(void *priv)
+{
+ PGP_Context *ctx = priv;
+
+ if (ctx->use_mdcbuf_filter)
+ return;
+ px_md_free(ctx->mdc_ctx);
+ ctx->mdc_ctx = NULL;
+}
+
+static int
+mdc_finish(PGP_Context *ctx, PullFilter *src, int len)
+{
+ int res;
+ uint8 hash[20];
+ uint8 tmpbuf[20];
+ uint8 *data;
+
+ /* should not happen */
+ if (ctx->use_mdcbuf_filter)
+ return PXE_BUG;
+
+ /* It's SHA1 */
+ if (len != 20)
+ return PXE_PGP_CORRUPT_DATA;
+
+ /* mdc_read should not call px_md_update */
+ ctx->in_mdc_pkt = 1;
+
+ /* read data */
+ res = pullf_read_max(src, len, &data, tmpbuf);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ {
+ px_debug("no mdc");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ /* is the packet sane? */
+ if (res != 20)
+ {
+ px_debug("mdc_finish: read failed, res=%d", res);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ /*
+ * ok, we got the hash, now check
+ */
+ px_md_finish(ctx->mdc_ctx, hash);
+ res = memcmp(hash, data, 20);
+ px_memset(hash, 0, 20);
+ px_memset(tmpbuf, 0, sizeof(tmpbuf));
+ if (res != 0)
+ {
+ px_debug("mdc_finish: mdc failed");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ ctx->mdc_checked = 1;
+ return 0;
+}
+
+static int
+mdc_read(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ int res;
+ PGP_Context *ctx = priv;
+
+ /* skip this filter? */
+ if (ctx->use_mdcbuf_filter || ctx->in_mdc_pkt)
+ return pullf_read(src, len, data_p);
+
+ res = pullf_read(src, len, data_p);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ {
+ px_debug("mdc_read: unexpected eof");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ px_md_update(ctx->mdc_ctx, *data_p, res);
+
+ return res;
+}
+
+static struct PullFilterOps mdc_filter = {
+ mdc_init, mdc_read, mdc_free
+};
+
+
+/*
+ * Combined Pkt reader and MDC hasher.
+ *
+ * For the case of SYMENCRYPTED_DATA_MDC packet, where
+ * the data part has 'context length', which means
+ * that data packet ends 22 bytes before end of parent
+ * packet, which is silly.
+ */
+#define MDCBUF_LEN 8192
+struct MDCBufData
+{
+ PGP_Context *ctx;
+ int eof;
+ int buflen;
+ int avail;
+ uint8 *pos;
+ int mdc_avail;
+ uint8 mdc_buf[22];
+ uint8 buf[MDCBUF_LEN];
+};
+
+static int
+mdcbuf_init(void **priv_p, void *arg, PullFilter *src)
+{
+ PGP_Context *ctx = arg;
+ struct MDCBufData *st;
+
+ st = palloc0(sizeof(*st));
+ st->buflen = sizeof(st->buf);
+ st->ctx = ctx;
+ *priv_p = st;
+
+ /* take over the work of mdc_filter */
+ ctx->use_mdcbuf_filter = 1;
+
+ return 0;
+}
+
+static int
+mdcbuf_finish(struct MDCBufData *st)
+{
+ uint8 hash[20];
+ int res;
+
+ st->eof = 1;
+
+ if (st->mdc_buf[0] != 0xD3 || st->mdc_buf[1] != 0x14)
+ {
+ px_debug("mdcbuf_finish: bad MDC pkt hdr");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ px_md_update(st->ctx->mdc_ctx, st->mdc_buf, 2);
+ px_md_finish(st->ctx->mdc_ctx, hash);
+ res = memcmp(hash, st->mdc_buf + 2, 20);
+ px_memset(hash, 0, 20);
+ if (res)
+ {
+ px_debug("mdcbuf_finish: MDC does not match");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ return res;
+}
+
+static void
+mdcbuf_load_data(struct MDCBufData *st, uint8 *src, int len)
+{
+ uint8 *dst = st->pos + st->avail;
+
+ memcpy(dst, src, len);
+ px_md_update(st->ctx->mdc_ctx, src, len);
+ st->avail += len;
+}
+
+static void
+mdcbuf_load_mdc(struct MDCBufData *st, uint8 *src, int len)
+{
+ memmove(st->mdc_buf + st->mdc_avail, src, len);
+ st->mdc_avail += len;
+}
+
+static int
+mdcbuf_refill(struct MDCBufData *st, PullFilter *src)
+{
+ uint8 *data;
+ int res;
+ int need;
+
+ /* put avail data in start */
+ if (st->avail > 0 && st->pos != st->buf)
+ memmove(st->buf, st->pos, st->avail);
+ st->pos = st->buf;
+
+ /* read new data */
+ need = st->buflen + 22 - st->avail - st->mdc_avail;
+ res = pullf_read(src, need, &data);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ return mdcbuf_finish(st);
+
+ /* add to buffer */
+ if (res >= 22)
+ {
+ mdcbuf_load_data(st, st->mdc_buf, st->mdc_avail);
+ st->mdc_avail = 0;
+
+ mdcbuf_load_data(st, data, res - 22);
+ mdcbuf_load_mdc(st, data + res - 22, 22);
+ }
+ else
+ {
+ int canmove = st->mdc_avail + res - 22;
+
+ if (canmove > 0)
+ {
+ mdcbuf_load_data(st, st->mdc_buf, canmove);
+ st->mdc_avail -= canmove;
+ memmove(st->mdc_buf, st->mdc_buf + canmove, st->mdc_avail);
+ }
+ mdcbuf_load_mdc(st, data, res);
+ }
+ return 0;
+}
+
+static int
+mdcbuf_read(void *priv, PullFilter *src, int len,
+ uint8 **data_p, uint8 *buf, int buflen)
+{
+ struct MDCBufData *st = priv;
+ int res;
+
+ if (!st->eof && len > st->avail)
+ {
+ res = mdcbuf_refill(st, src);
+ if (res < 0)
+ return res;
+ }
+
+ if (len > st->avail)
+ len = st->avail;
+
+ *data_p = st->pos;
+ st->pos += len;
+ st->avail -= len;
+ return len;
+}
+
+static void
+mdcbuf_free(void *priv)
+{
+ struct MDCBufData *st = priv;
+
+ px_md_free(st->ctx->mdc_ctx);
+ st->ctx->mdc_ctx = NULL;
+ px_memset(st, 0, sizeof(*st));
+ pfree(st);
+}
+
+static struct PullFilterOps mdcbuf_filter = {
+ mdcbuf_init, mdcbuf_read, mdcbuf_free
+};
+
+
+/*
+ * Decrypt separate session key
+ */
+static int
+decrypt_key(PGP_Context *ctx, const uint8 *src, int len)
+{
+ int res;
+ uint8 algo;
+ PGP_CFB *cfb;
+
+ res = pgp_cfb_create(&cfb, ctx->s2k_cipher_algo,
+ ctx->s2k.key, ctx->s2k.key_len, 0, NULL);
+ if (res < 0)
+ return res;
+
+ pgp_cfb_decrypt(cfb, src, 1, &algo);
+ src++;
+ len--;
+
+ pgp_cfb_decrypt(cfb, src, len, ctx->sess_key);
+ pgp_cfb_free(cfb);
+ ctx->sess_key_len = len;
+ ctx->cipher_algo = algo;
+
+ if (pgp_get_cipher_key_size(algo) != len)
+ {
+ px_debug("sesskey bad len: algo=%d, expected=%d, got=%d",
+ algo, pgp_get_cipher_key_size(algo), len);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ return 0;
+}
+
+/*
+ * Handle key packet
+ */
+static int
+parse_symenc_sesskey(PGP_Context *ctx, PullFilter *src)
+{
+ uint8 *p;
+ int res;
+ uint8 tmpbuf[PGP_MAX_KEY + 2];
+ uint8 ver;
+
+ GETBYTE(src, ver);
+ GETBYTE(src, ctx->s2k_cipher_algo);
+ if (ver != 4)
+ {
+ px_debug("bad key pkt ver");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ /*
+ * read S2K info
+ */
+ res = pgp_s2k_read(src, &ctx->s2k);
+ 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;
+
+ /*
+ * generate key from password
+ */
+ res = pgp_s2k_process(&ctx->s2k, ctx->s2k_cipher_algo,
+ ctx->sym_key, ctx->sym_key_len);
+ if (res < 0)
+ return res;
+
+ /*
+ * do we have separate session key?
+ */
+ res = pullf_read_max(src, PGP_MAX_KEY + 2, &p, tmpbuf);
+ if (res < 0)
+ return res;
+
+ if (res == 0)
+ {
+ /*
+ * no, s2k key is session key
+ */
+ memcpy(ctx->sess_key, ctx->s2k.key, ctx->s2k.key_len);
+ ctx->sess_key_len = ctx->s2k.key_len;
+ ctx->cipher_algo = ctx->s2k_cipher_algo;
+ res = 0;
+ ctx->use_sess_key = 0;
+ }
+ else
+ {
+ /*
+ * yes, decrypt it
+ */
+ if (res < 17 || res > PGP_MAX_KEY + 1)
+ {
+ px_debug("expect key, but bad data");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ ctx->use_sess_key = 1;
+ res = decrypt_key(ctx, p, res);
+ }
+
+ px_memset(tmpbuf, 0, sizeof(tmpbuf));
+ return res;
+}
+
+static int
+copy_crlf(MBuf *dst, uint8 *data, int len, int *got_cr)
+{
+ uint8 *data_end = data + len;
+ uint8 tmpbuf[1024];
+ uint8 *tmp_end = tmpbuf + sizeof(tmpbuf);
+ uint8 *p;
+ int res;
+
+ p = tmpbuf;
+ if (*got_cr)
+ {
+ if (*data != '\n')
+ *p++ = '\r';
+ *got_cr = 0;
+ }
+ while (data < data_end)
+ {
+ if (*data == '\r')
+ {
+ if (data + 1 < data_end)
+ {
+ if (*(data + 1) == '\n')
+ data++;
+ }
+ else
+ {
+ *got_cr = 1;
+ break;
+ }
+ }
+ *p++ = *data++;
+ if (p >= tmp_end)
+ {
+ res = mbuf_append(dst, tmpbuf, p - tmpbuf);
+ if (res < 0)
+ return res;
+ p = tmpbuf;
+ }
+ }
+ if (p - tmpbuf > 0)
+ {
+ res = mbuf_append(dst, tmpbuf, p - tmpbuf);
+ if (res < 0)
+ return res;
+ }
+ px_memset(tmpbuf, 0, sizeof(tmpbuf));
+ return 0;
+}
+
+static int
+parse_literal_data(PGP_Context *ctx, MBuf *dst, PullFilter *pkt)
+{
+ int type;
+ int name_len;
+ int res;
+ uint8 *buf;
+ uint8 tmpbuf[4];
+ int got_cr = 0;
+
+ GETBYTE(pkt, type);
+ GETBYTE(pkt, name_len);
+
+ /* skip name */
+ while (name_len > 0)
+ {
+ res = pullf_read(pkt, name_len, &buf);
+ if (res < 0)
+ return res;
+ if (res == 0)
+ break;
+ name_len -= res;
+ }
+ if (name_len > 0)
+ {
+ px_debug("parse_literal_data: unexpected eof");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ /* skip date */
+ res = pullf_read_max(pkt, 4, &buf, tmpbuf);
+ if (res != 4)
+ {
+ px_debug("parse_literal_data: unexpected eof");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ px_memset(tmpbuf, 0, 4);
+
+ /*
+ * If called from an SQL function that returns text, pgp_decrypt() rejects
+ * inputs not self-identifying as text.
+ */
+ if (ctx->text_mode)
+ if (type != 't' && type != 'u')
+ {
+ px_debug("parse_literal_data: data type=%c", type);
+ ctx->unexpected_binary = true;
+ }
+
+ ctx->unicode_mode = (type == 'u') ? 1 : 0;
+
+ /* read data */
+ while (1)
+ {
+ res = pullf_read(pkt, 32 * 1024, &buf);
+ if (res <= 0)
+ break;
+
+ if (ctx->text_mode && ctx->convert_crlf)
+ res = copy_crlf(dst, buf, res, &got_cr);
+ else
+ res = mbuf_append(dst, buf, res);
+ if (res < 0)
+ break;
+ }
+ if (res >= 0 && got_cr)
+ res = mbuf_append(dst, (const uint8 *) "\r", 1);
+ return res;
+}
+
+/* process_data_packets and parse_compressed_data call each other */
+static int process_data_packets(PGP_Context *ctx, MBuf *dst,
+ PullFilter *src, int allow_compr, int need_mdc);
+
+static int
+parse_compressed_data(PGP_Context *ctx, MBuf *dst, PullFilter *pkt)
+{
+ int res;
+ uint8 type;
+ PullFilter *pf_decompr;
+ uint8 *discard_buf;
+
+ GETBYTE(pkt, type);
+
+ ctx->compress_algo = type;
+ switch (type)
+ {
+ case PGP_COMPR_NONE:
+ res = process_data_packets(ctx, dst, pkt, NO_COMPR, NO_MDC);
+ break;
+
+ case PGP_COMPR_ZIP:
+ case PGP_COMPR_ZLIB:
+ res = pgp_decompress_filter(&pf_decompr, ctx, pkt);
+ if (res >= 0)
+ {
+ res = process_data_packets(ctx, dst, pf_decompr,
+ NO_COMPR, NO_MDC);
+ pullf_free(pf_decompr);
+ }
+ break;
+
+ case PGP_COMPR_BZIP2:
+ px_debug("parse_compressed_data: bzip2 unsupported");
+ /* report error in pgp_decrypt() */
+ ctx->unsupported_compr = 1;
+
+ /*
+ * Discard the compressed data, allowing it to first affect any
+ * MDC digest computation.
+ */
+ while (1)
+ {
+ res = pullf_read(pkt, 32 * 1024, &discard_buf);
+ if (res <= 0)
+ break;
+ }
+
+ break;
+
+ default:
+ px_debug("parse_compressed_data: unknown compr type");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+
+ return res;
+}
+
+static int
+process_data_packets(PGP_Context *ctx, MBuf *dst, PullFilter *src,
+ int allow_compr, int need_mdc)
+{
+ uint8 tag;
+ int len,
+ res;
+ int got_data = 0;
+ int got_mdc = 0;
+ PullFilter *pkt = NULL;
+
+ while (1)
+ {
+ res = pgp_parse_pkt_hdr(src, &tag, &len, ALLOW_CTX_SIZE);
+ if (res <= 0)
+ break;
+
+
+ /* mdc packet should be last */
+ if (got_mdc)
+ {
+ px_debug("process_data_packets: data after mdc");
+ res = PXE_PGP_CORRUPT_DATA;
+ break;
+ }
+
+ /*
+ * Context length inside SYMENCRYPTED_DATA_MDC packet needs special
+ * handling.
+ */
+ if (need_mdc && res == PKT_CONTEXT)
+ res = pullf_create(&pkt, &mdcbuf_filter, ctx, src);
+ else
+ res = pgp_create_pkt_reader(&pkt, src, len, res, ctx);
+ if (res < 0)
+ break;
+
+ switch (tag)
+ {
+ case PGP_PKT_LITERAL_DATA:
+ got_data = 1;
+ res = parse_literal_data(ctx, dst, pkt);
+ break;
+ case PGP_PKT_COMPRESSED_DATA:
+ if (allow_compr == 0)
+ {
+ px_debug("process_data_packets: unexpected compression");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ else if (got_data)
+ {
+ /*
+ * compr data must be alone
+ */
+ px_debug("process_data_packets: only one cmpr pkt allowed");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ else
+ {
+ got_data = 1;
+ res = parse_compressed_data(ctx, dst, pkt);
+ }
+ break;
+ case PGP_PKT_MDC:
+ if (need_mdc == NO_MDC)
+ {
+ px_debug("process_data_packets: unexpected MDC");
+ res = PXE_PGP_CORRUPT_DATA;
+ break;
+ }
+
+ res = mdc_finish(ctx, pkt, len);
+ if (res >= 0)
+ got_mdc = 1;
+ break;
+ default:
+ px_debug("process_data_packets: unexpected pkt tag=%d", tag);
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+
+ pullf_free(pkt);
+ pkt = NULL;
+
+ if (res < 0)
+ break;
+ }
+
+ if (pkt)
+ pullf_free(pkt);
+
+ if (res < 0)
+ return res;
+
+ if (!got_data)
+ {
+ px_debug("process_data_packets: no data");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ if (need_mdc && !got_mdc && !ctx->use_mdcbuf_filter)
+ {
+ px_debug("process_data_packets: got no mdc");
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+ return res;
+}
+
+static int
+parse_symenc_data(PGP_Context *ctx, PullFilter *pkt, MBuf *dst)
+{
+ int res;
+ PGP_CFB *cfb = NULL;
+ PullFilter *pf_decrypt = NULL;
+ PullFilter *pf_prefix = NULL;
+
+ res = pgp_cfb_create(&cfb, ctx->cipher_algo,
+ ctx->sess_key, ctx->sess_key_len, 1, NULL);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_decrypt, &pgp_decrypt_filter, cfb, pkt);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_prefix, &prefix_filter, ctx, pf_decrypt);
+ if (res < 0)
+ goto out;
+
+ res = process_data_packets(ctx, dst, pf_prefix, ALLOW_COMPR, NO_MDC);
+
+out:
+ if (pf_prefix)
+ pullf_free(pf_prefix);
+ if (pf_decrypt)
+ pullf_free(pf_decrypt);
+ if (cfb)
+ pgp_cfb_free(cfb);
+
+ return res;
+}
+
+static int
+parse_symenc_mdc_data(PGP_Context *ctx, PullFilter *pkt, MBuf *dst)
+{
+ int res;
+ PGP_CFB *cfb = NULL;
+ PullFilter *pf_decrypt = NULL;
+ PullFilter *pf_prefix = NULL;
+ PullFilter *pf_mdc = NULL;
+ uint8 ver;
+
+ GETBYTE(pkt, ver);
+ if (ver != 1)
+ {
+ px_debug("parse_symenc_mdc_data: pkt ver != 1");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ res = pgp_cfb_create(&cfb, ctx->cipher_algo,
+ ctx->sess_key, ctx->sess_key_len, 0, NULL);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_decrypt, &pgp_decrypt_filter, cfb, pkt);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_mdc, &mdc_filter, ctx, pf_decrypt);
+ if (res < 0)
+ goto out;
+
+ res = pullf_create(&pf_prefix, &prefix_filter, ctx, pf_mdc);
+ if (res < 0)
+ goto out;
+
+ res = process_data_packets(ctx, dst, pf_prefix, ALLOW_COMPR, NEED_MDC);
+
+out:
+ if (pf_prefix)
+ pullf_free(pf_prefix);
+ if (pf_mdc)
+ pullf_free(pf_mdc);
+ if (pf_decrypt)
+ pullf_free(pf_decrypt);
+ if (cfb)
+ pgp_cfb_free(cfb);
+
+ return res;
+}
+
+/*
+ * skip over packet contents
+ */
+int
+pgp_skip_packet(PullFilter *pkt)
+{
+ int res = 1;
+ uint8 *tmp;
+
+ while (res > 0)
+ res = pullf_read(pkt, 32 * 1024, &tmp);
+ return res;
+}
+
+/*
+ * expect to be at packet end, any data is error
+ */
+int
+pgp_expect_packet_end(PullFilter *pkt)
+{
+ int res;
+ uint8 *tmp;
+
+ res = pullf_read(pkt, 32 * 1024, &tmp);
+ if (res > 0)
+ {
+ px_debug("pgp_expect_packet_end: got data");
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ return res;
+}
+
+int
+pgp_decrypt(PGP_Context *ctx, MBuf *msrc, MBuf *mdst)
+{
+ int res;
+ PullFilter *src = NULL;
+ PullFilter *pkt = NULL;
+ uint8 tag;
+ int len;
+ int got_key = 0;
+ int got_data = 0;
+
+ res = pullf_create_mbuf_reader(&src, msrc);
+
+ while (res >= 0)
+ {
+ res = pgp_parse_pkt_hdr(src, &tag, &len, NO_CTX_SIZE);
+ if (res <= 0)
+ break;
+
+ res = pgp_create_pkt_reader(&pkt, src, len, res, ctx);
+ if (res < 0)
+ break;
+
+ res = PXE_PGP_CORRUPT_DATA;
+ switch (tag)
+ {
+ case PGP_PKT_MARKER:
+ res = pgp_skip_packet(pkt);
+ break;
+ case PGP_PKT_PUBENCRYPTED_SESSKEY:
+ /* fixme: skip those */
+ res = pgp_parse_pubenc_sesskey(ctx, pkt);
+ got_key = 1;
+ break;
+ case PGP_PKT_SYMENCRYPTED_SESSKEY:
+ if (got_key)
+
+ /*
+ * Theoretically, there could be several keys, both public
+ * and symmetric, all of which encrypt same session key.
+ * Decrypt should try with each one, before failing.
+ */
+ px_debug("pgp_decrypt: using first of several keys");
+ else
+ {
+ got_key = 1;
+ res = parse_symenc_sesskey(ctx, pkt);
+ }
+ break;
+ case PGP_PKT_SYMENCRYPTED_DATA:
+ if (!got_key)
+ px_debug("pgp_decrypt: have data but no key");
+ else if (got_data)
+ px_debug("pgp_decrypt: got second data packet");
+ else
+ {
+ got_data = 1;
+ ctx->disable_mdc = 1;
+ res = parse_symenc_data(ctx, pkt, mdst);
+ }
+ break;
+ case PGP_PKT_SYMENCRYPTED_DATA_MDC:
+ if (!got_key)
+ px_debug("pgp_decrypt: have data but no key");
+ else if (got_data)
+ px_debug("pgp_decrypt: several data pkts not supported");
+ else
+ {
+ got_data = 1;
+ ctx->disable_mdc = 0;
+ res = parse_symenc_mdc_data(ctx, pkt, mdst);
+ }
+ break;
+ default:
+ px_debug("pgp_decrypt: unknown tag: 0x%02x", tag);
+ }
+ pullf_free(pkt);
+ pkt = NULL;
+ }
+
+ if (pkt)
+ pullf_free(pkt);
+
+ if (src)
+ pullf_free(src);
+
+ if (res < 0)
+ return res;
+
+ /*
+ * Report a failure of the prefix_init() "quick check" now, rather than
+ * upon detection, to hinder timing attacks. pgcrypto is not generally
+ * secure against timing attacks, but this helps.
+ */
+ if (!got_data || ctx->corrupt_prefix)
+ return PXE_PGP_CORRUPT_DATA;
+
+ /*
+ * Code interpreting purportedly-decrypted data prior to this stage shall
+ * report no error other than PXE_PGP_CORRUPT_DATA. (PXE_BUG is okay so
+ * long as it remains unreachable.) This ensures that an attacker able to
+ * choose a ciphertext and receive a corresponding decryption error
+ * message cannot use that oracle to gather clues about the decryption
+ * key. See "An Attack on CFB Mode Encryption As Used By OpenPGP" by
+ * Serge Mister and Robert Zuccherato.
+ *
+ * A problematic value in the first octet of a Literal Data or Compressed
+ * Data packet may indicate a simple user error, such as the need to call
+ * pgp_sym_decrypt_bytea instead of pgp_sym_decrypt. Occasionally,
+ * though, it is the first symptom of the encryption key not matching the
+ * decryption key. When this was the only problem encountered, report a
+ * specific error to guide the user; otherwise, we will have reported
+ * PXE_PGP_CORRUPT_DATA before now. A key mismatch makes the other errors
+ * into red herrings, and this avoids leaking clues to attackers.
+ */
+ if (ctx->unsupported_compr)
+ return PXE_PGP_UNSUPPORTED_COMPR;
+ if (ctx->unexpected_binary)
+ return PXE_PGP_NOT_TEXT;
+
+ return res;
+}
diff --git a/contrib/pgcrypto/pgp-encrypt.c b/contrib/pgcrypto/pgp-encrypt.c
new file mode 100644
index 0000000..f7467c9
--- /dev/null
+++ b/contrib/pgcrypto/pgp-encrypt.c
@@ -0,0 +1,704 @@
+/*
+ * pgp-encrypt.c
+ * OpenPGP encrypt.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-encrypt.c
+ */
+
+#include "postgres.h"
+
+#include <time.h>
+
+#include "mbuf.h"
+#include "pgp.h"
+#include "px.h"
+
+#define MDC_DIGEST_LEN 20
+#define STREAM_ID 0xE0
+#define STREAM_BLOCK_SHIFT 14
+
+static uint8 *
+render_newlen(uint8 *h, int len)
+{
+ if (len <= 191)
+ {
+ *h++ = len & 255;
+ }
+ else if (len > 191 && len <= 8383)
+ {
+ *h++ = ((len - 192) >> 8) + 192;
+ *h++ = (len - 192) & 255;
+ }
+ else
+ {
+ *h++ = 255;
+ *h++ = (len >> 24) & 255;
+ *h++ = (len >> 16) & 255;
+ *h++ = (len >> 8) & 255;
+ *h++ = len & 255;
+ }
+ return h;
+}
+
+static int
+write_tag_only(PushFilter *dst, int tag)
+{
+ uint8 hdr = 0xC0 | tag;
+
+ return pushf_write(dst, &hdr, 1);
+}
+
+static int
+write_normal_header(PushFilter *dst, int tag, int len)
+{
+ uint8 hdr[8];
+ uint8 *h = hdr;
+
+ *h++ = 0xC0 | tag;
+ h = render_newlen(h, len);
+ return pushf_write(dst, hdr, h - hdr);
+}
+
+
+/*
+ * MAC writer
+ */
+
+static int
+mdc_init(PushFilter *dst, void *init_arg, void **priv_p)
+{
+ int res;
+ PX_MD *md;
+
+ res = pgp_load_digest(PGP_DIGEST_SHA1, &md);
+ if (res < 0)
+ return res;
+
+ *priv_p = md;
+ return 0;
+}
+
+static int
+mdc_write(PushFilter *dst, void *priv, const uint8 *data, int len)
+{
+ PX_MD *md = priv;
+
+ px_md_update(md, data, len);
+ return pushf_write(dst, data, len);
+}
+
+static int
+mdc_flush(PushFilter *dst, void *priv)
+{
+ int res;
+ uint8 pkt[2 + MDC_DIGEST_LEN];
+ PX_MD *md = priv;
+
+ /*
+ * create mdc pkt
+ */
+ pkt[0] = 0xD3;
+ pkt[1] = 0x14; /* MDC_DIGEST_LEN */
+ px_md_update(md, pkt, 2);
+ px_md_finish(md, pkt + 2);
+
+ res = pushf_write(dst, pkt, 2 + MDC_DIGEST_LEN);
+ px_memset(pkt, 0, 2 + MDC_DIGEST_LEN);
+ return res;
+}
+
+static void
+mdc_free(void *priv)
+{
+ PX_MD *md = priv;
+
+ px_md_free(md);
+}
+
+static const PushFilterOps mdc_filter = {
+ mdc_init, mdc_write, mdc_flush, mdc_free
+};
+
+
+/*
+ * Encrypted pkt writer
+ */
+#define ENCBUF 8192
+struct EncStat
+{
+ PGP_CFB *ciph;
+ uint8 buf[ENCBUF];
+};
+
+static int
+encrypt_init(PushFilter *next, void *init_arg, void **priv_p)
+{
+ struct EncStat *st;
+ PGP_Context *ctx = init_arg;
+ PGP_CFB *ciph;
+ int resync = 1;
+ int res;
+
+ /* should we use newer packet format? */
+ if (ctx->disable_mdc == 0)
+ {
+ uint8 ver = 1;
+
+ resync = 0;
+ res = pushf_write(next, &ver, 1);
+ if (res < 0)
+ return res;
+ }
+ res = pgp_cfb_create(&ciph, ctx->cipher_algo,
+ ctx->sess_key, ctx->sess_key_len, resync, NULL);
+ if (res < 0)
+ return res;
+
+ st = palloc0(sizeof(*st));
+ st->ciph = ciph;
+
+ *priv_p = st;
+ return ENCBUF;
+}
+
+static int
+encrypt_process(PushFilter *next, void *priv, const uint8 *data, int len)
+{
+ int res;
+ struct EncStat *st = priv;
+ int avail = len;
+
+ while (avail > 0)
+ {
+ int tmplen = avail > ENCBUF ? ENCBUF : avail;
+
+ res = pgp_cfb_encrypt(st->ciph, data, tmplen, st->buf);
+ if (res < 0)
+ return res;
+
+ res = pushf_write(next, st->buf, tmplen);
+ if (res < 0)
+ return res;
+
+ data += tmplen;
+ avail -= tmplen;
+ }
+ return 0;
+}
+
+static void
+encrypt_free(void *priv)
+{
+ struct EncStat *st = priv;
+
+ if (st->ciph)
+ pgp_cfb_free(st->ciph);
+ px_memset(st, 0, sizeof(*st));
+ pfree(st);
+}
+
+static const PushFilterOps encrypt_filter = {
+ encrypt_init, encrypt_process, NULL, encrypt_free
+};
+
+/*
+ * Write Streamable pkts
+ */
+
+struct PktStreamStat
+{
+ int final_done;
+ int pkt_block;
+};
+
+static int
+pkt_stream_init(PushFilter *next, void *init_arg, void **priv_p)
+{
+ struct PktStreamStat *st;
+
+ st = palloc(sizeof(*st));
+ st->final_done = 0;
+ st->pkt_block = 1 << STREAM_BLOCK_SHIFT;
+ *priv_p = st;
+
+ return st->pkt_block;
+}
+
+static int
+pkt_stream_process(PushFilter *next, void *priv, const uint8 *data, int len)
+{
+ int res;
+ uint8 hdr[8];
+ uint8 *h = hdr;
+ struct PktStreamStat *st = priv;
+
+ if (st->final_done)
+ return PXE_BUG;
+
+ if (len == st->pkt_block)
+ *h++ = STREAM_ID | STREAM_BLOCK_SHIFT;
+ else
+ {
+ h = render_newlen(h, len);
+ st->final_done = 1;
+ }
+
+ res = pushf_write(next, hdr, h - hdr);
+ if (res < 0)
+ return res;
+
+ return pushf_write(next, data, len);
+}
+
+static int
+pkt_stream_flush(PushFilter *next, void *priv)
+{
+ int res;
+ uint8 hdr[8];
+ uint8 *h = hdr;
+ struct PktStreamStat *st = priv;
+
+ /* stream MUST end with normal packet. */
+ if (!st->final_done)
+ {
+ h = render_newlen(h, 0);
+ res = pushf_write(next, hdr, h - hdr);
+ if (res < 0)
+ return res;
+ st->final_done = 1;
+ }
+ return 0;
+}
+
+static void
+pkt_stream_free(void *priv)
+{
+ struct PktStreamStat *st = priv;
+
+ px_memset(st, 0, sizeof(*st));
+ pfree(st);
+}
+
+static const PushFilterOps pkt_stream_filter = {
+ pkt_stream_init, pkt_stream_process, pkt_stream_flush, pkt_stream_free
+};
+
+int
+pgp_create_pkt_writer(PushFilter *dst, int tag, PushFilter **res_p)
+{
+ int res;
+
+ res = write_tag_only(dst, tag);
+ if (res < 0)
+ return res;
+
+ return pushf_create(res_p, &pkt_stream_filter, NULL, dst);
+}
+
+/*
+ * Text conversion filter
+ */
+
+static int
+crlf_process(PushFilter *dst, void *priv, const uint8 *data, int len)
+{
+ const uint8 *data_end = data + len;
+ const uint8 *p2,
+ *p1 = data;
+ int line_len;
+ static const uint8 crlf[] = {'\r', '\n'};
+ int res = 0;
+
+ while (p1 < data_end)
+ {
+ p2 = memchr(p1, '\n', data_end - p1);
+ if (p2 == NULL)
+ p2 = data_end;
+
+ line_len = p2 - p1;
+
+ /* write data */
+ res = 0;
+ if (line_len > 0)
+ {
+ res = pushf_write(dst, p1, line_len);
+ if (res < 0)
+ break;
+ p1 += line_len;
+ }
+
+ /* write crlf */
+ while (p1 < data_end && *p1 == '\n')
+ {
+ res = pushf_write(dst, crlf, 2);
+ if (res < 0)
+ break;
+ p1++;
+ }
+ }
+ return res;
+}
+
+static const PushFilterOps crlf_filter = {
+ NULL, crlf_process, NULL, NULL
+};
+
+/*
+ * Initialize literal data packet
+ */
+static int
+init_litdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
+{
+ int res;
+ int hdrlen;
+ uint8 hdr[6];
+ uint32 t;
+ PushFilter *pkt;
+ int type;
+
+ /*
+ * Create header
+ */
+
+ if (ctx->text_mode)
+ type = ctx->unicode_mode ? 'u' : 't';
+ else
+ type = 'b';
+
+ /*
+ * Store the creation time into packet. The goal is to have as few known
+ * bytes as possible.
+ */
+ t = (uint32) time(NULL);
+
+ hdr[0] = type;
+ hdr[1] = 0;
+ hdr[2] = (t >> 24) & 255;
+ hdr[3] = (t >> 16) & 255;
+ hdr[4] = (t >> 8) & 255;
+ hdr[5] = t & 255;
+ hdrlen = 6;
+
+ res = write_tag_only(dst, PGP_PKT_LITERAL_DATA);
+ if (res < 0)
+ return res;
+
+ res = pushf_create(&pkt, &pkt_stream_filter, ctx, dst);
+ if (res < 0)
+ return res;
+
+ res = pushf_write(pkt, hdr, hdrlen);
+ if (res < 0)
+ {
+ pushf_free(pkt);
+ return res;
+ }
+
+ *pf_res = pkt;
+ return 0;
+}
+
+/*
+ * Initialize compression filter
+ */
+static int
+init_compress(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
+{
+ int res;
+ uint8 type = ctx->compress_algo;
+ PushFilter *pkt;
+
+ res = write_tag_only(dst, PGP_PKT_COMPRESSED_DATA);
+ if (res < 0)
+ return res;
+
+ res = pushf_create(&pkt, &pkt_stream_filter, ctx, dst);
+ if (res < 0)
+ return res;
+
+ res = pushf_write(pkt, &type, 1);
+ if (res >= 0)
+ res = pgp_compress_filter(pf_res, ctx, pkt);
+
+ if (res < 0)
+ pushf_free(pkt);
+
+ return res;
+}
+
+/*
+ * Initialize encdata packet
+ */
+static int
+init_encdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
+{
+ int res;
+ int tag;
+
+ if (ctx->disable_mdc)
+ tag = PGP_PKT_SYMENCRYPTED_DATA;
+ else
+ tag = PGP_PKT_SYMENCRYPTED_DATA_MDC;
+
+ res = write_tag_only(dst, tag);
+ if (res < 0)
+ return res;
+
+ return pushf_create(pf_res, &pkt_stream_filter, ctx, dst);
+}
+
+/*
+ * write prefix
+ */
+static int
+write_prefix(PGP_Context *ctx, PushFilter *dst)
+{
+ uint8 prefix[PGP_MAX_BLOCK + 2];
+ int res,
+ bs;
+
+ bs = pgp_get_cipher_block_size(ctx->cipher_algo);
+ if (!pg_strong_random(prefix, bs))
+ return PXE_NO_RANDOM;
+
+ prefix[bs + 0] = prefix[bs - 2];
+ prefix[bs + 1] = prefix[bs - 1];
+
+ res = pushf_write(dst, prefix, bs + 2);
+ px_memset(prefix, 0, bs + 2);
+ return res < 0 ? res : 0;
+}
+
+/*
+ * write symmetrically encrypted session key packet
+ */
+
+static int
+symencrypt_sesskey(PGP_Context *ctx, uint8 *dst)
+{
+ int res;
+ PGP_CFB *cfb;
+ uint8 algo = ctx->cipher_algo;
+
+ res = pgp_cfb_create(&cfb, ctx->s2k_cipher_algo,
+ ctx->s2k.key, ctx->s2k.key_len, 0, NULL);
+ if (res < 0)
+ return res;
+
+ pgp_cfb_encrypt(cfb, &algo, 1, dst);
+ pgp_cfb_encrypt(cfb, ctx->sess_key, ctx->sess_key_len, dst + 1);
+
+ pgp_cfb_free(cfb);
+ return ctx->sess_key_len + 1;
+}
+
+/* 5.3: Symmetric-Key Encrypted Session-Key */
+static int
+write_symenc_sesskey(PGP_Context *ctx, PushFilter *dst)
+{
+ uint8 pkt[256];
+ int pktlen;
+ int res;
+ uint8 *p = pkt;
+
+ *p++ = 4; /* 5.3 - version number */
+ *p++ = ctx->s2k_cipher_algo;
+
+ *p++ = ctx->s2k.mode;
+ *p++ = ctx->s2k.digest_algo;
+ if (ctx->s2k.mode > 0)
+ {
+ memcpy(p, ctx->s2k.salt, 8);
+ p += 8;
+ }
+ if (ctx->s2k.mode == 3)
+ *p++ = ctx->s2k.iter;
+
+ if (ctx->use_sess_key)
+ {
+ res = symencrypt_sesskey(ctx, p);
+ if (res < 0)
+ return res;
+ p += res;
+ }
+
+ pktlen = p - pkt;
+ res = write_normal_header(dst, PGP_PKT_SYMENCRYPTED_SESSKEY, pktlen);
+ if (res >= 0)
+ res = pushf_write(dst, pkt, pktlen);
+
+ px_memset(pkt, 0, pktlen);
+ return res;
+}
+
+/*
+ * key setup
+ */
+static int
+init_s2k_key(PGP_Context *ctx)
+{
+ int res;
+
+ 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;
+
+ return pgp_s2k_process(&ctx->s2k, ctx->s2k_cipher_algo,
+ ctx->sym_key, ctx->sym_key_len);
+}
+
+static int
+init_sess_key(PGP_Context *ctx)
+{
+ if (ctx->use_sess_key || ctx->pub_key)
+ {
+ ctx->sess_key_len = pgp_get_cipher_key_size(ctx->cipher_algo);
+ if (!pg_strong_random(ctx->sess_key, ctx->sess_key_len))
+ return PXE_NO_RANDOM;
+ }
+ else
+ {
+ ctx->sess_key_len = ctx->s2k.key_len;
+ memcpy(ctx->sess_key, ctx->s2k.key, ctx->s2k.key_len);
+ }
+
+ return 0;
+}
+
+/*
+ * combine
+ */
+int
+pgp_encrypt(PGP_Context *ctx, MBuf *src, MBuf *dst)
+{
+ int res;
+ int len;
+ uint8 *buf;
+ PushFilter *pf,
+ *pf_tmp;
+
+ /*
+ * do we have any key
+ */
+ if (!ctx->sym_key && !ctx->pub_key)
+ return PXE_ARGUMENT_ERROR;
+
+ /* MBuf writer */
+ res = pushf_create_mbuf_writer(&pf, dst);
+ if (res < 0)
+ goto out;
+
+ /*
+ * initialize sym_key
+ */
+ if (ctx->sym_key)
+ {
+ res = init_s2k_key(ctx);
+ if (res < 0)
+ goto out;
+ }
+
+ res = init_sess_key(ctx);
+ if (res < 0)
+ goto out;
+
+ /*
+ * write keypkt
+ */
+ if (ctx->pub_key)
+ res = pgp_write_pubenc_sesskey(ctx, pf);
+ else
+ res = write_symenc_sesskey(ctx, pf);
+ if (res < 0)
+ goto out;
+
+ /* encrypted data pkt */
+ res = init_encdata_packet(&pf_tmp, ctx, pf);
+ if (res < 0)
+ goto out;
+ pf = pf_tmp;
+
+ /* encrypter */
+ res = pushf_create(&pf_tmp, &encrypt_filter, ctx, pf);
+ if (res < 0)
+ goto out;
+ pf = pf_tmp;
+
+ /* hasher */
+ if (ctx->disable_mdc == 0)
+ {
+ res = pushf_create(&pf_tmp, &mdc_filter, ctx, pf);
+ if (res < 0)
+ goto out;
+ pf = pf_tmp;
+ }
+
+ /* prefix */
+ res = write_prefix(ctx, pf);
+ if (res < 0)
+ goto out;
+
+ /* compressor */
+ if (ctx->compress_algo > 0 && ctx->compress_level > 0)
+ {
+ res = init_compress(&pf_tmp, ctx, pf);
+ if (res < 0)
+ goto out;
+ pf = pf_tmp;
+ }
+
+ /* data streamer */
+ res = init_litdata_packet(&pf_tmp, ctx, pf);
+ if (res < 0)
+ goto out;
+ pf = pf_tmp;
+
+
+ /* text conversion? */
+ if (ctx->text_mode && ctx->convert_crlf)
+ {
+ res = pushf_create(&pf_tmp, &crlf_filter, ctx, pf);
+ if (res < 0)
+ goto out;
+ pf = pf_tmp;
+ }
+
+ /*
+ * chain complete
+ */
+
+ len = mbuf_grab(src, mbuf_avail(src), &buf);
+ res = pushf_write(pf, buf, len);
+ if (res >= 0)
+ res = pushf_flush(pf);
+out:
+ pushf_free_all(pf);
+ return res;
+}
diff --git a/contrib/pgcrypto/pgp-info.c b/contrib/pgcrypto/pgp-info.c
new file mode 100644
index 0000000..83dc604
--- /dev/null
+++ b/contrib/pgcrypto/pgp-info.c
@@ -0,0 +1,235 @@
+/*
+ * pgp-info.c
+ * Provide info about PGP data.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-info.c
+ */
+#include "postgres.h"
+
+#include "mbuf.h"
+#include "pgp.h"
+#include "px.h"
+
+static int
+read_pubkey_keyid(PullFilter *pkt, uint8 *keyid_buf)
+{
+ int res;
+ PGP_PubKey *pk = NULL;
+
+ res = _pgp_read_public_key(pkt, &pk);
+ if (res < 0)
+ goto err;
+
+ /* skip secret key part, if it exists */
+ res = pgp_skip_packet(pkt);
+ if (res < 0)
+ goto err;
+
+ /* is it encryption key */
+ switch (pk->algo)
+ {
+ case PGP_PUB_ELG_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ memcpy(keyid_buf, pk->key_id, 8);
+ res = 1;
+ break;
+ default:
+ res = 0;
+ }
+
+err:
+ pgp_key_free(pk);
+ return res;
+}
+
+static int
+read_pubenc_keyid(PullFilter *pkt, uint8 *keyid_buf)
+{
+ uint8 ver;
+ int res;
+
+ GETBYTE(pkt, ver);
+ if (ver != 3)
+ return -1;
+
+ res = pullf_read_fixed(pkt, 8, keyid_buf);
+ if (res < 0)
+ return res;
+
+ return pgp_skip_packet(pkt);
+}
+
+static const char hextbl[] = "0123456789ABCDEF";
+
+static int
+print_key(uint8 *keyid, char *dst)
+{
+ int i;
+ unsigned c;
+
+ for (i = 0; i < 8; i++)
+ {
+ c = keyid[i];
+ *dst++ = hextbl[(c >> 4) & 0x0F];
+ *dst++ = hextbl[c & 0x0F];
+ }
+ *dst = 0;
+ return 8 * 2;
+}
+
+static const uint8 any_key[] =
+{0, 0, 0, 0, 0, 0, 0, 0};
+
+/*
+ * dst should have room for 17 bytes
+ */
+int
+pgp_get_keyid(MBuf *pgp_data, char *dst)
+{
+ int res;
+ PullFilter *src;
+ PullFilter *pkt = NULL;
+ int len;
+ uint8 tag;
+ int got_pub_key = 0,
+ got_symenc_key = 0,
+ got_pubenc_key = 0;
+ int got_data = 0;
+ uint8 keyid_buf[8];
+ int got_main_key = 0;
+
+
+ res = pullf_create_mbuf_reader(&src, pgp_data);
+ if (res < 0)
+ return res;
+
+ while (1)
+ {
+ res = pgp_parse_pkt_hdr(src, &tag, &len, 0);
+ if (res <= 0)
+ break;
+ res = pgp_create_pkt_reader(&pkt, src, len, res, NULL);
+ if (res < 0)
+ break;
+
+ switch (tag)
+ {
+ case PGP_PKT_SECRET_KEY:
+ case PGP_PKT_PUBLIC_KEY:
+ /* main key is for signing, so ignore it */
+ if (!got_main_key)
+ {
+ got_main_key = 1;
+ res = pgp_skip_packet(pkt);
+ }
+ else
+ res = PXE_PGP_MULTIPLE_KEYS;
+ break;
+ case PGP_PKT_SECRET_SUBKEY:
+ case PGP_PKT_PUBLIC_SUBKEY:
+ res = read_pubkey_keyid(pkt, keyid_buf);
+ if (res < 0)
+ break;
+ if (res > 0)
+ got_pub_key++;
+ break;
+ case PGP_PKT_PUBENCRYPTED_SESSKEY:
+ got_pubenc_key++;
+ res = read_pubenc_keyid(pkt, keyid_buf);
+ break;
+ case PGP_PKT_SYMENCRYPTED_DATA:
+ case PGP_PKT_SYMENCRYPTED_DATA_MDC:
+ /* don't skip it, just stop */
+ got_data = 1;
+ break;
+ case PGP_PKT_SYMENCRYPTED_SESSKEY:
+ got_symenc_key++;
+ /* fall through */
+ case PGP_PKT_SIGNATURE:
+ case PGP_PKT_MARKER:
+ case PGP_PKT_TRUST:
+ case PGP_PKT_USER_ID:
+ case PGP_PKT_USER_ATTR:
+ case PGP_PKT_PRIV_61:
+ res = pgp_skip_packet(pkt);
+ break;
+ default:
+ res = PXE_PGP_CORRUPT_DATA;
+ }
+
+ if (pkt)
+ pullf_free(pkt);
+ pkt = NULL;
+
+ if (res < 0 || got_data)
+ break;
+ }
+
+ pullf_free(src);
+ if (pkt)
+ pullf_free(pkt);
+
+ if (res < 0)
+ return res;
+
+ /* now check sanity */
+ if (got_pub_key && got_pubenc_key)
+ res = PXE_PGP_CORRUPT_DATA;
+
+ if (got_pub_key > 1)
+ res = PXE_PGP_MULTIPLE_KEYS;
+
+ if (got_pubenc_key > 1)
+ res = PXE_PGP_MULTIPLE_KEYS;
+
+ /*
+ * if still ok, look what we got
+ */
+ if (res >= 0)
+ {
+ if (got_pubenc_key || got_pub_key)
+ {
+ if (memcmp(keyid_buf, any_key, 8) == 0)
+ {
+ memcpy(dst, "ANYKEY", 7);
+ res = 6;
+ }
+ else
+ res = print_key(keyid_buf, dst);
+ }
+ else if (got_symenc_key)
+ {
+ memcpy(dst, "SYMKEY", 7);
+ res = 6;
+ }
+ else
+ res = PXE_PGP_NO_USABLE_KEY;
+ }
+
+ return res;
+}
diff --git a/contrib/pgcrypto/pgp-mpi-openssl.c b/contrib/pgcrypto/pgp-mpi-openssl.c
new file mode 100644
index 0000000..75e4c8b
--- /dev/null
+++ b/contrib/pgcrypto/pgp-mpi-openssl.c
@@ -0,0 +1,284 @@
+/*
+ * pgp-mpi-openssl.c
+ * OpenPGP MPI functions using OpenSSL BIGNUM code.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-mpi-openssl.c
+ */
+#include "postgres.h"
+
+#include <openssl/bn.h>
+
+#include "pgp.h"
+#include "px.h"
+
+static BIGNUM *
+mpi_to_bn(PGP_MPI *n)
+{
+ BIGNUM *bn = BN_bin2bn(n->data, n->bytes, NULL);
+
+ if (!bn)
+ return NULL;
+ if (BN_num_bits(bn) != n->bits)
+ {
+ px_debug("mpi_to_bn: bignum conversion failed: mpi=%d, bn=%d",
+ n->bits, BN_num_bits(bn));
+ BN_clear_free(bn);
+ return NULL;
+ }
+ return bn;
+}
+
+static PGP_MPI *
+bn_to_mpi(BIGNUM *bn)
+{
+ int res;
+ PGP_MPI *n;
+
+ res = pgp_mpi_alloc(BN_num_bits(bn), &n);
+ if (res < 0)
+ return NULL;
+
+ if (BN_num_bytes(bn) != n->bytes)
+ {
+ px_debug("bn_to_mpi: bignum conversion failed: bn=%d, mpi=%d",
+ BN_num_bytes(bn), n->bytes);
+ pgp_mpi_free(n);
+ return NULL;
+ }
+ BN_bn2bin(bn, n->data);
+ return n;
+}
+
+/*
+ * Decide the number of bits in the random component k
+ *
+ * It should be in the same range as p for signing (which
+ * is deprecated), but can be much smaller for encrypting.
+ *
+ * Until I research it further, I just mimic gpg behaviour.
+ * It has a special mapping table, for values <= 5120,
+ * above that it uses 'arbitrary high number'. Following
+ * algorithm hovers 10-70 bits above gpg values. And for
+ * larger p, it uses gpg's algorithm.
+ *
+ * The point is - if k gets large, encryption will be
+ * really slow. It does not matter for decryption.
+ */
+static int
+decide_k_bits(int p_bits)
+{
+ if (p_bits <= 5120)
+ return p_bits / 10 + 160;
+ else
+ return (p_bits / 8 + 200) * 3 / 2;
+}
+
+int
+pgp_elgamal_encrypt(PGP_PubKey *pk, PGP_MPI *_m,
+ PGP_MPI **c1_p, PGP_MPI **c2_p)
+{
+ int res = PXE_PGP_MATH_FAILED;
+ int k_bits;
+ BIGNUM *m = mpi_to_bn(_m);
+ BIGNUM *p = mpi_to_bn(pk->pub.elg.p);
+ BIGNUM *g = mpi_to_bn(pk->pub.elg.g);
+ BIGNUM *y = mpi_to_bn(pk->pub.elg.y);
+ BIGNUM *k = BN_new();
+ BIGNUM *yk = BN_new();
+ BIGNUM *c1 = BN_new();
+ BIGNUM *c2 = BN_new();
+ BN_CTX *tmp = BN_CTX_new();
+
+ if (!m || !p || !g || !y || !k || !yk || !c1 || !c2 || !tmp)
+ goto err;
+
+ /*
+ * generate k
+ */
+ k_bits = decide_k_bits(BN_num_bits(p));
+ if (!BN_rand(k, k_bits, 0, 0))
+ goto err;
+
+ /*
+ * c1 = g^k c2 = m * y^k
+ */
+ if (!BN_mod_exp(c1, g, k, p, tmp))
+ goto err;
+ if (!BN_mod_exp(yk, y, k, p, tmp))
+ goto err;
+ if (!BN_mod_mul(c2, m, yk, p, tmp))
+ goto err;
+
+ /* result */
+ *c1_p = bn_to_mpi(c1);
+ *c2_p = bn_to_mpi(c2);
+ if (*c1_p && *c2_p)
+ res = 0;
+err:
+ if (tmp)
+ BN_CTX_free(tmp);
+ if (c2)
+ BN_clear_free(c2);
+ if (c1)
+ BN_clear_free(c1);
+ if (yk)
+ BN_clear_free(yk);
+ if (k)
+ BN_clear_free(k);
+ if (y)
+ BN_clear_free(y);
+ if (g)
+ BN_clear_free(g);
+ if (p)
+ BN_clear_free(p);
+ if (m)
+ BN_clear_free(m);
+ return res;
+}
+
+int
+pgp_elgamal_decrypt(PGP_PubKey *pk, PGP_MPI *_c1, PGP_MPI *_c2,
+ PGP_MPI **msg_p)
+{
+ int res = PXE_PGP_MATH_FAILED;
+ BIGNUM *c1 = mpi_to_bn(_c1);
+ BIGNUM *c2 = mpi_to_bn(_c2);
+ BIGNUM *p = mpi_to_bn(pk->pub.elg.p);
+ BIGNUM *x = mpi_to_bn(pk->sec.elg.x);
+ BIGNUM *c1x = BN_new();
+ BIGNUM *div = BN_new();
+ BIGNUM *m = BN_new();
+ BN_CTX *tmp = BN_CTX_new();
+
+ if (!c1 || !c2 || !p || !x || !c1x || !div || !m || !tmp)
+ goto err;
+
+ /*
+ * m = c2 / (c1^x)
+ */
+ if (!BN_mod_exp(c1x, c1, x, p, tmp))
+ goto err;
+ if (!BN_mod_inverse(div, c1x, p, tmp))
+ goto err;
+ if (!BN_mod_mul(m, c2, div, p, tmp))
+ goto err;
+
+ /* result */
+ *msg_p = bn_to_mpi(m);
+ if (*msg_p)
+ res = 0;
+err:
+ if (tmp)
+ BN_CTX_free(tmp);
+ if (m)
+ BN_clear_free(m);
+ if (div)
+ BN_clear_free(div);
+ if (c1x)
+ BN_clear_free(c1x);
+ if (x)
+ BN_clear_free(x);
+ if (p)
+ BN_clear_free(p);
+ if (c2)
+ BN_clear_free(c2);
+ if (c1)
+ BN_clear_free(c1);
+ return res;
+}
+
+int
+pgp_rsa_encrypt(PGP_PubKey *pk, PGP_MPI *_m, PGP_MPI **c_p)
+{
+ int res = PXE_PGP_MATH_FAILED;
+ BIGNUM *m = mpi_to_bn(_m);
+ BIGNUM *e = mpi_to_bn(pk->pub.rsa.e);
+ BIGNUM *n = mpi_to_bn(pk->pub.rsa.n);
+ BIGNUM *c = BN_new();
+ BN_CTX *tmp = BN_CTX_new();
+
+ if (!m || !e || !n || !c || !tmp)
+ goto err;
+
+ /*
+ * c = m ^ e
+ */
+ if (!BN_mod_exp(c, m, e, n, tmp))
+ goto err;
+
+ *c_p = bn_to_mpi(c);
+ if (*c_p)
+ res = 0;
+err:
+ if (tmp)
+ BN_CTX_free(tmp);
+ if (c)
+ BN_clear_free(c);
+ if (n)
+ BN_clear_free(n);
+ if (e)
+ BN_clear_free(e);
+ if (m)
+ BN_clear_free(m);
+ return res;
+}
+
+int
+pgp_rsa_decrypt(PGP_PubKey *pk, PGP_MPI *_c, PGP_MPI **m_p)
+{
+ int res = PXE_PGP_MATH_FAILED;
+ BIGNUM *c = mpi_to_bn(_c);
+ BIGNUM *d = mpi_to_bn(pk->sec.rsa.d);
+ BIGNUM *n = mpi_to_bn(pk->pub.rsa.n);
+ BIGNUM *m = BN_new();
+ BN_CTX *tmp = BN_CTX_new();
+
+ if (!m || !d || !n || !c || !tmp)
+ goto err;
+
+ /*
+ * m = c ^ d
+ */
+ if (!BN_mod_exp(m, c, d, n, tmp))
+ goto err;
+
+ *m_p = bn_to_mpi(m);
+ if (*m_p)
+ res = 0;
+err:
+ if (tmp)
+ BN_CTX_free(tmp);
+ if (m)
+ BN_clear_free(m);
+ if (n)
+ BN_clear_free(n);
+ if (d)
+ BN_clear_free(d);
+ if (c)
+ BN_clear_free(c);
+ return res;
+}
diff --git a/contrib/pgcrypto/pgp-mpi.c b/contrib/pgcrypto/pgp-mpi.c
new file mode 100644
index 0000000..03be279
--- /dev/null
+++ b/contrib/pgcrypto/pgp-mpi.c
@@ -0,0 +1,142 @@
+/*
+ * pgp-mpi.c
+ * OpenPGP MPI helper functions.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-mpi.c
+ */
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+int
+pgp_mpi_alloc(int bits, PGP_MPI **mpi)
+{
+ PGP_MPI *n;
+ int len = (bits + 7) / 8;
+
+ if (bits < 0 || bits > 0xFFFF)
+ {
+ px_debug("pgp_mpi_alloc: unreasonable request: bits=%d", bits);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+ n = palloc(sizeof(*n) + len);
+ n->bits = bits;
+ n->bytes = len;
+ n->data = (uint8 *) (n) + sizeof(*n);
+ *mpi = n;
+ return 0;
+}
+
+int
+pgp_mpi_create(uint8 *data, int bits, PGP_MPI **mpi)
+{
+ int res;
+ PGP_MPI *n;
+
+ res = pgp_mpi_alloc(bits, &n);
+ if (res < 0)
+ return res;
+ memcpy(n->data, data, n->bytes);
+ *mpi = n;
+ return 0;
+}
+
+int
+pgp_mpi_free(PGP_MPI *mpi)
+{
+ if (mpi == NULL)
+ return 0;
+ px_memset(mpi, 0, sizeof(*mpi) + mpi->bytes);
+ pfree(mpi);
+ return 0;
+}
+
+int
+pgp_mpi_read(PullFilter *src, PGP_MPI **mpi)
+{
+ int res;
+ uint8 hdr[2];
+ int bits;
+ PGP_MPI *n;
+
+ res = pullf_read_fixed(src, 2, hdr);
+ if (res < 0)
+ return res;
+ bits = ((unsigned) hdr[0] << 8) + hdr[1];
+
+ res = pgp_mpi_alloc(bits, &n);
+ if (res < 0)
+ return res;
+
+ res = pullf_read_fixed(src, n->bytes, n->data);
+ if (res < 0)
+ pgp_mpi_free(n);
+ else
+ *mpi = n;
+ return res;
+}
+
+int
+pgp_mpi_write(PushFilter *dst, PGP_MPI *n)
+{
+ int res;
+ uint8 buf[2];
+
+ buf[0] = n->bits >> 8;
+ buf[1] = n->bits & 0xFF;
+ res = pushf_write(dst, buf, 2);
+ if (res >= 0)
+ res = pushf_write(dst, n->data, n->bytes);
+ return res;
+}
+
+int
+pgp_mpi_hash(PX_MD *md, PGP_MPI *n)
+{
+ uint8 buf[2];
+
+ buf[0] = n->bits >> 8;
+ buf[1] = n->bits & 0xFF;
+ px_md_update(md, buf, 2);
+ px_md_update(md, n->data, n->bytes);
+
+ return 0;
+}
+
+unsigned
+pgp_mpi_cksum(unsigned cksum, PGP_MPI *n)
+{
+ int i;
+
+ cksum += n->bits >> 8;
+ cksum += n->bits & 0xFF;
+ for (i = 0; i < n->bytes; i++)
+ cksum += n->data[i];
+
+ return cksum & 0xFFFF;
+}
diff --git a/contrib/pgcrypto/pgp-pgsql.c b/contrib/pgcrypto/pgp-pgsql.c
new file mode 100644
index 0000000..0536bfb
--- /dev/null
+++ b/contrib/pgcrypto/pgp-pgsql.c
@@ -0,0 +1,1006 @@
+/*
+ * pgp-pgsql.c
+ * PostgreSQL wrappers for pgp.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-pgsql.c
+ */
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "common/string.h"
+#include "funcapi.h"
+#include "lib/stringinfo.h"
+#include "mb/pg_wchar.h"
+#include "mbuf.h"
+#include "pgp.h"
+#include "px.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+
+/*
+ * public functions
+ */
+PG_FUNCTION_INFO_V1(pgp_sym_encrypt_bytea);
+PG_FUNCTION_INFO_V1(pgp_sym_encrypt_text);
+PG_FUNCTION_INFO_V1(pgp_sym_decrypt_bytea);
+PG_FUNCTION_INFO_V1(pgp_sym_decrypt_text);
+
+PG_FUNCTION_INFO_V1(pgp_pub_encrypt_bytea);
+PG_FUNCTION_INFO_V1(pgp_pub_encrypt_text);
+PG_FUNCTION_INFO_V1(pgp_pub_decrypt_bytea);
+PG_FUNCTION_INFO_V1(pgp_pub_decrypt_text);
+
+PG_FUNCTION_INFO_V1(pgp_key_id_w);
+
+PG_FUNCTION_INFO_V1(pg_armor);
+PG_FUNCTION_INFO_V1(pg_dearmor);
+PG_FUNCTION_INFO_V1(pgp_armor_headers);
+
+/*
+ * returns src in case of no conversion or error
+ */
+static text *
+convert_charset(text *src, int cset_from, int cset_to)
+{
+ int src_len = VARSIZE_ANY_EXHDR(src);
+ unsigned char *dst;
+ unsigned char *csrc = (unsigned char *) VARDATA_ANY(src);
+ text *res;
+
+ dst = pg_do_encoding_conversion(csrc, src_len, cset_from, cset_to);
+ if (dst == csrc)
+ return src;
+
+ res = cstring_to_text((char *) dst);
+ pfree(dst);
+ return res;
+}
+
+static text *
+convert_from_utf8(text *src)
+{
+ return convert_charset(src, PG_UTF8, GetDatabaseEncoding());
+}
+
+static text *
+convert_to_utf8(text *src)
+{
+ return convert_charset(src, GetDatabaseEncoding(), PG_UTF8);
+}
+
+static void
+clear_and_pfree(text *p)
+{
+ px_memset(p, 0, VARSIZE_ANY(p));
+ pfree(p);
+}
+
+/*
+ * expect-* arguments storage
+ */
+struct debug_expect
+{
+ int debug;
+ int expect;
+ int cipher_algo;
+ int s2k_mode;
+ int s2k_count;
+ int s2k_cipher_algo;
+ int s2k_digest_algo;
+ int compress_algo;
+ int use_sess_key;
+ int disable_mdc;
+ int unicode_mode;
+};
+
+static void
+fill_expect(struct debug_expect *ex, int text_mode)
+{
+ ex->debug = 0;
+ 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;
+ ex->use_sess_key = -1;
+ ex->disable_mdc = -1;
+ ex->unicode_mode = -1;
+}
+
+#define EX_MSG(arg) \
+ ereport(NOTICE, (errmsg( \
+ "pgp_decrypt: unexpected %s: expected %d got %d", \
+ CppAsString(arg), ex->arg, ctx->arg)))
+
+#define EX_CHECK(arg) do { \
+ if (ex->arg >= 0 && ex->arg != ctx->arg) EX_MSG(arg); \
+ } while (0)
+
+static void
+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)
+ EX_CHECK(s2k_cipher_algo);
+ EX_CHECK(disable_mdc);
+ EX_CHECK(compress_algo);
+ EX_CHECK(unicode_mode);
+}
+
+static void
+show_debug(const char *msg)
+{
+ ereport(NOTICE, (errmsg("dbg: %s", msg)));
+}
+
+static int
+set_arg(PGP_Context *ctx, char *key, char *val,
+ struct debug_expect *ex)
+{
+ int res = 0;
+
+ if (strcmp(key, "cipher-algo") == 0)
+ res = pgp_set_cipher_algo(ctx, val);
+ else if (strcmp(key, "disable-mdc") == 0)
+ res = pgp_disable_mdc(ctx, atoi(val));
+ else if (strcmp(key, "sess-key") == 0)
+ 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)
+ res = pgp_set_s2k_cipher_algo(ctx, val);
+ else if (strcmp(key, "compress-algo") == 0)
+ res = pgp_set_compress_algo(ctx, atoi(val));
+ else if (strcmp(key, "compress-level") == 0)
+ res = pgp_set_compress_level(ctx, atoi(val));
+ else if (strcmp(key, "convert-crlf") == 0)
+ res = pgp_set_convert_crlf(ctx, atoi(val));
+ else if (strcmp(key, "unicode-mode") == 0)
+ res = pgp_set_unicode_mode(ctx, atoi(val));
+
+ /*
+ * The remaining options are for debugging/testing and are therefore not
+ * documented in the user-facing docs.
+ */
+ else if (ex != NULL && strcmp(key, "debug") == 0)
+ ex->debug = atoi(val);
+ else if (ex != NULL && strcmp(key, "expect-cipher-algo") == 0)
+ {
+ ex->expect = 1;
+ ex->cipher_algo = pgp_get_cipher_code(val);
+ }
+ else if (ex != NULL && strcmp(key, "expect-disable-mdc") == 0)
+ {
+ ex->expect = 1;
+ ex->disable_mdc = atoi(val);
+ }
+ else if (ex != NULL && strcmp(key, "expect-sess-key") == 0)
+ {
+ ex->expect = 1;
+ ex->use_sess_key = atoi(val);
+ }
+ else if (ex != NULL && strcmp(key, "expect-s2k-mode") == 0)
+ {
+ 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;
+ ex->s2k_digest_algo = pgp_get_digest_code(val);
+ }
+ else if (ex != NULL && strcmp(key, "expect-s2k-cipher-algo") == 0)
+ {
+ ex->expect = 1;
+ ex->s2k_cipher_algo = pgp_get_cipher_code(val);
+ }
+ else if (ex != NULL && strcmp(key, "expect-compress-algo") == 0)
+ {
+ ex->expect = 1;
+ ex->compress_algo = atoi(val);
+ }
+ else if (ex != NULL && strcmp(key, "expect-unicode-mode") == 0)
+ {
+ ex->expect = 1;
+ ex->unicode_mode = atoi(val);
+ }
+ else
+ res = PXE_ARGUMENT_ERROR;
+
+ return res;
+}
+
+/*
+ * Find next word. Handle ',' and '=' as words. Skip whitespace.
+ * Put word info into res_p, res_len.
+ * Returns ptr to next word.
+ */
+static char *
+getword(char *p, char **res_p, int *res_len)
+{
+ /* whitespace at start */
+ while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
+ p++;
+
+ /* word data */
+ *res_p = p;
+ if (*p == '=' || *p == ',')
+ p++;
+ else
+ while (*p && !(*p == ' ' || *p == '\t' || *p == '\n'
+ || *p == '=' || *p == ','))
+ p++;
+
+ /* word end */
+ *res_len = p - *res_p;
+
+ /* whitespace at end */
+ while (*p && (*p == ' ' || *p == '\t' || *p == '\n'))
+ p++;
+
+ return p;
+}
+
+/*
+ * Convert to lowercase asciiz string.
+ */
+static char *
+downcase_convert(const uint8 *s, int len)
+{
+ int c,
+ i;
+ char *res = palloc(len + 1);
+
+ for (i = 0; i < len; i++)
+ {
+ c = s[i];
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ res[i] = c;
+ }
+ res[len] = 0;
+ return res;
+}
+
+static int
+parse_args(PGP_Context *ctx, uint8 *args, int arg_len,
+ struct debug_expect *ex)
+{
+ char *str = downcase_convert(args, arg_len);
+ char *key,
+ *val;
+ int key_len,
+ val_len;
+ int res = 0;
+ char *p = str;
+
+ while (*p)
+ {
+ res = PXE_ARGUMENT_ERROR;
+ p = getword(p, &key, &key_len);
+ if (*p++ != '=')
+ break;
+ p = getword(p, &val, &val_len);
+ if (*p == '\0')
+ ;
+ else if (*p++ != ',')
+ break;
+
+ if (*key == 0 || *val == 0 || val_len == 0)
+ break;
+
+ key[key_len] = 0;
+ val[val_len] = 0;
+
+ res = set_arg(ctx, key, val, ex);
+ if (res < 0)
+ break;
+ }
+ pfree(str);
+ return res;
+}
+
+static MBuf *
+create_mbuf_from_vardata(text *data)
+{
+ return mbuf_create_from_data((uint8 *) VARDATA_ANY(data),
+ VARSIZE_ANY_EXHDR(data));
+}
+
+static void
+init_work(PGP_Context **ctx_p, int is_text,
+ text *args, struct debug_expect *ex)
+{
+ int err = pgp_init(ctx_p);
+
+ fill_expect(ex, is_text);
+
+ if (err == 0 && args != NULL)
+ err = parse_args(*ctx_p, (uint8 *) VARDATA_ANY(args),
+ VARSIZE_ANY_EXHDR(args), ex);
+
+ if (err)
+ px_THROW_ERROR(err);
+
+ if (ex->debug)
+ px_set_debug_handler(show_debug);
+
+ pgp_set_text_mode(*ctx_p, is_text);
+}
+
+static bytea *
+encrypt_internal(int is_pubenc, int is_text,
+ text *data, text *key, text *args)
+{
+ MBuf *src,
+ *dst;
+ uint8 tmp[VARHDRSZ];
+ uint8 *restmp;
+ bytea *res;
+ int res_len;
+ PGP_Context *ctx;
+ int err;
+ struct debug_expect ex;
+ text *tmp_data = NULL;
+
+ init_work(&ctx, is_text, args, &ex);
+
+ if (is_text && pgp_get_unicode_mode(ctx))
+ {
+ tmp_data = convert_to_utf8(data);
+ if (tmp_data == data)
+ tmp_data = NULL;
+ else
+ data = tmp_data;
+ }
+
+ src = create_mbuf_from_vardata(data);
+ dst = mbuf_create(VARSIZE_ANY(data) + 128);
+
+ /*
+ * reserve room for header
+ */
+ mbuf_append(dst, tmp, VARHDRSZ);
+
+ /*
+ * set key
+ */
+ if (is_pubenc)
+ {
+ MBuf *kbuf = create_mbuf_from_vardata(key);
+
+ err = pgp_set_pubkey(ctx, kbuf,
+ NULL, 0, 0);
+ mbuf_free(kbuf);
+ }
+ else
+ err = pgp_set_symkey(ctx, (uint8 *) VARDATA_ANY(key),
+ VARSIZE_ANY_EXHDR(key));
+
+ /*
+ * encrypt
+ */
+ if (err >= 0)
+ err = pgp_encrypt(ctx, src, dst);
+
+ /*
+ * check for error
+ */
+ if (err)
+ {
+ if (ex.debug)
+ px_set_debug_handler(NULL);
+ if (tmp_data)
+ clear_and_pfree(tmp_data);
+ pgp_free(ctx);
+ mbuf_free(src);
+ mbuf_free(dst);
+ px_THROW_ERROR(err);
+ }
+
+ /* res_len includes VARHDRSZ */
+ res_len = mbuf_steal_data(dst, &restmp);
+ res = (bytea *) restmp;
+ SET_VARSIZE(res, res_len);
+
+ if (tmp_data)
+ clear_and_pfree(tmp_data);
+ pgp_free(ctx);
+ mbuf_free(src);
+ mbuf_free(dst);
+
+ px_set_debug_handler(NULL);
+
+ return res;
+}
+
+static bytea *
+decrypt_internal(int is_pubenc, int need_text, text *data,
+ text *key, text *keypsw, text *args)
+{
+ int err;
+ MBuf *src = NULL,
+ *dst = NULL;
+ uint8 tmp[VARHDRSZ];
+ uint8 *restmp;
+ bytea *res;
+ int res_len;
+ PGP_Context *ctx = NULL;
+ struct debug_expect ex;
+ int got_unicode = 0;
+
+
+ init_work(&ctx, need_text, args, &ex);
+
+ src = mbuf_create_from_data((uint8 *) VARDATA_ANY(data),
+ VARSIZE_ANY_EXHDR(data));
+ dst = mbuf_create(VARSIZE_ANY(data) + 2048);
+
+ /*
+ * reserve room for header
+ */
+ mbuf_append(dst, tmp, VARHDRSZ);
+
+ /*
+ * set key
+ */
+ if (is_pubenc)
+ {
+ uint8 *psw = NULL;
+ int psw_len = 0;
+ MBuf *kbuf;
+
+ if (keypsw)
+ {
+ psw = (uint8 *) VARDATA_ANY(keypsw);
+ psw_len = VARSIZE_ANY_EXHDR(keypsw);
+ }
+ kbuf = create_mbuf_from_vardata(key);
+ err = pgp_set_pubkey(ctx, kbuf, psw, psw_len, 1);
+ mbuf_free(kbuf);
+ }
+ else
+ err = pgp_set_symkey(ctx, (uint8 *) VARDATA_ANY(key),
+ VARSIZE_ANY_EXHDR(key));
+
+ /* decrypt */
+ if (err >= 0)
+ {
+ err = pgp_decrypt(ctx, src, dst);
+
+ if (ex.expect)
+ check_expect(ctx, &ex);
+
+ /* remember the setting */
+ got_unicode = pgp_get_unicode_mode(ctx);
+ }
+
+ mbuf_free(src);
+ pgp_free(ctx);
+
+ if (err)
+ {
+ px_set_debug_handler(NULL);
+ mbuf_free(dst);
+ px_THROW_ERROR(err);
+ }
+
+ res_len = mbuf_steal_data(dst, &restmp);
+ mbuf_free(dst);
+
+ /* res_len includes VARHDRSZ */
+ res = (bytea *) restmp;
+ SET_VARSIZE(res, res_len);
+
+ if (need_text && got_unicode)
+ {
+ text *utf = convert_from_utf8(res);
+
+ if (utf != res)
+ {
+ clear_and_pfree(res);
+ res = utf;
+ }
+ }
+ px_set_debug_handler(NULL);
+
+ return res;
+}
+
+/*
+ * Wrappers for symmetric-key functions
+ */
+Datum
+pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS)
+{
+ bytea *data,
+ *key;
+ text *arg = NULL;
+ text *res;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ if (PG_NARGS() > 2)
+ arg = PG_GETARG_BYTEA_PP(2);
+
+ res = encrypt_internal(0, 0, data, key, arg);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ if (PG_NARGS() > 2)
+ PG_FREE_IF_COPY(arg, 2);
+ PG_RETURN_TEXT_P(res);
+}
+
+Datum
+pgp_sym_encrypt_text(PG_FUNCTION_ARGS)
+{
+ bytea *data,
+ *key;
+ text *arg = NULL;
+ text *res;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ if (PG_NARGS() > 2)
+ arg = PG_GETARG_BYTEA_PP(2);
+
+ res = encrypt_internal(0, 1, data, key, arg);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ if (PG_NARGS() > 2)
+ PG_FREE_IF_COPY(arg, 2);
+ PG_RETURN_TEXT_P(res);
+}
+
+
+Datum
+pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS)
+{
+ bytea *data,
+ *key;
+ text *arg = NULL;
+ text *res;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ if (PG_NARGS() > 2)
+ arg = PG_GETARG_BYTEA_PP(2);
+
+ res = decrypt_internal(0, 0, data, key, NULL, arg);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ if (PG_NARGS() > 2)
+ PG_FREE_IF_COPY(arg, 2);
+ PG_RETURN_TEXT_P(res);
+}
+
+Datum
+pgp_sym_decrypt_text(PG_FUNCTION_ARGS)
+{
+ bytea *data,
+ *key;
+ text *arg = NULL;
+ text *res;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ if (PG_NARGS() > 2)
+ arg = PG_GETARG_BYTEA_PP(2);
+
+ res = decrypt_internal(0, 1, data, key, NULL, arg);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ if (PG_NARGS() > 2)
+ PG_FREE_IF_COPY(arg, 2);
+ PG_RETURN_TEXT_P(res);
+}
+
+/*
+ * Wrappers for public-key functions
+ */
+
+Datum
+pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS)
+{
+ bytea *data,
+ *key;
+ text *arg = NULL;
+ text *res;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ if (PG_NARGS() > 2)
+ arg = PG_GETARG_BYTEA_PP(2);
+
+ res = encrypt_internal(1, 0, data, key, arg);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ if (PG_NARGS() > 2)
+ PG_FREE_IF_COPY(arg, 2);
+ PG_RETURN_TEXT_P(res);
+}
+
+Datum
+pgp_pub_encrypt_text(PG_FUNCTION_ARGS)
+{
+ bytea *data,
+ *key;
+ text *arg = NULL;
+ text *res;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ if (PG_NARGS() > 2)
+ arg = PG_GETARG_BYTEA_PP(2);
+
+ res = encrypt_internal(1, 1, data, key, arg);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ if (PG_NARGS() > 2)
+ PG_FREE_IF_COPY(arg, 2);
+ PG_RETURN_TEXT_P(res);
+}
+
+
+Datum
+pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS)
+{
+ bytea *data,
+ *key;
+ text *psw = NULL,
+ *arg = NULL;
+ text *res;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ if (PG_NARGS() > 2)
+ psw = PG_GETARG_BYTEA_PP(2);
+ if (PG_NARGS() > 3)
+ arg = PG_GETARG_BYTEA_PP(3);
+
+ res = decrypt_internal(1, 0, data, key, psw, arg);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ if (PG_NARGS() > 2)
+ PG_FREE_IF_COPY(psw, 2);
+ if (PG_NARGS() > 3)
+ PG_FREE_IF_COPY(arg, 3);
+ PG_RETURN_TEXT_P(res);
+}
+
+Datum
+pgp_pub_decrypt_text(PG_FUNCTION_ARGS)
+{
+ bytea *data,
+ *key;
+ text *psw = NULL,
+ *arg = NULL;
+ text *res;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ key = PG_GETARG_BYTEA_PP(1);
+ if (PG_NARGS() > 2)
+ psw = PG_GETARG_BYTEA_PP(2);
+ if (PG_NARGS() > 3)
+ arg = PG_GETARG_BYTEA_PP(3);
+
+ res = decrypt_internal(1, 1, data, key, psw, arg);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_FREE_IF_COPY(key, 1);
+ if (PG_NARGS() > 2)
+ PG_FREE_IF_COPY(psw, 2);
+ if (PG_NARGS() > 3)
+ PG_FREE_IF_COPY(arg, 3);
+ PG_RETURN_TEXT_P(res);
+}
+
+
+/*
+ * Wrappers for PGP ascii armor
+ */
+
+/*
+ * Helper function for pg_armor. Converts arrays of keys and values into
+ * plain C arrays, and checks that they don't contain invalid characters.
+ */
+static int
+parse_key_value_arrays(ArrayType *key_array, ArrayType *val_array,
+ char ***p_keys, char ***p_values)
+{
+ int nkdims = ARR_NDIM(key_array);
+ int nvdims = ARR_NDIM(val_array);
+ char **keys,
+ **values;
+ Datum *key_datums,
+ *val_datums;
+ bool *key_nulls,
+ *val_nulls;
+ int key_count,
+ val_count;
+ int i;
+
+ if (nkdims > 1 || nkdims != nvdims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts")));
+ if (nkdims == 0)
+ return 0;
+
+ deconstruct_array(key_array,
+ TEXTOID, -1, false, TYPALIGN_INT,
+ &key_datums, &key_nulls, &key_count);
+
+ deconstruct_array(val_array,
+ TEXTOID, -1, false, TYPALIGN_INT,
+ &val_datums, &val_nulls, &val_count);
+
+ if (key_count != val_count)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("mismatched array dimensions")));
+
+ keys = (char **) palloc(sizeof(char *) * key_count);
+ values = (char **) palloc(sizeof(char *) * val_count);
+
+ for (i = 0; i < key_count; i++)
+ {
+ char *v;
+
+ /* Check that the key doesn't contain anything funny */
+ if (key_nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value not allowed for header key")));
+
+ v = TextDatumGetCString(key_datums[i]);
+
+ if (!pg_is_ascii(v))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("header key must not contain non-ASCII characters")));
+ if (strstr(v, ": "))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("header key must not contain \": \"")));
+ if (strchr(v, '\n'))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("header key must not contain newlines")));
+ keys[i] = v;
+
+ /* And the same for the value */
+ if (val_nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value not allowed for header value")));
+
+ v = TextDatumGetCString(val_datums[i]);
+
+ if (!pg_is_ascii(v))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("header value must not contain non-ASCII characters")));
+ if (strchr(v, '\n'))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("header value must not contain newlines")));
+
+ values[i] = v;
+ }
+
+ *p_keys = keys;
+ *p_values = values;
+ return key_count;
+}
+
+Datum
+pg_armor(PG_FUNCTION_ARGS)
+{
+ bytea *data;
+ text *res;
+ int data_len;
+ StringInfoData buf;
+ int num_headers;
+ char **keys = NULL,
+ **values = NULL;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ data_len = VARSIZE_ANY_EXHDR(data);
+ if (PG_NARGS() == 3)
+ {
+ num_headers = parse_key_value_arrays(PG_GETARG_ARRAYTYPE_P(1),
+ PG_GETARG_ARRAYTYPE_P(2),
+ &keys, &values);
+ }
+ else if (PG_NARGS() == 1)
+ num_headers = 0;
+ else
+ elog(ERROR, "unexpected number of arguments %d", PG_NARGS());
+
+ initStringInfo(&buf);
+
+ pgp_armor_encode((uint8 *) VARDATA_ANY(data), data_len, &buf,
+ num_headers, keys, values);
+
+ res = palloc(VARHDRSZ + buf.len);
+ SET_VARSIZE(res, VARHDRSZ + buf.len);
+ memcpy(VARDATA(res), buf.data, buf.len);
+ pfree(buf.data);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_RETURN_TEXT_P(res);
+}
+
+Datum
+pg_dearmor(PG_FUNCTION_ARGS)
+{
+ text *data;
+ bytea *res;
+ int data_len;
+ int ret;
+ StringInfoData buf;
+
+ data = PG_GETARG_TEXT_PP(0);
+ data_len = VARSIZE_ANY_EXHDR(data);
+
+ initStringInfo(&buf);
+
+ ret = pgp_armor_decode((uint8 *) VARDATA_ANY(data), data_len, &buf);
+ if (ret < 0)
+ px_THROW_ERROR(ret);
+ res = palloc(VARHDRSZ + buf.len);
+ SET_VARSIZE(res, VARHDRSZ + buf.len);
+ memcpy(VARDATA(res), buf.data, buf.len);
+ pfree(buf.data);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_RETURN_TEXT_P(res);
+}
+
+/* cross-call state for pgp_armor_headers */
+typedef struct
+{
+ int nheaders;
+ char **keys;
+ char **values;
+} pgp_armor_headers_state;
+
+Datum
+pgp_armor_headers(PG_FUNCTION_ARGS)
+{
+ FuncCallContext *funcctx;
+ pgp_armor_headers_state *state;
+ char *utf8key;
+ char *utf8val;
+ HeapTuple tuple;
+ TupleDesc tupdesc;
+ AttInMetadata *attinmeta;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ text *data = PG_GETARG_TEXT_PP(0);
+ int res;
+ MemoryContext oldcontext;
+
+ funcctx = SRF_FIRSTCALL_INIT();
+
+ /* we need the state allocated in the multi call context */
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ /* Build a tuple descriptor for our result type */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ elog(ERROR, "return type must be a row type");
+
+ attinmeta = TupleDescGetAttInMetadata(tupdesc);
+ funcctx->attinmeta = attinmeta;
+
+ state = (pgp_armor_headers_state *) palloc(sizeof(pgp_armor_headers_state));
+
+ res = pgp_extract_armor_headers((uint8 *) VARDATA_ANY(data),
+ VARSIZE_ANY_EXHDR(data),
+ &state->nheaders, &state->keys,
+ &state->values);
+ if (res < 0)
+ px_THROW_ERROR(res);
+
+ MemoryContextSwitchTo(oldcontext);
+ funcctx->user_fctx = state;
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ state = (pgp_armor_headers_state *) funcctx->user_fctx;
+
+ if (funcctx->call_cntr >= state->nheaders)
+ SRF_RETURN_DONE(funcctx);
+ else
+ {
+ char *values[2];
+
+ /* we assume that the keys (and values) are in UTF-8. */
+ utf8key = state->keys[funcctx->call_cntr];
+ utf8val = state->values[funcctx->call_cntr];
+
+ values[0] = pg_any_to_server(utf8key, strlen(utf8key), PG_UTF8);
+ values[1] = pg_any_to_server(utf8val, strlen(utf8val), PG_UTF8);
+
+ /* build a tuple */
+ tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
+ SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+ }
+}
+
+
+
+/*
+ * Wrappers for PGP key id
+ */
+
+Datum
+pgp_key_id_w(PG_FUNCTION_ARGS)
+{
+ bytea *data;
+ text *res;
+ int res_len;
+ MBuf *buf;
+
+ data = PG_GETARG_BYTEA_PP(0);
+ buf = create_mbuf_from_vardata(data);
+ res = palloc(VARHDRSZ + 17);
+
+ res_len = pgp_get_keyid(buf, VARDATA(res));
+ mbuf_free(buf);
+ if (res_len < 0)
+ px_THROW_ERROR(res_len);
+ SET_VARSIZE(res, VARHDRSZ + res_len);
+
+ PG_FREE_IF_COPY(data, 0);
+ PG_RETURN_TEXT_P(res);
+}
diff --git a/contrib/pgcrypto/pgp-pubdec.c b/contrib/pgcrypto/pgp-pubdec.c
new file mode 100644
index 0000000..a0a5738
--- /dev/null
+++ b/contrib/pgcrypto/pgp-pubdec.c
@@ -0,0 +1,235 @@
+/*
+ * pgp-pubdec.c
+ * Decrypt public-key encrypted session key.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-pubdec.c
+ */
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+/*
+ * padded msg = 02 || PS || 00 || M
+ * PS - pad bytes
+ * M - msg
+ */
+static uint8 *
+check_eme_pkcs1_v15(uint8 *data, int len)
+{
+ uint8 *data_end = data + len;
+ uint8 *p = data;
+ int rnd = 0;
+
+ if (len < 1 + 8 + 1)
+ return NULL;
+
+ if (*p++ != 2)
+ return NULL;
+
+ while (p < data_end && *p)
+ {
+ p++;
+ rnd++;
+ }
+
+ if (p == data_end)
+ return NULL;
+ if (*p != 0)
+ return NULL;
+ if (rnd < 8)
+ return NULL;
+ return p + 1;
+}
+
+/*
+ * secret message: 1 byte algo, sesskey, 2 byte cksum
+ * ignore algo in cksum
+ */
+static int
+control_cksum(uint8 *msg, int msglen)
+{
+ int i;
+ unsigned my_cksum,
+ got_cksum;
+
+ if (msglen < 3)
+ return PXE_PGP_WRONG_KEY;
+
+ my_cksum = 0;
+ for (i = 1; i < msglen - 2; i++)
+ my_cksum += msg[i];
+ my_cksum &= 0xFFFF;
+ got_cksum = ((unsigned) (msg[msglen - 2]) << 8) + msg[msglen - 1];
+ if (my_cksum != got_cksum)
+ {
+ px_debug("pubenc cksum failed");
+ return PXE_PGP_WRONG_KEY;
+ }
+ return 0;
+}
+
+static int
+decrypt_elgamal(PGP_PubKey *pk, PullFilter *pkt, PGP_MPI **m_p)
+{
+ int res;
+ PGP_MPI *c1 = NULL;
+ PGP_MPI *c2 = NULL;
+
+ if (pk->algo != PGP_PUB_ELG_ENCRYPT)
+ return PXE_PGP_WRONG_KEY;
+
+ /* read elgamal encrypted data */
+ res = pgp_mpi_read(pkt, &c1);
+ if (res < 0)
+ goto out;
+ res = pgp_mpi_read(pkt, &c2);
+ if (res < 0)
+ goto out;
+
+ /* decrypt */
+ res = pgp_elgamal_decrypt(pk, c1, c2, m_p);
+
+out:
+ pgp_mpi_free(c1);
+ pgp_mpi_free(c2);
+ return res;
+}
+
+static int
+decrypt_rsa(PGP_PubKey *pk, PullFilter *pkt, PGP_MPI **m_p)
+{
+ int res;
+ PGP_MPI *c;
+
+ if (pk->algo != PGP_PUB_RSA_ENCRYPT
+ && pk->algo != PGP_PUB_RSA_ENCRYPT_SIGN)
+ return PXE_PGP_WRONG_KEY;
+
+ /* read rsa encrypted data */
+ res = pgp_mpi_read(pkt, &c);
+ if (res < 0)
+ return res;
+
+ /* decrypt */
+ res = pgp_rsa_decrypt(pk, c, m_p);
+
+ pgp_mpi_free(c);
+ return res;
+}
+
+/* key id is missing - user is expected to try all keys */
+static const uint8
+ any_key[] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+int
+pgp_parse_pubenc_sesskey(PGP_Context *ctx, PullFilter *pkt)
+{
+ int ver;
+ int algo;
+ int res;
+ uint8 key_id[8];
+ PGP_PubKey *pk;
+ uint8 *msg;
+ int msglen;
+ PGP_MPI *m;
+
+ pk = ctx->pub_key;
+ if (pk == NULL)
+ {
+ px_debug("no pubkey?");
+ return PXE_BUG;
+ }
+
+ GETBYTE(pkt, ver);
+ if (ver != 3)
+ {
+ px_debug("unknown pubenc_sesskey pkt ver=%d", ver);
+ return PXE_PGP_CORRUPT_DATA;
+ }
+
+ /*
+ * check if keyid's match - user-friendly msg
+ */
+ res = pullf_read_fixed(pkt, 8, key_id);
+ if (res < 0)
+ return res;
+ if (memcmp(key_id, any_key, 8) != 0
+ && memcmp(key_id, pk->key_id, 8) != 0)
+ {
+ px_debug("key_id's does not match");
+ return PXE_PGP_WRONG_KEY;
+ }
+
+ /*
+ * Decrypt
+ */
+ GETBYTE(pkt, algo);
+ switch (algo)
+ {
+ case PGP_PUB_ELG_ENCRYPT:
+ res = decrypt_elgamal(pk, pkt, &m);
+ break;
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ res = decrypt_rsa(pk, pkt, &m);
+ break;
+ default:
+ res = PXE_PGP_UNKNOWN_PUBALGO;
+ }
+ if (res < 0)
+ return res;
+
+ /*
+ * extract message
+ */
+ msg = check_eme_pkcs1_v15(m->data, m->bytes);
+ if (msg == NULL)
+ {
+ px_debug("check_eme_pkcs1_v15 failed");
+ res = PXE_PGP_WRONG_KEY;
+ goto out;
+ }
+ msglen = m->bytes - (msg - m->data);
+
+ res = control_cksum(msg, msglen);
+ if (res < 0)
+ goto out;
+
+ /*
+ * got sesskey
+ */
+ ctx->cipher_algo = *msg;
+ ctx->sess_key_len = msglen - 3;
+ memcpy(ctx->sess_key, msg + 1, ctx->sess_key_len);
+
+out:
+ pgp_mpi_free(m);
+ if (res < 0)
+ return res;
+ return pgp_expect_packet_end(pkt);
+}
diff --git a/contrib/pgcrypto/pgp-pubenc.c b/contrib/pgcrypto/pgp-pubenc.c
new file mode 100644
index 0000000..c254a37
--- /dev/null
+++ b/contrib/pgcrypto/pgp-pubenc.c
@@ -0,0 +1,244 @@
+/*
+ * pgp-pubenc.c
+ * Encrypt session key with public key.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-pubenc.c
+ */
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+/*
+ * padded msg: 02 || non-zero pad bytes || 00 || msg
+ */
+static int
+pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p)
+{
+ uint8 *buf,
+ *p;
+ int pad_len = res_len - 2 - data_len;
+
+ if (pad_len < 8)
+ return PXE_BUG;
+
+ buf = palloc(res_len);
+ buf[0] = 0x02;
+
+ if (!pg_strong_random(buf + 1, pad_len))
+ {
+ pfree(buf);
+ return PXE_NO_RANDOM;
+ }
+
+ /* pad must not contain zero bytes */
+ p = buf + 1;
+ while (p < buf + 1 + pad_len)
+ {
+ if (*p == 0)
+ {
+ if (!pg_strong_random(p, 1))
+ {
+ px_memset(buf, 0, res_len);
+ pfree(buf);
+ return PXE_NO_RANDOM;
+ }
+ }
+ if (*p != 0)
+ p++;
+ }
+
+ buf[pad_len + 1] = 0;
+ memcpy(buf + pad_len + 2, data, data_len);
+ *res_p = buf;
+
+ return 0;
+}
+
+static int
+create_secmsg(PGP_Context *ctx, PGP_MPI **msg_p, int full_bytes)
+{
+ uint8 *secmsg;
+ int res,
+ i;
+ unsigned cksum = 0;
+ int klen = ctx->sess_key_len;
+ uint8 *padded = NULL;
+ PGP_MPI *m = NULL;
+
+ /* calc checksum */
+ for (i = 0; i < klen; i++)
+ cksum += ctx->sess_key[i];
+
+ /*
+ * create "secret message"
+ */
+ secmsg = palloc(klen + 3);
+ secmsg[0] = ctx->cipher_algo;
+ memcpy(secmsg + 1, ctx->sess_key, klen);
+ secmsg[klen + 1] = (cksum >> 8) & 0xFF;
+ secmsg[klen + 2] = cksum & 0xFF;
+
+ /*
+ * now create a large integer of it
+ */
+ res = pad_eme_pkcs1_v15(secmsg, klen + 3, full_bytes, &padded);
+ if (res >= 0)
+ {
+ /* first byte will be 0x02 */
+ int full_bits = full_bytes * 8 - 6;
+
+ res = pgp_mpi_create(padded, full_bits, &m);
+ }
+
+ if (padded)
+ {
+ px_memset(padded, 0, full_bytes);
+ pfree(padded);
+ }
+ px_memset(secmsg, 0, klen + 3);
+ pfree(secmsg);
+
+ if (res >= 0)
+ *msg_p = m;
+
+ return res;
+}
+
+static int
+encrypt_and_write_elgamal(PGP_Context *ctx, PGP_PubKey *pk, PushFilter *pkt)
+{
+ int res;
+ PGP_MPI *m = NULL,
+ *c1 = NULL,
+ *c2 = NULL;
+
+ /* create padded msg */
+ res = create_secmsg(ctx, &m, pk->pub.elg.p->bytes - 1);
+ if (res < 0)
+ goto err;
+
+ /* encrypt it */
+ res = pgp_elgamal_encrypt(pk, m, &c1, &c2);
+ if (res < 0)
+ goto err;
+
+ /* write out */
+ res = pgp_mpi_write(pkt, c1);
+ if (res < 0)
+ goto err;
+ res = pgp_mpi_write(pkt, c2);
+
+err:
+ pgp_mpi_free(m);
+ pgp_mpi_free(c1);
+ pgp_mpi_free(c2);
+ return res;
+}
+
+static int
+encrypt_and_write_rsa(PGP_Context *ctx, PGP_PubKey *pk, PushFilter *pkt)
+{
+ int res;
+ PGP_MPI *m = NULL,
+ *c = NULL;
+
+ /* create padded msg */
+ res = create_secmsg(ctx, &m, pk->pub.rsa.n->bytes - 1);
+ if (res < 0)
+ goto err;
+
+ /* encrypt it */
+ res = pgp_rsa_encrypt(pk, m, &c);
+ if (res < 0)
+ goto err;
+
+ /* write out */
+ res = pgp_mpi_write(pkt, c);
+
+err:
+ pgp_mpi_free(m);
+ pgp_mpi_free(c);
+ return res;
+}
+
+int
+pgp_write_pubenc_sesskey(PGP_Context *ctx, PushFilter *dst)
+{
+ int res;
+ PGP_PubKey *pk = ctx->pub_key;
+ uint8 ver = 3;
+ PushFilter *pkt = NULL;
+ uint8 algo;
+
+ if (pk == NULL)
+ {
+ px_debug("no pubkey?\n");
+ return PXE_BUG;
+ }
+
+ algo = pk->algo;
+
+ /*
+ * now write packet
+ */
+ res = pgp_create_pkt_writer(dst, PGP_PKT_PUBENCRYPTED_SESSKEY, &pkt);
+ if (res < 0)
+ goto err;
+ res = pushf_write(pkt, &ver, 1);
+ if (res < 0)
+ goto err;
+ res = pushf_write(pkt, pk->key_id, 8);
+ if (res < 0)
+ goto err;
+ res = pushf_write(pkt, &algo, 1);
+ if (res < 0)
+ goto err;
+
+ switch (algo)
+ {
+ case PGP_PUB_ELG_ENCRYPT:
+ res = encrypt_and_write_elgamal(ctx, pk, pkt);
+ break;
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ res = encrypt_and_write_rsa(ctx, pk, pkt);
+ break;
+ }
+ if (res < 0)
+ goto err;
+
+ /*
+ * done, signal packet end
+ */
+ res = pushf_flush(pkt);
+err:
+ if (pkt)
+ pushf_free(pkt);
+
+ return res;
+}
diff --git a/contrib/pgcrypto/pgp-pubkey.c b/contrib/pgcrypto/pgp-pubkey.c
new file mode 100644
index 0000000..9a6561c
--- /dev/null
+++ b/contrib/pgcrypto/pgp-pubkey.c
@@ -0,0 +1,583 @@
+/*
+ * pgp-pubkey.c
+ * Read public or secret key.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-pubkey.c
+ */
+#include "postgres.h"
+
+#include "mbuf.h"
+#include "pgp.h"
+#include "px.h"
+
+int
+pgp_key_alloc(PGP_PubKey **pk_p)
+{
+ PGP_PubKey *pk;
+
+ pk = palloc0(sizeof(*pk));
+ *pk_p = pk;
+ return 0;
+}
+
+void
+pgp_key_free(PGP_PubKey *pk)
+{
+ if (pk == NULL)
+ return;
+
+ switch (pk->algo)
+ {
+ case PGP_PUB_ELG_ENCRYPT:
+ pgp_mpi_free(pk->pub.elg.p);
+ pgp_mpi_free(pk->pub.elg.g);
+ pgp_mpi_free(pk->pub.elg.y);
+ pgp_mpi_free(pk->sec.elg.x);
+ break;
+ case PGP_PUB_RSA_SIGN:
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ pgp_mpi_free(pk->pub.rsa.n);
+ pgp_mpi_free(pk->pub.rsa.e);
+ pgp_mpi_free(pk->sec.rsa.d);
+ pgp_mpi_free(pk->sec.rsa.p);
+ pgp_mpi_free(pk->sec.rsa.q);
+ pgp_mpi_free(pk->sec.rsa.u);
+ break;
+ case PGP_PUB_DSA_SIGN:
+ pgp_mpi_free(pk->pub.dsa.p);
+ pgp_mpi_free(pk->pub.dsa.q);
+ pgp_mpi_free(pk->pub.dsa.g);
+ pgp_mpi_free(pk->pub.dsa.y);
+ pgp_mpi_free(pk->sec.dsa.x);
+ break;
+ }
+ px_memset(pk, 0, sizeof(*pk));
+ pfree(pk);
+}
+
+static int
+calc_key_id(PGP_PubKey *pk)
+{
+ int res;
+ PX_MD *md;
+ int len;
+ uint8 hdr[3];
+ uint8 hash[20];
+
+ res = pgp_load_digest(PGP_DIGEST_SHA1, &md);
+ if (res < 0)
+ return res;
+
+ len = 1 + 4 + 1;
+ switch (pk->algo)
+ {
+ case PGP_PUB_ELG_ENCRYPT:
+ len += 2 + pk->pub.elg.p->bytes;
+ len += 2 + pk->pub.elg.g->bytes;
+ len += 2 + pk->pub.elg.y->bytes;
+ break;
+ case PGP_PUB_RSA_SIGN:
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ len += 2 + pk->pub.rsa.n->bytes;
+ len += 2 + pk->pub.rsa.e->bytes;
+ break;
+ case PGP_PUB_DSA_SIGN:
+ len += 2 + pk->pub.dsa.p->bytes;
+ len += 2 + pk->pub.dsa.q->bytes;
+ len += 2 + pk->pub.dsa.g->bytes;
+ len += 2 + pk->pub.dsa.y->bytes;
+ break;
+ }
+
+ hdr[0] = 0x99;
+ hdr[1] = len >> 8;
+ hdr[2] = len & 0xFF;
+ px_md_update(md, hdr, 3);
+
+ px_md_update(md, &pk->ver, 1);
+ px_md_update(md, pk->time, 4);
+ px_md_update(md, &pk->algo, 1);
+
+ switch (pk->algo)
+ {
+ case PGP_PUB_ELG_ENCRYPT:
+ pgp_mpi_hash(md, pk->pub.elg.p);
+ pgp_mpi_hash(md, pk->pub.elg.g);
+ pgp_mpi_hash(md, pk->pub.elg.y);
+ break;
+ case PGP_PUB_RSA_SIGN:
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ pgp_mpi_hash(md, pk->pub.rsa.n);
+ pgp_mpi_hash(md, pk->pub.rsa.e);
+ break;
+ case PGP_PUB_DSA_SIGN:
+ pgp_mpi_hash(md, pk->pub.dsa.p);
+ pgp_mpi_hash(md, pk->pub.dsa.q);
+ pgp_mpi_hash(md, pk->pub.dsa.g);
+ pgp_mpi_hash(md, pk->pub.dsa.y);
+ break;
+ }
+
+ px_md_finish(md, hash);
+ px_md_free(md);
+
+ memcpy(pk->key_id, hash + 12, 8);
+ px_memset(hash, 0, 20);
+
+ return 0;
+}
+
+int
+_pgp_read_public_key(PullFilter *pkt, PGP_PubKey **pk_p)
+{
+ int res;
+ PGP_PubKey *pk;
+
+ res = pgp_key_alloc(&pk);
+ if (res < 0)
+ return res;
+
+ /* get version */
+ GETBYTE(pkt, pk->ver);
+ if (pk->ver != 4)
+ {
+ res = PXE_PGP_NOT_V4_KEYPKT;
+ goto out;
+ }
+
+ /* read time */
+ res = pullf_read_fixed(pkt, 4, pk->time);
+ if (res < 0)
+ goto out;
+
+ /* pubkey algorithm */
+ GETBYTE(pkt, pk->algo);
+
+ switch (pk->algo)
+ {
+ case PGP_PUB_DSA_SIGN:
+ res = pgp_mpi_read(pkt, &pk->pub.dsa.p);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pkt, &pk->pub.dsa.q);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pkt, &pk->pub.dsa.g);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pkt, &pk->pub.dsa.y);
+ if (res < 0)
+ break;
+
+ res = calc_key_id(pk);
+ break;
+
+ case PGP_PUB_RSA_SIGN:
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ res = pgp_mpi_read(pkt, &pk->pub.rsa.n);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pkt, &pk->pub.rsa.e);
+ if (res < 0)
+ break;
+
+ res = calc_key_id(pk);
+
+ if (pk->algo != PGP_PUB_RSA_SIGN)
+ pk->can_encrypt = 1;
+ break;
+
+ case PGP_PUB_ELG_ENCRYPT:
+ res = pgp_mpi_read(pkt, &pk->pub.elg.p);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pkt, &pk->pub.elg.g);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pkt, &pk->pub.elg.y);
+ if (res < 0)
+ break;
+
+ res = calc_key_id(pk);
+
+ pk->can_encrypt = 1;
+ break;
+
+ default:
+ px_debug("unknown public algo: %d", pk->algo);
+ res = PXE_PGP_UNKNOWN_PUBALGO;
+ }
+
+out:
+ if (res < 0)
+ pgp_key_free(pk);
+ else
+ *pk_p = pk;
+
+ return res;
+}
+
+#define HIDE_CLEAR 0
+#define HIDE_CKSUM 255
+#define HIDE_SHA1 254
+
+static int
+check_key_sha1(PullFilter *src, PGP_PubKey *pk)
+{
+ int res;
+ uint8 got_sha1[20];
+ uint8 my_sha1[20];
+ PX_MD *md;
+
+ res = pullf_read_fixed(src, 20, got_sha1);
+ if (res < 0)
+ return res;
+
+ res = pgp_load_digest(PGP_DIGEST_SHA1, &md);
+ if (res < 0)
+ goto err;
+ switch (pk->algo)
+ {
+ case PGP_PUB_ELG_ENCRYPT:
+ pgp_mpi_hash(md, pk->sec.elg.x);
+ break;
+ case PGP_PUB_RSA_SIGN:
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ pgp_mpi_hash(md, pk->sec.rsa.d);
+ pgp_mpi_hash(md, pk->sec.rsa.p);
+ pgp_mpi_hash(md, pk->sec.rsa.q);
+ pgp_mpi_hash(md, pk->sec.rsa.u);
+ break;
+ case PGP_PUB_DSA_SIGN:
+ pgp_mpi_hash(md, pk->sec.dsa.x);
+ break;
+ }
+ px_md_finish(md, my_sha1);
+ px_md_free(md);
+
+ if (memcmp(my_sha1, got_sha1, 20) != 0)
+ {
+ px_debug("key sha1 check failed");
+ res = PXE_PGP_KEYPKT_CORRUPT;
+ }
+err:
+ px_memset(got_sha1, 0, 20);
+ px_memset(my_sha1, 0, 20);
+ return res;
+}
+
+static int
+check_key_cksum(PullFilter *src, PGP_PubKey *pk)
+{
+ int res;
+ unsigned got_cksum,
+ my_cksum = 0;
+ uint8 buf[2];
+
+ res = pullf_read_fixed(src, 2, buf);
+ if (res < 0)
+ return res;
+
+ got_cksum = ((unsigned) buf[0] << 8) + buf[1];
+ switch (pk->algo)
+ {
+ case PGP_PUB_ELG_ENCRYPT:
+ my_cksum = pgp_mpi_cksum(0, pk->sec.elg.x);
+ break;
+ case PGP_PUB_RSA_SIGN:
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ my_cksum = pgp_mpi_cksum(0, pk->sec.rsa.d);
+ my_cksum = pgp_mpi_cksum(my_cksum, pk->sec.rsa.p);
+ my_cksum = pgp_mpi_cksum(my_cksum, pk->sec.rsa.q);
+ my_cksum = pgp_mpi_cksum(my_cksum, pk->sec.rsa.u);
+ break;
+ case PGP_PUB_DSA_SIGN:
+ my_cksum = pgp_mpi_cksum(0, pk->sec.dsa.x);
+ break;
+ }
+ if (my_cksum != got_cksum)
+ {
+ px_debug("key cksum check failed");
+ return PXE_PGP_KEYPKT_CORRUPT;
+ }
+ return 0;
+}
+
+static int
+process_secret_key(PullFilter *pkt, PGP_PubKey **pk_p,
+ const uint8 *key, int key_len)
+{
+ int res;
+ int hide_type;
+ int cipher_algo;
+ int bs;
+ uint8 iv[512];
+ PullFilter *pf_decrypt = NULL,
+ *pf_key;
+ PGP_CFB *cfb = NULL;
+ PGP_S2K s2k;
+ PGP_PubKey *pk;
+
+ /* first read public key part */
+ res = _pgp_read_public_key(pkt, &pk);
+ if (res < 0)
+ return res;
+
+ /*
+ * is secret key encrypted?
+ */
+ GETBYTE(pkt, hide_type);
+ if (hide_type == HIDE_SHA1 || hide_type == HIDE_CKSUM)
+ {
+ if (key == NULL)
+ return PXE_PGP_NEED_SECRET_PSW;
+ GETBYTE(pkt, cipher_algo);
+ res = pgp_s2k_read(pkt, &s2k);
+ if (res < 0)
+ return res;
+
+ res = pgp_s2k_process(&s2k, cipher_algo, key, key_len);
+ if (res < 0)
+ return res;
+
+ bs = pgp_get_cipher_block_size(cipher_algo);
+ if (bs == 0)
+ {
+ px_debug("unknown cipher algo=%d", cipher_algo);
+ return PXE_PGP_UNSUPPORTED_CIPHER;
+ }
+ res = pullf_read_fixed(pkt, bs, iv);
+ if (res < 0)
+ return res;
+
+ /*
+ * create decrypt filter
+ */
+ res = pgp_cfb_create(&cfb, cipher_algo, s2k.key, s2k.key_len, 0, iv);
+ if (res < 0)
+ return res;
+ res = pullf_create(&pf_decrypt, &pgp_decrypt_filter, cfb, pkt);
+ if (res < 0)
+ return res;
+ pf_key = pf_decrypt;
+ }
+ else if (hide_type == HIDE_CLEAR)
+ {
+ pf_key = pkt;
+ }
+ else
+ {
+ px_debug("unknown hide type");
+ return PXE_PGP_KEYPKT_CORRUPT;
+ }
+
+ /* read secret key */
+ switch (pk->algo)
+ {
+ case PGP_PUB_RSA_SIGN:
+ case PGP_PUB_RSA_ENCRYPT:
+ case PGP_PUB_RSA_ENCRYPT_SIGN:
+ res = pgp_mpi_read(pf_key, &pk->sec.rsa.d);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pf_key, &pk->sec.rsa.p);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pf_key, &pk->sec.rsa.q);
+ if (res < 0)
+ break;
+ res = pgp_mpi_read(pf_key, &pk->sec.rsa.u);
+ if (res < 0)
+ break;
+ break;
+ case PGP_PUB_ELG_ENCRYPT:
+ res = pgp_mpi_read(pf_key, &pk->sec.elg.x);
+ break;
+ case PGP_PUB_DSA_SIGN:
+ res = pgp_mpi_read(pf_key, &pk->sec.dsa.x);
+ break;
+ default:
+ px_debug("unknown public algo: %d", pk->algo);
+ res = PXE_PGP_KEYPKT_CORRUPT;
+ }
+ /* read checksum / sha1 */
+ if (res >= 0)
+ {
+ if (hide_type == HIDE_SHA1)
+ res = check_key_sha1(pf_key, pk);
+ else
+ res = check_key_cksum(pf_key, pk);
+ }
+ if (res >= 0)
+ res = pgp_expect_packet_end(pf_key);
+
+ if (pf_decrypt)
+ pullf_free(pf_decrypt);
+ if (cfb)
+ pgp_cfb_free(cfb);
+
+ if (res < 0)
+ pgp_key_free(pk);
+ else
+ *pk_p = pk;
+
+ return res;
+}
+
+static int
+internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
+ const uint8 *psw, int psw_len, int pubtype)
+{
+ PullFilter *pkt = NULL;
+ int res;
+ uint8 tag;
+ int len;
+ PGP_PubKey *enc_key = NULL;
+ PGP_PubKey *pk = NULL;
+ int got_main_key = 0;
+
+ /*
+ * Search for encryption key.
+ *
+ * Error out on anything fancy.
+ */
+ while (1)
+ {
+ res = pgp_parse_pkt_hdr(src, &tag, &len, 0);
+ if (res <= 0)
+ break;
+ res = pgp_create_pkt_reader(&pkt, src, len, res, NULL);
+ if (res < 0)
+ break;
+
+ switch (tag)
+ {
+ case PGP_PKT_PUBLIC_KEY:
+ case PGP_PKT_SECRET_KEY:
+ if (got_main_key)
+ {
+ res = PXE_PGP_MULTIPLE_KEYS;
+ break;
+ }
+ got_main_key = 1;
+ res = pgp_skip_packet(pkt);
+ break;
+
+ case PGP_PKT_PUBLIC_SUBKEY:
+ if (pubtype != 0)
+ res = PXE_PGP_EXPECT_SECRET_KEY;
+ else
+ res = _pgp_read_public_key(pkt, &pk);
+ break;
+
+ case PGP_PKT_SECRET_SUBKEY:
+ if (pubtype != 1)
+ res = PXE_PGP_EXPECT_PUBLIC_KEY;
+ else
+ res = process_secret_key(pkt, &pk, psw, psw_len);
+ break;
+
+ case PGP_PKT_SIGNATURE:
+ case PGP_PKT_MARKER:
+ case PGP_PKT_TRUST:
+ case PGP_PKT_USER_ID:
+ case PGP_PKT_USER_ATTR:
+ case PGP_PKT_PRIV_61:
+ res = pgp_skip_packet(pkt);
+ break;
+ default:
+ px_debug("unknown/unexpected packet: %d", tag);
+ res = PXE_PGP_UNEXPECTED_PKT;
+ }
+ pullf_free(pkt);
+ pkt = NULL;
+
+ if (pk != NULL)
+ {
+ if (res >= 0 && pk->can_encrypt)
+ {
+ if (enc_key == NULL)
+ {
+ enc_key = pk;
+ pk = NULL;
+ }
+ else
+ res = PXE_PGP_MULTIPLE_SUBKEYS;
+ }
+
+ if (pk)
+ pgp_key_free(pk);
+ pk = NULL;
+ }
+
+ if (res < 0)
+ break;
+ }
+
+ if (pkt)
+ pullf_free(pkt);
+
+ if (res < 0)
+ {
+ if (enc_key)
+ pgp_key_free(enc_key);
+ return res;
+ }
+
+ if (!enc_key)
+ res = PXE_PGP_NO_USABLE_KEY;
+ else
+ *pk_p = enc_key;
+ return res;
+}
+
+int
+pgp_set_pubkey(PGP_Context *ctx, MBuf *keypkt,
+ const uint8 *key, int key_len, int pubtype)
+{
+ int res;
+ PullFilter *src;
+ PGP_PubKey *pk = NULL;
+
+ res = pullf_create_mbuf_reader(&src, keypkt);
+ if (res < 0)
+ return res;
+
+ res = internal_read_key(src, &pk, key, key_len, pubtype);
+ pullf_free(src);
+
+ if (res >= 0)
+ ctx->pub_key = pk;
+
+ return res < 0 ? res : 0;
+}
diff --git a/contrib/pgcrypto/pgp-s2k.c b/contrib/pgcrypto/pgp-s2k.c
new file mode 100644
index 0000000..81ca1f0
--- /dev/null
+++ b/contrib/pgcrypto/pgp-s2k.c
@@ -0,0 +1,308 @@
+/*
+ * pgp-s2k.c
+ * OpenPGP string2key functions.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp-s2k.c
+ */
+
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+static int
+calc_s2k_simple(PGP_S2K *s2k, PX_MD *md, const uint8 *key,
+ unsigned key_len)
+{
+ unsigned md_rlen;
+ uint8 buf[PGP_MAX_DIGEST];
+ unsigned preload;
+ unsigned remain;
+ uint8 *dst = s2k->key;
+
+ md_rlen = px_md_result_size(md);
+
+ remain = s2k->key_len;
+ preload = 0;
+ while (remain > 0)
+ {
+ px_md_reset(md);
+
+ if (preload)
+ {
+ memset(buf, 0, preload);
+ px_md_update(md, buf, preload);
+ }
+ preload++;
+
+ px_md_update(md, key, key_len);
+ px_md_finish(md, buf);
+
+ if (remain > md_rlen)
+ {
+ memcpy(dst, buf, md_rlen);
+ dst += md_rlen;
+ remain -= md_rlen;
+ }
+ else
+ {
+ memcpy(dst, buf, remain);
+ remain = 0;
+ }
+ }
+ px_memset(buf, 0, sizeof(buf));
+ return 0;
+}
+
+static int
+calc_s2k_salted(PGP_S2K *s2k, PX_MD *md, const uint8 *key, unsigned key_len)
+{
+ unsigned md_rlen;
+ uint8 buf[PGP_MAX_DIGEST];
+ unsigned preload = 0;
+ uint8 *dst;
+ unsigned remain;
+
+ md_rlen = px_md_result_size(md);
+
+ dst = s2k->key;
+ remain = s2k->key_len;
+ while (remain > 0)
+ {
+ px_md_reset(md);
+
+ if (preload > 0)
+ {
+ memset(buf, 0, preload);
+ px_md_update(md, buf, preload);
+ }
+ preload++;
+
+ px_md_update(md, s2k->salt, PGP_S2K_SALT);
+ px_md_update(md, key, key_len);
+ px_md_finish(md, buf);
+
+ if (remain > md_rlen)
+ {
+ memcpy(dst, buf, md_rlen);
+ remain -= md_rlen;
+ dst += md_rlen;
+ }
+ else
+ {
+ memcpy(dst, buf, remain);
+ remain = 0;
+ }
+ }
+ px_memset(buf, 0, sizeof(buf));
+ return 0;
+}
+
+static int
+calc_s2k_iter_salted(PGP_S2K *s2k, PX_MD *md, const uint8 *key,
+ unsigned key_len)
+{
+ unsigned md_rlen;
+ uint8 buf[PGP_MAX_DIGEST];
+ uint8 *dst;
+ unsigned preload = 0;
+ unsigned remain,
+ c,
+ curcnt,
+ count;
+
+ count = s2k_decode_count(s2k->iter);
+
+ md_rlen = px_md_result_size(md);
+
+ remain = s2k->key_len;
+ dst = s2k->key;
+ while (remain > 0)
+ {
+ px_md_reset(md);
+
+ if (preload)
+ {
+ memset(buf, 0, preload);
+ px_md_update(md, buf, preload);
+ }
+ preload++;
+
+ px_md_update(md, s2k->salt, PGP_S2K_SALT);
+ px_md_update(md, key, key_len);
+ curcnt = PGP_S2K_SALT + key_len;
+
+ while (curcnt < count)
+ {
+ if (curcnt + PGP_S2K_SALT < count)
+ c = PGP_S2K_SALT;
+ else
+ c = count - curcnt;
+ px_md_update(md, s2k->salt, c);
+ curcnt += c;
+
+ if (curcnt + key_len < count)
+ c = key_len;
+ else if (curcnt < count)
+ c = count - curcnt;
+ else
+ break;
+ px_md_update(md, key, c);
+ curcnt += c;
+ }
+ px_md_finish(md, buf);
+
+ if (remain > md_rlen)
+ {
+ memcpy(dst, buf, md_rlen);
+ remain -= md_rlen;
+ dst += md_rlen;
+ }
+ else
+ {
+ memcpy(dst, buf, remain);
+ remain = 0;
+ }
+ }
+ px_memset(buf, 0, sizeof(buf));
+ return 0;
+}
+
+/*
+ * Decide PGP_S2K_ISALTED iteration count (in OpenPGP one-byte representation)
+ *
+ * Too small: weak
+ * Too big: slow
+ * gpg defaults to 96 => 65536 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 uint8
+decide_s2k_iter(unsigned rand_byte, int count)
+{
+ 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, int count)
+{
+ int res = 0;
+ uint8 tmp;
+
+ s2k->mode = mode;
+ s2k->digest_algo = digest_algo;
+
+ switch (s2k->mode)
+ {
+ case PGP_S2K_SIMPLE:
+ break;
+ case PGP_S2K_SALTED:
+ if (!pg_strong_random(s2k->salt, PGP_S2K_SALT))
+ return PXE_NO_RANDOM;
+ break;
+ case PGP_S2K_ISALTED:
+ if (!pg_strong_random(s2k->salt, PGP_S2K_SALT))
+ return PXE_NO_RANDOM;
+ if (!pg_strong_random(&tmp, 1))
+ return PXE_NO_RANDOM;
+ s2k->iter = decide_s2k_iter(tmp, count);
+ break;
+ default:
+ res = PXE_PGP_BAD_S2K_MODE;
+ }
+ return res;
+}
+
+int
+pgp_s2k_read(PullFilter *src, PGP_S2K *s2k)
+{
+ int res = 0;
+
+ GETBYTE(src, s2k->mode);
+ GETBYTE(src, s2k->digest_algo);
+ switch (s2k->mode)
+ {
+ case 0:
+ break;
+ case 1:
+ res = pullf_read_fixed(src, 8, s2k->salt);
+ break;
+ case 3:
+ res = pullf_read_fixed(src, 8, s2k->salt);
+ if (res < 0)
+ break;
+ GETBYTE(src, s2k->iter);
+ break;
+ default:
+ res = PXE_PGP_BAD_S2K_MODE;
+ }
+ return res;
+}
+
+int
+pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int key_len)
+{
+ int res;
+ PX_MD *md;
+
+ s2k->key_len = pgp_get_cipher_key_size(cipher);
+ if (s2k->key_len <= 0)
+ return PXE_PGP_UNSUPPORTED_CIPHER;
+
+ res = pgp_load_digest(s2k->digest_algo, &md);
+ if (res < 0)
+ return res;
+
+ switch (s2k->mode)
+ {
+ case 0:
+ res = calc_s2k_simple(s2k, md, key, key_len);
+ break;
+ case 1:
+ res = calc_s2k_salted(s2k, md, key, key_len);
+ break;
+ case 3:
+ res = calc_s2k_iter_salted(s2k, md, key, key_len);
+ break;
+ default:
+ res = PXE_PGP_BAD_S2K_MODE;
+ }
+ px_md_free(md);
+ return res;
+}
diff --git a/contrib/pgcrypto/pgp.c b/contrib/pgcrypto/pgp.c
new file mode 100644
index 0000000..3e9c2fe
--- /dev/null
+++ b/contrib/pgcrypto/pgp.c
@@ -0,0 +1,370 @@
+/*
+ * pgp.c
+ * Various utility stuff.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp.c
+ */
+
+#include "postgres.h"
+
+#include "pgp.h"
+#include "px.h"
+
+/*
+ * Defaults.
+ */
+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;
+static int def_disable_mdc = 0;
+static int def_use_sess_key = 0;
+static int def_text_mode = 0;
+static int def_unicode_mode = 0;
+static int def_convert_crlf = 0;
+
+struct digest_info
+{
+ const char *name;
+ int code;
+};
+
+struct cipher_info
+{
+ const char *name;
+ int code;
+ const char *int_name;
+ int key_len;
+ int block_len;
+};
+
+static const struct digest_info digest_list[] = {
+ {"md5", PGP_DIGEST_MD5},
+ {"sha1", PGP_DIGEST_SHA1},
+ {"sha-1", PGP_DIGEST_SHA1},
+ {"ripemd160", PGP_DIGEST_RIPEMD160},
+ {"sha256", PGP_DIGEST_SHA256},
+ {"sha384", PGP_DIGEST_SHA384},
+ {"sha512", PGP_DIGEST_SHA512},
+ {NULL, 0}
+};
+
+static const struct cipher_info cipher_list[] = {
+ {"3des", PGP_SYM_DES3, "3des-ecb", 192 / 8, 64 / 8},
+ {"cast5", PGP_SYM_CAST5, "cast5-ecb", 128 / 8, 64 / 8},
+ {"bf", PGP_SYM_BLOWFISH, "bf-ecb", 128 / 8, 64 / 8},
+ {"blowfish", PGP_SYM_BLOWFISH, "bf-ecb", 128 / 8, 64 / 8},
+ {"aes", PGP_SYM_AES_128, "aes-ecb", 128 / 8, 128 / 8},
+ {"aes128", PGP_SYM_AES_128, "aes-ecb", 128 / 8, 128 / 8},
+ {"aes192", PGP_SYM_AES_192, "aes-ecb", 192 / 8, 128 / 8},
+ {"aes256", PGP_SYM_AES_256, "aes-ecb", 256 / 8, 128 / 8},
+ {"twofish", PGP_SYM_TWOFISH, "twofish-ecb", 256 / 8, 128 / 8},
+ {NULL, 0, NULL}
+};
+
+static const struct cipher_info *
+get_cipher_info(int code)
+{
+ const struct cipher_info *i;
+
+ for (i = cipher_list; i->name; i++)
+ if (i->code == code)
+ return i;
+ return NULL;
+}
+
+int
+pgp_get_digest_code(const char *name)
+{
+ const struct digest_info *i;
+
+ for (i = digest_list; i->name; i++)
+ if (pg_strcasecmp(i->name, name) == 0)
+ return i->code;
+ return PXE_PGP_UNSUPPORTED_HASH;
+}
+
+int
+pgp_get_cipher_code(const char *name)
+{
+ const struct cipher_info *i;
+
+ for (i = cipher_list; i->name; i++)
+ if (pg_strcasecmp(i->name, name) == 0)
+ return i->code;
+ return PXE_PGP_UNSUPPORTED_CIPHER;
+}
+
+const char *
+pgp_get_digest_name(int code)
+{
+ const struct digest_info *i;
+
+ for (i = digest_list; i->name; i++)
+ if (i->code == code)
+ return i->name;
+ return NULL;
+}
+
+const char *
+pgp_get_cipher_name(int code)
+{
+ const struct cipher_info *i = get_cipher_info(code);
+
+ if (i != NULL)
+ return i->name;
+ return NULL;
+}
+
+int
+pgp_get_cipher_key_size(int code)
+{
+ const struct cipher_info *i = get_cipher_info(code);
+
+ if (i != NULL)
+ return i->key_len;
+ return 0;
+}
+
+int
+pgp_get_cipher_block_size(int code)
+{
+ const struct cipher_info *i = get_cipher_info(code);
+
+ if (i != NULL)
+ return i->block_len;
+ return 0;
+}
+
+int
+pgp_load_cipher(int code, PX_Cipher **res)
+{
+ int err;
+ const struct cipher_info *i = get_cipher_info(code);
+
+ if (i == NULL)
+ return PXE_PGP_CORRUPT_DATA;
+
+ err = px_find_cipher(i->int_name, res);
+ if (err == 0)
+ return 0;
+
+ return PXE_PGP_UNSUPPORTED_CIPHER;
+}
+
+int
+pgp_load_digest(int code, PX_MD **res)
+{
+ int err;
+ const char *name = pgp_get_digest_name(code);
+
+ if (name == NULL)
+ return PXE_PGP_CORRUPT_DATA;
+
+ err = px_find_digest(name, res);
+ if (err == 0)
+ return 0;
+
+ return PXE_PGP_UNSUPPORTED_HASH;
+}
+
+int
+pgp_init(PGP_Context **ctx_p)
+{
+ PGP_Context *ctx;
+
+ ctx = palloc0(sizeof *ctx);
+
+ 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;
+ ctx->disable_mdc = def_disable_mdc;
+ ctx->use_sess_key = def_use_sess_key;
+ ctx->unicode_mode = def_unicode_mode;
+ ctx->convert_crlf = def_convert_crlf;
+ ctx->text_mode = def_text_mode;
+
+ *ctx_p = ctx;
+ return 0;
+}
+
+int
+pgp_free(PGP_Context *ctx)
+{
+ if (ctx->pub_key)
+ pgp_key_free(ctx->pub_key);
+ px_memset(ctx, 0, sizeof *ctx);
+ pfree(ctx);
+ return 0;
+}
+
+int
+pgp_disable_mdc(PGP_Context *ctx, int disable)
+{
+ ctx->disable_mdc = disable ? 1 : 0;
+ return 0;
+}
+
+int
+pgp_set_sess_key(PGP_Context *ctx, int use)
+{
+ ctx->use_sess_key = use ? 1 : 0;
+ return 0;
+}
+
+int
+pgp_set_convert_crlf(PGP_Context *ctx, int doit)
+{
+ ctx->convert_crlf = doit ? 1 : 0;
+ return 0;
+}
+
+int
+pgp_set_s2k_mode(PGP_Context *ctx, int mode)
+{
+ int err = PXE_OK;
+
+ switch (mode)
+ {
+ case PGP_S2K_SIMPLE:
+ case PGP_S2K_SALTED:
+ case PGP_S2K_ISALTED:
+ ctx->s2k_mode = mode;
+ break;
+ default:
+ err = PXE_ARGUMENT_ERROR;
+ break;
+ }
+ 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)
+{
+ switch (algo)
+ {
+ case PGP_COMPR_NONE:
+ case PGP_COMPR_ZIP:
+ case PGP_COMPR_ZLIB:
+ case PGP_COMPR_BZIP2:
+ ctx->compress_algo = algo;
+ return 0;
+ }
+ return PXE_ARGUMENT_ERROR;
+}
+
+int
+pgp_set_compress_level(PGP_Context *ctx, int level)
+{
+ if (level >= 0 && level <= 9)
+ {
+ ctx->compress_level = level;
+ return 0;
+ }
+ return PXE_ARGUMENT_ERROR;
+}
+
+int
+pgp_set_text_mode(PGP_Context *ctx, int mode)
+{
+ ctx->text_mode = mode;
+ return 0;
+}
+
+int
+pgp_set_cipher_algo(PGP_Context *ctx, const char *name)
+{
+ int code = pgp_get_cipher_code(name);
+
+ if (code < 0)
+ return code;
+ ctx->cipher_algo = code;
+ return 0;
+}
+
+int
+pgp_set_s2k_cipher_algo(PGP_Context *ctx, const char *name)
+{
+ int code = pgp_get_cipher_code(name);
+
+ if (code < 0)
+ return code;
+ ctx->s2k_cipher_algo = code;
+ return 0;
+}
+
+int
+pgp_set_s2k_digest_algo(PGP_Context *ctx, const char *name)
+{
+ int code = pgp_get_digest_code(name);
+
+ if (code < 0)
+ return code;
+ ctx->s2k_digest_algo = code;
+ return 0;
+}
+
+int
+pgp_get_unicode_mode(PGP_Context *ctx)
+{
+ return ctx->unicode_mode;
+}
+
+int
+pgp_set_unicode_mode(PGP_Context *ctx, int mode)
+{
+ ctx->unicode_mode = mode ? 1 : 0;
+ return 0;
+}
+
+int
+pgp_set_symkey(PGP_Context *ctx, const uint8 *key, int len)
+{
+ if (key == NULL || len < 1)
+ return PXE_ARGUMENT_ERROR;
+ ctx->sym_key = key;
+ ctx->sym_key_len = len;
+ return 0;
+}
diff --git a/contrib/pgcrypto/pgp.h b/contrib/pgcrypto/pgp.h
new file mode 100644
index 0000000..f338523
--- /dev/null
+++ b/contrib/pgcrypto/pgp.h
@@ -0,0 +1,327 @@
+/*
+ * pgp.h
+ * OpenPGP implementation.
+ *
+ * Copyright (c) 2005 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/pgp.h
+ */
+
+#include "lib/stringinfo.h"
+
+#include "mbuf.h"
+#include "px.h"
+
+enum PGP_S2K_TYPE
+{
+ PGP_S2K_SIMPLE = 0,
+ PGP_S2K_SALTED = 1,
+ PGP_S2K_ISALTED = 3
+};
+
+enum PGP_PKT_TYPE
+{
+ PGP_PKT_RESERVED = 0,
+ PGP_PKT_PUBENCRYPTED_SESSKEY = 1,
+ PGP_PKT_SIGNATURE = 2,
+ PGP_PKT_SYMENCRYPTED_SESSKEY = 3,
+ PGP_PKT_SECRET_KEY = 5,
+ PGP_PKT_PUBLIC_KEY = 6,
+ PGP_PKT_SECRET_SUBKEY = 7,
+ PGP_PKT_COMPRESSED_DATA = 8,
+ PGP_PKT_SYMENCRYPTED_DATA = 9,
+ PGP_PKT_MARKER = 10,
+ PGP_PKT_LITERAL_DATA = 11,
+ PGP_PKT_TRUST = 12,
+ PGP_PKT_USER_ID = 13,
+ PGP_PKT_PUBLIC_SUBKEY = 14,
+ PGP_PKT_USER_ATTR = 17,
+ PGP_PKT_SYMENCRYPTED_DATA_MDC = 18,
+ PGP_PKT_MDC = 19,
+ PGP_PKT_PRIV_61 = 61 /* occurs in gpg secring */
+};
+
+enum PGP_PUB_ALGO_TYPE
+{
+ PGP_PUB_RSA_ENCRYPT_SIGN = 1,
+ PGP_PUB_RSA_ENCRYPT = 2,
+ PGP_PUB_RSA_SIGN = 3,
+ PGP_PUB_ELG_ENCRYPT = 16,
+ PGP_PUB_DSA_SIGN = 17
+};
+
+enum PGP_SYMENC_TYPE
+{
+ PGP_SYM_PLAIN = 0, /* ?? */
+ PGP_SYM_IDEA = 1, /* obsolete, PGP 2.6 compat */
+ PGP_SYM_DES3 = 2, /* must */
+ PGP_SYM_CAST5 = 3, /* should */
+ PGP_SYM_BLOWFISH = 4,
+ PGP_SYM_SAFER_SK128 = 5, /* obsolete */
+ PGP_SYM_DES_SK = 6, /* obsolete */
+ PGP_SYM_AES_128 = 7, /* should */
+ PGP_SYM_AES_192 = 8,
+ PGP_SYM_AES_256 = 9,
+ PGP_SYM_TWOFISH = 10
+};
+
+enum PGP_COMPR_TYPE
+{
+ PGP_COMPR_NONE = 0, /* must */
+ PGP_COMPR_ZIP = 1, /* should */
+ PGP_COMPR_ZLIB = 2,
+ PGP_COMPR_BZIP2 = 3
+};
+
+enum PGP_DIGEST_TYPE
+{
+ PGP_DIGEST_MD5 = 1, /* should, deprecated */
+ PGP_DIGEST_SHA1 = 2, /* must */
+ PGP_DIGEST_RIPEMD160 = 3,
+ PGP_DIGEST_XSHA = 4, /* obsolete */
+ PGP_DIGEST_MD2 = 5, /* obsolete */
+ PGP_DIGEST_TIGER192 = 6, /* obsolete */
+ PGP_DIGEST_HAVAL5_160 = 7, /* obsolete */
+ PGP_DIGEST_SHA256 = 8,
+ PGP_DIGEST_SHA384 = 9,
+ PGP_DIGEST_SHA512 = 10
+};
+
+#define PGP_MAX_KEY (256/8)
+#define PGP_MAX_BLOCK (256/8)
+#define PGP_MAX_DIGEST (512/8)
+#define PGP_S2K_SALT 8
+
+typedef struct PGP_MPI PGP_MPI;
+typedef struct PGP_PubKey PGP_PubKey;
+typedef struct PGP_Context PGP_Context;
+typedef struct PGP_S2K PGP_S2K;
+
+struct PGP_S2K
+{
+ uint8 mode;
+ uint8 digest_algo;
+ uint8 salt[8];
+ uint8 iter; /* encoded (one-octet) count */
+ /* calculated: */
+ uint8 key[PGP_MAX_KEY];
+ uint8 key_len;
+};
+
+
+struct PGP_Context
+{
+ /*
+ * parameters
+ */
+ PGP_S2K s2k;
+ int s2k_mode;
+ int s2k_count; /* 4-byte decoded count */
+ int s2k_digest_algo;
+ int s2k_cipher_algo;
+ int cipher_algo;
+ int compress_algo;
+ int compress_level;
+ int disable_mdc;
+ int use_sess_key;
+ int text_mode;
+ int convert_crlf;
+ int unicode_mode;
+
+ /*
+ * internal variables
+ */
+ int mdc_checked;
+ int corrupt_prefix; /* prefix failed RFC 4880 "quick check" */
+ int unsupported_compr; /* has bzip2 compression */
+ int unexpected_binary; /* binary data seen in text_mode */
+ int in_mdc_pkt;
+ int use_mdcbuf_filter;
+ PX_MD *mdc_ctx;
+
+ PGP_PubKey *pub_key; /* ctx owns it */
+ const uint8 *sym_key; /* ctx does not own it */
+ int sym_key_len;
+
+ /*
+ * read or generated data
+ */
+ uint8 sess_key[PGP_MAX_KEY];
+ 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;
+ int bits;
+ int bytes;
+};
+
+struct PGP_PubKey
+{
+ uint8 ver;
+ uint8 time[4];
+ uint8 algo;
+
+ /* public part */
+ union
+ {
+ struct
+ {
+ PGP_MPI *p;
+ PGP_MPI *g;
+ PGP_MPI *y;
+ } elg;
+ struct
+ {
+ PGP_MPI *n;
+ PGP_MPI *e;
+ } rsa;
+ struct
+ {
+ PGP_MPI *p;
+ PGP_MPI *q;
+ PGP_MPI *g;
+ PGP_MPI *y;
+ } dsa;
+ } pub;
+
+ /* secret part */
+ union
+ {
+ struct
+ {
+ PGP_MPI *x;
+ } elg;
+ struct
+ {
+ PGP_MPI *d;
+ PGP_MPI *p;
+ PGP_MPI *q;
+ PGP_MPI *u;
+ } rsa;
+ struct
+ {
+ PGP_MPI *x;
+ } dsa;
+ } sec;
+
+ uint8 key_id[8];
+ int can_encrypt;
+};
+
+int pgp_init(PGP_Context **ctx);
+int pgp_encrypt(PGP_Context *ctx, MBuf *src, MBuf *dst);
+int pgp_decrypt(PGP_Context *ctx, MBuf *src, MBuf *dst);
+int pgp_free(PGP_Context *ctx);
+
+int pgp_get_digest_code(const char *name);
+int pgp_get_cipher_code(const char *name);
+const char *pgp_get_digest_name(int code);
+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);
+int pgp_disable_mdc(PGP_Context *ctx, int disable);
+int pgp_set_sess_key(PGP_Context *ctx, int use);
+int pgp_set_compress_algo(PGP_Context *ctx, int algo);
+int pgp_set_compress_level(PGP_Context *ctx, int level);
+int pgp_set_text_mode(PGP_Context *ctx, int mode);
+int pgp_set_unicode_mode(PGP_Context *ctx, int mode);
+int pgp_get_unicode_mode(PGP_Context *ctx);
+
+int pgp_set_symkey(PGP_Context *ctx, const uint8 *key, int klen);
+int pgp_set_pubkey(PGP_Context *ctx, MBuf *keypkt,
+ const uint8 *key, int klen, int pubtype);
+
+int pgp_get_keyid(MBuf *pgp_data, char *dst);
+
+/* internal functions */
+
+int pgp_load_digest(int c, PX_MD **res);
+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 count);
+int pgp_s2k_read(PullFilter *src, PGP_S2K *s2k);
+int pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int klen);
+
+typedef struct PGP_CFB PGP_CFB;
+int pgp_cfb_create(PGP_CFB **ctx_p, int algo,
+ const uint8 *key, int key_len, int resync, uint8 *iv);
+void pgp_cfb_free(PGP_CFB *ctx);
+int pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
+int pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
+
+void pgp_armor_encode(const uint8 *src, unsigned len, StringInfo dst,
+ int num_headers, char **keys, char **values);
+int pgp_armor_decode(const uint8 *src, int len, StringInfo dst);
+int pgp_extract_armor_headers(const uint8 *src, unsigned len,
+ int *nheaders, char ***keys, char ***values);
+
+int pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst);
+int pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src);
+
+int pgp_key_alloc(PGP_PubKey **pk_p);
+void pgp_key_free(PGP_PubKey *pk);
+int _pgp_read_public_key(PullFilter *pkt, PGP_PubKey **pk_p);
+
+int pgp_parse_pubenc_sesskey(PGP_Context *ctx, PullFilter *pkt);
+int pgp_create_pkt_reader(PullFilter **pf_p, PullFilter *src, int len,
+ int pkttype, PGP_Context *ctx);
+int pgp_parse_pkt_hdr(PullFilter *src, uint8 *tag, int *len_p,
+ int allow_ctx);
+
+int pgp_skip_packet(PullFilter *pkt);
+int pgp_expect_packet_end(PullFilter *pkt);
+
+int pgp_write_pubenc_sesskey(PGP_Context *ctx, PushFilter *dst);
+int pgp_create_pkt_writer(PushFilter *dst, int tag, PushFilter **res_p);
+
+int pgp_mpi_alloc(int bits, PGP_MPI **mpi);
+int pgp_mpi_create(uint8 *data, int bits, PGP_MPI **mpi);
+int pgp_mpi_free(PGP_MPI *mpi);
+int pgp_mpi_read(PullFilter *src, PGP_MPI **mpi);
+int pgp_mpi_write(PushFilter *dst, PGP_MPI *n);
+int pgp_mpi_hash(PX_MD *md, PGP_MPI *n);
+unsigned pgp_mpi_cksum(unsigned cksum, PGP_MPI *n);
+
+int pgp_elgamal_encrypt(PGP_PubKey *pk, PGP_MPI *m,
+ PGP_MPI **c1, PGP_MPI **c2);
+int pgp_elgamal_decrypt(PGP_PubKey *pk, PGP_MPI *c1, PGP_MPI *c2,
+ PGP_MPI **m);
+int pgp_rsa_encrypt(PGP_PubKey *pk, PGP_MPI *m, PGP_MPI **c);
+int pgp_rsa_decrypt(PGP_PubKey *pk, PGP_MPI *c, PGP_MPI **m);
+
+extern struct PullFilterOps pgp_decrypt_filter;
diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c
new file mode 100644
index 0000000..0913ff2
--- /dev/null
+++ b/contrib/pgcrypto/px-crypt.c
@@ -0,0 +1,164 @@
+/*
+ * px-crypt.c
+ * Wrapper for various crypt algorithms.
+ *
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/px-crypt.c
+ */
+
+#include "postgres.h"
+
+#include "px-crypt.h"
+#include "px.h"
+
+static char *
+run_crypt_des(const char *psw, const char *salt,
+ char *buf, unsigned len)
+{
+ char *res;
+
+ res = px_crypt_des(psw, salt);
+ if (res == NULL || strlen(res) > len - 1)
+ return NULL;
+ strcpy(buf, res);
+ return buf;
+}
+
+static char *
+run_crypt_md5(const char *psw, const char *salt,
+ char *buf, unsigned len)
+{
+ char *res;
+
+ res = px_crypt_md5(psw, salt, buf, len);
+ return res;
+}
+
+static char *
+run_crypt_bf(const char *psw, const char *salt,
+ char *buf, unsigned len)
+{
+ char *res;
+
+ res = _crypt_blowfish_rn(psw, salt, buf, len);
+ return res;
+}
+
+struct px_crypt_algo
+{
+ char *id;
+ unsigned id_len;
+ char *(*crypt) (const char *psw, const char *salt,
+ char *buf, unsigned len);
+};
+
+static const struct px_crypt_algo
+ px_crypt_list[] = {
+ {"$2a$", 4, run_crypt_bf},
+ {"$2x$", 4, run_crypt_bf},
+ {"$2$", 3, NULL}, /* N/A */
+ {"$1$", 3, run_crypt_md5},
+ {"_", 1, run_crypt_des},
+ {"", 0, run_crypt_des},
+ {NULL, 0, NULL}
+};
+
+char *
+px_crypt(const char *psw, const char *salt, char *buf, unsigned len)
+{
+ const struct px_crypt_algo *c;
+
+ for (c = px_crypt_list; c->id; c++)
+ {
+ if (!c->id_len)
+ break;
+ if (strncmp(salt, c->id, c->id_len) == 0)
+ break;
+ }
+
+ if (c->crypt == NULL)
+ return NULL;
+
+ return c->crypt(psw, salt, buf, len);
+}
+
+/*
+ * salt generators
+ */
+
+struct generator
+{
+ char *name;
+ char *(*gen) (unsigned long count, const char *input, int size,
+ char *output, int output_size);
+ int input_len;
+ int def_rounds;
+ int min_rounds;
+ int max_rounds;
+};
+
+static struct generator gen_list[] = {
+ {"des", _crypt_gensalt_traditional_rn, 2, 0, 0, 0},
+ {"md5", _crypt_gensalt_md5_rn, 6, 0, 0, 0},
+ {"xdes", _crypt_gensalt_extended_rn, 3, PX_XDES_ROUNDS, 1, 0xFFFFFF},
+ {"bf", _crypt_gensalt_blowfish_rn, 16, PX_BF_ROUNDS, 4, 31},
+ {NULL, NULL, 0, 0, 0, 0}
+};
+
+int
+px_gen_salt(const char *salt_type, char *buf, int rounds)
+{
+ struct generator *g;
+ char *p;
+ char rbuf[16];
+
+ for (g = gen_list; g->name; g++)
+ if (pg_strcasecmp(g->name, salt_type) == 0)
+ break;
+
+ if (g->name == NULL)
+ return PXE_UNKNOWN_SALT_ALGO;
+
+ if (g->def_rounds)
+ {
+ if (rounds == 0)
+ rounds = g->def_rounds;
+
+ if (rounds < g->min_rounds || rounds > g->max_rounds)
+ return PXE_BAD_SALT_ROUNDS;
+ }
+
+ if (!pg_strong_random(rbuf, g->input_len))
+ return PXE_NO_RANDOM;
+
+ p = g->gen(rounds, rbuf, g->input_len, buf, PX_MAX_SALT_LEN);
+ px_memset(rbuf, 0, sizeof(rbuf));
+
+ if (p == NULL)
+ return PXE_BAD_SALT_ROUNDS;
+
+ return strlen(p);
+}
diff --git a/contrib/pgcrypto/px-crypt.h b/contrib/pgcrypto/px-crypt.h
new file mode 100644
index 0000000..08001a8
--- /dev/null
+++ b/contrib/pgcrypto/px-crypt.h
@@ -0,0 +1,82 @@
+/*
+ * px-crypt.h
+ * Header file for px_crypt().
+ *
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/px-crypt.h
+ */
+
+#ifndef _PX_CRYPT_H
+#define _PX_CRYPT_H
+
+/* max room for result */
+#define PX_MAX_CRYPT 128
+
+/* max salt returned by gen_salt() */
+#define PX_MAX_SALT_LEN 128
+
+/* default rounds for xdes salt */
+/* NetBSD bin/passwd/local_passwd.c has (29 * 25)*/
+#define PX_XDES_ROUNDS (29 * 25)
+
+/* default for blowfish salt */
+#define PX_BF_ROUNDS 6
+
+/*
+ * main interface
+ */
+char *px_crypt(const char *psw, const char *salt, char *buf, unsigned buflen);
+int px_gen_salt(const char *salt_type, char *dst, int rounds);
+
+/*
+ * internal functions
+ */
+
+/* crypt-gensalt.c */
+char *_crypt_gensalt_traditional_rn(unsigned long count,
+ const char *input, int size, char *output, int output_size);
+char *_crypt_gensalt_extended_rn(unsigned long count,
+ const char *input, int size, char *output, int output_size);
+char *_crypt_gensalt_md5_rn(unsigned long count,
+ const char *input, int size, char *output, int output_size);
+char *_crypt_gensalt_blowfish_rn(unsigned long count,
+ const char *input, int size, char *output, int output_size);
+
+/* disable 'extended DES crypt' */
+/* #define DISABLE_XDES */
+
+/* crypt-blowfish.c */
+char *_crypt_blowfish_rn(const char *key, const char *setting,
+ char *output, int size);
+
+/* crypt-des.c */
+char *px_crypt_des(const char *key, const char *setting);
+
+/* crypt-md5.c */
+char *px_crypt_md5(const char *pw, const char *salt,
+ char *dst, unsigned dstlen);
+
+#endif /* _PX_CRYPT_H */
diff --git a/contrib/pgcrypto/px-hmac.c b/contrib/pgcrypto/px-hmac.c
new file mode 100644
index 0000000..99174d2
--- /dev/null
+++ b/contrib/pgcrypto/px-hmac.c
@@ -0,0 +1,176 @@
+/*
+ * px-hmac.c
+ * HMAC implementation.
+ *
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/px-hmac.c
+ */
+
+#include "postgres.h"
+
+#include "px.h"
+
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5C
+
+static unsigned
+hmac_result_size(PX_HMAC *h)
+{
+ return px_md_result_size(h->md);
+}
+
+static unsigned
+hmac_block_size(PX_HMAC *h)
+{
+ return px_md_block_size(h->md);
+}
+
+static void
+hmac_init(PX_HMAC *h, const uint8 *key, unsigned klen)
+{
+ unsigned bs,
+ i;
+ uint8 *keybuf;
+ PX_MD *md = h->md;
+
+ bs = px_md_block_size(md);
+ keybuf = palloc0(bs);
+
+ if (klen > bs)
+ {
+ px_md_update(md, key, klen);
+ px_md_finish(md, keybuf);
+ px_md_reset(md);
+ }
+ else
+ memcpy(keybuf, key, klen);
+
+ for (i = 0; i < bs; i++)
+ {
+ h->p.ipad[i] = keybuf[i] ^ HMAC_IPAD;
+ h->p.opad[i] = keybuf[i] ^ HMAC_OPAD;
+ }
+
+ px_memset(keybuf, 0, bs);
+ pfree(keybuf);
+
+ px_md_update(md, h->p.ipad, bs);
+}
+
+static void
+hmac_reset(PX_HMAC *h)
+{
+ PX_MD *md = h->md;
+ unsigned bs = px_md_block_size(md);
+
+ px_md_reset(md);
+ px_md_update(md, h->p.ipad, bs);
+}
+
+static void
+hmac_update(PX_HMAC *h, const uint8 *data, unsigned dlen)
+{
+ px_md_update(h->md, data, dlen);
+}
+
+static void
+hmac_finish(PX_HMAC *h, uint8 *dst)
+{
+ PX_MD *md = h->md;
+ unsigned bs,
+ hlen;
+ uint8 *buf;
+
+ bs = px_md_block_size(md);
+ hlen = px_md_result_size(md);
+
+ buf = palloc(hlen);
+
+ px_md_finish(md, buf);
+
+ px_md_reset(md);
+ px_md_update(md, h->p.opad, bs);
+ px_md_update(md, buf, hlen);
+ px_md_finish(md, dst);
+
+ px_memset(buf, 0, hlen);
+ pfree(buf);
+}
+
+static void
+hmac_free(PX_HMAC *h)
+{
+ unsigned bs;
+
+ bs = px_md_block_size(h->md);
+ px_md_free(h->md);
+
+ px_memset(h->p.ipad, 0, bs);
+ px_memset(h->p.opad, 0, bs);
+ pfree(h->p.ipad);
+ pfree(h->p.opad);
+ pfree(h);
+}
+
+
+/* PUBLIC FUNCTIONS */
+
+int
+px_find_hmac(const char *name, PX_HMAC **res)
+{
+ int err;
+ PX_MD *md;
+ PX_HMAC *h;
+ unsigned bs;
+
+ err = px_find_digest(name, &md);
+ if (err)
+ return err;
+
+ bs = px_md_block_size(md);
+ if (bs < 2)
+ {
+ px_md_free(md);
+ return PXE_HASH_UNUSABLE_FOR_HMAC;
+ }
+
+ h = palloc(sizeof(*h));
+ h->p.ipad = palloc(bs);
+ h->p.opad = palloc(bs);
+ h->md = md;
+
+ h->result_size = hmac_result_size;
+ h->block_size = hmac_block_size;
+ h->reset = hmac_reset;
+ h->update = hmac_update;
+ h->finish = hmac_finish;
+ h->free = hmac_free;
+ h->init = hmac_init;
+
+ *res = h;
+
+ return 0;
+}
diff --git a/contrib/pgcrypto/px.c b/contrib/pgcrypto/px.c
new file mode 100644
index 0000000..3b098c6
--- /dev/null
+++ b/contrib/pgcrypto/px.c
@@ -0,0 +1,341 @@
+/*
+ * px.c
+ * Various cryptographic stuff for PostgreSQL.
+ *
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/px.c
+ */
+
+#include "postgres.h"
+
+#include "px.h"
+
+struct error_desc
+{
+ int err;
+ const char *desc;
+};
+
+static const struct error_desc px_err_list[] = {
+ {PXE_OK, "Everything ok"},
+ {PXE_NO_HASH, "No such hash algorithm"},
+ {PXE_NO_CIPHER, "No such cipher algorithm"},
+ {PXE_BAD_OPTION, "Unknown option"},
+ {PXE_BAD_FORMAT, "Badly formatted type"},
+ {PXE_KEY_TOO_BIG, "Key was too big"},
+ {PXE_CIPHER_INIT, "Cipher cannot be initialized"},
+ {PXE_HASH_UNUSABLE_FOR_HMAC, "This hash algorithm is unusable for HMAC"},
+ {PXE_BUG, "pgcrypto bug"},
+ {PXE_ARGUMENT_ERROR, "Illegal argument to function"},
+ {PXE_UNKNOWN_SALT_ALGO, "Unknown salt algorithm"},
+ {PXE_BAD_SALT_ROUNDS, "Incorrect number of rounds"},
+ {PXE_NO_RANDOM, "Failed to generate strong random bits"},
+ {PXE_DECRYPT_FAILED, "Decryption failed"},
+ {PXE_ENCRYPT_FAILED, "Encryption failed"},
+ {PXE_PGP_CORRUPT_DATA, "Wrong key or corrupt data"},
+ {PXE_PGP_CORRUPT_ARMOR, "Corrupt ascii-armor"},
+ {PXE_PGP_UNSUPPORTED_COMPR, "Unsupported compression algorithm"},
+ {PXE_PGP_UNSUPPORTED_CIPHER, "Unsupported cipher algorithm"},
+ {PXE_PGP_UNSUPPORTED_HASH, "Unsupported digest algorithm"},
+ {PXE_PGP_COMPRESSION_ERROR, "Compression error"},
+ {PXE_PGP_NOT_TEXT, "Not text data"},
+ {PXE_PGP_UNEXPECTED_PKT, "Unexpected packet in key data"},
+ {PXE_PGP_MATH_FAILED, "Math operation failed"},
+ {PXE_PGP_SHORT_ELGAMAL_KEY, "Elgamal keys must be at least 1024 bits long"},
+ {PXE_PGP_UNKNOWN_PUBALGO, "Unknown public-key encryption algorithm"},
+ {PXE_PGP_WRONG_KEY, "Wrong key"},
+ {PXE_PGP_MULTIPLE_KEYS,
+ "Several keys given - pgcrypto does not handle keyring"},
+ {PXE_PGP_EXPECT_PUBLIC_KEY, "Refusing to encrypt with secret key"},
+ {PXE_PGP_EXPECT_SECRET_KEY, "Cannot decrypt with public key"},
+ {PXE_PGP_NOT_V4_KEYPKT, "Only V4 key packets are supported"},
+ {PXE_PGP_KEYPKT_CORRUPT, "Corrupt key packet"},
+ {PXE_PGP_NO_USABLE_KEY, "No encryption key found"},
+ {PXE_PGP_NEED_SECRET_PSW, "Need password for secret key"},
+ {PXE_PGP_BAD_S2K_MODE, "Bad S2K mode"},
+ {PXE_PGP_UNSUPPORTED_PUBALGO, "Unsupported public key algorithm"},
+ {PXE_PGP_MULTIPLE_SUBKEYS, "Several subkeys not supported"},
+
+ {0, NULL},
+};
+
+/*
+ * Call ereport(ERROR, ...), with an error code and message corresponding to
+ * the PXE_* error code given as argument.
+ *
+ * This is similar to px_strerror(err), but for some errors, we fill in the
+ * error code and detail fields more appropriately.
+ */
+void
+px_THROW_ERROR(int err)
+{
+ if (err == PXE_NO_RANDOM)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("could not generate a random number")));
+ }
+ else
+ {
+ /* For other errors, use the message from the above list. */
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("%s", px_strerror(err))));
+ }
+}
+
+const char *
+px_strerror(int err)
+{
+ const struct error_desc *e;
+
+ for (e = px_err_list; e->desc; e++)
+ if (e->err == err)
+ return e->desc;
+ return "Bad error code";
+}
+
+/* memset that must not be optimized away */
+void
+px_memset(void *ptr, int c, size_t len)
+{
+ memset(ptr, c, len);
+}
+
+const char *
+px_resolve_alias(const PX_Alias *list, const char *name)
+{
+ while (list->name)
+ {
+ if (pg_strcasecmp(list->alias, name) == 0)
+ return list->name;
+ list++;
+ }
+ return name;
+}
+
+static void (*debug_handler) (const char *) = NULL;
+
+void
+px_set_debug_handler(void (*handler) (const char *))
+{
+ debug_handler = handler;
+}
+
+void
+px_debug(const char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug_handler)
+ {
+ char buf[512];
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ debug_handler(buf);
+ }
+ va_end(ap);
+}
+
+/*
+ * combo - cipher + padding (+ checksum)
+ */
+
+static unsigned
+combo_encrypt_len(PX_Combo *cx, unsigned dlen)
+{
+ return dlen + 512;
+}
+
+static unsigned
+combo_decrypt_len(PX_Combo *cx, unsigned dlen)
+{
+ return dlen;
+}
+
+static int
+combo_init(PX_Combo *cx, const uint8 *key, unsigned klen,
+ const uint8 *iv, unsigned ivlen)
+{
+ int err;
+ unsigned ks,
+ ivs;
+ PX_Cipher *c = cx->cipher;
+ uint8 *ivbuf = NULL;
+ uint8 *keybuf;
+
+ ks = px_cipher_key_size(c);
+
+ ivs = px_cipher_iv_size(c);
+ if (ivs > 0)
+ {
+ ivbuf = palloc0(ivs);
+ if (ivlen > ivs)
+ memcpy(ivbuf, iv, ivs);
+ else if (ivlen > 0)
+ memcpy(ivbuf, iv, ivlen);
+ }
+
+ if (klen > ks)
+ klen = ks;
+ keybuf = palloc0(ks);
+ memset(keybuf, 0, ks);
+ memcpy(keybuf, key, klen);
+
+ err = px_cipher_init(c, keybuf, klen, ivbuf);
+
+ if (ivbuf)
+ pfree(ivbuf);
+ pfree(keybuf);
+
+ return err;
+}
+
+static int
+combo_encrypt(PX_Combo *cx, const uint8 *data, unsigned dlen,
+ uint8 *res, unsigned *rlen)
+{
+ return px_cipher_encrypt(cx->cipher, cx->padding, data, dlen, res, rlen);
+}
+
+static int
+combo_decrypt(PX_Combo *cx, const uint8 *data, unsigned dlen,
+ uint8 *res, unsigned *rlen)
+{
+ return px_cipher_decrypt(cx->cipher, cx->padding, data, dlen, res, rlen);
+}
+
+static void
+combo_free(PX_Combo *cx)
+{
+ if (cx->cipher)
+ px_cipher_free(cx->cipher);
+ px_memset(cx, 0, sizeof(*cx));
+ pfree(cx);
+}
+
+/* PARSER */
+
+static int
+parse_cipher_name(char *full, char **cipher, char **pad)
+{
+ char *p,
+ *p2,
+ *q;
+
+ *cipher = full;
+ *pad = NULL;
+
+ p = strchr(full, '/');
+ if (p != NULL)
+ *p++ = 0;
+ while (p != NULL)
+ {
+ if ((q = strchr(p, '/')) != NULL)
+ *q++ = 0;
+
+ if (!*p)
+ {
+ p = q;
+ continue;
+ }
+ p2 = strchr(p, ':');
+ if (p2 != NULL)
+ {
+ *p2++ = 0;
+ if (strcmp(p, "pad") == 0)
+ *pad = p2;
+ else
+ return PXE_BAD_OPTION;
+ }
+ else
+ return PXE_BAD_FORMAT;
+
+ p = q;
+ }
+ return 0;
+}
+
+/* provider */
+
+int
+px_find_combo(const char *name, PX_Combo **res)
+{
+ int err;
+ char *buf,
+ *s_cipher,
+ *s_pad;
+
+ PX_Combo *cx;
+
+ cx = palloc0(sizeof(*cx));
+ buf = pstrdup(name);
+
+ err = parse_cipher_name(buf, &s_cipher, &s_pad);
+ if (err)
+ {
+ pfree(buf);
+ pfree(cx);
+ return err;
+ }
+
+ err = px_find_cipher(s_cipher, &cx->cipher);
+ if (err)
+ goto err1;
+
+ if (s_pad != NULL)
+ {
+ if (strcmp(s_pad, "pkcs") == 0)
+ cx->padding = 1;
+ else if (strcmp(s_pad, "none") == 0)
+ cx->padding = 0;
+ else
+ goto err1;
+ }
+ else
+ cx->padding = 1;
+
+ cx->init = combo_init;
+ cx->encrypt = combo_encrypt;
+ cx->decrypt = combo_decrypt;
+ cx->encrypt_len = combo_encrypt_len;
+ cx->decrypt_len = combo_decrypt_len;
+ cx->free = combo_free;
+
+ pfree(buf);
+
+ *res = cx;
+
+ return 0;
+
+err1:
+ if (cx->cipher)
+ px_cipher_free(cx->cipher);
+ pfree(cx);
+ pfree(buf);
+ return PXE_NO_CIPHER;
+}
diff --git a/contrib/pgcrypto/px.h b/contrib/pgcrypto/px.h
new file mode 100644
index 0000000..4ef40f3
--- /dev/null
+++ b/contrib/pgcrypto/px.h
@@ -0,0 +1,228 @@
+/*
+ * px.h
+ * Header file for pgcrypto.
+ *
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/px.h
+ */
+
+#ifndef __PX_H
+#define __PX_H
+
+#include <sys/param.h>
+
+/* keep debug messages? */
+#define PX_DEBUG
+
+/* max salt returned */
+#define PX_MAX_SALT_LEN 128
+
+/*
+ * PX error codes
+ */
+#define PXE_OK 0
+/* -1 is unused */
+#define PXE_NO_HASH -2
+#define PXE_NO_CIPHER -3
+/* -4 is unused */
+#define PXE_BAD_OPTION -5
+#define PXE_BAD_FORMAT -6
+#define PXE_KEY_TOO_BIG -7
+#define PXE_CIPHER_INIT -8
+#define PXE_HASH_UNUSABLE_FOR_HMAC -9
+/* -10 is unused */
+/* -11 is unused */
+#define PXE_BUG -12
+#define PXE_ARGUMENT_ERROR -13
+#define PXE_UNKNOWN_SALT_ALGO -14
+#define PXE_BAD_SALT_ROUNDS -15
+/* -16 is unused */
+#define PXE_NO_RANDOM -17
+#define PXE_DECRYPT_FAILED -18
+#define PXE_ENCRYPT_FAILED -19
+
+#define PXE_PGP_CORRUPT_DATA -100
+#define PXE_PGP_CORRUPT_ARMOR -101
+#define PXE_PGP_UNSUPPORTED_COMPR -102
+#define PXE_PGP_UNSUPPORTED_CIPHER -103
+#define PXE_PGP_UNSUPPORTED_HASH -104
+#define PXE_PGP_COMPRESSION_ERROR -105
+#define PXE_PGP_NOT_TEXT -106
+#define PXE_PGP_UNEXPECTED_PKT -107
+/* -108 is unused */
+#define PXE_PGP_MATH_FAILED -109
+#define PXE_PGP_SHORT_ELGAMAL_KEY -110
+/* -111 is unused */
+#define PXE_PGP_UNKNOWN_PUBALGO -112
+#define PXE_PGP_WRONG_KEY -113
+#define PXE_PGP_MULTIPLE_KEYS -114
+#define PXE_PGP_EXPECT_PUBLIC_KEY -115
+#define PXE_PGP_EXPECT_SECRET_KEY -116
+#define PXE_PGP_NOT_V4_KEYPKT -117
+#define PXE_PGP_KEYPKT_CORRUPT -118
+#define PXE_PGP_NO_USABLE_KEY -119
+#define PXE_PGP_NEED_SECRET_PSW -120
+#define PXE_PGP_BAD_S2K_MODE -121
+#define PXE_PGP_UNSUPPORTED_PUBALGO -122
+#define PXE_PGP_MULTIPLE_SUBKEYS -123
+
+
+typedef struct px_digest PX_MD;
+typedef struct px_alias PX_Alias;
+typedef struct px_hmac PX_HMAC;
+typedef struct px_cipher PX_Cipher;
+typedef struct px_combo PX_Combo;
+
+struct px_digest
+{
+ unsigned (*result_size) (PX_MD *h);
+ unsigned (*block_size) (PX_MD *h);
+ void (*reset) (PX_MD *h);
+ void (*update) (PX_MD *h, const uint8 *data, unsigned dlen);
+ void (*finish) (PX_MD *h, uint8 *dst);
+ void (*free) (PX_MD *h);
+ /* private */
+ union
+ {
+ unsigned code;
+ void *ptr;
+ } p;
+};
+
+struct px_alias
+{
+ char *alias;
+ char *name;
+};
+
+struct px_hmac
+{
+ unsigned (*result_size) (PX_HMAC *h);
+ unsigned (*block_size) (PX_HMAC *h);
+ void (*reset) (PX_HMAC *h);
+ void (*update) (PX_HMAC *h, const uint8 *data, unsigned dlen);
+ void (*finish) (PX_HMAC *h, uint8 *dst);
+ void (*free) (PX_HMAC *h);
+ void (*init) (PX_HMAC *h, const uint8 *key, unsigned klen);
+
+ PX_MD *md;
+ /* private */
+ struct
+ {
+ uint8 *ipad;
+ uint8 *opad;
+ } p;
+};
+
+struct px_cipher
+{
+ unsigned (*block_size) (PX_Cipher *c);
+ unsigned (*key_size) (PX_Cipher *c); /* max key len */
+ unsigned (*iv_size) (PX_Cipher *c);
+
+ int (*init) (PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv);
+ int (*encrypt) (PX_Cipher *c, int padding, const uint8 *data, unsigned dlen, uint8 *res, unsigned *rlen);
+ int (*decrypt) (PX_Cipher *c, int padding, const uint8 *data, unsigned dlen, uint8 *res, unsigned *rlen);
+ void (*free) (PX_Cipher *c);
+ /* private */
+ void *ptr;
+ int pstat; /* mcrypt uses it */
+};
+
+struct px_combo
+{
+ int (*init) (PX_Combo *cx, const uint8 *key, unsigned klen,
+ const uint8 *iv, unsigned ivlen);
+ int (*encrypt) (PX_Combo *cx, const uint8 *data, unsigned dlen,
+ uint8 *res, unsigned *rlen);
+ int (*decrypt) (PX_Combo *cx, const uint8 *data, unsigned dlen,
+ uint8 *res, unsigned *rlen);
+ unsigned (*encrypt_len) (PX_Combo *cx, unsigned dlen);
+ unsigned (*decrypt_len) (PX_Combo *cx, unsigned dlen);
+ void (*free) (PX_Combo *cx);
+
+ PX_Cipher *cipher;
+ unsigned padding;
+};
+
+int px_find_digest(const char *name, PX_MD **res);
+int px_find_hmac(const char *name, PX_HMAC **res);
+int px_find_cipher(const char *name, PX_Cipher **res);
+int px_find_combo(const char *name, PX_Combo **res);
+
+void px_THROW_ERROR(int err) pg_attribute_noreturn();
+const char *px_strerror(int err);
+
+const char *px_resolve_alias(const PX_Alias *aliases, const char *name);
+
+void px_set_debug_handler(void (*handler) (const char *));
+
+void px_memset(void *ptr, int c, size_t len);
+
+#ifdef PX_DEBUG
+void px_debug(const char *fmt,...) pg_attribute_printf(1, 2);
+#else
+#define px_debug(...)
+#endif
+
+#define px_md_result_size(md) (md)->result_size(md)
+#define px_md_block_size(md) (md)->block_size(md)
+#define px_md_reset(md) (md)->reset(md)
+#define px_md_update(md, data, dlen) (md)->update(md, data, dlen)
+#define px_md_finish(md, buf) (md)->finish(md, buf)
+#define px_md_free(md) (md)->free(md)
+
+#define px_hmac_result_size(hmac) (hmac)->result_size(hmac)
+#define px_hmac_block_size(hmac) (hmac)->block_size(hmac)
+#define px_hmac_reset(hmac) (hmac)->reset(hmac)
+#define px_hmac_init(hmac, key, klen) (hmac)->init(hmac, key, klen)
+#define px_hmac_update(hmac, data, dlen) (hmac)->update(hmac, data, dlen)
+#define px_hmac_finish(hmac, buf) (hmac)->finish(hmac, buf)
+#define px_hmac_free(hmac) (hmac)->free(hmac)
+
+
+#define px_cipher_key_size(c) (c)->key_size(c)
+#define px_cipher_block_size(c) (c)->block_size(c)
+#define px_cipher_iv_size(c) (c)->iv_size(c)
+#define px_cipher_init(c, k, klen, iv) (c)->init(c, k, klen, iv)
+#define px_cipher_encrypt(c, padding, data, dlen, res, rlen) \
+ (c)->encrypt(c, padding, data, dlen, res, rlen)
+#define px_cipher_decrypt(c, padding, data, dlen, res, rlen) \
+ (c)->decrypt(c, padding, data, dlen, res, rlen)
+#define px_cipher_free(c) (c)->free(c)
+
+
+#define px_combo_encrypt_len(c, dlen) (c)->encrypt_len(c, dlen)
+#define px_combo_decrypt_len(c, dlen) (c)->decrypt_len(c, dlen)
+#define px_combo_init(c, key, klen, iv, ivlen) \
+ (c)->init(c, key, klen, iv, ivlen)
+#define px_combo_encrypt(c, data, dlen, res, rlen) \
+ (c)->encrypt(c, data, dlen, res, rlen)
+#define px_combo_decrypt(c, data, dlen, res, rlen) \
+ (c)->decrypt(c, data, dlen, res, rlen)
+#define px_combo_free(c) (c)->free(c)
+
+#endif /* __PX_H */
diff --git a/contrib/pgcrypto/sql/3des.sql b/contrib/pgcrypto/sql/3des.sql
new file mode 100644
index 0000000..1b71a10
--- /dev/null
+++ b/contrib/pgcrypto/sql/3des.sql
@@ -0,0 +1,25 @@
+--
+-- 3DES cipher
+--
+
+-- test vector from somewhere
+SELECT encrypt('\x8000000000000000',
+ '\x010101010101010101010101010101010101010101010101',
+ '3des-ecb/pad:none');
+
+select encrypt('', 'foo', '3des');
+-- 10 bytes key
+select encrypt('foo', '0123456789', '3des');
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', '3des');
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', '3des'), '0123456', '3des'), 'escape');
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', '3des');
+select encode(decrypt_iv('\x50735067b073bb93', '0123456', 'abcd', '3des'), 'escape');
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789012345678901', '3des');
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789012345678901', '3des'), '0123456789012345678901', '3des'), 'escape');
diff --git a/contrib/pgcrypto/sql/blowfish.sql b/contrib/pgcrypto/sql/blowfish.sql
new file mode 100644
index 0000000..c212cf2
--- /dev/null
+++ b/contrib/pgcrypto/sql/blowfish.sql
@@ -0,0 +1,53 @@
+--
+-- Blowfish cipher
+--
+
+-- some standard Blowfish testvalues
+SELECT encrypt('\x0000000000000000', '\x0000000000000000', 'bf-ecb/pad:none');
+SELECT encrypt('\xffffffffffffffff', '\xffffffffffffffff', 'bf-ecb/pad:none');
+SELECT encrypt('\x1000000000000001', '\x3000000000000000', 'bf-ecb/pad:none');
+SELECT encrypt('\x1111111111111111', '\x1111111111111111', 'bf-ecb/pad:none');
+SELECT encrypt('\x0123456789abcdef', '\xfedcba9876543210', 'bf-ecb/pad:none');
+SELECT encrypt('\x01a1d6d039776742', '\xfedcba9876543210', 'bf-ecb/pad:none');
+SELECT encrypt('\xffffffffffffffff', '\x0000000000000000', 'bf-ecb/pad:none');
+
+-- setkey
+SELECT encrypt('\xfedcba9876543210', '\xf0e1d2c3b4a5968778695a4b3c2d1e0f', 'bf-ecb/pad:none');
+
+-- with padding
+SELECT encrypt('\x01234567890123456789', '\x33443344334433443344334433443344', 'bf-ecb');
+
+-- cbc
+
+-- 28 bytes key
+SELECT encrypt('\x6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5',
+ '\x37363534333231204e6f77206973207468652074696d6520666f7220',
+ 'bf-cbc');
+
+-- 29 bytes key
+SELECT encrypt('\x6b77b4d63006dee605b156e27403979358deb9e7154616d959f1652bd5ff92cc',
+ '\x37363534333231204e6f77206973207468652074696d6520666f722000',
+ 'bf-cbc');
+
+-- blowfish-448
+SELECT encrypt('\xfedcba9876543210',
+ '\xf0e1d2c3b4a5968778695a4b3c2d1e0f001122334455667704689104c2fd3b2f584023641aba61761f1f1f1f0e0e0e0effffffffffffffff',
+ 'bf-ecb/pad:none');
+
+-- empty data
+select encrypt('', 'foo', 'bf');
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'bf');
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', 'bf');
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'bf'), '0123456', 'bf'), 'escape');
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'bf');
+select encode(decrypt_iv('\x95c7e89322525d59', '0123456', 'abcd', 'bf'), 'escape');
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'bf');
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'bf'), '0123456789', 'bf'), 'escape');
diff --git a/contrib/pgcrypto/sql/cast5.sql b/contrib/pgcrypto/sql/cast5.sql
new file mode 100644
index 0000000..b3089b2
--- /dev/null
+++ b/contrib/pgcrypto/sql/cast5.sql
@@ -0,0 +1,32 @@
+--
+-- Cast5 cipher
+--
+
+-- test vectors from RFC2144
+
+-- 128 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x0123456712345678234567893456789A', 'cast5-ecb/pad:none');
+
+-- 80 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x01234567123456782345', 'cast5-ecb/pad:none');
+
+-- 40 bit key
+SELECT encrypt('\x0123456789ABCDEF', '\x0123456712', 'cast5-ecb/pad:none');
+
+-- cbc
+
+-- empty data
+select encrypt('', 'foo', 'cast5');
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'cast5');
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'cast5'), '0123456', 'cast5'), 'escape');
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'cast5');
+select encode(decrypt_iv('\x384a970695ce016a', '0123456', 'abcd', 'cast5'), 'escape');
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'cast5');
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'cast5'), '0123456789', 'cast5'), 'escape');
diff --git a/contrib/pgcrypto/sql/crypt-blowfish.sql b/contrib/pgcrypto/sql/crypt-blowfish.sql
new file mode 100644
index 0000000..3b5a681
--- /dev/null
+++ b/contrib/pgcrypto/sql/crypt-blowfish.sql
@@ -0,0 +1,26 @@
+--
+-- crypt() and gen_salt(): bcrypt
+--
+
+SELECT crypt('', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
+
+SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
+
+-- error, salt too short:
+SELECT crypt('foox', '$2a$');
+
+-- error, first digit of count in salt invalid
+SELECT crypt('foox', '$2a$40$RQiOJ.3ELirrXwxIZY8q0O');
+
+-- error, count in salt too small
+SELECT crypt('foox', '$2a$00$RQiOJ.3ELirrXwxIZY8q0O');
+
+CREATE TABLE ctest (data text, res text, salt text);
+INSERT INTO ctest VALUES ('password', '', '');
+
+UPDATE ctest SET salt = gen_salt('bf', 8);
+UPDATE ctest SET res = crypt(data, salt);
+SELECT res = crypt(data, res) AS "worked"
+FROM ctest;
+
+DROP TABLE ctest;
diff --git a/contrib/pgcrypto/sql/crypt-des.sql b/contrib/pgcrypto/sql/crypt-des.sql
new file mode 100644
index 0000000..a85ec1e
--- /dev/null
+++ b/contrib/pgcrypto/sql/crypt-des.sql
@@ -0,0 +1,21 @@
+--
+-- crypt() and gen_salt(): crypt-des
+--
+
+SELECT crypt('', 'NB');
+
+SELECT crypt('foox', 'NB');
+
+-- We are supposed to pass in a 2-character salt.
+-- error since salt is too short:
+SELECT crypt('password', 'a');
+
+CREATE TABLE ctest (data text, res text, salt text);
+INSERT INTO ctest VALUES ('password', '', '');
+
+UPDATE ctest SET salt = gen_salt('des');
+UPDATE ctest SET res = crypt(data, salt);
+SELECT res = crypt(data, res) AS "worked"
+FROM ctest;
+
+DROP TABLE ctest;
diff --git a/contrib/pgcrypto/sql/crypt-md5.sql b/contrib/pgcrypto/sql/crypt-md5.sql
new file mode 100644
index 0000000..ba7befb
--- /dev/null
+++ b/contrib/pgcrypto/sql/crypt-md5.sql
@@ -0,0 +1,17 @@
+--
+-- crypt() and gen_salt(): md5
+--
+
+SELECT crypt('', '$1$Szzz0yzz');
+
+SELECT crypt('foox', '$1$Szzz0yzz');
+
+CREATE TABLE ctest (data text, res text, salt text);
+INSERT INTO ctest VALUES ('password', '', '');
+
+UPDATE ctest SET salt = gen_salt('md5');
+UPDATE ctest SET res = crypt(data, salt);
+SELECT res = crypt(data, res) AS "worked"
+FROM ctest;
+
+DROP TABLE ctest;
diff --git a/contrib/pgcrypto/sql/crypt-xdes.sql b/contrib/pgcrypto/sql/crypt-xdes.sql
new file mode 100644
index 0000000..8171cd8
--- /dev/null
+++ b/contrib/pgcrypto/sql/crypt-xdes.sql
@@ -0,0 +1,33 @@
+--
+-- crypt() and gen_salt(): extended des
+--
+
+SELECT crypt('', '_J9..j2zz');
+
+SELECT crypt('foox', '_J9..j2zz');
+
+-- check XDES handling of keys longer than 8 chars
+SELECT crypt('longlongpassword', '_J9..j2zz');
+
+-- error, salt too short
+SELECT crypt('foox', '_J9..BWH');
+
+-- error, count specified in the second argument is 0
+SELECT crypt('password', '_........');
+
+-- error, count will wind up still being 0 due to invalid encoding
+-- of the count: only chars ``./0-9A-Za-z' are valid
+SELECT crypt('password', '_..!!!!!!');
+
+-- count should be non-zero here, will work
+SELECT crypt('password', '_/!!!!!!!');
+
+CREATE TABLE ctest (data text, res text, salt text);
+INSERT INTO ctest VALUES ('password', '', '');
+
+UPDATE ctest SET salt = gen_salt('xdes', 1001);
+UPDATE ctest SET res = crypt(data, salt);
+SELECT res = crypt(data, res) AS "worked"
+FROM ctest;
+
+DROP TABLE ctest;
diff --git a/contrib/pgcrypto/sql/des.sql b/contrib/pgcrypto/sql/des.sql
new file mode 100644
index 0000000..08c77a4
--- /dev/null
+++ b/contrib/pgcrypto/sql/des.sql
@@ -0,0 +1,24 @@
+--
+-- DES cipher
+--
+
+-- no official test vectors atm
+
+-- from blowfish.sql
+SELECT encrypt('\x0123456789abcdef', '\xfedcba9876543210', 'des-ecb/pad:none');
+
+-- empty data
+select encrypt('', 'foo', 'des');
+-- 8 bytes key
+select encrypt('foo', '01234589', 'des');
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'des'), '0123456', 'des'), 'escape');
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'des');
+select encode(decrypt_iv('\x50735067b073bb93', '0123456', 'abcd', 'des'), 'escape');
+
+-- long message
+select encrypt('Lets try a longer message.', '01234567', 'des');
+select encode(decrypt(encrypt('Lets try a longer message.', '01234567', 'des'), '01234567', 'des'), 'escape');
diff --git a/contrib/pgcrypto/sql/hmac-md5.sql b/contrib/pgcrypto/sql/hmac-md5.sql
new file mode 100644
index 0000000..981ed09
--- /dev/null
+++ b/contrib/pgcrypto/sql/hmac-md5.sql
@@ -0,0 +1,44 @@
+--
+-- HMAC-MD5
+--
+
+SELECT hmac(
+'Hi There',
+'\x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'::bytea,
+'md5');
+
+-- 2
+SELECT hmac(
+'Jefe',
+'what do ya want for nothing?',
+'md5');
+
+-- 3
+SELECT hmac(
+'\xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'::bytea,
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'md5');
+
+-- 4
+SELECT hmac(
+'\xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd'::bytea,
+'\x0102030405060708090a0b0c0d0e0f10111213141516171819'::bytea,
+'md5');
+
+-- 5
+SELECT hmac(
+'Test With Truncation',
+'\x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c'::bytea,
+'md5');
+
+-- 6
+SELECT hmac(
+'Test Using Larger Than Block-Size Key - Hash Key First',
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'md5');
+
+-- 7
+SELECT hmac(
+'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data',
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'md5');
diff --git a/contrib/pgcrypto/sql/hmac-sha1.sql b/contrib/pgcrypto/sql/hmac-sha1.sql
new file mode 100644
index 0000000..f9a7b71
--- /dev/null
+++ b/contrib/pgcrypto/sql/hmac-sha1.sql
@@ -0,0 +1,44 @@
+--
+-- HMAC-SHA1
+--
+
+SELECT hmac(
+'Hi There',
+'\x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b'::bytea,
+'sha1');
+
+-- 2
+SELECT hmac(
+'Jefe',
+'what do ya want for nothing?',
+'sha1');
+
+-- 3
+SELECT hmac(
+'\xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd'::bytea,
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'sha1');
+
+-- 4
+SELECT hmac(
+'\xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd'::bytea,
+'\x0102030405060708090a0b0c0d0e0f10111213141516171819'::bytea,
+'sha1');
+
+-- 5
+SELECT hmac(
+'Test With Truncation',
+'\x0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c'::bytea,
+'sha1');
+
+-- 6
+SELECT hmac(
+'Test Using Larger Than Block-Size Key - Hash Key First',
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'sha1');
+
+-- 7
+SELECT hmac(
+'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data',
+'\xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'::bytea,
+'sha1');
diff --git a/contrib/pgcrypto/sql/init.sql b/contrib/pgcrypto/sql/init.sql
new file mode 100644
index 0000000..6388187
--- /dev/null
+++ b/contrib/pgcrypto/sql/init.sql
@@ -0,0 +1,11 @@
+--
+-- init pgcrypto
+--
+
+CREATE EXTENSION pgcrypto;
+
+-- check error handling
+select gen_salt('foo');
+select digest('foo', 'foo');
+select hmac('foo', 'foo', 'foo');
+select encrypt('foo', 'foo', 'foo');
diff --git a/contrib/pgcrypto/sql/md5.sql b/contrib/pgcrypto/sql/md5.sql
new file mode 100644
index 0000000..0403613
--- /dev/null
+++ b/contrib/pgcrypto/sql/md5.sql
@@ -0,0 +1,11 @@
+--
+-- MD5 message digest
+--
+
+SELECT digest('', 'md5');
+SELECT digest('a', 'md5');
+SELECT digest('abc', 'md5');
+SELECT digest('message digest', 'md5');
+SELECT digest('abcdefghijklmnopqrstuvwxyz', 'md5');
+SELECT digest('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'md5');
+SELECT digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'md5');
diff --git a/contrib/pgcrypto/sql/pgp-armor.sql b/contrib/pgcrypto/sql/pgp-armor.sql
new file mode 100644
index 0000000..736b542
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-armor.sql
@@ -0,0 +1,214 @@
+--
+-- PGP Armor
+--
+
+select armor('');
+select armor('test');
+select encode(dearmor(armor('')), 'escape');
+select encode(dearmor(armor('zooka')), 'escape');
+
+select armor('0123456789abcdef0123456789abcdef0123456789abcdef
+0123456789abcdef0123456789abcdef0123456789abcdef');
+
+-- lots formatting
+select encode(dearmor(' a pgp msg:
+
+-----BEGIN PGP MESSAGE-----
+Comment: Some junk
+
+em9va2E=
+
+ =D5cR
+
+-----END PGP MESSAGE-----'), 'escape');
+
+-- lots messages
+select encode(dearmor('
+wrong packet:
+ -----BEGIN PGP MESSAGE-----
+
+ d3Jvbmc=
+ =vCYP
+ -----END PGP MESSAGE-----
+
+right packet:
+-----BEGIN PGP MESSAGE-----
+
+cmlnaHQ=
+=nbpj
+-----END PGP MESSAGE-----
+
+use only first packet
+-----BEGIN PGP MESSAGE-----
+
+d3Jvbmc=
+=vCYP
+-----END PGP MESSAGE-----
+'), 'escape');
+
+-- bad crc
+select dearmor('
+-----BEGIN PGP MESSAGE-----
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- corrupt (no space after the colon)
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+foo:
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- corrupt (no empty line)
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- no headers
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- header with empty value
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+foo:
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- simple
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+fookey: foovalue
+barkey: barvalue
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- insane keys, part 1
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+insane:key :
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- insane keys, part 2
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+insane:key : text value here
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- long value
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+long: this value is more than 76 characters long, but it should still parse correctly as that''s permitted by RFC 4880
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- long value, split up
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+long: this value is more than 76 characters long, but it should still
+long: parse correctly as that''s permitted by RFC 4880
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- long value, split up, part 2
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+long: this value is more than
+long: 76 characters long, but it should still
+long: parse correctly as that''s permitted by RFC 4880
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+-- long value, split up, part 3
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+emptykey:
+long: this value is more than
+emptykey:
+long: 76 characters long, but it should still
+emptykey:
+long: parse correctly as that''s permitted by RFC 4880
+emptykey:
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+
+select * from pgp_armor_headers('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.blowfish.sha1.mdc.s2k3.z0
+
+jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
+yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
+=JcP+
+-----END PGP MESSAGE-----
+');
+
+-- test CR+LF line endings
+select * from pgp_armor_headers(replace('
+-----BEGIN PGP MESSAGE-----
+fookey: foovalue
+barkey: barvalue
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+', E'\n', E'\r\n'));
+
+-- test header generation
+select armor('zooka', array['foo'], array['bar']);
+select armor('zooka', array['Version', 'Comment'], array['Created by pgcrypto', 'PostgreSQL, the world''s most advanced open source database']);
+select * from pgp_armor_headers(
+ armor('zooka', array['Version', 'Comment'],
+ array['Created by pgcrypto', 'PostgreSQL, the world''s most advanced open source database']));
+
+-- error/corner cases
+select armor('', array['foo'], array['too', 'many']);
+select armor('', array['too', 'many'], array['foo']);
+select armor('', array[['']], array['foo']);
+select armor('', array['foo'], array[['']]);
+select armor('', array[null], array['foo']);
+select armor('', array['foo'], array[null]);
+select armor('', '[0:0]={"foo"}', array['foo']);
+select armor('', array['foo'], '[0:0]={"foo"}');
+select armor('', array[E'embedded\nnewline'], array['foo']);
+select armor('', array['foo'], array[E'embedded\nnewline']);
+select armor('', array['embedded: colon+space'], array['foo']);
diff --git a/contrib/pgcrypto/sql/pgp-compression.sql b/contrib/pgcrypto/sql/pgp-compression.sql
new file mode 100644
index 0000000..87c59c6
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-compression.sql
@@ -0,0 +1,51 @@
+--
+-- PGP compression support
+--
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0ECQMCsci6AdHnELlh0kQB4jFcVwHMJg0Bulop7m3Mi36s15TAhBo0AnzIrRFrdLVCkKohsS6+
+DMcmR53SXfLoDJOv/M8uKj3QSq7oWNIp95pxfA==
+=tbSn
+-----END PGP MESSAGE-----
+'), 'key', 'expect-compress-algo=1');
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret message', 'key', 'compress-algo=0'),
+ 'key', 'expect-compress-algo=0');
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret message', 'key', 'compress-algo=1'),
+ 'key', 'expect-compress-algo=1');
+
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret message', 'key', 'compress-algo=2'),
+ 'key', 'expect-compress-algo=2');
+
+-- level=0 should turn compression off
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret message', 'key',
+ 'compress-algo=2, compress-level=0'),
+ 'key', 'expect-compress-algo=0');
+
+-- check corner case involving an input string of 16kB, as per bug #16476.
+SELECT setseed(0);
+WITH random_string AS
+(
+ -- This generates a random string of 16366 bytes. This is chosen
+ -- as random so that it does not get compressed, and the decompression
+ -- would work on a string with the same length as the origin, making the
+ -- test behavior more predictible. lpad() ensures that the generated
+ -- hexadecimal value is completed by extra zero characters if random()
+ -- has generated a value strictly lower than 16.
+ SELECT string_agg(decode(lpad(to_hex((random()*256)::int), 2, '0'), 'hex'), '') as bytes
+ FROM generate_series(0, 16365)
+)
+SELECT bytes =
+ pgp_sym_decrypt_bytea(
+ pgp_sym_encrypt_bytea(bytes, 'key',
+ 'compress-algo=1,compress-level=1'),
+ 'key', 'expect-compress-algo=1')
+ AS is_same
+ FROM random_string;
diff --git a/contrib/pgcrypto/sql/pgp-decrypt.sql b/contrib/pgcrypto/sql/pgp-decrypt.sql
new file mode 100644
index 0000000..49a0267
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-decrypt.sql
@@ -0,0 +1,309 @@
+--
+-- pgp decrypt tests
+--
+
+-- Checking ciphers
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.blowfish.sha1.mdc.s2k3.z0
+
+jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
+yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
+=JcP+
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCci97v0Q6Z0Zg0kQBsVf5Oe3iC+FBzUmuMV9KxmAyOMyjCc/5i8f1Eest
+UTAsG35A1vYs02VARKzGz6xI2UHwFUirP+brPBg3Ee7muOx8pA==
+=XtrP
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k3.z0
+
+jA0ECAMCI7YQpWqp3D1g0kQBCjB7GlX7+SQeXNleXeXQ78ZAPNliquGDq9u378zI
+5FPTqAhIB2/2fjY8QEIs1ai00qphjX2NitxV/3Wn+6dufB4Q4g==
+=rCZt
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k3.z0
+
+jA0ECQMC4f/5djqCC1Rg0kQBTHEPsD+Sw7biBsM2er3vKyGPAQkuTBGKC5ie7hT/
+lceMfQdbAg6oTFyJpk/wH18GzRDphCofg0X8uLgkAKMrpcmgog==
+=fB6S
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+-- Checking MDC modes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.nomdc.s2k3.z0
+
+jA0EBwMCnv07rlXqWctgyS2Dm2JfOKCRL4sLSLJUC8RS2cH7cIhKSuLitOtyquB+
+u9YkgfJfsuRJmgQ9tmo=
+=60ui
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCEeP3idNjQ1Bg0kQBf4G0wX+2QNzLh2YNwYkQgQkfYhn/hLXjV4nK9nsE
+8Ex1Dsdt5UPvOz8W8VKQRS6loOfOe+yyXil8W3IYFwUpdDUi+Q==
+=moGf
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+-- Checking hashes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.md5.mdc.s2k3.z0
+
+jA0EBwMClrXXtOXetohg0kQBn0Kl1ymevQZRHkdoYRHgzCwSQEiss7zYff2UNzgO
+KyRrHf7zEBuZiZ2AG34jNVMOLToj1jJUg5zTSdecUzQVCykWTA==
+=NyLk
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCApbdlrURoWJg0kQBzHM/E0o7djY82bNuspjxjAcPFrrtp0uvDdMQ4z2m
+/PM8jhgI5vxFYfNQjLl8y3fHYIomk9YflN9K/Q13iq8A8sjeTw==
+=FxbQ
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+-- Checking S2K modes
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k0.z0
+
+jAQEBwAC0kQBKTaLAKE3xzps+QIZowqRNb2eAdzBw2LxEW2YD5PgNlbhJdGg+dvw
+Ah9GXjGS1TVALzTImJbz1uHUZRfhJlFbc5yGQw==
+=YvkV
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k1.z0
+
+jAwEBwEC/QTByBLI3b/SRAHPxKzI6SZBo5lAEOD+EsvKQWO4adL9tDY+++Iqy1xK
+4IaWXVKEj9R2Lr2xntWWMGZtcKtjD2lFFRXXd9dZp1ZThNDz
+=dbXm
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCEq4Su3ZqNEJg0kQB4QG5jBTKF0i04xtH+avzmLhstBNRxvV3nsmB3cwl
+z+9ZaA/XdSx5ZiFnMym8P6r8uY9rLjjNptvvRHlxIReF+p9MNg==
+=VJKg
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k0.z0
+
+jAQECAAC0kQBBDnQWkgsx9YFaqDfWmpsiyAJ6y2xG/sBvap1dySYEMuZ+wJTXQ9E
+Cr3i2M7TgVZ0M4jp4QL0adG1lpN5iK7aQeOwMw==
+=cg+i
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k1.z0
+
+jAwECAECruOfyNDFiTnSRAEVoGXm4A9UZKkWljdzjEO/iaE7mIraltIpQMkiqCh9
+7h8uZ2u9uRBOv222fZodGvc6bvq/4R4hAa/6qSHtm8mdmvGt
+=aHmC
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes192.sha1.mdc.s2k3.z0
+
+jA0ECAMCjFn6SRi3SONg0kQBqtSHPaD0m7rXfDAhCWU/ypAsI93GuHGRyM99cvMv
+q6eF6859ZVnli3BFSDSk3a4e/pXhglxmDYCfjAXkozKNYLo6yw==
+=K0LS
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k0.z0
+
+jAQECQAC0kQB4L1eMbani07XF2ZYiXNK9LW3v8w41oUPl7dStmrJPQFwsdxmrDHu
+rQr3WbdKdY9ufjOE5+mXI+EFkSPrF9rL9NCq6w==
+=RGts
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k1.z0
+
+jAwECQECKHhrou7ZOIXSRAHWIVP+xjVQcjAVBTt+qh9SNzYe248xFTwozkwev3mO
++KVJW0qhk0An+Y2KF99/bYFl9cL5D3Tl43fC8fXGl3x3m7pR
+=SUrU
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes256.sha1.mdc.s2k3.z0
+
+jA0ECQMCjc8lwZu8Fz1g0kQBkEzjImi21liep5jj+3dAJ2aZFfUkohi8b3n9z+7+
+4+NRzL7cMW2RLAFnJbiqXDlRHMwleeuLN1up2WIxsxtYYuaBjA==
+=XZrG
+-----END PGP MESSAGE-----
+'), 'foobar');
+
+-- Checking longer passwords
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCx6dBiuqrYNRg0kQBEo63AvA1SCslxP7ayanLf1H0/hlk2nONVhTwVEWi
+tTGup1mMz6Cfh1uDRErUuXpx9A0gdMu7zX0o5XjrL7WGDAZdSw==
+=XKKG
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCBDvYuS990iFg0kQBW31UK5OiCjWf5x6KJ8qNNT2HZWQCjCBZMU0XsOC6
+CMxFKadf144H/vpoV9GA0f22keQgCl0EsTE4V4lweVOPTKCMJg==
+=gWDh
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij2jk4h5g2j54khg23h54g2kh54g2khj54g23hj54');
+
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCqXbFafC+ofVg0kQBejyiPqH0QMERVGfmPOjtAxvyG5KDIJPYojTgVSDt
+FwsDabdQUz5O7bgNSnxfmyw1OifGF+W2bIn/8W+0rDf8u3+O+Q==
+=OxOF
+-----END PGP MESSAGE-----
+'), 'x');
+
+-- Checking various data
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat1.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCGJ+SpuOysINg0kQBJfSjzsW0x4OVcAyr17O7FBvMTwIGeGcJd99oTQU8
+Xtx3kDqnhUq9Z1fS3qPbi5iNP2A9NxOBxPWz2JzxhydANlgbxg==
+=W/ik
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat2.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCvdpDvidNzMxg0jUBvj8eS2+1t/9/zgemxvhtc0fvdKGGbjH7dleaTJRB
+SaV9L04ky1qECNDx3XjnoKLC+H7IOQ==
+=Fxen
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: dat3.aes.sha1.mdc.s2k3.z0
+
+jA0EBwMCxQvxJZ3G/HRg0lgBeYmTa7/uDAjPyFwSX4CYBgpZWVn/JS8JzILrcWF8
+gFnkUKIE0PSaYFp+Yi1VlRfUtRQ/X/LYNGa7tWZS+4VQajz2Xtz4vUeAEiYFYPXk
+73Hb8m1yRhQK
+=ivrD
+-----END PGP MESSAGE-----
+'), '0123456789abcdefghij'), 'sha1');
+
+-- Checking CRLF
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: crlf mess
+
+ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
+a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
+=mBP9
+-----END PGP MESSAGE-----
+'), 'key', 'convert-crlf=0'), 'sha1');
+
+select digest(pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+Comment: crlf mess
+
+ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms
+a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs
+=mBP9
+-----END PGP MESSAGE-----
+'), 'key', 'convert-crlf=1'), 'sha1');
+
+-- check BUG #11905, problem with messages 6 less than a power of 2.
+select pgp_sym_decrypt(pgp_sym_encrypt(repeat('x',65530),'1'),'1') = repeat('x',65530);
+
+
+-- Negative tests
+
+-- Decryption with a certain incorrect key yields an apparent Literal Data
+-- packet reporting its content to be binary data. Ciphertext source:
+-- iterative pgp_sym_encrypt('secret', 'key') until the random prefix gave
+-- rise to that property.
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0EBwMCxf8PTrQBmJdl0jcB6y2joE7GSLKRv7trbNsF5Z8ou5NISLUg31llVH/S0B2wl4bvzZjV
+VsxxqLSPzNLAeIspJk5G
+=mSd/
+-----END PGP MESSAGE-----
+'), 'wrong-key', 'debug=1');
+
+-- Routine text/binary mismatch.
+select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1');
+
+-- Decryption with a certain incorrect key yields an apparent BZip2-compressed
+-- plaintext. Ciphertext source: iterative pgp_sym_encrypt('secret', 'key')
+-- until the random prefix gave rise to that property.
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+ww0EBwMC9rK/dMkF5Zlt0jcBlzAQ1mQY2qYbKYbw8h3EZ5Jk0K2IiY92R82TRhWzBIF/8cmXDPtP
+GXsd65oYJZp3Khz0qfyn
+=Nmpq
+-----END PGP MESSAGE-----
+'), 'wrong-key', 'debug=1');
+
+-- Routine use of BZip2 compression. Ciphertext source:
+-- echo x | gpg --homedir /nonexistent --personal-compress-preferences bzip2 \
+-- --personal-cipher-preferences aes --no-emit-version --batch \
+-- --symmetric --passphrase key --armor
+select pgp_sym_decrypt(dearmor('
+-----BEGIN PGP MESSAGE-----
+
+jA0EBwMCRhFrAKNcLVJg0mMBLJG1cCASNk/x/3dt1zJ+2eo7jHfjgg3N6wpB3XIe
+QCwkWJwlBG5pzbO5gu7xuPQN+TbPJ7aQ2sLx3bAHhtYb0i3vV9RO10Gw++yUyd4R
+UCAAw2JRIISttRHMfDpDuZJpvYo=
+=AZ9M
+-----END PGP MESSAGE-----
+'), 'key', 'debug=1');
diff --git a/contrib/pgcrypto/sql/pgp-encrypt.sql b/contrib/pgcrypto/sql/pgp-encrypt.sql
new file mode 100644
index 0000000..ed8b177
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-encrypt.sql
@@ -0,0 +1,104 @@
+--
+-- PGP encrypt
+--
+
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), 'key');
+
+-- check whether the defaults are ok
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
+ 'key', 'expect-cipher-algo=aes128,
+ expect-disable-mdc=0,
+ expect-sess-key=0,
+ expect-s2k-mode=3,
+ expect-s2k-digest-algo=sha1,
+ expect-compress-algo=0
+ ');
+
+-- maybe the expect- stuff simply does not work
+select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
+ 'key', 'expect-cipher-algo=bf,
+ expect-disable-mdc=1,
+ expect-sess-key=1,
+ expect-s2k-mode=0,
+ expect-s2k-digest-algo=md5,
+ expect-compress-algo=1
+ ');
+
+-- bytea as text
+select pgp_sym_decrypt(pgp_sym_encrypt_bytea('Binary', 'baz'), 'baz');
+
+-- text as bytea
+select encode(pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz'), 'escape');
+
+
+-- algorithm change
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=bf'),
+ 'key', 'expect-cipher-algo=bf');
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes'),
+ 'key', 'expect-cipher-algo=aes128');
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes192'),
+ 'key', 'expect-cipher-algo=aes192');
+
+-- s2k change
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 's2k-mode=0'),
+ 'key', 'expect-s2k-mode=0');
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 's2k-mode=1'),
+ 'key', 'expect-s2k-mode=1');
+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'),
+ 'key', 'expect-s2k-digest-algo=md5');
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=sha1'),
+ 'key', 'expect-s2k-digest-algo=sha1');
+
+-- sess key
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=0'),
+ 'key', 'expect-sess-key=0');
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=1'),
+ 'key', 'expect-sess-key=1');
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=bf'),
+ 'key', 'expect-sess-key=1, expect-cipher-algo=bf');
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes192'),
+ 'key', 'expect-sess-key=1, expect-cipher-algo=aes192');
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes256'),
+ 'key', 'expect-sess-key=1, expect-cipher-algo=aes256');
+
+-- no mdc
+select pgp_sym_decrypt(
+ pgp_sym_encrypt('Secret.', 'key', 'disable-mdc=1'),
+ 'key', 'expect-disable-mdc=1');
+
+-- crlf
+select pgp_sym_decrypt_bytea(
+ pgp_sym_encrypt(E'1\n2\n3\r\n', 'key', 'convert-crlf=1'),
+ 'key');
+
+-- conversion should be lossless
+select digest(pgp_sym_decrypt(
+ pgp_sym_encrypt(E'\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'),
+ 'key', 'convert-crlf=1'), 'sha1') as result,
+ digest(E'\r\n0\n1\r\r\n\n2\r', 'sha1') as expect;
diff --git a/contrib/pgcrypto/sql/pgp-info.sql b/contrib/pgcrypto/sql/pgp-info.sql
new file mode 100644
index 0000000..8e1d72a
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-info.sql
@@ -0,0 +1,22 @@
+--
+-- PGP info functions
+--
+
+-- pgp_key_id
+
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=1;
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=2;
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=3;
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=4; -- should fail
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=5;
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=6;
+
+select pgp_key_id(dearmor(seckey)) from keytbl where id=1;
+select pgp_key_id(dearmor(seckey)) from keytbl where id=2;
+select pgp_key_id(dearmor(seckey)) from keytbl where id=3;
+select pgp_key_id(dearmor(seckey)) from keytbl where id=4; -- should fail
+select pgp_key_id(dearmor(seckey)) from keytbl where id=5;
+select pgp_key_id(dearmor(seckey)) from keytbl where id=6;
+
+select pgp_key_id(dearmor(data)) as data_key_id
+from encdata order by id;
diff --git a/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql b/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql
new file mode 100644
index 0000000..3f2bae9
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql
@@ -0,0 +1,647 @@
+--
+-- PGP Public Key Encryption
+--
+
+-- As most of the low-level stuff is tested in symmetric key
+-- tests, here's only public-key specific tests
+
+create table keytbl (
+ id int4,
+ name text,
+ pubkey text,
+ seckey text
+);
+create table encdata (
+ id int4,
+ data text
+);
+
+insert into keytbl (id, name, pubkey, seckey)
+values (1, 'elg1024', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx
+MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc
+AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd
+ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P
+sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI
++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9
+6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF
+k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v
+iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F
+ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80
+=RWci
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQAAAnj4i4st+s+C6
+WKTIDcL1Iy0Saq8lCp60H0VsZ2FtYWwgMTAyNCA8dGVzdEBleGFtcGxlLm9yZz6I
+XgQTEQIAHgUCQsghSAIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRAcKbwNGBdz
+ZDrbAJ9cp6AsjOhiLxwznsMJheGf4xkH8wCfUPjMCLm4tAEnyYn2hDNt7CB8B6Kd
+ATEEQsghShAEAIeUjW2yTALCfrEG3FhM3ZLvAHWAec2O0Mn/RDr59IN/W8wDYcZp
+m+oG0ZUDdIqMppQ8K2kylAH7gmYDXIP9D7MRRm/Zw3L4yFfKnVaZ6tT7szBbgW5h
+iOsHoOz49NXZT4jtMLdZS1/krm5Lam2MSPod9XN0Q2asY/igIMUfGDRjAAMGA/sE
+LNh3tWefqeDkoDBEYjcxdGnGVGJnNHvv/eoHy9H7dyD/kkhaOoRAa5ClYWSqD0kk
+a+SqTWhKG4XcbJyo1GsP6sqGhXDTM2+LBZPMKuVJQpEfoe9ruob/BbpXglfEiVE9
+VNiY7ZVyUdj3svYn4fK2X7ue1G3cHR2tL4lnOA4pYQAA9030E4u2ZKOfJBpUM+EM
+m9VmsGjaQZV4teB0R/q3W8sRIYhJBBgRAgAJBQJCyCFKAhsMAAoJEBwpvA0YF3Nk
+7a8AniFFotw1x2X+oryu3Q3nNtmxoKHpAJ9HU7jw7ydg33dI9J8gVkrmsSZ2/w==
+=nvqq
+-----END PGP PRIVATE KEY BLOCK-----
+');
+
+insert into keytbl (id, name, pubkey, seckey)
+values (2, 'elg2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n
+vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke
+5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO
+RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij
+8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl
+Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt
+J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/
+T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5
+0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6a7QjRWxnYW1hbCAy
+MDQ4IDx0ZXN0MjA0OEBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgiCgIbAwYLCQgH
+AwIDFQIDAxYCAQIeAQIXgAAKCRBI6c1W/qZo29PDAKCG724enIxRog1j+aeCp/uq
+or6mbwCePuKy2/1kD1FvnhkZ/R5fpm+pdm25Ag0EQsgiIhAIAJI3Gb2Ehtz1taQ9
+AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMX
+MhoWyw8ZF5Zs1mLIjFGVorePrm94N3MNPWM7x9M36bHUjx0vCZKFIhcGY1g+htE/
+QweaJzNVeA5z4qZmik41FbQyQSyHa3bOkTZu++/U6ghP+iDp5UDBjMTkVyqITUVN
+gC+MR+da/I60irBVhue7younh4ovF+CrVDQJC06HZl6CAJJyA81SmRfi+dmKbbjZ
+LF6rhz0norPjISJvkIqvdtM4VPBKI5wpgwCzpEqjuiKrAVujRT68zvBvJ4aVqb11
+k5QdJscAAwUH/jVJh0HbWAoiFTe+NvohfrA8vPcD0rtU3Y+siiqrabotnxJd2NuC
+bxghJYGfNtnx0KDjFbCRKJVeTFok4UnuVYhXdH/c6i0/rCTNdeW2D6pmR4GfBozR
+Pw/ARf+jONawGLyUj7uq13iquwMSE7VyNuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0R
+QsetMq/iNBWraayKZnWUd+eQqNzE+NUo7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiF
+Z1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qunfGW00ZMMTCWabg0ZgxPzMfMeIcm6525A
+Yn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/RdWaISQQYEQIACQUCQsgiIgIbDAAKCRBI
+6c1W/qZo25ZSAJ98WTrtl2HiX8ZqZq95v1+9cHtZPQCfZDoWQPybkNescLmXC7q5
+1kNTmEU=
+=8QM5
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n
+vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke
+5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO
+RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij
+8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl
+Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt
+J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/
+T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5
+0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6awAAn2F+iNBElfJS
+8azqO/kEiIfpqu6/DQG0I0VsZ2FtYWwgMjA0OCA8dGVzdDIwNDhAZXhhbXBsZS5v
+cmc+iF0EExECAB4FAkLIIgoCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQSOnN
+Vv6maNvTwwCYkpcJmpl3aHCQdGomz7dFohDgjgCgiThZt2xTEi6GhBB1vuhk+f55
+n3+dAj0EQsgiIhAIAJI3Gb2Ehtz1taQ9AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D
+29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMXMhoWyw8ZF5Zs1mLIjFGVorePrm94N3MN
+PWM7x9M36bHUjx0vCZKFIhcGY1g+htE/QweaJzNVeA5z4qZmik41FbQyQSyHa3bO
+kTZu++/U6ghP+iDp5UDBjMTkVyqITUVNgC+MR+da/I60irBVhue7younh4ovF+Cr
+VDQJC06HZl6CAJJyA81SmRfi+dmKbbjZLF6rhz0norPjISJvkIqvdtM4VPBKI5wp
+gwCzpEqjuiKrAVujRT68zvBvJ4aVqb11k5QdJscAAwUH/jVJh0HbWAoiFTe+Nvoh
+frA8vPcD0rtU3Y+siiqrabotnxJd2NuCbxghJYGfNtnx0KDjFbCRKJVeTFok4Unu
+VYhXdH/c6i0/rCTNdeW2D6pmR4GfBozRPw/ARf+jONawGLyUj7uq13iquwMSE7Vy
+NuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0RQsetMq/iNBWraayKZnWUd+eQqNzE+NUo
+7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiFZ1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qun
+fGW00ZMMTCWabg0ZgxPzMfMeIcm6525AYn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/R
+dWYAAVQKFPXbRaxbdArwRVXMzSD3qj/+VwwhwEDt8zmBGnlBfwVdkjQQrDUMmV1S
+EwyISQQYEQIACQUCQsgiIgIbDAAKCRBI6c1W/qZo25ZSAJ4sgUfHTVsG/x3p3fcM
+3b5R86qKEACggYKSwPWCs0YVRHOWqZY0pnHtLH8=
+=3Dgk
+-----END PGP PRIVATE KEY BLOCK-----
+');
+
+insert into keytbl (id, name, pubkey, seckey)
+values (3, 'elg4096', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=kRxT
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=Gjq6
+-----END PGP PRIVATE KEY BLOCK-----
+');
+
+insert into keytbl (id, name, pubkey, seckey)
+values (4, 'rsa2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQELBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP
+ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2
+55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx
+5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K
+MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz
+R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYptB5SU0EgMjA0OCA8cnNhMjA0
+OEBleGFtcGxlLm9yZz6JATQEEwECAB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgEC
+HgECF4AACgkQnc+OnJvTHyQqHwf8DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liR
+nrLuVlLBpdO6CWmMUzfKRvyZlx54GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzw
+bLZyM5Gb3lsE/FEmE7Dxw/0Utf59uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDP
+D3dnU4uzKPhMcjnSN00pzjusP7C9NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv
+9bgGWopumlOkt8Zu5YG6+CtTbJXprPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhv
+S3NZKoJ/1DrGgoDAu1mGkM4KvLAxfDs/qQ9dZhtEmDbKPLTVEA==
+=lR4n
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQOWBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP
+ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2
+55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx
+5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K
+MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz
+R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYpAAf/QZsrrz0c7dgWwGqMIpw6
+fP+/lLa74+fa2CFRWtYowEiKsfDg/wN7Ua07036dNhPa8aZPsU6SRzm5PybKOURe
+D9pNt0FxJkX0j5pCWfjSJgTbc1rCdqZ/oyBk/U6pQtf//zfw3PbDl7I8TC6GOt2w
+5NgcXdsWHP7LAmPctOVUyzFsenevR0MFTHkMbmKI1HpFm8XN/e1Fl+qIAD+OagTF
+5B32VvpoJtkh5nxnIuToNJsa9Iy7F9MM2CeFOyTMihMcjXKBBUaAYoF115irBvqu
+7N/qWmzqLg8yxBZ56mh6meCF3+67VA2y7fL8rhw2QuqgLg1JFlKAVL+9crCSrn//
+GQQA1kT7FytW6BNOffblFYZkrJer3icoRDqa/ljgH/yVaWoVT1igy0E9XzYO7MwP
+2usj/resLy0NC1qCthk51cZ/wthooMl88e5Wb4l5FYwBEac7muSBTo4W8cAH1hFj
+TWL6XAGvEzGX3Mt9pn8uYGlQLZAhJoNCAU2EOCbN1PchDvsEAOWNKYesuUVk8+sQ
+St0NDNhd9BWtTWTHkCZb1dKC3JTfr9PqkTBLrWFbYjkOtvdPAW7FDaXXXZfdH1jH
+WfwP3Q+I6sqgSaWpCS4dBAns3/RVtO7czVgyIwma04iIvJqderYrfvkUq95KfwP2
+V8wXkhrPPPxyrg5y3wQlpY2jb5RBBAC17SK1ms+DBtck4vpdjp3SJ32SbyC/DU30
+89Q12j74S7Zdu1qZlKnvy3kWPYX/hMuSzGZ+mLVJNFEqH2X01aFzppYz0hdI9PGB
+9tTFEqZWQL9ZkXfjc79Cgnt12pNukRbtw0N/kyutOdIFHVT79wVAd+powqziXJsC
+Kc+4xjwSCkZitB5SU0EgMjA0OCA8cnNhMjA0OEBleGFtcGxlLm9yZz6JATQEEwEC
+AB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQnc+OnJvTHyQqHwf8
+DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liRnrLuVlLBpdO6CWmMUzfKRvyZlx54
+GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzwbLZyM5Gb3lsE/FEmE7Dxw/0Utf59
+uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDPD3dnU4uzKPhMcjnSN00pzjusP7C9
+NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv9bgGWopumlOkt8Zu5YG6+CtTbJXp
+rPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhvS3NZKoJ/1DrGgoDAu1mGkM4KvLAx
+fDs/qQ9dZhtEmDbKPLTVEA==
+=WKAv
+-----END PGP PRIVATE KEY BLOCK-----
+');
+
+insert into keytbl (id, name, pubkey, seckey)
+values (5, 'psw-elg1024', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx
+MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc
+AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd
+ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P
+sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI
++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9
+6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF
+k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v
+iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F
+ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80
+=RWci
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQHpBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE
+xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth
+klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5
+YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic
+PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL
+jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv
+saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v
+IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQP4HAwImKZ5q2QwT
+D2DDAY/IQBjes7WgqZeacfLPDoB8ecD/KLoSCH6Z3etvbPHSOKiazxoJ962Ix74H
+ZAE6ZbMTtl5dZW1ptB9FbGdhbWFsIDEwMjQgPHRlc3RAZXhhbXBsZS5vcmc+iF4E
+ExECAB4FAkLIIUgCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQHCm8DRgXc2Q6
+2wCfXKegLIzoYi8cM57DCYXhn+MZB/MAn1D4zAi5uLQBJ8mJ9oQzbewgfAeinQFf
+BELIIUoQBACHlI1tskwCwn6xBtxYTN2S7wB1gHnNjtDJ/0Q6+fSDf1vMA2HGaZvq
+BtGVA3SKjKaUPCtpMpQB+4JmA1yD/Q+zEUZv2cNy+MhXyp1WmerU+7MwW4FuYYjr
+B6Ds+PTV2U+I7TC3WUtf5K5uS2ptjEj6HfVzdENmrGP4oCDFHxg0YwADBgP7BCzY
+d7Vnn6ng5KAwRGI3MXRpxlRiZzR77/3qB8vR+3cg/5JIWjqEQGuQpWFkqg9JJGvk
+qk1oShuF3GycqNRrD+rKhoVw0zNviwWTzCrlSUKRH6Hva7qG/wW6V4JXxIlRPVTY
+mO2VclHY97L2J+Hytl+7ntRt3B0drS+JZzgOKWH+BwMCJimeatkMEw9gRkFjt4Xa
+9rX8awMBE5+vVcGKv/DNiCvJnlYvSdCj8VfuHsYFliiJo6u17NJon+K43e3yvDNk
+f631VOVanGEz7TyqOkWQiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCe
+IUWi3DXHZf6ivK7dDec22bGgoekAn0dTuPDvJ2Dfd0j0nyBWSuaxJnb/
+=SNvr
+-----END PGP PRIVATE KEY BLOCK-----
+');
+
+insert into keytbl (id, name, pubkey, seckey)
+values (6, 'rsaenc2048', '
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+mQELBELr2m0BCADOrnknlnXI0EzRExf/TgoHvK7Xx/E0keWqV3KrOyC3/tY2KOrj
+UVxaAX5pkFX9wdQObGPIJm06u6D16CH6CildX/vxG7YgvvKzK8JGAbwrXAfk7OIW
+czO2zRaZGDynoK3mAxHRBReyTKtNv8rDQhuZs6AOozJNARdbyUO/yqUnqNNygWuT
+4htFDEuLPIJwAbMSD0BvFW6YQaPdxzaAZm3EWVNbwDzjgbBUdBiUUwRdZIFUhsjJ
+dirFdy5+uuZru6y6CNC1OERkJ7P8EyoFiZckAIE5gshVZzNuyLOZjc5DhWBvLbX4
+NZElAnfiv+4nA6y8wQLSIbmHA3nqJaBklj85AAYptCVSU0EgMjA0OCBFbmMgPHJz
+YTIwNDhlbmNAZXhhbXBsZS5vcmc+iQE0BBMBAgAeBQJC69ptAhsDBgsJCAcDAgMV
+AgMDFgIBAh4BAheAAAoJEMiZ6pNEGVVZHMkIAJtGHHZ9iM8Yq1rr0zl1L6SvlQP8
+JCaxHa31wH3PKqGtq2M+cpb2rXf7gAY/doHJPXggfVzkyFrysmQ1gPbDGYLyOutw
++IkhihEb5bWxQBNj+3zAFs1YX6v2HXWbSUSmyY1V9/+NTtKk03olDc/swd3lXzku
+UOhcgfpBgIt3Q+MpT6M2+OIF7lVfSb1rWdpwTfGhZzW9szQOeoS4gPvxCCRyuabQ
+RJ6DWH61F8fFIDJg1z+A/Obx4fqX6GOA69RzgZ3oukFBIXxNwV9PZNnAmHtZVYO8
+0g/oVYBbuvOYedffDBeQarhERZ5W2TnIE+nqY61YOLBqosliygdZTXULzNi5AQsE
+QuvaugEIAOuCJZdkzORA6e1lr81Lnr4JzMsVBFA+X/yIkBbV6qX/A4nVSLAZKNPX
+z1YIrMTu+1rMIiy10IWbA6zgMTpzPhJRfgePONgdnCYyK5Ksh5/C5ntzKwwGwxfK
+lAXIxJurCHXTbEa+YvPdn76vJ3HsXOXVEL+fLb4U3l3Ng87YM202Lh1Ha2MeS2zE
+FZcAoKbFqAAjDLEai64SoOFh0W3CsD1DL4zmfp+YZrUPHTtZadsi53i4KKW/ws9U
+rHlolqYNhYze/uRLyfnUx9PN4r/GhEzauyDMV0smo91uB3aewPft+eCpmeWnu0PF
+JVK4xyRmhIq2rVCw16a1pBJirvGM+y0ABimJAR8EGAECAAkFAkLr2roCGwwACgkQ
+yJnqk0QZVVku1wgAg1bLSjPkhw+ldG5HzumpqR84+JKyozdJaJzefu2+1iqYE0B0
+WLz2PJVIiK41xiEkKhBvTOQYuXmtWqAWXptD91P5SoXoNJWLQO3TNwarANhHxkWg
+w/TOUxQqoctlRUej5NDD+4eW5G9lcS1FEGuKDWtX096u80vO+TbyJjvx2eVM1k+X
+dmeYsGOiNgDimCreJGYc14G7eY9jt24gw10n1sMAKI1qm6lcoHqZ9OOyla+wJdro
+PYZGO7R8+1O9R22WrK6BYDT5j/1JwMZqbOESjNvDEVT0yOHClCHRN4CChbt6LhKh
+CLUNdz/udIt0JAC6c/HdPLSW3HnmM3+iNj+Kug==
+=pwU2
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+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
+=UKh3
+-----END PGP PRIVATE KEY BLOCK-----
+');
+
+insert into keytbl (id, name, pubkey, seckey)
+values (7, 'rsaenc2048-psw', '
+same key with password
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+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
+=QFm5
+-----END PGP PRIVATE KEY BLOCK-----
+');
+
+
+-- elg1024 / aes128
+insert into encdata (id, data) values (1, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQEOA9k2z2S7c/RmEAQAgVWW0DeLrZ+1thWJGBPp2WRFL9HeNqqWHbKJCXJbz1Uy
+faUY7yxVvG5Eutmo+JMiY3mg23/DgVVXHQZsTWpGvGM6djgUNGKUjZDbW6Nog7Mr
+e78IywattCOmgUP9vIwwg3OVjuDCN/nVirGQFnXpJBc8DzWqDMWRWDy1M0ZsK7AD
+/2JTosSFxUdpON0DKtIY3GLzmh6Nk3iV0g8VgJKUBT1rhCXuMDj3snm//EMm7hTY
+PlnObq4mIhgz8NqprmhooxnU0Kapofb3P3wCHPpU14zxhXY8iKO/3JhBq2uFcx4X
+uBMwkW4AdNxY/mzJZELteTL8Tr0s7PISk+owb4URpG3n0jsBc0CVULxrjh5Ejkdw
+wCM195J6+KbQxOOFQ0b3uOVvv4dEgd/hRERCOq5EPaFhlHegyYJ7YO842vnSDA==
+=PABx
+-----END PGP MESSAGE-----
+');
+
+-- elg2048 / blowfish
+insert into encdata (id, data) values (2, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQIOAywibh/+XMfUEAf+OINhBngEsw4a/IJIeJvUgv1gTQzBwOdQEuc/runr4Oa8
+Skw/Bj0X/zgABVZLem1a35NHaNwaQaCFwMQ41YyWCu+jTdsiyX/Nw0w8LKKz0rNC
+vVpG6YuV7Turtsf8a5lXy1K0SHkLlgxQ6c76GS4gtSl5+bsL2+5R1gSRJ9NXqCQP
+OHRipEiYwBPqr5R21ZG0FXXNKGOGkj6jt/M/wh3WVtAhYuBI+HPKRfAEjd/Pu/eD
+e1zYtkH1dKKFmp44+nF0tTI274xpuso7ShfKYrOK3saFWrl0DWiWteUinjSA1YBY
+m7dG7NZ8PW+g1SZWhEoPjEEEHz3kWMvlKheMRDudnQf/dDyX6kZVIAQF/5B012hq
+QyVewgTGysowFIDn01uIewoEA9cASw699jw9IoJp+k5WZXnU+INllBLzQxniQCSu
+iEcr0x3fYqNtj9QBfbIqyRcY6HTWcmzyOUeGaSyX76j+tRAvtVtXpraFFFnaHB70
+YpXTjLkp8EBafzMghFaKDeXlr2TG/T7rbwcwWrFIwPqEAUKWN5m97Q3eyo8/ioMd
+YoFD64J9ovSsgbuU5IpIGAsjxK+NKzg/2STH7zZFEVCtgcIXsTHTZfiwS98/+1H9
+p1DIDaXIcUFV2ztmcKxh9gt2sXRz1W+x6D8O0k3nanU5yGG4miLKaq18fbcA0BD1
++NIzAfelq6nvvxYKcGcamBMgLo5JkZOBHvyr6RsAKIT5QYc0QTjysTk9l0Am3gYc
+G2pAE+3k
+=TBHV
+-----END PGP MESSAGE-----
+');
+
+-- elg4096 / aes256
+insert into encdata (id, data) values (3, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQQOA7aFBP0Sjh/5EA/+JCgncc8IZmmRjPStWnGf9tVJhgHTn+smIclibGzs0deS
+SPSCitzpblwbUDvu964+/5e5Q1l7rRuNN+AgETlEd4eppv7Swn2ChdgOXxRwukcT
+Nh3G+PTFvD4ayi7w1db3qvXIt0MwN4Alt436wJmK1oz2Ka9IcyO+wHWrDy1nSGSx
+z5x7YEj+EZPgWc/YAvudqE8Jpzd/OT5zSHN09UFkIAk6NxisKaIstbEGFgpqtoDZ
+1SJM84XAdL2IcaJ3YY7k/yzwlawhsakKd4GSd5vWmAwvyzzbSiBMfKsDE16ePLNU
+ZBF7CzmlCBPZ7YrFAHLpXBXXkCQvzD2BEYOjse50ZEfJ036T7950Ozcdy1EQbGon
+nyQ4Gh0PBpnMcBuiXOceWuYzhlzFOzDtlVKdNTxFRDcbEyW2jo9xQYvCCLnYy8EH
+2M7S8jCtVYJBbn63a82ELv+3+kWYcsvBJv2ZVBh4ncrBu9o0P+OYS7ApoOU+j6p2
++t0RXHksqXS1YiUwYF5KSw09EbYMgNZ9G04Px/PxLU6fSC9iDrGX7Xt3kOUP0mku
+C518fPckT0zzRXqfFruJNRzDytW50KxkOQZzU1/Az1YlYN9QzWeU4EtLPb2fftZo
+D0qH/ln+f9Op5t6sD2fcxZVECU1b/bFtZsxvwH406YL+UQ7hU/XnZrzVVzODal8P
+/j1hg7v7BdJqu1DTp9nFWUuwMFcYAczuXn29IG183NZ7Ts4whDeYEhS8eNoLPX4j
+txY12ILD/w/3Q4LoW/hPa6OdfEzsn0U5GLf1WiGmJE1H6ft2U/xUnerc/u0kt+FU
+WAisArd4MuKtf7B5Vu/VF3kUdrR0hTniUKUivmC4o1jSId31Dufxj4aadVyldXAr
+6TNBcdyragZjxEZ6hsBCYzA0Rd1a8atd6OaQoIEEfAzCu5Ks29pydHErStYGjWJ1
+KA5KPLVvjbHpDmRhlCcm8vgpYQsBYEB5gE9fx5yCTlsVhCB6y23h7hfdMqerDqkO
+ZOPsO5h+tiHCdIrQ36sMjuINy1/K2rYcXd+Crh2iHcfidpU9fvDz2ihTRNQlhjuT
+0cQZM5JhctEx4VXF4LDctRhit7Hn0iqsk604woQfJVvP8O673xSXT/kBY0A/v9C0
+3C4YoFNeSaKwbfZQ/4u1ZFPJxK2IIJa8UGpyAUewLMlzGVVagljybv/f4Z9ERAhy
+huq5sMmw8UPsrJF2TUGHz5WSIwoh0J/qovoQI09I9sdEnFczDvRavMO2Mldy3E5i
+exz9oewtel6GOmsZQSYWT/vJzbYMmvHNmNpVwwoKrLV6oI3kyQ80GHBwI1WlwHoK
+2iRB0w8q4VVvJeYAz8ZIp380cqC3pfO0uZsrOx4g3k4X0jsB5y7rF5xXcZfnVbvG
+DYKcOy60/OHMWVvpw6trAoA+iP+cVWPtrbRvLglTVTfYmi1ToZDDipkALBhndQ==
+=L/M/
+-----END PGP MESSAGE-----
+');
+
+-- rsaenc2048 / aes128
+insert into encdata (id, data) values (4, '
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+hQEMA/0CBsQJt0h1AQf+JyYnCiortj26P11zk28MKOGfWpWyAhuIgwbJXsdQ+e6r
+pEyyqs9GC6gI7SNF6+J8B/gsMwvkAL4FHAQCvA4ZZ6eeXR1Of4YG22JQGmpWVWZg
+DTyfhA2vkczuqfAD2tgUpMT6sdyGkQ/fnQ0lknlfHgC5GRx7aavOoAKtMqiZW5PR
+yae/qR48mjX7Mb+mLvbagv9mHEgQSmHwFpaq2k456BbcZ23bvCmBnCvqV/90Ggfb
+VP6gkSoFVsJ19RHsOhW1dk9ehbl51WB3zUOO5FZWwUTY9DJvKblRK/frF0+CXjE4
+HfcZXHSpSjx4haGGTsMvEJ85qFjZpr0eTGOdY5cFhNJAAVP8MZfji7OhPRAoOOIK
+eRGOCkao12pvPyFTFnPd5vqmyBbdNpK4Q0hS82ljugMJvM0p3vJZVzW402Kz6iBL
+GQ==
+=XHkF
+-----END PGP MESSAGE-----
+');
+
+-- rsaenc2048 / aes128 (not from gnupg)
+insert into encdata (id, data) values (5, '
+-----BEGIN PGP MESSAGE-----
+
+wcBMA/0CBsQJt0h1AQgAzxZ8j+OTeZ8IlLxfZ/mVd28/gUsCY+xigWBk/anZlK3T
+p2tNU2idHzKdAttH2Hu/PWbZp4kwjl9spezYxMqCeBZqtfGED88Y+rqK0n/ul30A
+7jjFHaw0XUOqFNlST1v6H2i7UXndnp+kcLfHPhnO5BIYWxB2CYBehItqtrn75eqr
+C7trGzU/cr74efcWagbCDSNjiAV7GlEptlzmgVMmNikyI6w0ojEUx8lCLc/OsFz9
+pJUAX8xuwjxDVv+W7xk6c96grQiQlm+FLDYGiGNXoAzx3Wi/howu3uV40dXfY+jx
+3WBrhEew5Pkpt1SsWoFnJWOfJ8GLd0ec8vfRCqAIVdLgAeS7NyawQYtd6wuVrEAj
+5SMg4Thb4d+g45RksuGLHUUr4qO9tiXglODa4InhmJfgNuLk+RGz4LXjq8wepEmW
+vRbgFOG54+Cf4C/gC+HkreDm5JKSKjvvw4B/jC6CDxq+JoziEe2Z1uEjCuEcr+Es
+/eGzeOi36BejXPMHeKxXejj5qBBHKV0pHVhZSgffR0TtlXdB967Yl/5agV0R89hI
+7Gw52emfnH4Z0Y4V0au2H0k1dR/2IxXdJEWSTG7Be1JHT59p9ei2gSEOrdBMIOjP
+tbYYUlmmbvD49bHfThkDiC+oc9947LgQsk3kOOLbNHcjkbrjH8R5kjII4m/SEZA1
+g09T+338SzevBcVXh/cFrQ6/Et+lyyO2LJRUMs69g/HyzJOVWT2Iu8E0eS9MWevY
+Qtrkrhrpkl3Y02qEp/j6M03Yu2t6ZF7dp51aJ5VhO2mmmtHaTnCyCc8Fcf72LmD8
+blH2nKZC9d6fi4YzSYMepZpMOFR65M80MCMiDUGnZBB8sEADu2/iVtqDUeG8mAA=
+=PHJ1
+-----END PGP MESSAGE-----
+');
+
+-- successful decrypt
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=1 and encdata.id=1;
+
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=2 and encdata.id=2;
+
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=3 and encdata.id=3;
+
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=6 and encdata.id=4;
+
+-- wrong key
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=2 and encdata.id=1;
+
+-- sign-only key
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=4 and encdata.id=1;
+
+-- rsa: password-protected secret key, wrong password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), '123')
+from keytbl, encdata where keytbl.id=7 and encdata.id=4;
+
+-- rsa: password-protected secret key, right password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'parool')
+from keytbl, encdata where keytbl.id=7 and encdata.id=4;
+
+-- password-protected secret key, no password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+
+-- password-protected secret key, wrong password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'foo')
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+
+-- password-protected secret key, right password
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'parool')
+from keytbl, encdata where keytbl.id=5 and encdata.id=1;
+
+-- test for a short read from prefix_init
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=6 and encdata.id=5;
diff --git a/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql b/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql
new file mode 100644
index 0000000..c9edbc6
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql
@@ -0,0 +1,48 @@
+--
+-- PGP Public Key Encryption
+--
+
+-- successful encrypt/decrypt
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=1;
+
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=2;
+
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=3;
+
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=6;
+
+-- try with rsa-sign only
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=4;
+
+-- try with secret key
+select pgp_pub_decrypt(
+ pgp_pub_encrypt('Secret msg', dearmor(seckey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=1;
+
+-- does text-to-bytea works
+select encode(pgp_pub_decrypt_bytea(
+ pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+ dearmor(seckey)), 'escape')
+from keytbl where keytbl.id=1;
+
+-- and bytea-to-text?
+select pgp_pub_decrypt(
+ pgp_pub_encrypt_bytea('Secret msg', dearmor(pubkey)),
+ dearmor(seckey))
+from keytbl where keytbl.id=1;
diff --git a/contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql b/contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql
new file mode 100644
index 0000000..6f4eccd
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql
@@ -0,0 +1 @@
+-- zlib is disabled
diff --git a/contrib/pgcrypto/sql/rijndael.sql b/contrib/pgcrypto/sql/rijndael.sql
new file mode 100644
index 0000000..a276641
--- /dev/null
+++ b/contrib/pgcrypto/sql/rijndael.sql
@@ -0,0 +1,72 @@
+--
+-- AES cipher (aka Rijndael-128, -192, or -256)
+--
+
+-- some standard Rijndael testvalues
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f',
+'aes-ecb/pad:none');
+
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f1011121314151617',
+'aes-ecb/pad:none');
+
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-ecb/pad:none');
+
+-- cbc
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-cbc/pad:none');
+
+-- without padding, input not multiple of block size
+SELECT encrypt(
+'\x00112233445566778899aabbccddeeff00',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
+'aes-cbc/pad:none');
+
+-- key padding
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405',
+'aes-cbc');
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405060708090a0b0c0d0e0f10111213',
+'aes-cbc');
+
+SELECT encrypt(
+'\x0011223344',
+'\x000102030405060708090a0b0c0d0e0f101112131415161718191a1b',
+'aes-cbc');
+
+-- empty data
+select encrypt('', 'foo', 'aes');
+-- 10 bytes key
+select encrypt('foo', '0123456789', 'aes');
+-- 22 bytes key
+select encrypt('foo', '0123456789012345678901', 'aes');
+
+-- decrypt
+select encode(decrypt(encrypt('foo', '0123456', 'aes'), '0123456', 'aes'), 'escape');
+-- data not multiple of block size
+select encode(decrypt(encrypt('foo', '0123456', 'aes') || '\x00'::bytea, '0123456', 'aes'), 'escape');
+-- bad padding
+-- (The input value is the result of encrypt_iv('abcdefghijklmnopqrstuvwxyz', '0123456', 'abcd', 'aes')
+-- with the 16th byte changed (s/db/eb/) to corrupt the padding of the last block.)
+select encode(decrypt_iv('\xa21a9c15231465964e3396d32095e67eb52bab05f556a581621dee1b85385789', '0123456', 'abcd', 'aes'), 'escape');
+
+-- iv
+select encrypt_iv('foo', '0123456', 'abcd', 'aes');
+select encode(decrypt_iv('\x2c24cb7da91d6d5699801268b0f5adad', '0123456', 'abcd', 'aes'), 'escape');
+
+-- long message
+select encrypt('Lets try a longer message.', '0123456789', 'aes');
+select encode(decrypt(encrypt('Lets try a longer message.', '0123456789', 'aes'), '0123456789', 'aes'), 'escape');
diff --git a/contrib/pgcrypto/sql/sha1.sql b/contrib/pgcrypto/sql/sha1.sql
new file mode 100644
index 0000000..6d1f24e
--- /dev/null
+++ b/contrib/pgcrypto/sql/sha1.sql
@@ -0,0 +1,11 @@
+--
+-- SHA1 message digest
+--
+
+SELECT digest('', 'sha1');
+SELECT digest('a', 'sha1');
+SELECT digest('abc', 'sha1');
+SELECT digest('message digest', 'sha1');
+SELECT digest('abcdefghijklmnopqrstuvwxyz', 'sha1');
+SELECT digest('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'sha1');
+SELECT digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha1');
diff --git a/contrib/pgcrypto/sql/sha2.sql b/contrib/pgcrypto/sql/sha2.sql
new file mode 100644
index 0000000..3aafd35
--- /dev/null
+++ b/contrib/pgcrypto/sql/sha2.sql
@@ -0,0 +1,33 @@
+--
+-- SHA2 family
+--
+
+-- SHA224
+SELECT digest('', 'sha224');
+SELECT digest('a', 'sha224');
+SELECT digest('abc', 'sha224');
+SELECT digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha224');
+SELECT digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha224');
+
+-- SHA256
+SELECT digest('', 'sha256');
+SELECT digest('a', 'sha256');
+SELECT digest('abc', 'sha256');
+SELECT digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha256');
+SELECT digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha256');
+
+-- SHA384
+SELECT digest('', 'sha384');
+SELECT digest('a', 'sha384');
+SELECT digest('abc', 'sha384');
+SELECT digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha384');
+SELECT digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha384');
+SELECT digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha384');
+
+-- SHA512
+SELECT digest('', 'sha512');
+SELECT digest('a', 'sha512');
+SELECT digest('abc', 'sha512');
+SELECT digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha512');
+SELECT digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha512');
+SELECT digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha512');