From e94dd6ab9100961511e3407fea9e5b191dec7165 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Sun, 10 Jul 2005 13:46:29 +0000
Subject: [PATCH] Add missing pgcrypto files from previous commit.

---
 contrib/pgcrypto/expected/pgp-armor.out       |  102 ++
 contrib/pgcrypto/expected/pgp-compression.out |   50 +
 contrib/pgcrypto/expected/pgp-decrypt.out     |  366 ++++++
 .../expected/pgp-encrypt-DISABLED.out         |    1 +
 contrib/pgcrypto/expected/pgp-encrypt.out     |  189 +++
 contrib/pgcrypto/expected/pgp-info.out        |   65 +
 .../pgcrypto/expected/pgp-pubkey-DISABLED.out |    1 +
 .../pgcrypto/expected/pgp-pubkey-decrypt.out  |  441 +++++++
 .../pgcrypto/expected/pgp-pubkey-encrypt.out  |   59 +
 .../pgcrypto/expected/pgp-zlib-DISABLED.out   |    1 +
 contrib/pgcrypto/expected/sha2.out            |  108 ++
 contrib/pgcrypto/fortuna.c                    |  365 ++++++
 contrib/pgcrypto/fortuna.h                    |   45 +
 contrib/pgcrypto/mbuf.c                       |  556 ++++++++
 contrib/pgcrypto/mbuf.h                       |  121 ++
 contrib/pgcrypto/pgp-armor.c                  |  383 ++++++
 contrib/pgcrypto/pgp-cfb.c                    |  257 ++++
 contrib/pgcrypto/pgp-compress.c               |  322 +++++
 contrib/pgcrypto/pgp-decrypt.c                | 1156 +++++++++++++++++
 contrib/pgcrypto/pgp-encrypt.c                |  698 ++++++++++
 contrib/pgcrypto/pgp-info.c                   |  213 +++
 contrib/pgcrypto/pgp-mpi-internal.c           |   50 +
 contrib/pgcrypto/pgp-mpi-openssl.c            |  197 +++
 contrib/pgcrypto/pgp-mpi.c                    |  134 ++
 contrib/pgcrypto/pgp-pgsql.c                  |  901 +++++++++++++
 contrib/pgcrypto/pgp-pubdec.c                 |  193 +++
 contrib/pgcrypto/pgp-pubenc.c                 |  230 ++++
 contrib/pgcrypto/pgp-pubkey.c                 |  448 +++++++
 contrib/pgcrypto/pgp-s2k.c                    |  299 +++++
 contrib/pgcrypto/pgp.c                        |  351 +++++
 contrib/pgcrypto/pgp.h                        |  273 ++++
 contrib/pgcrypto/sha2.c                       |  895 +++++++++++++
 contrib/pgcrypto/sha2.h                       |   80 ++
 contrib/pgcrypto/sql/pgp-armor.sql            |   56 +
 contrib/pgcrypto/sql/pgp-compression.sql      |   31 +
 contrib/pgcrypto/sql/pgp-decrypt.sql          |  266 ++++
 contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql |    3 +
 contrib/pgcrypto/sql/pgp-encrypt.sql          |   96 ++
 contrib/pgcrypto/sql/pgp-info.sql             |   21 +
 contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql  |    3 +
 contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql   |  437 +++++++
 contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql   |   45 +
 contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql    |    3 +
 contrib/pgcrypto/sql/sha2.sql                 |   28 +
 44 files changed, 10539 insertions(+)
 create mode 100644 contrib/pgcrypto/expected/pgp-armor.out
 create mode 100644 contrib/pgcrypto/expected/pgp-compression.out
 create mode 100644 contrib/pgcrypto/expected/pgp-decrypt.out
 create mode 100644 contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out
 create mode 100644 contrib/pgcrypto/expected/pgp-encrypt.out
 create mode 100644 contrib/pgcrypto/expected/pgp-info.out
 create mode 100644 contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out
 create mode 100644 contrib/pgcrypto/expected/pgp-pubkey-decrypt.out
 create mode 100644 contrib/pgcrypto/expected/pgp-pubkey-encrypt.out
 create mode 100644 contrib/pgcrypto/expected/pgp-zlib-DISABLED.out
 create mode 100644 contrib/pgcrypto/expected/sha2.out
 create mode 100644 contrib/pgcrypto/fortuna.c
 create mode 100644 contrib/pgcrypto/fortuna.h
 create mode 100644 contrib/pgcrypto/mbuf.c
 create mode 100644 contrib/pgcrypto/mbuf.h
 create mode 100644 contrib/pgcrypto/pgp-armor.c
 create mode 100644 contrib/pgcrypto/pgp-cfb.c
 create mode 100644 contrib/pgcrypto/pgp-compress.c
 create mode 100644 contrib/pgcrypto/pgp-decrypt.c
 create mode 100644 contrib/pgcrypto/pgp-encrypt.c
 create mode 100644 contrib/pgcrypto/pgp-info.c
 create mode 100644 contrib/pgcrypto/pgp-mpi-internal.c
 create mode 100644 contrib/pgcrypto/pgp-mpi-openssl.c
 create mode 100644 contrib/pgcrypto/pgp-mpi.c
 create mode 100644 contrib/pgcrypto/pgp-pgsql.c
 create mode 100644 contrib/pgcrypto/pgp-pubdec.c
 create mode 100644 contrib/pgcrypto/pgp-pubenc.c
 create mode 100644 contrib/pgcrypto/pgp-pubkey.c
 create mode 100644 contrib/pgcrypto/pgp-s2k.c
 create mode 100644 contrib/pgcrypto/pgp.c
 create mode 100644 contrib/pgcrypto/pgp.h
 create mode 100644 contrib/pgcrypto/sha2.c
 create mode 100644 contrib/pgcrypto/sha2.h
 create mode 100644 contrib/pgcrypto/sql/pgp-armor.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-compression.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-decrypt.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-encrypt.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-info.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql
 create mode 100644 contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql
 create mode 100644 contrib/pgcrypto/sql/sha2.sql

diff --git a/contrib/pgcrypto/expected/pgp-armor.out b/contrib/pgcrypto/expected/pgp-armor.out
new file mode 100644
index 00000000000..ba7d4262377
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-armor.out
@@ -0,0 +1,102 @@
+--
+-- 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 dearmor(armor(''));
+ dearmor 
+---------
+ 
+(1 row)
+
+select dearmor(armor('zooka'));
+ dearmor 
+---------
+ zooka
+(1 row)
+
+select armor('0123456789abcdef0123456789abcdef0123456789abcdef
+0123456789abcdef0123456789abcdef0123456789abcdef');
+                                                                                                armor                                                                                                
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ -----BEGIN PGP MESSAGE-----
+
+MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmCjAxMjM0NTY3
+ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZg==
+=JFw5
+-----END PGP MESSAGE-----
+
+(1 row)
+
+-- lots formatting
+select dearmor(' a pgp msg:
+
+-----BEGIN PGP MESSAGE-----
+Comment: Some junk
+
+em9va2E=
+
+  =D5cR
+
+-----END PGP MESSAGE-----');
+ dearmor 
+---------
+ zooka
+(1 row)
+
+-- lots messages
+select 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-----
+');
+ dearmor 
+---------
+ right
+(1 row)
+
+-- bad crc
+select dearmor('
+-----BEGIN PGP MESSAGE-----
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
+ERROR:  dearmor: Corrupt ascii-armor
diff --git a/contrib/pgcrypto/expected/pgp-compression.out b/contrib/pgcrypto/expected/pgp-compression.out
new file mode 100644
index 00000000000..32b350b8fe0
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-compression.out
@@ -0,0 +1,50 @@
+--
+-- 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)
+
diff --git a/contrib/pgcrypto/expected/pgp-decrypt.out b/contrib/pgcrypto/expected/pgp-decrypt.out
new file mode 100644
index 00000000000..8ecb56f0098
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-decrypt.out
@@ -0,0 +1,366 @@
+--
+-- pgp_descrypt 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 encode(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'), 'hex');
+                  encode                  
+------------------------------------------
+ 0225e3ede6f2587b076d021a189ff60aad67e066
+(1 row)
+
+-- expected: 0225e3ede6f2587b076d021a189ff60aad67e066
+select encode(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'), 'hex');
+                  encode                  
+------------------------------------------
+ da39a3ee5e6b4b0d3255bfef95601890afd80709
+(1 row)
+
+-- expected: da39a3ee5e6b4b0d3255bfef95601890afd80709
+select encode(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'), 'hex');
+                  encode                  
+------------------------------------------
+ 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
+(1 row)
+
+-- expected: 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
+-- Checking CRLF
+select encode(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'), 'hex');
+                  encode                  
+------------------------------------------
+ 9353062be7720f1446d30b9e75573a4833886784
+(1 row)
+
+-- expected: 9353062be7720f1446d30b9e75573a4833886784
+select encode(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'), 'hex');
+                  encode                  
+------------------------------------------
+ 7efefcab38467f7484d6fa43dc86cf5281bd78e2
+(1 row)
+
+-- expected: 7efefcab38467f7484d6fa43dc86cf5281bd78e2
diff --git a/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out b/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out
new file mode 100644
index 00000000000..4122300d97a
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out
@@ -0,0 +1 @@
+-- no random source
diff --git a/contrib/pgcrypto/expected/pgp-encrypt.out b/contrib/pgcrypto/expected/pgp-encrypt.out
new file mode 100644
index 00000000000..b04bae57207
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-encrypt.out
@@ -0,0 +1,189 @@
+--
+-- 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:  pgp_decrypt error: Not text data
+-- text as bytea
+select pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz');
+ pgp_sym_decrypt_bytea 
+-----------------------
+ 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 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 encode(pgp_sym_decrypt_bytea(
+	pgp_sym_encrypt('1\n2\n3\r\n', 'key', 'convert-crlf=1'),
+ 	'key'), 'hex');
+        encode        
+----------------------
+ 310d0a320d0a330d0d0a
+(1 row)
+
+-- conversion should be lossless
+select encode(digest(pgp_sym_decrypt(
+  pgp_sym_encrypt('\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'),
+ 	'key', 'convert-crlf=1'), 'sha1'), 'hex') as result,
+  encode(digest('\r\n0\n1\r\r\n\n2\r', 'sha1'), 'hex') as expect;
+                  result                  |                  expect                  
+------------------------------------------+------------------------------------------
+ 47bde5d88d6ef8770572b9cbb4278b402aa69966 | 47bde5d88d6ef8770572b9cbb4278b402aa69966
+(1 row)
+
diff --git a/contrib/pgcrypto/expected/pgp-info.out b/contrib/pgcrypto/expected/pgp-info.out
new file mode 100644
index 00000000000..300d41adcd1
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-info.out
@@ -0,0 +1,65 @@
+--
+-- 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 usable key found (expecting Elgamal key)
+select pgp_key_id(dearmor(pubkey)) from keytbl where id=5;
+    pgp_key_id    
+------------------
+ D936CF64BB73F466
+(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 usable key found (expecting Elgamal key)
+select pgp_key_id(dearmor(seckey)) from keytbl where id=5;
+    pgp_key_id    
+------------------
+ D936CF64BB73F466
+(1 row)
+
+select pgp_key_id(dearmor(data)) as data_key_id
+from encdata order by id;
+   data_key_id    
+------------------
+ D936CF64BB73F466
+ 2C226E1FFE5CC7D4
+ B68504FD128E1FF9
+(3 rows)
+
diff --git a/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out b/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out
new file mode 100644
index 00000000000..d35c0971ba1
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out
@@ -0,0 +1 @@
+-- no bignum support
diff --git a/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out b/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out
new file mode 100644
index 00000000000..28881ad3479
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out
@@ -0,0 +1,441 @@
+--
+-- 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)
+
+mQGiBELII7wRBACFuaAvb11cIvjJK9LkZr4cYuYhLWh3DJdojNNnLNiym5OEksvY
+05cw8OgqKtPzICU7o/mHXTWhzJYUt3i50/AeYygI8Q0uATS6RnDAKNlES1EMoHKz
+2a5iFbYs4bm4IwlkvYd8uWjcu+U0YLbxir39u+anIc6eT+q3WiH/q3zDRwCgkT98
+cnIG8iO8PdwDSP8G4Lt6TYED/R45GvCzJ4onQALLE92KkLUz8aFWSl05r84kczEN
+SxiP9Ss6m465RmwWHfwYAu4b+c4GeNyU8fIU2EM8cezchC+edEi3xu1s+pCV0Dk4
+18DGC8WKCICO30vBynuNmYg7W/7Zd4wtjss454fMW7+idVDNM701mmXBtI1nsBtG
+7Z4tA/9FxjFbJK9jh24RewfjHpLYqcfCo2SsUjOwsnMZ5yg2yv9KyVVQhRqwmrqt
+q8MRyjGmfoD9PPdCgvqgzy0hHvAHUtTm2zUczGTG+0g4hNIklxC/Mv6J4KE+NWTh
+uB4acqofHyaw2WnKOuRUsoDi6rG5AyjNMyAK/vVcEGj7J1tk27QjRWxnYW1hbCA0
+MDk2IDx0ZXN0NDA5NkBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgjvAIbAwYLCQgH
+AwIDFQIDAxYCAQIeAQIXgAAKCRBj+HX2P2d0oAEDAJ9lI+CNmb42z3+a6TnVusM6
+FI7oLwCfUwA1zEcRdsT3nIkoYh0iKxFSDFW5BA0EQsgkdhAQAJQbLXlgcJ/jq+Xh
+Eujb77/eeftFJObNIRYD9fmJ7HFIXbUcknEpbs+cRH/nrj5dGSY3OT3jCXOUtvec
+sCoX/CpZWL0oqDjAiZtNSFiulw5Gav4gHYkWKgKdSo+2rkavEPqKIVHvMeXaJtGT
+d7v/AmL/P8T7gls93o5WFBOLtPbDvWqaKRy2U5TAhl1laiM0vGALRVjvSCgnGw9g
+FpSnXbO3AfenUSjDzZujfGLHtU44ixHSS/D4DepiF3YaYLsN4CBqZRv6FbMZD5W3
+DnJY4kS1kH0MzdcF19TlcZ3itTCcGIt1tMKf84mccPoqdMzH7vumBGTeFEly5Afp
+9berJcirqh2fzlunN0GS02z6SGWnjTbDlkNDxuxPSBbpcpNyD3jpYAUqSwRsZ/+5
+zkzcbGtDmvy9sJ5lAXkxGoIoQ1tEVX/LOHnh2NQHK8ourVOnr7MS0nozssITZJ5E
+XqtHiREjiYEuPyZiVZKJHLWuYYaF+n40znnz3sJuXFRreHhHbbvRdlYUU5mJV+XZ
+BLgKuS33NdpGeMIngnCc/9IQ6OZb6ixc94kbkd3w2PVr8CbKlu/IHTjWOO2mAo+D
++OydlYl23FiM3KOyMP1HcEOJMB/nwkMtrvd+522Lu9n77ktKfot9IPrQDIQTyXjR
+3pCOFtCOBnk2tJHMPoG9jn9ah/LHAAMHEACDZ5I/MHGfmiKg2hrmqBu2J2j/deC8
+CpwcyDH1ovQ0gHvb9ESa+CVRU2Wdy2CD7Q9SmtMverB5eneL418iPVRcQdwRmQ2y
+IH4udlBa6ce9HTUCaecAZ4/tYBnaC0Av/9l9tz14eYcwRMDpB+bnkhgF+PZ1KAfD
+9wcY2aHbtsf3lZBc5h4owPJkxpe/BNzuJxW3q4VpSbLsZhwnCZ2wg7DRwP44wFIk
+00ptmoBY59gsU6I40XtzrF8JDr0cA57xND5RY21Z8lnnYRE1Tc8h5REps9ZIxW3/
+yl91404bPLqxczpUHQAMSTAmBaStPYX1nS51uofOhLs5SKPCUmxfGKIOhsD0oLUn
+78DnkONVGeXzBibSwwtbgfMzee4G8wSUfJ7w8WXz1TyanaGLnJ+DuKASSOrFoBCD
+HEDuWZWgSL74NOQupFRk0gxOPmqU94Y8HziQWma/cETbmD83q8rxN+GM2oBxQkQG
+xcbqMTHE7aVhV3tymbSWVaYhww3oIwsZS9oUIi1DnPEowS6CpVRrwdvLjLJnJzzV
+O3AFPn9eZ1Q7R1tNx+zZ4OOfhvI/OlRJ3HBx2L53embkbdY9gFYCCdTjPyjKoDIx
+kALgCajjCYMNUsAKNSd6mMCQ8TtvukSzkZS1RGKP27ohsdnzIVsiEAbxDMMcI4k1
+ul0LExUTCXSjeIhJBBgRAgAJBQJCyCR2AhsMAAoJEGP4dfY/Z3Sg19sAn0NDS8pb
+qrMpQAxSb7zRTmcXEFd9AJ435H0ttP/NhLHXC9ezgbCMmpXMOQ==
+=kRxT
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BELII7wRBACFuaAvb11cIvjJK9LkZr4cYuYhLWh3DJdojNNnLNiym5OEksvY
+05cw8OgqKtPzICU7o/mHXTWhzJYUt3i50/AeYygI8Q0uATS6RnDAKNlES1EMoHKz
+2a5iFbYs4bm4IwlkvYd8uWjcu+U0YLbxir39u+anIc6eT+q3WiH/q3zDRwCgkT98
+cnIG8iO8PdwDSP8G4Lt6TYED/R45GvCzJ4onQALLE92KkLUz8aFWSl05r84kczEN
+SxiP9Ss6m465RmwWHfwYAu4b+c4GeNyU8fIU2EM8cezchC+edEi3xu1s+pCV0Dk4
+18DGC8WKCICO30vBynuNmYg7W/7Zd4wtjss454fMW7+idVDNM701mmXBtI1nsBtG
+7Z4tA/9FxjFbJK9jh24RewfjHpLYqcfCo2SsUjOwsnMZ5yg2yv9KyVVQhRqwmrqt
+q8MRyjGmfoD9PPdCgvqgzy0hHvAHUtTm2zUczGTG+0g4hNIklxC/Mv6J4KE+NWTh
+uB4acqofHyaw2WnKOuRUsoDi6rG5AyjNMyAK/vVcEGj7J1tk2wAAoJCUNy6awTkw
+XfbLbpqh0fvDst7jDLa0I0VsZ2FtYWwgNDA5NiA8dGVzdDQwOTZAZXhhbXBsZS5v
+cmc+iF4EExECAB4FAkLII7wCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQY/h1
+9j9ndKABAwCeNEOVK87EzXYbtxYBsnjrUI948NIAn2+f3BXiBFDV5NvqPwIZ0m77
+Fwy4nQRMBELIJHYQEACUGy15YHCf46vl4RLo2++/3nn7RSTmzSEWA/X5iexxSF21
+HJJxKW7PnER/564+XRkmNzk94wlzlLb3nLAqF/wqWVi9KKg4wImbTUhYrpcORmr+
+IB2JFioCnUqPtq5GrxD6iiFR7zHl2ibRk3e7/wJi/z/E+4JbPd6OVhQTi7T2w71q
+mikctlOUwIZdZWojNLxgC0VY70goJxsPYBaUp12ztwH3p1Eow82bo3xix7VOOIsR
+0kvw+A3qYhd2GmC7DeAgamUb+hWzGQ+Vtw5yWOJEtZB9DM3XBdfU5XGd4rUwnBiL
+dbTCn/OJnHD6KnTMx+77pgRk3hRJcuQH6fW3qyXIq6odn85bpzdBktNs+khlp402
+w5ZDQ8bsT0gW6XKTcg946WAFKksEbGf/uc5M3GxrQ5r8vbCeZQF5MRqCKENbRFV/
+yzh54djUByvKLq1Tp6+zEtJ6M7LCE2SeRF6rR4kRI4mBLj8mYlWSiRy1rmGGhfp+
+NM55897CblxUa3h4R2270XZWFFOZiVfl2QS4Crkt9zXaRnjCJ4JwnP/SEOjmW+os
+XPeJG5Hd8Nj1a/AmypbvyB041jjtpgKPg/jsnZWJdtxYjNyjsjD9R3BDiTAf58JD
+La73fudti7vZ++5LSn6LfSD60AyEE8l40d6QjhbQjgZ5NrSRzD6BvY5/WofyxwAD
+BxAAg2eSPzBxn5oioNoa5qgbtido/3XgvAqcHMgx9aL0NIB72/REmvglUVNlnctg
+g+0PUprTL3qweXp3i+NfIj1UXEHcEZkNsiB+LnZQWunHvR01AmnnAGeP7WAZ2gtA
+L//Zfbc9eHmHMETA6Qfm55IYBfj2dSgHw/cHGNmh27bH95WQXOYeKMDyZMaXvwTc
+7icVt6uFaUmy7GYcJwmdsIOw0cD+OMBSJNNKbZqAWOfYLFOiONF7c6xfCQ69HAOe
+8TQ+UWNtWfJZ52ERNU3PIeURKbPWSMVt/8pfdeNOGzy6sXM6VB0ADEkwJgWkrT2F
+9Z0udbqHzoS7OUijwlJsXxiiDobA9KC1J+/A55DjVRnl8wYm0sMLW4HzM3nuBvME
+lHye8PFl89U8mp2hi5yfg7igEkjqxaAQgxxA7lmVoEi++DTkLqRUZNIMTj5qlPeG
+PB84kFpmv3BE25g/N6vK8TfhjNqAcUJEBsXG6jExxO2lYVd7cpm0llWmIcMN6CML
+GUvaFCItQ5zxKMEugqVUa8Hby4yyZyc81TtwBT5/XmdUO0dbTcfs2eDjn4byPzpU
+Sdxwcdi+d3pm5G3WPYBWAgnU4z8oyqAyMZAC4Amo4wmDDVLACjUnepjAkPE7b7pE
+s5GUtURij9u6IbHZ8yFbIhAG8QzDHCOJNbpdCxMVEwl0o3gAAckBdfKuasiNUn5G
+L5XRnSvaOFzftr8zteOlZChCSNvzH5k+i1j7RJbWq06OeKRywPzjfjgM2MvRzI43
+ICeISQQYEQIACQUCQsgkdgIbDAAKCRBj+HX2P2d0oNfbAJ9+G3SeXrk+dWwo9EGi
+hqMi2GVTsgCfeoQJPsc8FLYUgfymc/3xqAVLUtg=
+=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)
+
+lQHhBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+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+sAKqROqe6dLjJ7vebQP4DAwL3TCgrYdj6
++GAnoSqGa87twi8a6QRRYIlEx3ddUCDCjzkJmRfF+LFtvX3OtWWK0+Syi3kj2IK9
+YT7pF7QfRWxnYW1hbCAxMDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJC
+yCFIAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAn1ynoCyM
+6GIvHDOewwmF4Z/jGQfzAJ9Q+MwIubi0ASfJifaEM23sIHwHop0BVwRCyCFKEAQA
+h5SNbbJMAsJ+sQbcWEzdku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioym
+lDwraTKUAfuCZgNcg/0PsxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlP
+iO0wt1lLX+SubktqbYxI+h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSg
+MERiNzF0acZUYmc0e+/96gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxs
+nKjUaw/qyoaFcNMzb4sFk8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey
+9ifh8rZfu57UbdwdHa0viWc4Dilh/gMDAvdMKCth2Pr4YCCPsELdgJuzhGfDNRSg
+nKMRWBWHSJRk6JmCjM1iJQNHc4mMhR8gvi2TeqYLOhYjcF7nr/LA+JvLV+adj/mI
+SQQYEQIACQUCQsghSgIbDAAKCRAcKbwNGBdzZO2vAJ4hRaLcNcdl/qK8rt0N5zbZ
+saCh6QCfR1O48O8nYN93SPSfIFZK5rEmdv8=
+=Y6Qv
+-----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-----
+');
+-- 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)
+
+-- wrong key
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=2 and encdata.id=1;
+ERROR:  pgp_decrypt error: Data is not encrypted with this key
+-- sign-only key
+select pgp_pub_decrypt(dearmor(data), dearmor(seckey))
+from keytbl, encdata where keytbl.id=4 and encdata.id=1;
+ERROR:  pgp_decrypt error: No usable key found (expecting Elgamal key)
+-- 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:  pgp_decrypt 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:  pgp_decrypt error: 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)
+
diff --git a/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out b/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out
new file mode 100644
index 00000000000..a7b1c027eef
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out
@@ -0,0 +1,59 @@
+--
+-- 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)
+
+-- 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:  pgp_encrypt error: No usable key found (expecting Elgamal key)
+-- try with secret key
+select pgp_pub_decrypt(
+		pgp_pub_encrypt('Secret msg', dearmor(seckey)),
+		dearmor(seckey))
+from keytbl where keytbl.id=1;
+ERROR:  pgp_encrypt error: Refusing to encrypt with secret key
+-- does text-to-bytea works
+select pgp_pub_decrypt_bytea(
+		pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+		dearmor(seckey))
+from keytbl where keytbl.id=1;
+ pgp_pub_decrypt_bytea 
+-----------------------
+ 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:  pgp_decrypt 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 00000000000..6f4eccd4578
--- /dev/null
+++ b/contrib/pgcrypto/expected/pgp-zlib-DISABLED.out
@@ -0,0 +1 @@
+-- zlib is disabled
diff --git a/contrib/pgcrypto/expected/sha2.out b/contrib/pgcrypto/expected/sha2.out
new file mode 100644
index 00000000000..20bdf0ed814
--- /dev/null
+++ b/contrib/pgcrypto/expected/sha2.out
@@ -0,0 +1,108 @@
+--
+-- SHA2 family
+--
+-- SHA256
+SELECT encode(digest('', 'sha256'), 'hex');
+                              encode                              
+------------------------------------------------------------------
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+(1 row)
+
+SELECT encode(digest('a', 'sha256'), 'hex');
+                              encode                              
+------------------------------------------------------------------
+ ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
+(1 row)
+
+SELECT encode(digest('abc', 'sha256'), 'hex');
+                              encode                              
+------------------------------------------------------------------
+ ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
+(1 row)
+
+SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha256'), 'hex');
+                              encode                              
+------------------------------------------------------------------
+ 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1
+(1 row)
+
+SELECT encode(digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha256'), 'hex');
+                              encode                              
+------------------------------------------------------------------
+ f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e
+(1 row)
+
+-- SHA384
+SELECT encode(digest('', 'sha384'), 'hex');
+                                              encode                                              
+--------------------------------------------------------------------------------------------------
+ 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b
+(1 row)
+
+SELECT encode(digest('a', 'sha384'), 'hex');
+                                              encode                                              
+--------------------------------------------------------------------------------------------------
+ 54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31
+(1 row)
+
+SELECT encode(digest('abc', 'sha384'), 'hex');
+                                              encode                                              
+--------------------------------------------------------------------------------------------------
+ cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7
+(1 row)
+
+SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha384'), 'hex');
+                                              encode                                              
+--------------------------------------------------------------------------------------------------
+ 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b
+(1 row)
+
+SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha384'), 'hex');
+                                              encode                                              
+--------------------------------------------------------------------------------------------------
+ 09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039
+(1 row)
+
+SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha384'), 'hex');
+                                              encode                                              
+--------------------------------------------------------------------------------------------------
+ 3d208973ab3508dbbd7e2c2862ba290ad3010e4978c198dc4d8fd014e582823a89e16f9b2a7bbc1ac938e2d199e8bea4
+(1 row)
+
+-- SHA512
+SELECT encode(digest('', 'sha512'), 'hex');
+                                                              encode                                                              
+----------------------------------------------------------------------------------------------------------------------------------
+ cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
+(1 row)
+
+SELECT encode(digest('a', 'sha512'), 'hex');
+                                                              encode                                                              
+----------------------------------------------------------------------------------------------------------------------------------
+ 1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75
+(1 row)
+
+SELECT encode(digest('abc', 'sha512'), 'hex');
+                                                              encode                                                              
+----------------------------------------------------------------------------------------------------------------------------------
+ ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f
+(1 row)
+
+SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha512'), 'hex');
+                                                              encode                                                              
+----------------------------------------------------------------------------------------------------------------------------------
+ 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445
+(1 row)
+
+SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha512'), 'hex');
+                                                              encode                                                              
+----------------------------------------------------------------------------------------------------------------------------------
+ 8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909
+(1 row)
+
+SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha512'), 'hex');
+                                                              encode                                                              
+----------------------------------------------------------------------------------------------------------------------------------
+ 930d0cefcb30ff1133b6898121f1cf3d27578afcafe8677c5257cf069911f75d8f5831b56ebfda67b278e66dff8b84fe2b2870f742a580d8edb41987232850c9
+(1 row)
+
diff --git a/contrib/pgcrypto/fortuna.c b/contrib/pgcrypto/fortuna.c
new file mode 100644
index 00000000000..ff02a9773e3
--- /dev/null
+++ b/contrib/pgcrypto/fortuna.c
@@ -0,0 +1,365 @@
+/*
+ * fortuna.c
+ *		Fortuna-like PRNG.
+ *
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.c,v 1.1 2005/07/10 13:46:27 momjian Exp $
+ */
+
+#include <postgres.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "rijndael.h"
+#include "sha2.h"
+
+#include "fortuna.h"
+
+
+/*
+ * Why Fortuna-like: There does not seem to be any definitive reference
+ * on Fortuna in the net.  Instead this implementation is based on
+ * following references:
+ * 
+ * http://en.wikipedia.org/wiki/Fortuna_(PRNG)
+ *   - Wikipedia article
+ * http://jlcooke.ca/random/
+ *   - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux.
+ */
+
+/*
+ * There is some confusion about whether and how to carry forward
+ * the state of the pools.  Seems like original Fortuna does not
+ * do it, resetting hash after each request.  I guess expecting
+ * feeding to happen more often that requesting.   This is absolutely
+ * unsuitable for pgcrypto, as nothing asynchronous happens here.
+ *
+ * J.L. Cooke fixed this by feeding previous hash to new re-initialized
+ * hash context.
+ *
+ * Fortuna predecessor Yarrow requires ability to query intermediate
+ * 'final result' from hash, without affecting it.
+ *
+ * This implementation uses the Yarrow method - asking intermediate
+ * results, but continuing with old state.
+ */
+
+
+/*
+ * Algorithm parameters
+ */
+
+/*
+ * How many pools.
+ *
+ * Original Fortuna uses 32 pools, that means 32'th pool is
+ * used not earlier than in 13th year.  This is a waste in
+ * pgcrypto, as we have very low-frequancy seeding.  Here
+ * is preferable to have all entropy usable in reasonable time.
+ *
+ * With 23 pools, 23th pool is used after 9 days which seems
+ * more sane.
+ *
+ * In our case the minimal cycle time would be bit longer
+ * than the system-randomness feeding frequency.
+ */
+#define NUM_POOLS		23
+
+/* in microseconds */
+#define RESEED_INTERVAL	100000 /* 0.1 sec */
+
+/* for one big request, reseed after this many bytes */
+#define RESEED_BYTES	(1024*1024)
+
+
+/*
+ * Algorithm constants
+ */
+
+/* max sources */
+#define MAX_SOURCES		8
+
+/* Both cipher key size and hash result size */
+#define BLOCK			32
+
+/* cipher block size */
+#define CIPH_BLOCK		16
+
+/* for internal wrappers */
+#define MD_CTX			SHA256_CTX
+#define CIPH_CTX		rijndael_ctx
+
+struct fortuna_state {
+	uint8			counter[CIPH_BLOCK];
+	uint8			result[CIPH_BLOCK];
+	uint8			key[BLOCK];
+	MD_CTX			pool[NUM_POOLS];
+	CIPH_CTX		ciph;
+	unsigned		source_pos[MAX_SOURCES];
+	unsigned		reseed_count;
+	struct timeval	last_reseed_time;
+};
+typedef struct fortuna_state FState;
+
+
+/*
+ * Use our own wrappers here.
+ * - Need to get intermediate result from digest, without affecting it.
+ * - Need re-set key on a cipher context.
+ * - Algorithms are guaranteed to exist.
+ * - No memory allocations.
+ */
+
+static void ciph_init(CIPH_CTX *ctx, const uint8 *key, int klen)
+{
+	rijndael_set_key(ctx, (const uint32 *)key, klen, 1);
+}
+
+static void ciph_encrypt(CIPH_CTX *ctx, const uint8 *in, uint8 *out)
+{
+	rijndael_encrypt(ctx, (const uint32 *)in, (uint32 *)out);
+}
+
+static void md_init(MD_CTX *ctx)
+{
+	SHA256_Init(ctx);
+}
+
+static void md_update(MD_CTX *ctx, const uint8 *data, int len)
+{
+	SHA256_Update(ctx, data, len);
+}
+
+static void md_result(MD_CTX *ctx, uint8 *dst)
+{
+	SHA256_CTX tmp;
+	memcpy(&tmp, ctx, sizeof(*ctx));
+	SHA256_Final(dst, &tmp);
+	memset(&tmp, 0, sizeof(tmp));
+}
+
+
+/*
+ * initialize state
+ */
+static void init_state(FState *st)
+{
+	int i;
+	memset(st, 0, sizeof(*st));
+	for (i = 0; i < NUM_POOLS; i++)
+		md_init(&st->pool[i]);
+}
+
+/*
+ * Must not reseed more ofter than RESEED_PER_SEC
+ * times per second.
+ */
+static int too_often(FState *st)
+{
+	int ok;
+	struct timeval tv;
+	struct timeval *last = &st->last_reseed_time;
+	
+	gettimeofday(&tv, NULL);
+
+	ok = 0;
+	if (tv.tv_sec != last->tv_sec)
+		ok = 1;
+	else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL)
+		ok = 1;
+
+	memcpy(last, &tv, sizeof(tv));
+	memset(&tv, 0, sizeof(tv));
+
+	return ok;
+}
+
+/*
+ * generate new key from all the pools
+ */
+static void reseed(FState *st)
+{
+	unsigned k;
+	unsigned n;
+	MD_CTX key_md;
+	uint8 buf[BLOCK];
+
+	/* check frequency */
+	if (too_often(st))
+		return;
+
+	/*
+	 * Both #0 and #1 reseed would use only pool 0.
+	 * Just skip #0 then.
+	 */
+	n = ++st->reseed_count;
+
+	/*
+	 * The goal: use k-th pool only 1/(2^k) of the time.
+	 */
+	md_init(&key_md);
+	for (k = 0; k < NUM_POOLS; k++) {
+		md_result(&st->pool[k], buf);
+		md_update(&key_md, buf, BLOCK);
+
+		if (n & 1 || !n)
+			break;
+		n >>= 1;
+	}
+
+	/* add old key into mix too */
+	md_update(&key_md, st->key, BLOCK);
+
+	/* now we have new key */
+	md_result(&key_md, st->key);
+
+	/* use new key */
+	ciph_init(&st->ciph, st->key, BLOCK);
+
+	memset(&key_md, 0, sizeof(key_md));
+	memset(buf, 0, BLOCK);
+	n = k = 0;
+}
+
+/*
+ * update pools
+ */
+static void add_entropy(FState *st, unsigned src_id, const uint8 *data, unsigned len)
+{
+	unsigned pos;
+	uint8 hash[BLOCK];
+	MD_CTX md;
+
+	/* just in case there's a bug somewhere */
+	if (src_id >= MAX_SOURCES)
+		src_id = USER_ENTROPY;
+
+	/* hash given data */
+	md_init(&md);
+	md_update(&md, data, len);
+	md_result(&md, hash);
+
+	/* update pools round-robin manner */
+	pos = st->source_pos[src_id];
+	md_update( &st->pool[pos], hash, BLOCK);
+
+	if (++pos >= NUM_POOLS)
+		pos = 0;
+	st->source_pos[src_id] = pos;
+
+	memset(hash, 0, BLOCK);
+	memset(&md, 0, sizeof(md));
+}
+
+/*
+ * Endianess does not matter.
+ * It just needs to change without repeating.
+ */
+static void inc_counter(FState *st)
+{
+	uint32 *val = (uint32*)st->counter;
+	if (++val[0])
+		return;
+	if (++val[1])
+		return;
+	if (++val[2])
+		return;
+	++val[3];
+}
+
+static void extract_data(FState *st, unsigned count, uint8 *dst)
+{
+	unsigned n;
+	unsigned block_nr = 0;
+
+	/*
+	 * Every request should be with different key,
+	 * if possible.
+	 */
+	reseed(st);
+
+	/*
+	 * If the reseed didn't happen, don't use the old data
+	 * rather encrypt again.
+	 */
+
+	while (count > 0) {
+		/* must not give out too many bytes with one key */
+		if (block_nr > (RESEED_BYTES / CIPH_BLOCK))
+		{
+			reseed(st);
+			block_nr = 0;
+		}
+
+		/* produce bytes */
+		ciph_encrypt(&st->ciph, st->counter, st->result);
+		block_nr++;
+
+		/* prepare for next time */
+		inc_counter(st);
+
+		/* copy result */
+		if (count > CIPH_BLOCK)
+			n = CIPH_BLOCK;
+		else
+			n = count;
+		memcpy(dst, st->result, n);
+		dst += n;
+		count -= n;
+	}
+}
+
+/*
+ * public interface
+ */
+
+static FState main_state;
+static int init_done = 0;
+
+void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len)
+{
+	if (!init_done)
+	{
+		init_state(&main_state);
+		init_done = 1;
+	}
+	if (!data || !len)
+		return;
+	add_entropy(&main_state, src_id, data, len);
+}
+
+void fortuna_get_bytes(unsigned len, uint8 *dst)
+{
+	if (!init_done)
+	{
+		init_state(&main_state);
+		init_done = 1;
+	}
+	if (!dst || !len)
+		return;
+	extract_data(&main_state, len, dst);
+}
+
diff --git a/contrib/pgcrypto/fortuna.h b/contrib/pgcrypto/fortuna.h
new file mode 100644
index 00000000000..b576eb981d1
--- /dev/null
+++ b/contrib/pgcrypto/fortuna.h
@@ -0,0 +1,45 @@
+/*
+ * fortuna.c
+ *		Fortuna PRNG.
+ *
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/fortuna.h,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#ifndef __FORTUNA_H
+#define __FORTUNA_H
+
+/*
+ * Event source ID's
+ */
+#define SYSTEM_ENTROPY	0
+#define USER_ENTROPY	1
+
+void fortuna_get_bytes(unsigned len, uint8 *dst);
+void fortuna_add_entropy(unsigned src_id, const uint8 *data, unsigned len);
+
+#endif
+
diff --git a/contrib/pgcrypto/mbuf.c b/contrib/pgcrypto/mbuf.c
new file mode 100644
index 00000000000..eb5dff92654
--- /dev/null
+++ b/contrib/pgcrypto/mbuf.c
@@ -0,0 +1,556 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/mbuf.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+
+#define STEP  (16*1024)
+
+struct MBuf
+{
+	uint8	   *data;
+	uint8	   *data_end;
+	uint8	   *read_pos;
+	uint8	   *buf_end;
+	int			no_write:1;
+	int			own_data:1;
+};
+
+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)
+	{
+		memset(mbuf->data, 0, mbuf->buf_end - mbuf->data);
+		px_free(mbuf->data);
+	}
+	px_free(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 = px_realloc(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;
+
+	return;
+}
+
+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 = px_alloc(sizeof *mbuf);
+	mbuf->data = px_alloc(len);
+	mbuf->buf_end = mbuf->data + len;
+	mbuf->data_end = mbuf->data;
+	mbuf->read_pos = mbuf->data;
+
+	mbuf->no_write = 0;
+	mbuf->own_data = 1;
+
+	return mbuf;
+}
+
+MBuf *
+mbuf_create_from_data(const uint8 *data, int len)
+{
+	MBuf	   *mbuf;
+
+	mbuf = px_alloc(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 = 1;
+	mbuf->own_data = 0;
+
+	return mbuf;
+}
+
+
+int
+mbuf_grab(MBuf * mbuf, int len, uint8 **data_p)
+{
+	if (len > mbuf_avail(mbuf))
+		len = mbuf_avail(mbuf);
+
+	mbuf->no_write = 1;
+
+	*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 = 1;
+	mbuf->own_data = 0;
+
+	*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 = px_alloc(sizeof(*pf));
+	memset(pf, 0, sizeof(*pf));
+	pf->buflen = res;
+	pf->op = op;
+	pf->priv = priv;
+	pf->src = src;
+	if (pf->buflen > 0)
+	{
+		pf->buf = px_alloc(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)
+	{
+		memset(pf->buf, 0, pf->buflen);
+		px_free(pf->buf);
+	}
+
+	memset(pf, 0, sizeof(*pf));
+	px_free(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 */
+			memset(tmpbuf, 0, total);
+			return res;
+		}
+		if (res == 0)
+			break;
+		memcpy(tmpbuf + total, tmp, res);
+		total += res;
+	}
+	return total;
+}
+
+/*
+ * caller wants exatly len bytes and dont 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_MBUF_SHORT_READ;
+	}
+	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 = px_alloc(sizeof(*mp));
+	memset(mp, 0, sizeof(*mp));
+	mp->block_size = res;
+	mp->op = op;
+	mp->priv = priv;
+	mp->next = next;
+	if (mp->block_size > 0)
+	{
+		mp->buf = px_alloc(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)
+	{
+		memset(mp->buf, 0, mp->block_size);
+		px_free(mp->buf);
+	}
+
+	memset(mp, 0, sizeof(*mp));
+	px_free(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 00000000000..6042a4fc2e7
--- /dev/null
+++ b/contrib/pgcrypto/mbuf.h
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/mbuf.h,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#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(const 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/pgp-armor.c b/contrib/pgcrypto/pgp-armor.c
new file mode 100644
index 00000000000..20f443f22e8
--- /dev/null
+++ b/contrib/pgcrypto/pgp-armor.c
@@ -0,0 +1,383 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-armor.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#include <postgres.h>
+
+#include <string.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.h"
+
+/*
+ * BASE64 - duplicated :(
+ */
+
+static const unsigned char _base64[] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int
+b64_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
+b64_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
+b64_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
+b64_dec_len(unsigned srclen)
+{
+	return (srclen * 3) >> 2;
+}
+
+/*
+ * PGP armor
+ */
+
+static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n\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;
+}
+
+int
+pgp_armor_encode(const uint8 *src, unsigned len, uint8 *dst)
+{
+	int			n;
+	uint8	   *pos = dst;
+	unsigned	crc = crc24(src, len);
+
+	n = strlen(armor_header);
+	memcpy(pos, armor_header, n);
+	pos += n;
+
+	n = b64_encode(src, len, pos);
+	pos += n;
+
+	if (*(pos - 1) != '\n')
+		*pos++ = '\n';
+
+	*pos++ = '=';
+	pos[3] = _base64[crc & 0x3f];
+	crc >>= 6;
+	pos[2] = _base64[crc & 0x3f];
+	crc >>= 6;
+	pos[1] = _base64[crc & 0x3f];
+	crc >>= 6;
+	pos[0] = _base64[crc & 0x3f];
+	pos += 4;
+
+	n = strlen(armor_footer);
+	memcpy(pos, armor_footer, n);
+	pos += n;
+
+	return pos - dst;
+}
+
+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, unsigned len, uint8 *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			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 (b64_decode(p + 1, 4, buf) != 3)
+		goto out;
+	crc = (((long)buf[0]) << 16) + (((long)buf[1]) << 8) + (long)buf[2];
+
+	/* decode data */
+	res = b64_decode(base64_start, base64_end - base64_start, dst);
+
+	/* check crc */
+	if (res >= 0 && crc24(dst, res) != crc)
+		res = PXE_PGP_CORRUPT_ARMOR;
+out:
+	return res;
+}
+
+unsigned
+pgp_armor_enc_len(unsigned len)
+{
+	return b64_enc_len(len) + strlen(armor_header) + strlen(armor_footer) + 16;
+}
+
+unsigned
+pgp_armor_dec_len(unsigned len)
+{
+	return b64_dec_len(len);
+}
+
diff --git a/contrib/pgcrypto/pgp-cfb.c b/contrib/pgcrypto/pgp-cfb.c
new file mode 100644
index 00000000000..52f37228c7d
--- /dev/null
+++ b/contrib/pgcrypto/pgp-cfb.c
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-cfb.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#include <postgres.h>
+#include "mbuf.h"
+#include "px.h"
+#include "pgp.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 = px_alloc(sizeof(*ctx));
+	memset(ctx, 0, 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);
+	memset(ctx, 0, sizeof(*ctx));
+	px_free(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)
+	{
+		px_cipher_encrypt(ctx->ciph, ctx->fr, ctx->block_size, ctx->fre);
+		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 00000000000..4faf0e4e1ae
--- /dev/null
+++ b/contrib/pgcrypto/pgp-compress.c
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-compress.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#include <postgres.h>
+
+#include "mbuf.h"
+#include "px.h"
+#include "pgp.h"
+
+
+/*
+ * Compressed pkt writer
+ */
+
+#ifndef DISABLE_ZLIB
+
+#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 px_alloc(n_items * item_len);
+}
+
+static void
+z_free(void *priv, void *addr)
+{
+	px_free(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 = px_alloc(sizeof(*st));
+	memset(st, 0, 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)
+	{
+		px_free(st);
+		return PXE_PGP_COMPRESSION_ERROR;
+	}
+	*priv_p = st;
+
+	return ZIP_IN_BLOCK;
+}
+
+/* writes compressed data packet */
+
+/* cant handle zero-len incoming data, but shouldnt */
+static int
+compress_process(PushFilter * next, void *priv, const uint8 *data, int len)
+{
+	int			res,
+				n_out;
+	struct ZipStat *st = priv;
+
+	/*
+	 * process data
+	 */
+	while (len > 0)
+	{
+		st->stream.next_in = (void *) data;
+		st->stream.avail_in = len;
+		st->stream.next_out = st->buf;
+		st->stream.avail_out = st->buf_len;
+		res = deflate(&st->stream, 0);
+		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;
+		}
+		len = st->stream.avail_in;
+	}
+
+	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);
+	memset(st, 0, sizeof(*st));
+	px_free(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 = px_alloc(sizeof(*dec));
+	memset(dec, 0, 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)
+	{
+		px_free(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_NO_FLUSH, Z_SYNC_FLUSH,
+	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)
+		dec->eof = 1;
+	goto restart;
+}
+
+static void decompress_free(void *priv)
+{
+	struct DecomprData *dec = priv;
+	inflateEnd(&dec->stream);
+	memset(dec, 0, sizeof(*dec));
+	px_free(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 /* DISABLE_ZLIB */
+
+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 00000000000..edffc6a3443
--- /dev/null
+++ b/contrib/pgcrypto/pgp-decrypt.c
@@ -0,0 +1,1156 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-decrypt.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.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 dont 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);
+
+	if (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;
+	memset(pkt, 0, sizeof(*pkt));
+	px_free(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 = px_alloc(sizeof(*pkt));
+	pkt->type = pkttype;
+	pkt->len = len;
+	res = pullf_create(pf_p, &pktreader_filter, pkt, src);
+	if (res < 0)
+		px_free(pkt);
+	return res;
+}
+
+/*
+ * Prefix check filter
+ */
+
+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");
+		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");
+		/*
+		 * The original purpose of the 2-byte check was
+		 * to show user a friendly "wrong key" message.
+		 * This made following possible:
+		 *
+		 *   "An Attack on CFB Mode Encryption As Used By OpenPGP"
+		 *   by Serge Mister and Robert Zuccherato
+		 *
+		 * To avoid being 'oracle', we delay reporting, which
+		 * basically means we prefer to run into corrupt packet
+		 * header.
+		 *
+		 * We _could_ throw PXE_PGP_CORRUPT_DATA here, but
+		 * there is possibility of attack via timing, so we don't.
+		 */
+		ctx->corrupt_prefix = 1;
+	}
+	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;
+}
+
+// fixme: clarify
+static int mdc_finish(PGP_Context *ctx, PullFilter *src,
+		int len, uint8 **data_p)
+{
+	int res;
+	uint8 hash[20];
+	uint8 tmpbuf[22];
+
+	if (len + 1 > sizeof(tmpbuf))
+		return PXE_BUG;
+
+	/* read data */
+	res = pullf_read_max(src, len + 1, data_p, tmpbuf);
+	if (res < 0)
+		return res;
+	if (res == 0)
+	{
+		if (ctx->mdc_checked == 0)
+		{
+			px_debug("no mdc");
+			return PXE_PGP_CORRUPT_DATA;
+		}
+		return 0;
+	}
+	
+	if (ctx->in_mdc_pkt > 1)
+	{
+		px_debug("mdc_finish: several times here?");
+		return PXE_PGP_CORRUPT_DATA;
+	}
+	ctx->in_mdc_pkt++;
+	
+	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_p, 20);
+	memset(hash, 0, 20);
+	memset(tmpbuf, 0, sizeof(tmpbuf));
+	if (res != 0)
+	{
+		px_debug("mdc_finish: mdc failed");
+		return PXE_PGP_CORRUPT_DATA;
+	}
+	ctx->mdc_checked = 1;
+	return len;
+}
+
+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)
+		return pullf_read(src, len, data_p);
+
+	if (ctx->in_mdc_pkt)
+		return mdc_finish(ctx, 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_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 = px_alloc(sizeof(*st));
+	memset(st, 0, 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);
+	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;
+	memset(st, 0, sizeof(*st));
+	px_free(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_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);
+	}
+
+	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;
+	}
+	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;
+	}
+	memset(tmpbuf, 0, 4);
+
+	/* check if text */
+	if (ctx->text_mode)
+		if (type != 't' && type != 'u')
+		{
+			px_debug("parse_literal_data: data type=%c", type);
+			return PXE_PGP_NOT_TEXT;
+		}
+
+	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, "\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;
+
+	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");
+			res = PXE_PGP_UNSUPPORTED_COMPR;
+			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;
+	uint8 *tmp;
+
+	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 SYMENC_MDC 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;
+				}
+
+				/* notify mdc_filter */
+				ctx->in_mdc_pkt = 1;
+
+				res = pullf_read(pkt, 8192, &tmp);
+				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 < 0 ? res : 0;				 
+}
+
+/*
+ * expect to be at packet end, any data is error
+ */
+int
+pgp_expect_packet_end(PullFilter *pkt)
+{
+	int res = 1;
+	uint8 *tmp;
+  	while (res > 0)
+	{
+		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 < 0 ? res : 0;				 
+}
+
+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;
+
+	if (!got_data || ctx->corrupt_prefix)
+		res = PXE_PGP_CORRUPT_DATA;
+
+	return res;
+}
+
diff --git a/contrib/pgcrypto/pgp-encrypt.c b/contrib/pgcrypto/pgp-encrypt.c
new file mode 100644
index 00000000000..8aee9bfba2c
--- /dev/null
+++ b/contrib/pgcrypto/pgp-encrypt.c
@@ -0,0 +1,698 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-encrypt.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+
+#include <postgres.h>
+#include <time.h>
+
+#include "mbuf.h"
+#include "px.h"
+#include "pgp.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);
+	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 = px_alloc(sizeof(*st));
+	memset(st, 0, 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;
+
+	memset(st, 0, sizeof(*st));
+	px_free(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 = px_alloc(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;
+
+	memset(st, 0, sizeof(*st));
+	px_free(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);
+	res = px_get_random_bytes(prefix, bs);
+	if (res < 0)
+		return res;
+
+	prefix[bs + 0] = prefix[bs - 2];
+	prefix[bs + 1] = prefix[bs - 1];
+
+	res = pushf_write(dst, prefix, bs + 2);
+	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);
+
+	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);
+	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)
+{
+	int res;
+	if (ctx->use_sess_key || ctx->pub_key)
+	{
+		ctx->sess_key_len = pgp_get_cipher_key_size(ctx->cipher_algo);
+		res = px_get_random_bytes(ctx->sess_key, ctx->sess_key_len);
+		if (res < 0)
+			return res;
+	}
+	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 symkey
+	 */
+	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 00000000000..d757e8c921e
--- /dev/null
+++ b/contrib/pgcrypto/pgp-info.c
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-info.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.h"
+
+static int read_pubkey_keyid(PullFilter *pkt, uint8 *keyid_buf)
+{
+	int res = 0;
+	PGP_PubKey *pk;
+
+	res = pgp_key_alloc(&pk);
+	if (res < 0)
+		return res;
+
+	res = _pgp_read_public_key(pkt, pk);
+	if (res < 0)
+		goto err;
+	res = pgp_skip_packet(pkt);
+	if (res < 0)
+		goto err;
+
+	res = 0;
+	if (pk->algo == PGP_PUB_ELG_ENCRYPT)
+	{
+		memcpy(keyid_buf, pk->key_id, 8);
+		res = 1;
+	}
+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];
+
+
+	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:
+			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:
+				got_data = 1;
+				break;
+			case PGP_PKT_SYMENCRYPTED_SESSKEY:
+				got_symenc_key++;
+				/* fallthru */
+			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 = -1;
+
+	if (got_pubenc_key > 1)
+		res = -1;
+
+	/*
+	 * 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-internal.c b/contrib/pgcrypto/pgp-mpi-internal.c
new file mode 100644
index 00000000000..12831b1af70
--- /dev/null
+++ b/contrib/pgcrypto/pgp-mpi-internal.c
@@ -0,0 +1,50 @@
+/*
+ * pgp-mpi-internal.c
+ *	  OpenPGP MPI 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-mpi-internal.c,v 1.1 2005/07/10 13:46:28 momjian Exp $
+ */
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.h"
+
+int
+pgp_elgamal_encrypt(PGP_PubKey *pk, PGP_MPI *_m,
+		PGP_MPI **c1_p, PGP_MPI **c2_p)
+{
+	return PXE_PGP_NO_BIGNUM;
+}
+
+int
+pgp_elgamal_decrypt(PGP_PubKey *pk, PGP_MPI *_c1, PGP_MPI *_c2,
+		PGP_MPI **msg_p)
+{
+	return PXE_PGP_NO_BIGNUM;
+}
+
diff --git a/contrib/pgcrypto/pgp-mpi-openssl.c b/contrib/pgcrypto/pgp-mpi-openssl.c
new file mode 100644
index 00000000000..c44e1935bd1
--- /dev/null
+++ b/contrib/pgcrypto/pgp-mpi-openssl.c
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-mpi-openssl.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+#include <postgres.h>
+
+#include <openssl/bn.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.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 componont 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
+ * algorihm hovers 10-70 bits above gpg values.  And for
+ * larger p, it uses gpg's algorihm.
+ *
+ * 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->elg_p);
+	BIGNUM *g = mpi_to_bn(pk->elg_g);
+	BIGNUM *y = mpi_to_bn(pk->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_generate_prime(k, k_bits, 0, NULL, NULL, NULL, NULL))
+		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->elg_p);
+	BIGNUM *x = mpi_to_bn(pk->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;
+}
+
diff --git a/contrib/pgcrypto/pgp-mpi.c b/contrib/pgcrypto/pgp-mpi.c
new file mode 100644
index 00000000000..6d3eff499fc
--- /dev/null
+++ b/contrib/pgcrypto/pgp-mpi.c
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-mpi.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.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 = px_alloc(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)
+{
+	memset(mpi, 0, sizeof(*mpi) + mpi->bytes);
+	px_free(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;
+}
+
diff --git a/contrib/pgcrypto/pgp-pgsql.c b/contrib/pgcrypto/pgp-pgsql.c
new file mode 100644
index 00000000000..c17d952b39d
--- /dev/null
+++ b/contrib/pgcrypto/pgp-pgsql.c
@@ -0,0 +1,901 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-pgsql.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+
+#include <postgres.h>
+#include <fmgr.h>
+#include <parser/scansup.h>
+#include <mb/pg_wchar.h>
+
+#include "mbuf.h"
+#include "px.h"
+#include "pgp.h"
+
+/*
+ * public functions
+ */
+Datum pgp_sym_encrypt_text(PG_FUNCTION_ARGS);
+Datum pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS);
+Datum pgp_sym_decrypt_text(PG_FUNCTION_ARGS);
+Datum pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS);
+
+Datum pgp_pub_encrypt_text(PG_FUNCTION_ARGS);
+Datum pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS);
+Datum pgp_pub_decrypt_text(PG_FUNCTION_ARGS);
+Datum pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS);
+
+Datum pgp_key_id_w(PG_FUNCTION_ARGS);
+
+Datum pg_armor(PG_FUNCTION_ARGS);
+Datum pg_dearmor(PG_FUNCTION_ARGS);
+
+/* function headers */
+
+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);
+
+/*
+ * check for NULL arguments
+ */
+#define CHECK_ARGS() \
+	do { \
+		int a; \
+		for (a = 0; a < PG_NARGS(); a++) { \
+			if (PG_ARGISNULL(a)) \
+				PG_RETURN_NULL(); \
+		} \
+	} while (0)
+
+/*
+ * Mix user data into RNG.  It is for user own interests to have
+ * RNG state shuffled.
+ */
+static void add_entropy(text *data1,  text *data2, text *data3)
+{
+	PX_MD *md;
+	uint8 sha1[20];
+	int res;
+
+	if (!data1 && !data2 && !data3)
+		return;
+
+	res = px_find_digest("sha1", &md);
+	if (res < 0)
+		return;
+
+	if (data1)
+		px_md_update(md, VARDATA(data1), VARSIZE(data1) - VARHDRSZ);
+	if (data2)
+		px_md_update(md, VARDATA(data2), VARSIZE(data2) - VARHDRSZ);
+	if (data3)
+		px_md_update(md, VARDATA(data3), VARSIZE(data3) - VARHDRSZ);
+
+	px_md_finish(md, sha1);
+	px_md_free(md);
+
+	res = px_add_entropy(sha1, 20);
+	memset(sha1, 0, 20);
+
+	if (res < 0)
+		ereport(NOTICE, (errmsg("add_entropy: %s", px_strerror(res))));
+}
+
+/*
+ * 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(src) - VARHDRSZ;
+	int dst_len;
+	unsigned char *dst;
+	unsigned char *csrc = VARDATA(src);
+	text *res;
+	
+	dst = pg_do_encoding_conversion(csrc, src_len, cset_from, cset_to);
+	if (dst == csrc)
+		return src;
+
+	dst_len = strlen(dst);
+	res = palloc(dst_len + VARHDRSZ);
+	memcpy(VARDATA(res), dst, dst_len);
+	VARATT_SIZEP(res) = VARHDRSZ + dst_len;
+	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)
+{
+	memset(p, 0, VARSIZE(p));
+	pfree(p);
+}
+
+/*
+ * expect-* arguments storage
+ */
+struct debug_expect {
+	int debug;
+	int expect;
+	int cipher_algo;
+	int s2k_mode;
+	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_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_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-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));
+	/* decrypt debug */
+	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-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(VARDATA(data), VARSIZE(data) - VARHDRSZ);
+}
+
+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, VARDATA(args), VARSIZE(args) - VARHDRSZ, ex);
+
+	if (err)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+				 errmsg("%s", px_strerror(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;
+
+	/*
+	 * Add data and key info RNG.
+	 */
+	add_entropy(data, key, 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(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, VARDATA(key), VARSIZE(key) - VARHDRSZ);
+	
+	/*
+	 * 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);
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+				 errmsg("pgp_encrypt error: %s", px_strerror(err))));
+	}
+
+	/* res_len includes VARHDRSZ */
+	res_len = mbuf_steal_data(dst, &restmp);
+	res = (bytea *) restmp;
+	VARATT_SIZEP(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(VARDATA(data), VARSIZE(data) - VARHDRSZ);
+	dst = mbuf_create(VARSIZE(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 = VARDATA(keypsw);
+			psw_len = VARSIZE(keypsw) - VARHDRSZ;
+		}
+		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, VARDATA(key), VARSIZE(key) - VARHDRSZ);
+
+	/*
+	 * decrypt
+	 */
+	if (err >= 0)
+		err = pgp_decrypt(ctx, src, dst);
+
+	/*
+	 * failed?
+	 */
+	if (err < 0)
+		goto out;
+
+	if (ex.expect)
+		check_expect(ctx, &ex);
+
+	/* remember the setting */
+	got_unicode = pgp_get_unicode_mode(ctx);
+
+out:
+	if (src)
+		mbuf_free(src);
+	if (ctx)
+		pgp_free(ctx);
+
+	if (err)
+	{
+		px_set_debug_handler(NULL);
+		if (dst)
+			mbuf_free(dst);
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+				 errmsg("pgp_decrypt error: %s", px_strerror(err))));
+	}
+
+	res_len = mbuf_steal_data(dst, &restmp);
+	mbuf_free(dst);
+
+	/* res_len includes VARHDRSZ */
+	res = (bytea *) restmp;
+	VARATT_SIZEP(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);
+
+	/*
+	 * add successfull decryptions also into RNG
+	 */
+	add_entropy(res, key, keypsw);
+
+	return res;
+}
+
+/*
+ * Wrappers for symmetric-key functions
+ */
+Datum
+pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS)
+{
+	bytea	   *data,
+			   *key;
+	text	   *arg = NULL;
+	text	   *res;
+
+	CHECK_ARGS();
+	data = PG_GETARG_BYTEA_P(0);
+	key = PG_GETARG_BYTEA_P(1);
+	if (PG_NARGS() > 2)
+		arg = PG_GETARG_BYTEA_P(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;
+
+	CHECK_ARGS();
+	data = PG_GETARG_BYTEA_P(0);
+	key = PG_GETARG_BYTEA_P(1);
+	if (PG_NARGS() > 2)
+		arg = PG_GETARG_BYTEA_P(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;
+
+	CHECK_ARGS();
+	data = PG_GETARG_BYTEA_P(0);
+	key = PG_GETARG_BYTEA_P(1);
+	if (PG_NARGS() > 2)
+		arg = PG_GETARG_BYTEA_P(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;
+
+	CHECK_ARGS();
+	data = PG_GETARG_BYTEA_P(0);
+	key = PG_GETARG_BYTEA_P(1);
+	if (PG_NARGS() > 2)
+		arg = PG_GETARG_BYTEA_P(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;
+
+	CHECK_ARGS();
+	data = PG_GETARG_BYTEA_P(0);
+	key = PG_GETARG_BYTEA_P(1);
+	if (PG_NARGS() > 2)
+		arg = PG_GETARG_BYTEA_P(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;
+
+	CHECK_ARGS();
+	data = PG_GETARG_BYTEA_P(0);
+	key = PG_GETARG_BYTEA_P(1);
+	if (PG_NARGS() > 2)
+		arg = PG_GETARG_BYTEA_P(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;
+
+	CHECK_ARGS();
+	data = PG_GETARG_BYTEA_P(0);
+	key = PG_GETARG_BYTEA_P(1);
+	if (PG_NARGS() > 2)
+		psw = PG_GETARG_BYTEA_P(2);
+	if (PG_NARGS() > 3)
+		arg = PG_GETARG_BYTEA_P(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;
+
+	CHECK_ARGS();
+	data = PG_GETARG_BYTEA_P(0);
+	key = PG_GETARG_BYTEA_P(1);
+	if (PG_NARGS() > 2)
+		psw = PG_GETARG_BYTEA_P(2);
+	if (PG_NARGS() > 3)
+		arg = PG_GETARG_BYTEA_P(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
+ */
+
+Datum
+pg_armor(PG_FUNCTION_ARGS)
+{
+	bytea	   *data;
+	text	   *res;
+	int			data_len,
+				res_len,
+				guess_len;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+
+	data = PG_GETARG_BYTEA_P(0);
+	data_len = VARSIZE(data) - VARHDRSZ;
+
+	guess_len = pgp_armor_enc_len(data_len);
+	res = palloc(VARHDRSZ + guess_len);
+
+	res_len = pgp_armor_encode(VARDATA(data), data_len, VARDATA(res));
+	if (res_len > guess_len)
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+				 errmsg("Overflow - encode estimate too small")));
+	VARATT_SIZEP(res) = VARHDRSZ + res_len;
+
+	PG_FREE_IF_COPY(data, 0);
+	PG_RETURN_TEXT_P(res);
+}
+
+Datum
+pg_dearmor(PG_FUNCTION_ARGS)
+{
+	text	   *data;
+	bytea	   *res;
+	int			data_len,
+				res_len,
+				guess_len;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+
+	data = PG_GETARG_TEXT_P(0);
+	data_len = VARSIZE(data) - VARHDRSZ;
+
+	guess_len = pgp_armor_dec_len(data_len);
+	res = palloc(VARHDRSZ + guess_len);
+
+	res_len = pgp_armor_decode(VARDATA(data), data_len, VARDATA(res));
+	if (res_len < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+				 errmsg("dearmor: %s", px_strerror(res_len))));
+	if (res_len > guess_len)
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+				 errmsg("Overflow - decode estimate too small")));
+	VARATT_SIZEP(res) = VARHDRSZ + res_len;
+
+	PG_FREE_IF_COPY(data, 0);
+	PG_RETURN_TEXT_P(res);
+}
+
+/*
+ * Wrappers for PGP key id
+ */
+
+Datum
+pgp_key_id_w(PG_FUNCTION_ARGS)
+{
+	bytea	   *data;
+	text	   *res;
+	int			res_len;
+	MBuf	   *buf;
+
+	if (PG_ARGISNULL(0))
+		PG_RETURN_NULL();
+
+	data = PG_GETARG_BYTEA_P(0);
+	buf = create_mbuf_from_vardata(data);
+	res = palloc(VARHDRSZ + 17);
+
+	px_set_debug_handler(show_debug);
+	res_len = pgp_get_keyid(buf, VARDATA(res));
+	px_set_debug_handler(NULL);
+	mbuf_free(buf);
+	if (res_len < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+				 errmsg("%s", px_strerror(res_len))));
+	VARATT_SIZEP(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 00000000000..ad4ceb2f7a4
--- /dev/null
+++ b/contrib/pgcrypto/pgp-pubdec.c
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-pubdec.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+#include <postgres.h>
+
+#include <openssl/bn.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.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_CORRUPT_DATA;
+
+	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_CORRUPT_DATA;
+	}
+	return 0;
+}
+
+/* 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_MPI *c1, *c2;
+	PGP_PubKey *pk;
+	uint8 *msg;
+	int msglen;
+	PGP_MPI *m;
+
+	pk = ctx->pub_key;
+	if (pk == NULL) {
+		px_debug("no pubkey?");
+		return PXE_BUG;
+	}
+	if (!pk->elg_p || !pk->elg_g || !pk->elg_y || !pk->elg_x) {
+		px_debug("seckey not loaded?");
+		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_KEYID;
+	}
+
+	GETBYTE(pkt, algo);
+	if (algo != PGP_PUB_ELG_ENCRYPT)
+	{
+		px_debug("unknown public-key algo=%d", algo);
+		if (algo == PGP_PUB_RSA_ENCRYPT || algo == PGP_PUB_RSA_ENCRYPT_SIGN)
+			return PXE_PGP_RSA_UNSUPPORTED;
+		else
+			return PXE_PGP_UNKNOWN_PUBALGO;
+	}
+
+	/*
+	 * read elgamal encrypted data
+	 */
+	res = pgp_mpi_read(pkt, &c1);
+	if (res < 0)
+		return res;
+	res = pgp_mpi_read(pkt, &c2);
+	if (res < 0)
+		return res;
+
+	/*
+	 * decrypt
+	 */
+	res = pgp_elgamal_decrypt(pk, c1, c2, &m);
+	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");
+		return PXE_PGP_CORRUPT_DATA;
+	}
+	msglen = m->bytes - (msg - m->data);
+
+	res = control_cksum(msg, msglen);
+	if (res < 0)
+		return res;
+
+	/*
+	 * got sesskey
+	 */
+	ctx->cipher_algo = *msg;
+	ctx->sess_key_len = msglen - 3;
+	memcpy(ctx->sess_key, msg + 1, ctx->sess_key_len);
+
+	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 00000000000..7a0ba33409e
--- /dev/null
+++ b/contrib/pgcrypto/pgp-pubenc.c
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-pubenc.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.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)
+{
+	int res;
+	uint8 *buf, *p;
+	int pad_len = res_len - 2 - data_len;
+
+	if (pad_len < 8)
+		return PXE_BUG;
+
+	buf = px_alloc(res_len);
+	buf[0] = 0x02;
+	res = px_get_random_bytes(buf + 1, pad_len);
+	if (res < 0)
+	{
+		px_free(buf);
+		return res;
+	}
+
+	/* pad must not contain zero bytes */
+	p = buf + 1;
+	while (p < buf + 1 + pad_len)
+	{
+		if (*p == 0)
+		{
+			res = px_get_random_bytes(p, 1);
+			if (res < 0)
+				break;
+		}
+		if (*p != 0)
+			p++;
+	}
+
+	if (res < 0)
+	{
+		memset(buf, 0, res_len);
+		px_free(buf);
+		return res;
+	}
+			
+	buf[pad_len + 1] = 0;
+	memcpy(buf + pad_len + 2, data, data_len);
+	*res_p = buf;
+
+	return 0;
+}
+
+/*
+ * Decide the padded message length in bytes.
+ * It should be as large as possible, but not larger
+ * than p.
+ *
+ * To get max size (and assuming p may have weird sizes):
+ * ((p->bytes * 8 - 6) > p->bits) ? (p->bytes - 1) : p->bytes
+ *
+ * Following mirrors gnupg behaviour.
+ */
+static int
+decide_msglen(PGP_MPI *p)
+{
+	return p->bytes - 1;
+}
+
+static int
+create_secmsg(PGP_Context *ctx, PGP_MPI **msg_p)
+{
+	uint8 *secmsg;
+	int res, i, full_bytes;
+	unsigned cksum = 0;
+	int klen = ctx->sess_key_len;
+	uint8 *padded = NULL;
+	PGP_MPI *m = NULL;
+	PGP_PubKey *pk = ctx->pub_key;
+
+	/*
+	 * Refuse to operate with keys < 1024
+	 */
+	if (pk->elg_p->bits < 1024)
+		return PXE_PGP_SHORT_ELGAMAL_KEY;
+	
+	/* calc checksum */
+	for (i = 0; i < klen; i++)
+		cksum += ctx->sess_key[i];
+	
+	/*
+	 * create "secret message"
+	 */
+	secmsg = px_alloc(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
+	 */
+	full_bytes = decide_msglen(pk->elg_p);
+	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)
+	{
+		memset(padded, 0, full_bytes);
+		px_free(padded);
+	}
+	memset(secmsg, 0, klen + 3);
+	px_free(secmsg);
+
+	if (res >= 0)
+		*msg_p = m;
+
+	return res;
+}
+
+int pgp_write_pubenc_sesskey(PGP_Context *ctx, PushFilter *dst)
+{
+	int res;
+	PGP_PubKey *pk = ctx->pub_key;
+	PGP_MPI *m = NULL, *c1 = NULL, *c2 = NULL;
+	uint8 ver = 3;
+	uint8 algo = PGP_PUB_ELG_ENCRYPT;
+	PushFilter *pkt = NULL;
+
+	if (pk == NULL) {
+		px_debug("no pubkey?\n");
+		return PXE_BUG;
+	}
+	if (!pk->elg_p || !pk->elg_g || !pk->elg_y) {
+		px_debug("pubkey not loaded?\n");
+		return PXE_BUG;
+	}
+
+	/*
+	 * sesskey packet
+	 */
+	res = create_secmsg(ctx, &m);
+	if (res < 0)
+		goto err;
+
+	/*
+	 * encrypt it
+	 */
+	res = pgp_elgamal_encrypt(pk, m, &c1, &c2);
+	if (res < 0)
+		goto err;
+
+	/*
+	 * 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;
+	res = pgp_mpi_write(pkt, c1);
+	if (res < 0)
+		goto err;
+	res = pgp_mpi_write(pkt, c2);
+	if (res < 0)
+		goto err;
+
+	/*
+	 * done, signal packet end
+	 */
+	res = pushf_flush(pkt);
+err:
+	if (pkt)
+		pushf_free(pkt);
+	if (m)
+		pgp_mpi_free(m);
+	if (c1)
+		pgp_mpi_free(c1);
+	if (c2)
+		pgp_mpi_free(c2);
+
+	return res;
+}
+
+
diff --git a/contrib/pgcrypto/pgp-pubkey.c b/contrib/pgcrypto/pgp-pubkey.c
new file mode 100644
index 00000000000..6a00947c87b
--- /dev/null
+++ b/contrib/pgcrypto/pgp-pubkey.c
@@ -0,0 +1,448 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-pubkey.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.h"
+
+#define PXE_PGP_BAD_KEY -90
+
+int pgp_key_alloc(PGP_PubKey **pk_p)
+{
+	PGP_PubKey *pk;
+	pk = px_alloc(sizeof(*pk));
+	memset(pk, 0, sizeof(*pk));
+	*pk_p = pk;
+	return 0;
+}
+
+void pgp_key_free(PGP_PubKey *pk)
+{
+	if (pk->elg_p)
+		pgp_mpi_free(pk->elg_p);
+	if (pk->elg_g)
+		pgp_mpi_free(pk->elg_g);
+	if (pk->elg_y)
+		pgp_mpi_free(pk->elg_y);
+	if (pk->elg_x)
+		pgp_mpi_free(pk->elg_x);
+	memset(pk, 0, sizeof(*pk));
+	px_free(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->elg_p->bytes;
+			len += 2 + pk->elg_g->bytes;
+			len += 2 + pk->elg_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->elg_p);
+			pgp_mpi_hash(md, pk->elg_g);
+			pgp_mpi_hash(md, pk->elg_y);
+			break;
+	}
+
+	px_md_finish(md, hash);
+	px_md_free(md);
+
+	memcpy(pk->key_id, hash + 12, 8);
+	memset(hash, 0, 20);
+
+	return 0;
+}
+
+int _pgp_read_public_key(PullFilter *pkt, PGP_PubKey *pk)
+{
+	int res;
+
+	/* get version */
+	GETBYTE(pkt, pk->ver);
+	if (pk->ver != 4) {
+		px_debug("\tunsupported version: %d", pk->ver);
+		return PXE_PGP_NOT_V4_KEYPKT;
+	}
+	
+	/* read time */
+	res = pullf_read_fixed(pkt, 4, pk->time);
+	if (res < 0)
+		return res;
+
+	/* pubkey algorithm */
+	GETBYTE(pkt, pk->algo);
+
+	switch (pk->algo) {
+		case PGP_PUB_RSA_ENCRYPT_SIGN:
+		case PGP_PUB_RSA_ENCRYPT:
+		case PGP_PUB_RSA_SIGN:
+		case PGP_PUB_DSA_SIGN:
+			res = pgp_skip_packet(pkt);
+			break;
+		case PGP_PUB_ELG_ENCRYPT:
+			res = pgp_mpi_read(pkt, &pk->elg_p);
+			if (res < 0) break;
+			res = pgp_mpi_read(pkt, &pk->elg_g);
+			if (res < 0) break;
+			res = pgp_mpi_read(pkt, &pk->elg_y);
+			if (res < 0) break;
+
+			res = calc_key_id(pk);
+			break;
+		default:
+			px_debug("unknown public algo: %d", pk->algo);
+			res = PXE_PGP_UNKNOWN_PUBALGO;
+	}
+
+	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->elg_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:
+	memset(got_sha1, 0, 20);
+	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->elg_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,
+		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;
+
+	/* first read public key part */
+	res = _pgp_read_public_key(pkt, pk);
+	if (res < 0)
+		return res;
+
+	/* skip key? */
+	if (pk->algo != PGP_PUB_ELG_ENCRYPT)
+		return 0;
+
+	/*
+	 * 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_ENCRYPT_SIGN:
+		case PGP_PUB_RSA_ENCRYPT:
+		case PGP_PUB_RSA_SIGN:
+		case PGP_PUB_DSA_SIGN:
+			px_debug("unsupported public algo: %d", pk->algo);
+			res = PXE_PGP_UNSUPPORTED_PUBALGO;
+			break;
+		case PGP_PUB_ELG_ENCRYPT:
+			res = pgp_mpi_read(pf_key, &pk->elg_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);
+
+	return res;
+}
+
+static int
+internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
+					const uint8 *key, int key_len, int pubtype)
+{
+	PullFilter *pkt = NULL;
+	int res;
+	uint8 tag;
+	int len;
+	PGP_PubKey *pk = NULL;
+	int got_key = 0;
+	int n_subkey = 0;
+
+	res = pgp_key_alloc(&pk);
+	if (res < 0)
+		return res;
+
+	/*
+	 * Search for Elgamal key.
+	 *
+	 * Error out on anything fancy.
+	 */
+	res = PXE_PGP_KEYPKT_CORRUPT;
+	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:
+				if (got_key)
+				{
+					res = PXE_PGP_MULTIPLE_KEYS;
+					break;
+				}
+				got_key = 1;
+				n_subkey = 0;
+				/* fallthru */
+			case PGP_PKT_SECRET_SUBKEY:
+				if (tag == PGP_PKT_SECRET_SUBKEY)
+					n_subkey++;
+
+				if (n_subkey > 1)
+					res = PXE_PGP_MULTIPLE_SUBKEYS;
+				else if (pubtype == 1)
+					res = process_secret_key(pkt, pk, key, key_len);
+				else
+					res = PXE_PGP_EXPECT_PUBLIC_KEY;
+				break;
+			case PGP_PKT_PUBLIC_KEY:
+				if (got_key)
+				{
+					res = PXE_PGP_MULTIPLE_KEYS;
+					break;
+				}
+				got_key = 1;
+				n_subkey = 0;
+				/* fallthru */
+			case PGP_PKT_PUBLIC_SUBKEY:
+				if (tag == PGP_PKT_PUBLIC_SUBKEY)
+					n_subkey++;
+
+				if (n_subkey > 1)
+					res = PXE_PGP_MULTIPLE_SUBKEYS;
+				else if (pubtype == 0)
+					res = _pgp_read_public_key(pkt, pk);
+				else
+					res = PXE_PGP_EXPECT_SECRET_KEY;
+				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 (res < 0)
+			break;
+
+		if (pk->algo == PGP_PUB_ELG_ENCRYPT)
+			break;
+	}
+
+	if (pkt)
+		pullf_free(pkt);
+
+	if (res < 0)
+		pgp_key_free(pk);
+	else {
+		if (pk->algo == PGP_PUB_ELG_ENCRYPT)
+			*pk_p = pk;
+		else {
+			pgp_key_free(pk);
+			px_debug("non-elg");
+			res = PXE_PGP_NO_USABLE_KEY;
+		}
+	}
+	return res < 0 ? res : 0;
+}
+
+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 00000000000..0916a0074e1
--- /dev/null
+++ b/contrib/pgcrypto/pgp-s2k.c
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp-s2k.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.h"
+
+static int
+calc_s2k_simple(PGP_S2K * s2k, PX_MD *md, const uint8 *key,
+				unsigned key_len)
+{
+	unsigned	md_bs,
+				md_rlen;
+	uint8		buf[PGP_MAX_DIGEST];
+	unsigned	preload;
+	unsigned	remain;
+	uint8	   *dst = s2k->key;
+
+	md_bs = px_md_block_size(md);
+	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;
+		}
+	}
+	return 0;
+}
+
+static int
+calc_s2k_salted(PGP_S2K * s2k, PX_MD *md, const uint8 *key, unsigned key_len)
+{
+	unsigned	md_bs,
+				md_rlen;
+	uint8		buf[PGP_MAX_DIGEST];
+	unsigned	preload = 0;
+	uint8	   *dst;
+	unsigned	remain;
+
+	md_bs = px_md_block_size(md);
+	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;
+		}
+	}
+	return 0;
+}
+
+static int
+calc_s2k_iter_salted(PGP_S2K * s2k, PX_MD *md, const uint8 *key,
+					unsigned key_len)
+{
+	unsigned	md_bs,
+				md_rlen;
+	uint8		buf[PGP_MAX_DIGEST];
+	uint8	   *dst;
+	unsigned	preload = 0;
+	unsigned	remain,
+				c,
+				cval,
+				curcnt,
+				count;
+
+	cval = s2k->iter;
+	count = ((unsigned) 16 + (cval & 15)) << ((cval >> 4) + 6);
+
+	md_bs = px_md_block_size(md);
+	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;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Decide S2K_ISALTED iteration count
+ * 
+ * Too small: weak
+ * Too big: slow
+ * gpg defaults to 96 => 65536 iters
+ * let it float a bit: 96 + 32 => 262144 iters
+ */
+static int
+decide_count(unsigned rand_byte)
+{
+	return 96 + (rand_byte & 0x1F);
+}
+
+int
+pgp_s2k_fill(PGP_S2K *s2k, int mode,int digest_algo)
+{
+	int res = 0;
+	uint8 tmp;
+
+	s2k->mode = mode;
+	s2k->digest_algo = digest_algo;
+
+	switch (s2k->mode) {
+		case 0:
+			break;
+		case 1:
+			res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT);
+			break;
+		case 3:
+			res = px_get_random_bytes(s2k->salt, PGP_S2K_SALT);
+			if (res < 0)
+				break;
+			res = px_get_random_bytes(&tmp, 1);
+			if (res < 0)
+				break;
+			s2k->iter = decide_count(tmp);
+			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 00000000000..8c7144104de
--- /dev/null
+++ b/contrib/pgcrypto/pgp.c
@@ -0,0 +1,351 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+
+#include <postgres.h>
+
+#include "px.h"
+#include "mbuf.h"
+#include "pgp.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_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;
+	const char *int_name;
+};
+
+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 = px_alloc(sizeof *ctx);
+	memset(ctx, 0, sizeof *ctx);
+
+	ctx->cipher_algo = def_cipher_algo;
+	ctx->s2k_cipher_algo = def_s2k_cipher_algo;
+	ctx->s2k_mode = def_s2k_mode;
+	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);
+	memset(ctx, 0, sizeof *ctx);
+	px_free(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_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 00000000000..7f31aa3d732
--- /dev/null
+++ b/contrib/pgcrypto/pgp.h
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ *
+ * $PostgreSQL: pgsql/contrib/pgcrypto/pgp.h,v 1.1 2005/07/10 13:46:29 momjian Exp $
+ */
+
+enum
+{
+	PGP_S2K_SIMPLE = 0,
+	PGP_S2K_SALTED = 1,
+	PGP_S2K_ISALTED = 3
+} PGP_S2K_TYPE;
+
+enum
+{
+	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 */
+} PGP_PKT_TYPE;
+
+enum
+{
+	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
+} PGP_PUB_ALGO_TYPE;
+
+enum
+{
+	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
+} PGP_SYMENC_TYPE;
+
+enum
+{
+	PGP_COMPR_NONE = 0,			/* must */
+	PGP_COMPR_ZIP = 1,			/* should */
+	PGP_COMPR_ZLIB = 2,
+	PGP_COMPR_BZIP2 = 3
+} PGP_COMPR_TYPE;
+
+enum
+{
+	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
+} PGP_DIGEST_TYPE;
+
+#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;
+	/* calculated: */
+	uint8 key[PGP_MAX_KEY];
+	uint8 key_len;
+};
+
+
+struct PGP_Context
+{
+	/*
+	 * parameters
+	 */
+	PGP_S2K		s2k;
+	int			s2k_mode;
+	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;
+	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;
+};
+
+struct PGP_MPI {
+	uint8 *data;
+	int bits;
+	int bytes;
+};
+
+struct PGP_PubKey {
+	uint8 ver;
+	uint8 time[4];
+	uint8 algo;
+	/* public */
+	PGP_MPI *elg_p;
+	PGP_MPI *elg_g;
+	PGP_MPI *elg_y;
+	/* secret */
+	PGP_MPI *elg_x;
+
+	uint8 key_id[8];
+};
+
+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_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 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 recync, 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);
+
+int			pgp_armor_encode(const uint8 *src, unsigned len, uint8 *dst);
+int			pgp_armor_decode(const uint8 *src, unsigned len, uint8 *dst);
+unsigned	pgp_armor_enc_len(unsigned len);
+unsigned	pgp_armor_dec_len(unsigned len);
+
+int pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst);
+int pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src);
+
+extern void (*pgp_packet_debug) (int tag, uint8 *buf, int len);
+
+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);
+
+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);
+
+extern struct PullFilterOps pgp_decrypt_filter;
+
diff --git a/contrib/pgcrypto/sha2.c b/contrib/pgcrypto/sha2.c
new file mode 100644
index 00000000000..9c35107bdc0
--- /dev/null
+++ b/contrib/pgcrypto/sha2.c
@@ -0,0 +1,895 @@
+/*	$PostgreSQL: pgsql/contrib/pgcrypto/sha2.c,v 1.1 2005/07/10 13:46:29 momjian Exp $ */
+/*	$OpenBSD: sha2.c,v 1.6 2004/05/03 02:57:36 millert Exp $	*/
+
+/*
+ * FILE:	sha2.c
+ * AUTHOR:	Aaron D. Gifford <me@aarongifford.com>
+ * 
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * 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.
+ * 3. Neither the name of the copyright holder nor the names of 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 CONTRIBUTOR(S) ``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 CONTRIBUTOR(S) 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.
+ *
+ * $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
+ */
+
+#include <postgres.h>
+
+#include "sha2.h"
+
+#undef bcopy
+#undef bzero
+#define bcopy(src, dst, len)	memcpy((dst), (src), (len))
+#define bzero(ptr, len)			memset((ptr), 0, (len))
+
+/*
+ * UNROLLED TRANSFORM LOOP NOTE:
+ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
+ * loop version for the hash transform rounds (defined using macros
+ * later in this file).  Either define on the command line, for example:
+ *
+ *   cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
+ *
+ * or define below:
+ *
+ *   #define SHA2_UNROLL_TRANSFORM
+ *
+ */
+
+
+/*** SHA-256/384/512 Machine Architecture Definitions *****************/
+/*
+ * BYTE_ORDER NOTE:
+ *
+ * Please make sure that your system defines BYTE_ORDER.  If your
+ * architecture is little-endian, make sure it also defines
+ * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
+ * equivilent.
+ *
+ * If your system does not define the above, then you can do so by
+ * hand like this:
+ *
+ *   #define LITTLE_ENDIAN 1234
+ *   #define BIG_ENDIAN    4321
+ *
+ * And for little-endian machines, add:
+ *
+ *   #define BYTE_ORDER LITTLE_ENDIAN 
+ *
+ * Or for big-endian machines:
+ *
+ *   #define BYTE_ORDER BIG_ENDIAN
+ *
+ * The FreeBSD machine this was written on defines BYTE_ORDER
+ * appropriately by including <sys/types.h> (which in turn includes
+ * <machine/endian.h> where the appropriate definitions are actually
+ * made).
+ */
+#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
+#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
+#endif
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+/* NOTE: Most of these are in sha2.h */
+#define SHA256_SHORT_BLOCK_LENGTH	(SHA256_BLOCK_LENGTH - 8)
+#define SHA384_SHORT_BLOCK_LENGTH	(SHA384_BLOCK_LENGTH - 16)
+#define SHA512_SHORT_BLOCK_LENGTH	(SHA512_BLOCK_LENGTH - 16)
+
+
+/*** ENDIAN REVERSAL MACROS *******************************************/
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define REVERSE32(w,x)	{ \
+	uint32 tmp = (w); \
+	tmp = (tmp >> 16) | (tmp << 16); \
+	(x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
+}
+#define REVERSE64(w,x)	{ \
+	uint64 tmp = (w); \
+	tmp = (tmp >> 32) | (tmp << 32); \
+	tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
+	      ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
+	(x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
+	      ((tmp & 0x0000ffff0000ffffULL) << 16); \
+}
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+/*
+ * Macro for incrementally adding the unsigned 64-bit integer n to the
+ * unsigned 128-bit integer (represented using a two-element array of
+ * 64-bit words):
+ */
+#define ADDINC128(w,n)	{ \
+	(w)[0] += (uint64)(n); \
+	if ((w)[0] < (n)) { \
+		(w)[1]++; \
+	} \
+}
+
+/*** THE SIX LOGICAL FUNCTIONS ****************************************/
+/*
+ * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
+ *
+ *   NOTE:  The naming of R and S appears backwards here (R is a SHIFT and
+ *   S is a ROTATION) because the SHA-256/384/512 description document
+ *   (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
+ *   same "backwards" definition.
+ */
+/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
+#define R(b,x) 		((x) >> (b))
+/* 32-bit Rotate-right (used in SHA-256): */
+#define S32(b,x)	(((x) >> (b)) | ((x) << (32 - (b))))
+/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
+#define S64(b,x)	(((x) >> (b)) | ((x) << (64 - (b))))
+
+/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
+#define Ch(x,y,z)	(((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z)	(((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/* Four of six logical functions used in SHA-256: */
+#define Sigma0_256(x)	(S32(2,  (x)) ^ S32(13, (x)) ^ S32(22, (x)))
+#define Sigma1_256(x)	(S32(6,  (x)) ^ S32(11, (x)) ^ S32(25, (x)))
+#define sigma0_256(x)	(S32(7,  (x)) ^ S32(18, (x)) ^ R(3 ,   (x)))
+#define sigma1_256(x)	(S32(17, (x)) ^ S32(19, (x)) ^ R(10,   (x)))
+
+/* Four of six logical functions used in SHA-384 and SHA-512: */
+#define Sigma0_512(x)	(S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
+#define Sigma1_512(x)	(S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
+#define sigma0_512(x)	(S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7,   (x)))
+#define sigma1_512(x)	(S64(19, (x)) ^ S64(61, (x)) ^ R( 6,   (x)))
+
+/*** INTERNAL FUNCTION PROTOTYPES *************************************/
+/* NOTE: These should not be accessed directly from outside this
+ * library -- they are intended for private internal visibility/use
+ * only.
+ */
+void SHA512_Last(SHA512_CTX *);
+void SHA256_Transform(SHA256_CTX *, const uint8 *);
+void SHA512_Transform(SHA512_CTX *, const uint8 *);
+
+
+/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
+/* Hash constant words K for SHA-256: */
+const static uint32 K256[64] = {
+	0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+	0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+	0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+	0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+	0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+	0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+	0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+	0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+	0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+	0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+	0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+	0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+	0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+	0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+	0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+	0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Initial hash value H for SHA-256: */
+const static uint32 sha256_initial_hash_value[8] = {
+	0x6a09e667UL,
+	0xbb67ae85UL,
+	0x3c6ef372UL,
+	0xa54ff53aUL,
+	0x510e527fUL,
+	0x9b05688cUL,
+	0x1f83d9abUL,
+	0x5be0cd19UL
+};
+
+/* Hash constant words K for SHA-384 and SHA-512: */
+const static uint64 K512[80] = {
+	0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+	0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+	0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+	0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+	0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+	0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+	0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+	0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+	0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+	0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+	0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+	0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+	0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+	0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+	0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+	0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+	0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+	0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+	0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+	0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+	0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+	0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+	0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+	0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+	0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+	0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+	0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+	0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+	0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+	0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+	0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+	0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+	0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+	0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+	0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+	0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+	0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+	0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+	0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+	0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+/* Initial hash value H for SHA-384 */
+const static uint64 sha384_initial_hash_value[8] = {
+	0xcbbb9d5dc1059ed8ULL,
+	0x629a292a367cd507ULL,
+	0x9159015a3070dd17ULL,
+	0x152fecd8f70e5939ULL,
+	0x67332667ffc00b31ULL,
+	0x8eb44a8768581511ULL,
+	0xdb0c2e0d64f98fa7ULL,
+	0x47b5481dbefa4fa4ULL
+};
+
+/* Initial hash value H for SHA-512 */
+const static uint64 sha512_initial_hash_value[8] = {
+	0x6a09e667f3bcc908ULL,
+	0xbb67ae8584caa73bULL,
+	0x3c6ef372fe94f82bULL,
+	0xa54ff53a5f1d36f1ULL,
+	0x510e527fade682d1ULL,
+	0x9b05688c2b3e6c1fULL,
+	0x1f83d9abfb41bd6bULL,
+	0x5be0cd19137e2179ULL
+};
+
+
+/*** SHA-256: *********************************************************/
+void
+SHA256_Init(SHA256_CTX *context)
+{
+	if (context == NULL)
+		return;
+	bcopy(sha256_initial_hash_value, context->state, SHA256_DIGEST_LENGTH);
+	bzero(context->buffer, SHA256_BLOCK_LENGTH);
+	context->bitcount = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-256 round macros: */
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do {				    \
+	W256[j] = (uint32)data[3] | ((uint32)data[2] << 8) |	    \
+	    ((uint32)data[1] << 16) | ((uint32)data[0] << 24);	    \
+	data += 4;							    \
+	T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \
+	(d) += T1;							    \
+	(h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c));		    \
+	j++;								    \
+} while(0)
+
+#define ROUND256(a,b,c,d,e,f,g,h) do {					    \
+	s0 = W256[(j+1)&0x0f];						    \
+	s0 = sigma0_256(s0);						    \
+	s1 = W256[(j+14)&0x0f];						    \
+	s1 = sigma1_256(s1);						    \
+	T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] +	    \
+	     (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);		    \
+	(d) += T1;							    \
+	(h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c));		    \
+	j++;								    \
+} while(0)
+
+void
+SHA256_Transform(SHA256_CTX *context, const uint8 *data)
+{
+	uint32	a, b, c, d, e, f, g, h, s0, s1;
+	uint32	T1, *W256;
+	int		j;
+
+	W256 = (uint32 *)context->buffer;
+
+	/* Initialize registers with the prev. intermediate value */
+	a = context->state[0];
+	b = context->state[1];
+	c = context->state[2];
+	d = context->state[3];
+	e = context->state[4];
+	f = context->state[5];
+	g = context->state[6];
+	h = context->state[7];
+
+	j = 0;
+	do {
+		/* Rounds 0 to 15 (unrolled): */
+		ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
+		ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
+		ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
+		ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
+		ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
+		ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
+		ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
+		ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
+	} while (j < 16);
+
+	/* Now for the remaining rounds to 64: */
+	do {
+		ROUND256(a,b,c,d,e,f,g,h);
+		ROUND256(h,a,b,c,d,e,f,g);
+		ROUND256(g,h,a,b,c,d,e,f);
+		ROUND256(f,g,h,a,b,c,d,e);
+		ROUND256(e,f,g,h,a,b,c,d);
+		ROUND256(d,e,f,g,h,a,b,c);
+		ROUND256(c,d,e,f,g,h,a,b);
+		ROUND256(b,c,d,e,f,g,h,a);
+	} while (j < 64);
+
+	/* Compute the current intermediate hash value */
+	context->state[0] += a;
+	context->state[1] += b;
+	context->state[2] += c;
+	context->state[3] += d;
+	context->state[4] += e;
+	context->state[5] += f;
+	context->state[6] += g;
+	context->state[7] += h;
+
+	/* Clean up */
+	a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+void
+SHA256_Transform(SHA256_CTX *context, const uint8 *data)
+{
+	uint32	a, b, c, d, e, f, g, h, s0, s1;
+	uint32	T1, T2, *W256;
+	int		j;
+
+	W256 = (uint32 *)context->buffer;
+
+	/* Initialize registers with the prev. intermediate value */
+	a = context->state[0];
+	b = context->state[1];
+	c = context->state[2];
+	d = context->state[3];
+	e = context->state[4];
+	f = context->state[5];
+	g = context->state[6];
+	h = context->state[7];
+
+	j = 0;
+	do {
+		W256[j] = (uint32)data[3] | ((uint32)data[2] << 8) |
+		    ((uint32)data[1] << 16) | ((uint32)data[0] << 24);
+		data += 4;
+		/* Apply the SHA-256 compression function to update a..h */
+		T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
+		T2 = Sigma0_256(a) + Maj(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + T1;
+		d = c;
+		c = b;
+		b = a;
+		a = T1 + T2;
+
+		j++;
+	} while (j < 16);
+
+	do {
+		/* Part of the message block expansion: */
+		s0 = W256[(j+1)&0x0f];
+		s0 = sigma0_256(s0);
+		s1 = W256[(j+14)&0x0f];	
+		s1 = sigma1_256(s1);
+
+		/* Apply the SHA-256 compression function to update a..h */
+		T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + 
+		     (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
+		T2 = Sigma0_256(a) + Maj(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + T1;
+		d = c;
+		c = b;
+		b = a;
+		a = T1 + T2;
+
+		j++;
+	} while (j < 64);
+
+	/* Compute the current intermediate hash value */
+	context->state[0] += a;
+	context->state[1] += b;
+	context->state[2] += c;
+	context->state[3] += d;
+	context->state[4] += e;
+	context->state[5] += f;
+	context->state[6] += g;
+	context->state[7] += h;
+
+	/* Clean up */
+	a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void
+SHA256_Update(SHA256_CTX *context, const uint8 *data, size_t len)
+{
+	size_t	freespace, usedspace;
+
+	/* Calling with no data is valid (we do nothing) */
+	if (len == 0)
+		return;
+
+	usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+	if (usedspace > 0) {
+		/* Calculate how much free space is available in the buffer */
+		freespace = SHA256_BLOCK_LENGTH - usedspace;
+
+		if (len >= freespace) {
+			/* Fill the buffer completely and process it */
+			bcopy(data, &context->buffer[usedspace], freespace);
+			context->bitcount += freespace << 3;
+			len -= freespace;
+			data += freespace;
+			SHA256_Transform(context, context->buffer);
+		} else {
+			/* The buffer is not yet full */
+			bcopy(data, &context->buffer[usedspace], len);
+			context->bitcount += len << 3;
+			/* Clean up: */
+			usedspace = freespace = 0;
+			return;
+		}
+	}
+	while (len >= SHA256_BLOCK_LENGTH) {
+		/* Process as many complete blocks as we can */
+		SHA256_Transform(context, data);
+		context->bitcount += SHA256_BLOCK_LENGTH << 3;
+		len -= SHA256_BLOCK_LENGTH;
+		data += SHA256_BLOCK_LENGTH;
+	}
+	if (len > 0) {
+		/* There's left-overs, so save 'em */
+		bcopy(data, context->buffer, len);
+		context->bitcount += len << 3;
+	}
+	/* Clean up: */
+	usedspace = freespace = 0;
+}
+
+void
+SHA256_Final(uint8 digest[], SHA256_CTX *context)
+{
+	uint32	*d = (uint32 *)digest;
+	unsigned int	usedspace;
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != NULL) {
+		usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+		/* Convert FROM host byte order */
+		REVERSE64(context->bitcount,context->bitcount);
+#endif
+		if (usedspace > 0) {
+			/* Begin padding with a 1 bit: */
+			context->buffer[usedspace++] = 0x80;
+
+			if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
+				/* Set-up for the last transform: */
+				bzero(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
+			} else {
+				if (usedspace < SHA256_BLOCK_LENGTH) {
+					bzero(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);
+				}
+				/* Do second-to-last transform: */
+				SHA256_Transform(context, context->buffer);
+
+				/* And set-up for the last transform: */
+				bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+			}
+		} else {
+			/* Set-up for the last transform: */
+			bzero(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+
+			/* Begin padding with a 1 bit: */
+			*context->buffer = 0x80;
+		}
+		/* Set the bit count: */
+		*(uint64 *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount;
+
+		/* Final transform: */
+		SHA256_Transform(context, context->buffer);
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+		{
+			/* Convert TO host byte order */
+			int	j;
+			for (j = 0; j < 8; j++) {
+				REVERSE32(context->state[j],context->state[j]);
+				*d++ = context->state[j];
+			}
+		}
+#else
+		bcopy(context->state, d, SHA256_DIGEST_LENGTH);
+#endif
+	}
+
+	/* Clean up state data: */
+	bzero(context, sizeof(*context));
+	usedspace = 0;
+}
+
+
+/*** SHA-512: *********************************************************/
+void
+SHA512_Init(SHA512_CTX *context)
+{
+	if (context == NULL)
+		return;
+	bcopy(sha512_initial_hash_value, context->state, SHA512_DIGEST_LENGTH);
+	bzero(context->buffer, SHA512_BLOCK_LENGTH);
+	context->bitcount[0] = context->bitcount[1] =  0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-512 round macros: */
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do {				    \
+	W512[j] = (uint64)data[7] | ((uint64)data[6] << 8) |	    \
+	    ((uint64)data[5] << 16) | ((uint64)data[4] << 24) |	    \
+	    ((uint64)data[3] << 32) | ((uint64)data[2] << 40) |	    \
+	    ((uint64)data[1] << 48) | ((uint64)data[0] << 56);	    \
+	data += 8;							    \
+	T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \
+	(d) += T1;							    \
+	(h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c));		    \
+	j++;								    \
+} while(0)
+
+
+#define ROUND512(a,b,c,d,e,f,g,h) do {					    \
+	s0 = W512[(j+1)&0x0f];						    \
+	s0 = sigma0_512(s0);						    \
+	s1 = W512[(j+14)&0x0f];						    \
+	s1 = sigma1_512(s1);						    \
+	T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] +	    \
+             (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);		    \
+	(d) += T1;							    \
+	(h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c));		    \
+	j++;								    \
+} while(0)
+
+void
+SHA512_Transform(SHA512_CTX *context, const uint8 *data)
+{
+	uint64	a, b, c, d, e, f, g, h, s0, s1;
+	uint64	T1, *W512 = (uint64 *)context->buffer;
+	int		j;
+
+	/* Initialize registers with the prev. intermediate value */
+	a = context->state[0];
+	b = context->state[1];
+	c = context->state[2];
+	d = context->state[3];
+	e = context->state[4];
+	f = context->state[5];
+	g = context->state[6];
+	h = context->state[7];
+
+	j = 0;
+	do {
+		ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
+		ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
+		ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
+		ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
+		ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
+		ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
+		ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
+		ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
+	} while (j < 16);
+
+	/* Now for the remaining rounds up to 79: */
+	do {
+		ROUND512(a,b,c,d,e,f,g,h);
+		ROUND512(h,a,b,c,d,e,f,g);
+		ROUND512(g,h,a,b,c,d,e,f);
+		ROUND512(f,g,h,a,b,c,d,e);
+		ROUND512(e,f,g,h,a,b,c,d);
+		ROUND512(d,e,f,g,h,a,b,c);
+		ROUND512(c,d,e,f,g,h,a,b);
+		ROUND512(b,c,d,e,f,g,h,a);
+	} while (j < 80);
+
+	/* Compute the current intermediate hash value */
+	context->state[0] += a;
+	context->state[1] += b;
+	context->state[2] += c;
+	context->state[3] += d;
+	context->state[4] += e;
+	context->state[5] += f;
+	context->state[6] += g;
+	context->state[7] += h;
+
+	/* Clean up */
+	a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+void
+SHA512_Transform(SHA512_CTX *context, const uint8 *data)
+{
+	uint64	a, b, c, d, e, f, g, h, s0, s1;
+	uint64	T1, T2, *W512 = (uint64 *)context->buffer;
+	int		j;
+
+	/* Initialize registers with the prev. intermediate value */
+	a = context->state[0];
+	b = context->state[1];
+	c = context->state[2];
+	d = context->state[3];
+	e = context->state[4];
+	f = context->state[5];
+	g = context->state[6];
+	h = context->state[7];
+
+	j = 0;
+	do {
+		W512[j] = (uint64)data[7] | ((uint64)data[6] << 8) |
+		    ((uint64)data[5] << 16) | ((uint64)data[4] << 24) |
+		    ((uint64)data[3] << 32) | ((uint64)data[2] << 40) |
+		    ((uint64)data[1] << 48) | ((uint64)data[0] << 56);
+		data += 8;
+		/* Apply the SHA-512 compression function to update a..h */
+		T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
+		T2 = Sigma0_512(a) + Maj(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + T1;
+		d = c;
+		c = b;
+		b = a;
+		a = T1 + T2;
+
+		j++;
+	} while (j < 16);
+
+	do {
+		/* Part of the message block expansion: */
+		s0 = W512[(j+1)&0x0f];
+		s0 = sigma0_512(s0);
+		s1 = W512[(j+14)&0x0f];
+		s1 =  sigma1_512(s1);
+
+		/* Apply the SHA-512 compression function to update a..h */
+		T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
+		     (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
+		T2 = Sigma0_512(a) + Maj(a, b, c);
+		h = g;
+		g = f;
+		f = e;
+		e = d + T1;
+		d = c;
+		c = b;
+		b = a;
+		a = T1 + T2;
+
+		j++;
+	} while (j < 80);
+
+	/* Compute the current intermediate hash value */
+	context->state[0] += a;
+	context->state[1] += b;
+	context->state[2] += c;
+	context->state[3] += d;
+	context->state[4] += e;
+	context->state[5] += f;
+	context->state[6] += g;
+	context->state[7] += h;
+
+	/* Clean up */
+	a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void
+SHA512_Update(SHA512_CTX *context, const uint8 *data, size_t len)
+{
+	size_t	freespace, usedspace;
+
+	/* Calling with no data is valid (we do nothing) */
+	if (len == 0)
+		return;
+
+	usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+	if (usedspace > 0) {
+		/* Calculate how much free space is available in the buffer */
+		freespace = SHA512_BLOCK_LENGTH - usedspace;
+
+		if (len >= freespace) {
+			/* Fill the buffer completely and process it */
+			bcopy(data, &context->buffer[usedspace], freespace);
+			ADDINC128(context->bitcount, freespace << 3);
+			len -= freespace;
+			data += freespace;
+			SHA512_Transform(context, context->buffer);
+		} else {
+			/* The buffer is not yet full */
+			bcopy(data, &context->buffer[usedspace], len);
+			ADDINC128(context->bitcount, len << 3);
+			/* Clean up: */
+			usedspace = freespace = 0;
+			return;
+		}
+	}
+	while (len >= SHA512_BLOCK_LENGTH) {
+		/* Process as many complete blocks as we can */
+		SHA512_Transform(context, data);
+		ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
+		len -= SHA512_BLOCK_LENGTH;
+		data += SHA512_BLOCK_LENGTH;
+	}
+	if (len > 0) {
+		/* There's left-overs, so save 'em */
+		bcopy(data, context->buffer, len);
+		ADDINC128(context->bitcount, len << 3);
+	}
+	/* Clean up: */
+	usedspace = freespace = 0;
+}
+
+void
+SHA512_Last(SHA512_CTX *context)
+{
+	unsigned int	usedspace;
+
+	usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+	/* Convert FROM host byte order */
+	REVERSE64(context->bitcount[0],context->bitcount[0]);
+	REVERSE64(context->bitcount[1],context->bitcount[1]);
+#endif
+	if (usedspace > 0) {
+		/* Begin padding with a 1 bit: */
+		context->buffer[usedspace++] = 0x80;
+
+		if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
+			/* Set-up for the last transform: */
+			bzero(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
+		} else {
+			if (usedspace < SHA512_BLOCK_LENGTH) {
+				bzero(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);
+			}
+			/* Do second-to-last transform: */
+			SHA512_Transform(context, context->buffer);
+
+			/* And set-up for the last transform: */
+			bzero(context->buffer, SHA512_BLOCK_LENGTH - 2);
+		}
+	} else {
+		/* Prepare for final transform: */
+		bzero(context->buffer, SHA512_SHORT_BLOCK_LENGTH);
+
+		/* Begin padding with a 1 bit: */
+		*context->buffer = 0x80;
+	}
+	/* Store the length of input data (in bits): */
+	*(uint64 *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1];
+	*(uint64 *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0];
+
+	/* Final transform: */
+	SHA512_Transform(context, context->buffer);
+}
+
+void
+SHA512_Final(uint8 digest[], SHA512_CTX *context)
+{
+	uint64	*d = (uint64 *)digest;
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != NULL) {
+		SHA512_Last(context);
+
+		/* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+		{
+			/* Convert TO host byte order */
+			int	j;
+			for (j = 0; j < 8; j++) {
+				REVERSE64(context->state[j],context->state[j]);
+				*d++ = context->state[j];
+			}
+		}
+#else
+		bcopy(context->state, d, SHA512_DIGEST_LENGTH);
+#endif
+	}
+
+	/* Zero out state data */
+	bzero(context, sizeof(*context));
+}
+
+
+/*** SHA-384: *********************************************************/
+void
+SHA384_Init(SHA384_CTX *context)
+{
+	if (context == NULL)
+		return;
+	bcopy(sha384_initial_hash_value, context->state, SHA512_DIGEST_LENGTH);
+	bzero(context->buffer, SHA384_BLOCK_LENGTH);
+	context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+void
+SHA384_Update(SHA384_CTX *context, const uint8 *data, size_t len)
+{
+	SHA512_Update((SHA512_CTX *)context, data, len);
+}
+
+void
+SHA384_Final(uint8 digest[], SHA384_CTX *context)
+{
+	uint64	*d = (uint64 *)digest;
+
+	/* If no digest buffer is passed, we don't bother doing this: */
+	if (digest != NULL) {
+		SHA512_Last((SHA512_CTX *)context);
+
+		/* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+		{
+			/* Convert TO host byte order */
+			int	j;
+			for (j = 0; j < 6; j++) {
+				REVERSE64(context->state[j],context->state[j]);
+				*d++ = context->state[j];
+			}
+		}
+#else
+		bcopy(context->state, d, SHA384_DIGEST_LENGTH);
+#endif
+	}
+
+	/* Zero out state data */
+	bzero(context, sizeof(*context));
+}
diff --git a/contrib/pgcrypto/sha2.h b/contrib/pgcrypto/sha2.h
new file mode 100644
index 00000000000..2dfb13ceb12
--- /dev/null
+++ b/contrib/pgcrypto/sha2.h
@@ -0,0 +1,80 @@
+/*	$PostgreSQL: pgsql/contrib/pgcrypto/sha2.h,v 1.1 2005/07/10 13:46:29 momjian Exp $ */
+/*	$OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $	*/
+
+/*
+ * FILE:	sha2.h
+ * AUTHOR:	Aaron D. Gifford <me@aarongifford.com>
+ * 
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * 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.
+ * 3. Neither the name of the copyright holder nor the names of 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 CONTRIBUTOR(S) ``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 CONTRIBUTOR(S) 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.
+ *
+ * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
+ */
+
+#ifndef _SHA2_H
+#define _SHA2_H
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+#define SHA256_BLOCK_LENGTH		64
+#define SHA256_DIGEST_LENGTH		32
+#define SHA256_DIGEST_STRING_LENGTH	(SHA256_DIGEST_LENGTH * 2 + 1)
+#define SHA384_BLOCK_LENGTH		128
+#define SHA384_DIGEST_LENGTH		48
+#define SHA384_DIGEST_STRING_LENGTH	(SHA384_DIGEST_LENGTH * 2 + 1)
+#define SHA512_BLOCK_LENGTH		128
+#define SHA512_DIGEST_LENGTH		64
+#define SHA512_DIGEST_STRING_LENGTH	(SHA512_DIGEST_LENGTH * 2 + 1)
+
+
+/*** SHA-256/384/512 Context Structures *******************************/
+typedef struct _SHA256_CTX {
+	uint32	state[8];
+	uint64	bitcount;
+	uint8	buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+	uint64	state[8];
+	uint64	bitcount[2];
+	uint8	buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+typedef SHA512_CTX SHA384_CTX;
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX *, const uint8 *, size_t);
+void SHA256_Final(uint8[SHA256_DIGEST_LENGTH], SHA256_CTX *);
+
+void SHA384_Init(SHA384_CTX *);
+void SHA384_Update(SHA384_CTX *, const uint8 *, size_t);
+void SHA384_Final(uint8[SHA384_DIGEST_LENGTH], SHA384_CTX *);
+
+void SHA512_Init(SHA512_CTX *);
+void SHA512_Update(SHA512_CTX *, const uint8 *, size_t);
+void SHA512_Final(uint8[SHA512_DIGEST_LENGTH], SHA512_CTX *);
+
+#endif /* _SHA2_H */
diff --git a/contrib/pgcrypto/sql/pgp-armor.sql b/contrib/pgcrypto/sql/pgp-armor.sql
new file mode 100644
index 00000000000..040c4ac038f
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-armor.sql
@@ -0,0 +1,56 @@
+--
+-- PGP Armor
+--
+
+select armor('');
+select armor('test');
+select dearmor(armor(''));
+select dearmor(armor('zooka'));
+
+select armor('0123456789abcdef0123456789abcdef0123456789abcdef
+0123456789abcdef0123456789abcdef0123456789abcdef');
+
+-- lots formatting
+select dearmor(' a pgp msg:
+
+-----BEGIN PGP MESSAGE-----
+Comment: Some junk
+
+em9va2E=
+
+  =D5cR
+
+-----END PGP MESSAGE-----');
+
+-- lots messages
+select 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-----
+');
+
+-- bad crc
+select dearmor('
+-----BEGIN PGP MESSAGE-----
+
+em9va2E=
+=ZZZZ
+-----END PGP MESSAGE-----
+');
diff --git a/contrib/pgcrypto/sql/pgp-compression.sql b/contrib/pgcrypto/sql/pgp-compression.sql
new file mode 100644
index 00000000000..f71c176da5a
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-compression.sql
@@ -0,0 +1,31 @@
+--
+-- 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');
+
diff --git a/contrib/pgcrypto/sql/pgp-decrypt.sql b/contrib/pgcrypto/sql/pgp-decrypt.sql
new file mode 100644
index 00000000000..93535ab016a
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-decrypt.sql
@@ -0,0 +1,266 @@
+--
+-- pgp_descrypt 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 encode(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'), 'hex');
+-- expected: 0225e3ede6f2587b076d021a189ff60aad67e066
+
+select encode(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'), 'hex');
+-- expected: da39a3ee5e6b4b0d3255bfef95601890afd80709
+
+select encode(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'), 'hex');
+-- expected: 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c
+
+-- Checking CRLF
+select encode(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'), 'hex');
+-- expected: 9353062be7720f1446d30b9e75573a4833886784
+
+select encode(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'), 'hex');
+-- expected: 7efefcab38467f7484d6fa43dc86cf5281bd78e2
diff --git a/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql b/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql
new file mode 100644
index 00000000000..80fb4d123c0
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql
@@ -0,0 +1,3 @@
+
+-- no random source
+
diff --git a/contrib/pgcrypto/sql/pgp-encrypt.sql b/contrib/pgcrypto/sql/pgp-encrypt.sql
new file mode 100644
index 00000000000..b663e051aec
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-encrypt.sql
@@ -0,0 +1,96 @@
+--
+-- 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 pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz');
+
+
+-- 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 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 encode(pgp_sym_decrypt_bytea(
+	pgp_sym_encrypt('1\n2\n3\r\n', 'key', 'convert-crlf=1'),
+ 	'key'), 'hex');
+
+-- conversion should be lossless
+select encode(digest(pgp_sym_decrypt(
+  pgp_sym_encrypt('\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'),
+ 	'key', 'convert-crlf=1'), 'sha1'), 'hex') as result,
+  encode(digest('\r\n0\n1\r\r\n\n2\r', 'sha1'), 'hex') as expect;
+
diff --git a/contrib/pgcrypto/sql/pgp-info.sql b/contrib/pgcrypto/sql/pgp-info.sql
new file mode 100644
index 00000000000..d979bb93d97
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-info.sql
@@ -0,0 +1,21 @@
+--
+-- 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(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(data)) as data_key_id
+from encdata order by id;
+
diff --git a/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql b/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql
new file mode 100644
index 00000000000..b4aec5daf6d
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql
@@ -0,0 +1,3 @@
+
+-- no bignum support
+
diff --git a/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql b/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql
new file mode 100644
index 00000000000..13fe090c674
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql
@@ -0,0 +1,437 @@
+--
+-- 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)
+
+mQGiBELII7wRBACFuaAvb11cIvjJK9LkZr4cYuYhLWh3DJdojNNnLNiym5OEksvY
+05cw8OgqKtPzICU7o/mHXTWhzJYUt3i50/AeYygI8Q0uATS6RnDAKNlES1EMoHKz
+2a5iFbYs4bm4IwlkvYd8uWjcu+U0YLbxir39u+anIc6eT+q3WiH/q3zDRwCgkT98
+cnIG8iO8PdwDSP8G4Lt6TYED/R45GvCzJ4onQALLE92KkLUz8aFWSl05r84kczEN
+SxiP9Ss6m465RmwWHfwYAu4b+c4GeNyU8fIU2EM8cezchC+edEi3xu1s+pCV0Dk4
+18DGC8WKCICO30vBynuNmYg7W/7Zd4wtjss454fMW7+idVDNM701mmXBtI1nsBtG
+7Z4tA/9FxjFbJK9jh24RewfjHpLYqcfCo2SsUjOwsnMZ5yg2yv9KyVVQhRqwmrqt
+q8MRyjGmfoD9PPdCgvqgzy0hHvAHUtTm2zUczGTG+0g4hNIklxC/Mv6J4KE+NWTh
+uB4acqofHyaw2WnKOuRUsoDi6rG5AyjNMyAK/vVcEGj7J1tk27QjRWxnYW1hbCA0
+MDk2IDx0ZXN0NDA5NkBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgjvAIbAwYLCQgH
+AwIDFQIDAxYCAQIeAQIXgAAKCRBj+HX2P2d0oAEDAJ9lI+CNmb42z3+a6TnVusM6
+FI7oLwCfUwA1zEcRdsT3nIkoYh0iKxFSDFW5BA0EQsgkdhAQAJQbLXlgcJ/jq+Xh
+Eujb77/eeftFJObNIRYD9fmJ7HFIXbUcknEpbs+cRH/nrj5dGSY3OT3jCXOUtvec
+sCoX/CpZWL0oqDjAiZtNSFiulw5Gav4gHYkWKgKdSo+2rkavEPqKIVHvMeXaJtGT
+d7v/AmL/P8T7gls93o5WFBOLtPbDvWqaKRy2U5TAhl1laiM0vGALRVjvSCgnGw9g
+FpSnXbO3AfenUSjDzZujfGLHtU44ixHSS/D4DepiF3YaYLsN4CBqZRv6FbMZD5W3
+DnJY4kS1kH0MzdcF19TlcZ3itTCcGIt1tMKf84mccPoqdMzH7vumBGTeFEly5Afp
+9berJcirqh2fzlunN0GS02z6SGWnjTbDlkNDxuxPSBbpcpNyD3jpYAUqSwRsZ/+5
+zkzcbGtDmvy9sJ5lAXkxGoIoQ1tEVX/LOHnh2NQHK8ourVOnr7MS0nozssITZJ5E
+XqtHiREjiYEuPyZiVZKJHLWuYYaF+n40znnz3sJuXFRreHhHbbvRdlYUU5mJV+XZ
+BLgKuS33NdpGeMIngnCc/9IQ6OZb6ixc94kbkd3w2PVr8CbKlu/IHTjWOO2mAo+D
++OydlYl23FiM3KOyMP1HcEOJMB/nwkMtrvd+522Lu9n77ktKfot9IPrQDIQTyXjR
+3pCOFtCOBnk2tJHMPoG9jn9ah/LHAAMHEACDZ5I/MHGfmiKg2hrmqBu2J2j/deC8
+CpwcyDH1ovQ0gHvb9ESa+CVRU2Wdy2CD7Q9SmtMverB5eneL418iPVRcQdwRmQ2y
+IH4udlBa6ce9HTUCaecAZ4/tYBnaC0Av/9l9tz14eYcwRMDpB+bnkhgF+PZ1KAfD
+9wcY2aHbtsf3lZBc5h4owPJkxpe/BNzuJxW3q4VpSbLsZhwnCZ2wg7DRwP44wFIk
+00ptmoBY59gsU6I40XtzrF8JDr0cA57xND5RY21Z8lnnYRE1Tc8h5REps9ZIxW3/
+yl91404bPLqxczpUHQAMSTAmBaStPYX1nS51uofOhLs5SKPCUmxfGKIOhsD0oLUn
+78DnkONVGeXzBibSwwtbgfMzee4G8wSUfJ7w8WXz1TyanaGLnJ+DuKASSOrFoBCD
+HEDuWZWgSL74NOQupFRk0gxOPmqU94Y8HziQWma/cETbmD83q8rxN+GM2oBxQkQG
+xcbqMTHE7aVhV3tymbSWVaYhww3oIwsZS9oUIi1DnPEowS6CpVRrwdvLjLJnJzzV
+O3AFPn9eZ1Q7R1tNx+zZ4OOfhvI/OlRJ3HBx2L53embkbdY9gFYCCdTjPyjKoDIx
+kALgCajjCYMNUsAKNSd6mMCQ8TtvukSzkZS1RGKP27ohsdnzIVsiEAbxDMMcI4k1
+ul0LExUTCXSjeIhJBBgRAgAJBQJCyCR2AhsMAAoJEGP4dfY/Z3Sg19sAn0NDS8pb
+qrMpQAxSb7zRTmcXEFd9AJ435H0ttP/NhLHXC9ezgbCMmpXMOQ==
+=kRxT
+-----END PGP PUBLIC KEY BLOCK-----
+', '
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v1.4.1 (GNU/Linux)
+
+lQG7BELII7wRBACFuaAvb11cIvjJK9LkZr4cYuYhLWh3DJdojNNnLNiym5OEksvY
+05cw8OgqKtPzICU7o/mHXTWhzJYUt3i50/AeYygI8Q0uATS6RnDAKNlES1EMoHKz
+2a5iFbYs4bm4IwlkvYd8uWjcu+U0YLbxir39u+anIc6eT+q3WiH/q3zDRwCgkT98
+cnIG8iO8PdwDSP8G4Lt6TYED/R45GvCzJ4onQALLE92KkLUz8aFWSl05r84kczEN
+SxiP9Ss6m465RmwWHfwYAu4b+c4GeNyU8fIU2EM8cezchC+edEi3xu1s+pCV0Dk4
+18DGC8WKCICO30vBynuNmYg7W/7Zd4wtjss454fMW7+idVDNM701mmXBtI1nsBtG
+7Z4tA/9FxjFbJK9jh24RewfjHpLYqcfCo2SsUjOwsnMZ5yg2yv9KyVVQhRqwmrqt
+q8MRyjGmfoD9PPdCgvqgzy0hHvAHUtTm2zUczGTG+0g4hNIklxC/Mv6J4KE+NWTh
+uB4acqofHyaw2WnKOuRUsoDi6rG5AyjNMyAK/vVcEGj7J1tk2wAAoJCUNy6awTkw
+XfbLbpqh0fvDst7jDLa0I0VsZ2FtYWwgNDA5NiA8dGVzdDQwOTZAZXhhbXBsZS5v
+cmc+iF4EExECAB4FAkLII7wCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQY/h1
+9j9ndKABAwCeNEOVK87EzXYbtxYBsnjrUI948NIAn2+f3BXiBFDV5NvqPwIZ0m77
+Fwy4nQRMBELIJHYQEACUGy15YHCf46vl4RLo2++/3nn7RSTmzSEWA/X5iexxSF21
+HJJxKW7PnER/564+XRkmNzk94wlzlLb3nLAqF/wqWVi9KKg4wImbTUhYrpcORmr+
+IB2JFioCnUqPtq5GrxD6iiFR7zHl2ibRk3e7/wJi/z/E+4JbPd6OVhQTi7T2w71q
+mikctlOUwIZdZWojNLxgC0VY70goJxsPYBaUp12ztwH3p1Eow82bo3xix7VOOIsR
+0kvw+A3qYhd2GmC7DeAgamUb+hWzGQ+Vtw5yWOJEtZB9DM3XBdfU5XGd4rUwnBiL
+dbTCn/OJnHD6KnTMx+77pgRk3hRJcuQH6fW3qyXIq6odn85bpzdBktNs+khlp402
+w5ZDQ8bsT0gW6XKTcg946WAFKksEbGf/uc5M3GxrQ5r8vbCeZQF5MRqCKENbRFV/
+yzh54djUByvKLq1Tp6+zEtJ6M7LCE2SeRF6rR4kRI4mBLj8mYlWSiRy1rmGGhfp+
+NM55897CblxUa3h4R2270XZWFFOZiVfl2QS4Crkt9zXaRnjCJ4JwnP/SEOjmW+os
+XPeJG5Hd8Nj1a/AmypbvyB041jjtpgKPg/jsnZWJdtxYjNyjsjD9R3BDiTAf58JD
+La73fudti7vZ++5LSn6LfSD60AyEE8l40d6QjhbQjgZ5NrSRzD6BvY5/WofyxwAD
+BxAAg2eSPzBxn5oioNoa5qgbtido/3XgvAqcHMgx9aL0NIB72/REmvglUVNlnctg
+g+0PUprTL3qweXp3i+NfIj1UXEHcEZkNsiB+LnZQWunHvR01AmnnAGeP7WAZ2gtA
+L//Zfbc9eHmHMETA6Qfm55IYBfj2dSgHw/cHGNmh27bH95WQXOYeKMDyZMaXvwTc
+7icVt6uFaUmy7GYcJwmdsIOw0cD+OMBSJNNKbZqAWOfYLFOiONF7c6xfCQ69HAOe
+8TQ+UWNtWfJZ52ERNU3PIeURKbPWSMVt/8pfdeNOGzy6sXM6VB0ADEkwJgWkrT2F
+9Z0udbqHzoS7OUijwlJsXxiiDobA9KC1J+/A55DjVRnl8wYm0sMLW4HzM3nuBvME
+lHye8PFl89U8mp2hi5yfg7igEkjqxaAQgxxA7lmVoEi++DTkLqRUZNIMTj5qlPeG
+PB84kFpmv3BE25g/N6vK8TfhjNqAcUJEBsXG6jExxO2lYVd7cpm0llWmIcMN6CML
+GUvaFCItQ5zxKMEugqVUa8Hby4yyZyc81TtwBT5/XmdUO0dbTcfs2eDjn4byPzpU
+Sdxwcdi+d3pm5G3WPYBWAgnU4z8oyqAyMZAC4Amo4wmDDVLACjUnepjAkPE7b7pE
+s5GUtURij9u6IbHZ8yFbIhAG8QzDHCOJNbpdCxMVEwl0o3gAAckBdfKuasiNUn5G
+L5XRnSvaOFzftr8zteOlZChCSNvzH5k+i1j7RJbWq06OeKRywPzjfjgM2MvRzI43
+ICeISQQYEQIACQUCQsgkdgIbDAAKCRBj+HX2P2d0oNfbAJ9+G3SeXrk+dWwo9EGi
+hqMi2GVTsgCfeoQJPsc8FLYUgfymc/3xqAVLUtg=
+=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)
+
+lQHhBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9
+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+sAKqROqe6dLjJ7vebQP4DAwL3TCgrYdj6
++GAnoSqGa87twi8a6QRRYIlEx3ddUCDCjzkJmRfF+LFtvX3OtWWK0+Syi3kj2IK9
+YT7pF7QfRWxnYW1hbCAxMDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJC
+yCFIAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAn1ynoCyM
+6GIvHDOewwmF4Z/jGQfzAJ9Q+MwIubi0ASfJifaEM23sIHwHop0BVwRCyCFKEAQA
+h5SNbbJMAsJ+sQbcWEzdku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioym
+lDwraTKUAfuCZgNcg/0PsxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlP
+iO0wt1lLX+SubktqbYxI+h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSg
+MERiNzF0acZUYmc0e+/96gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxs
+nKjUaw/qyoaFcNMzb4sFk8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey
+9ifh8rZfu57UbdwdHa0viWc4Dilh/gMDAvdMKCth2Pr4YCCPsELdgJuzhGfDNRSg
+nKMRWBWHSJRk6JmCjM1iJQNHc4mMhR8gvi2TeqYLOhYjcF7nr/LA+JvLV+adj/mI
+SQQYEQIACQUCQsghSgIbDAAKCRAcKbwNGBdzZO2vAJ4hRaLcNcdl/qK8rt0N5zbZ
+saCh6QCfR1O48O8nYN93SPSfIFZK5rEmdv8=
+=Y6Qv
+-----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-----
+');
+
+-- 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;
+
+-- 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;
+
+-- 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;
+
diff --git a/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql b/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql
new file mode 100644
index 00000000000..89d05a7a364
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql
@@ -0,0 +1,45 @@
+--
+-- 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;
+
+-- 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 pgp_pub_decrypt_bytea(
+		pgp_pub_encrypt('Secret msg', dearmor(pubkey)),
+		dearmor(seckey))
+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 00000000000..85c24683be5
--- /dev/null
+++ b/contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql
@@ -0,0 +1,3 @@
+
+-- zlib is disabled
+
diff --git a/contrib/pgcrypto/sql/sha2.sql b/contrib/pgcrypto/sql/sha2.sql
new file mode 100644
index 00000000000..f1d0ca63f01
--- /dev/null
+++ b/contrib/pgcrypto/sql/sha2.sql
@@ -0,0 +1,28 @@
+--
+-- SHA2 family
+--
+
+-- SHA256
+SELECT encode(digest('', 'sha256'), 'hex');
+SELECT encode(digest('a', 'sha256'), 'hex');
+SELECT encode(digest('abc', 'sha256'), 'hex');
+SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha256'), 'hex');
+SELECT encode(digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha256'), 'hex');
+
+-- SHA384
+SELECT encode(digest('', 'sha384'), 'hex');
+SELECT encode(digest('a', 'sha384'), 'hex');
+SELECT encode(digest('abc', 'sha384'), 'hex');
+SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha384'), 'hex');
+SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha384'), 'hex');
+SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha384'), 'hex');
+
+-- SHA512
+SELECT encode(digest('', 'sha512'), 'hex');
+SELECT encode(digest('a', 'sha512'), 'hex');
+SELECT encode(digest('abc', 'sha512'), 'hex');
+SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha512'), 'hex');
+SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha512'), 'hex');
+SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha512'), 'hex');
+
+
-- 
GitLab