Add missing pgcrypto files from previous commit.
authorBruce Momjian <bruce@momjian.us>
Sun, 10 Jul 2005 13:46:29 +0000 (13:46 +0000)
committerBruce Momjian <bruce@momjian.us>
Sun, 10 Jul 2005 13:46:29 +0000 (13:46 +0000)
44 files changed:
contrib/pgcrypto/expected/pgp-armor.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-compression.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-decrypt.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-encrypt.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-info.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-pubkey-decrypt.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-pubkey-encrypt.out [new file with mode: 0644]
contrib/pgcrypto/expected/pgp-zlib-DISABLED.out [new file with mode: 0644]
contrib/pgcrypto/expected/sha2.out [new file with mode: 0644]
contrib/pgcrypto/fortuna.c [new file with mode: 0644]
contrib/pgcrypto/fortuna.h [new file with mode: 0644]
contrib/pgcrypto/mbuf.c [new file with mode: 0644]
contrib/pgcrypto/mbuf.h [new file with mode: 0644]
contrib/pgcrypto/pgp-armor.c [new file with mode: 0644]
contrib/pgcrypto/pgp-cfb.c [new file with mode: 0644]
contrib/pgcrypto/pgp-compress.c [new file with mode: 0644]
contrib/pgcrypto/pgp-decrypt.c [new file with mode: 0644]
contrib/pgcrypto/pgp-encrypt.c [new file with mode: 0644]
contrib/pgcrypto/pgp-info.c [new file with mode: 0644]
contrib/pgcrypto/pgp-mpi-internal.c [new file with mode: 0644]
contrib/pgcrypto/pgp-mpi-openssl.c [new file with mode: 0644]
contrib/pgcrypto/pgp-mpi.c [new file with mode: 0644]
contrib/pgcrypto/pgp-pgsql.c [new file with mode: 0644]
contrib/pgcrypto/pgp-pubdec.c [new file with mode: 0644]
contrib/pgcrypto/pgp-pubenc.c [new file with mode: 0644]
contrib/pgcrypto/pgp-pubkey.c [new file with mode: 0644]
contrib/pgcrypto/pgp-s2k.c [new file with mode: 0644]
contrib/pgcrypto/pgp.c [new file with mode: 0644]
contrib/pgcrypto/pgp.h [new file with mode: 0644]
contrib/pgcrypto/sha2.c [new file with mode: 0644]
contrib/pgcrypto/sha2.h [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-armor.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-compression.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-decrypt.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-encrypt.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-info.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql [new file with mode: 0644]
contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql [new file with mode: 0644]
contrib/pgcrypto/sql/sha2.sql [new file with mode: 0644]

diff --git a/contrib/pgcrypto/expected/pgp-armor.out b/contrib/pgcrypto/expected/pgp-armor.out
new file mode 100644 (file)
index 0000000..ba7d426
--- /dev/null
@@ -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 (file)
index 0000000..32b350b
--- /dev/null
@@ -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 (file)
index 0000000..8ecb56f
--- /dev/null
@@ -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 (file)
index 0000000..4122300
--- /dev/null
@@ -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 (file)
index 0000000..b04bae5
--- /dev/null
@@ -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 (file)
index 0000000..300d41a
--- /dev/null
@@ -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 (file)
index 0000000..d35c097
--- /dev/null
@@ -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 (file)
index 0000000..28881ad
--- /dev/null
@@ -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 (file)
index 0000000..a7b1c02
--- /dev/null
@@ -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 (file)
index 0000000..6f4eccd
--- /dev/null
@@ -0,0 +1 @@
+-- zlib is disabled
diff --git a/contrib/pgcrypto/expected/sha2.out b/contrib/pgcrypto/expected/sha2.out
new file mode 100644 (file)
index 0000000..20bdf0e
--- /dev/null
@@ -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 (file)
index 0000000..ff02a97
--- /dev/null
@@ -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 (file)
index 0000000..b576eb9
--- /dev/null
@@ -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 (file)
index 0000000..eb5dff9
--- /dev/null
@@ -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 (file)
index 0000000..6042a4f
--- /dev/null
@@ -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 (file)
index 0000000..20f443f
--- /dev/null
@@ -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 (file)
index 0000000..52f3722
--- /dev/null
@@ -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 (file)
index 0000000..4faf0e4
--- /dev/null
@@ -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 (file)
index 0000000..edffc6a
--- /dev/null
@@ -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 (file)
index 0000000..8aee9bf
--- /dev/null
@@ -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 (file)
index 0000000..d757e8c
--- /dev/null
@@ -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 (file)
index 0000000..12831b1
--- /dev/null
@@ -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 (file)
index 0000000..c44e193
--- /dev/null
@@ -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 (file)
index 0000000..6d3eff4
--- /dev/null
@@ -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 (file)
index 0000000..c17d952
--- /dev/null
@@ -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 (file)
index 0000000..ad4ceb2
--- /dev/null
@@ -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 (file)
index 0000000..7a0ba33
--- /dev/null
@@ -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 (file)
index 0000000..6a00947
--- /dev/null
@@ -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 (file)
index 0000000..0916a00
--- /dev/null
@@ -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 (file)
index 0000000..8c71441
--- /dev/null
@@ -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 (file)
index 0000000..7f31aa3
--- /dev/null
@@ -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 (file)
index 0000000..9c35107
--- /dev/null
@@ -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 (file)
index 0000000..2dfb13c
--- /dev/null
@@ -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 (file)
index 0000000..040c4ac
--- /dev/null
@@ -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 (file)
index 0000000..f71c176
--- /dev/null
@@ -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 (file)
index 0000000..93535ab
--- /dev/null
@@ -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 (file)
index 0000000..80fb4d1
--- /dev/null
@@ -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 (file)
index 0000000..b663e05
--- /dev/null
@@ -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 (file)
index 0000000..d979bb9
--- /dev/null
@@ -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 (file)
index 0000000..b4aec5d
--- /dev/null
@@ -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 (file)
index 0000000..13fe090
--- /dev/null
@@ -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 (file)
index 0000000..89d05a7
--- /dev/null
@@ -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 (file)
index 0000000..85c2468
--- /dev/null
@@ -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 (file)
index 0000000..f1d0ca6
--- /dev/null
@@ -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');
+
+