--- /dev/null
+--
+-- 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
--- /dev/null
+--
+-- 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)
+
--- /dev/null
+--
+-- 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
--- /dev/null
+-- no random source
--- /dev/null
+--
+-- 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)
+
--- /dev/null
+--
+-- 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)
+
--- /dev/null
+-- no bignum support
--- /dev/null
+--
+-- 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)
+
--- /dev/null
+--
+-- 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
--- /dev/null
+-- zlib is disabled
--- /dev/null
+--
+-- 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)
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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
+
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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);
+}
+
+
--- /dev/null
+/*
+ * 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;
+}
+
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+
--- /dev/null
+/* $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));
+}
--- /dev/null
+/* $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 */
--- /dev/null
+--
+-- 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-----
+');
--- /dev/null
+--
+-- 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');
+
--- /dev/null
+--
+-- 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
--- /dev/null
+
+-- no random source
+
--- /dev/null
+--
+-- 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;
+
--- /dev/null
+--
+-- 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;
+
--- /dev/null
+
+-- no bignum support
+
--- /dev/null
+--
+-- 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;
+
--- /dev/null
+--
+-- 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;
+
+
--- /dev/null
+
+-- zlib is disabled
+
--- /dev/null
+--
+-- 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');
+
+