Hi hackers,

Attached is a patch to add support for PGP signatures in encrypted messages into pgcrypto.

Currently, the list of limitations is the following:

- It only knows how to generate one signature per message. I don't see that as a problem. - If a message has been signed with multiple keys which have the same keyid as the one specified to verify the message, an error is returned. Naively, it seems that we should try all of them and return "OK" if even one of them matches, but that seems icky. - Only RSA signatures are supported. It wouldn't be too hard for someone familiar with DSA to add it in, but I'm not volunteering to do it. Personally I think supporting RSA is better than no support at all.

As per usual, I'll also add this to the upcoming commitfest. Any feedback appreciated before that, of course.


.marko
*** a/contrib/pgcrypto/Makefile
--- b/contrib/pgcrypto/Makefile
***************
*** 20,39 **** SRCS             = pgcrypto.c px.c px-hmac.c px-crypt.c \
                mbuf.c pgp.c pgp-armor.c pgp-cfb.c pgp-compress.c \
                pgp-decrypt.c pgp-encrypt.c pgp-info.c pgp-mpi.c \
                pgp-pubdec.c pgp-pubenc.c pgp-pubkey.c pgp-s2k.c \
!               pgp-pgsql.c
  
  MODULE_big    = pgcrypto
  OBJS          = $(SRCS:.c=.o) $(WIN32RES)
  
  EXTENSION = pgcrypto
! DATA = pgcrypto--1.1.sql pgcrypto--1.0--1.1.sql pgcrypto--unpackaged--1.0.sql
  PGFILEDESC = "pgcrypto - cryptographic functions"
  
  REGRESS = init md5 sha1 hmac-md5 hmac-sha1 blowfish rijndael \
        $(CF_TESTS) \
        crypt-des crypt-md5 crypt-blowfish crypt-xdes \
        pgp-armor pgp-decrypt pgp-encrypt $(CF_PGP_TESTS) \
!       pgp-pubkey-decrypt pgp-pubkey-encrypt pgp-info
  
  EXTRA_CLEAN = gen-rtab
  
--- 20,39 ----
                mbuf.c pgp.c pgp-armor.c pgp-cfb.c pgp-compress.c \
                pgp-decrypt.c pgp-encrypt.c pgp-info.c pgp-mpi.c \
                pgp-pubdec.c pgp-pubenc.c pgp-pubkey.c pgp-s2k.c \
!               pgp-sig.c pgp-pgsql.c
  
  MODULE_big    = pgcrypto
  OBJS          = $(SRCS:.c=.o) $(WIN32RES)
  
  EXTENSION = pgcrypto
! DATA = pgcrypto--1.2.sql pgcrypto--1.0--1.1.sql pgcrypto--1.1--1.2.sql 
pgcrypto--unpackaged--1.0.sql
  PGFILEDESC = "pgcrypto - cryptographic functions"
  
  REGRESS = init md5 sha1 hmac-md5 hmac-sha1 blowfish rijndael \
        $(CF_TESTS) \
        crypt-des crypt-md5 crypt-blowfish crypt-xdes \
        pgp-armor pgp-decrypt pgp-encrypt $(CF_PGP_TESTS) \
!       pgp-pubkey-decrypt pgp-pubkey-encrypt pgp-info pgp-sign
  
  EXTRA_CLEAN = gen-rtab
  
*** a/contrib/pgcrypto/expected/pgp-encrypt.out
--- b/contrib/pgcrypto/expected/pgp-encrypt.out
***************
*** 16,22 **** select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
                expect-sess-key=0,
                expect-s2k-mode=3,
                expect-s2k-digest-algo=sha1,
!               expect-compress-algo=0
                ');
   pgp_sym_decrypt 
  -----------------
--- 16,23 ----
                expect-sess-key=0,
                expect-s2k-mode=3,
                expect-s2k-digest-algo=sha1,
!               expect-compress-algo=0,
!               expect-digest-algo=sha512
                ');
   pgp_sym_decrypt 
  -----------------
***************
*** 30,38 **** select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
                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
--- 31,41 ----
                expect-sess-key=1,
                expect-s2k-mode=0,
                expect-s2k-digest-algo=md5,
!               expect-compress-algo=1,
!               expect-digest-algo=md5
                ');
  NOTICE:  pgp_decrypt: unexpected cipher_algo: expected 4 got 7
+ NOTICE:  pgp_decrypt: unexpected digest_algo: expected 1 got 10
  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
*** a/contrib/pgcrypto/expected/pgp-info.out
--- b/contrib/pgcrypto/expected/pgp-info.out
***************
*** 76,78 **** from encdata order by id;
--- 76,151 ----
   FD0206C409B74875
  (4 rows)
  
+ -- pgp_main_key_id
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=1;
+  pgp_main_key_id  
+ ------------------
+  1C29BC0D18177364
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=2;
+  pgp_main_key_id  
+ ------------------
+  48E9CD56FEA668DB
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=3;
+  pgp_main_key_id  
+ ------------------
+  63F875F63F6774A0
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=4;
+  pgp_main_key_id  
+ ------------------
+  9DCF8E9C9BD31F24
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=5;
+  pgp_main_key_id  
+ ------------------
+  1C29BC0D18177364
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=6;
+  pgp_main_key_id  
+ ------------------
+  C899EA9344195559
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=1;
+  pgp_main_key_id  
+ ------------------
+  1C29BC0D18177364
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=2;
+  pgp_main_key_id  
+ ------------------
+  48E9CD56FEA668DB
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=3;
+  pgp_main_key_id  
+ ------------------
+  63F875F63F6774A0
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=4;
+  pgp_main_key_id  
+ ------------------
+  9DCF8E9C9BD31F24
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=5;
+  pgp_main_key_id  
+ ------------------
+  1C29BC0D18177364
+ (1 row)
+ 
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=6;
+  pgp_main_key_id  
+ ------------------
+  C899EA9344195559
+ (1 row)
+ 
*** /dev/null
--- b/contrib/pgcrypto/expected/pgp-sign.out
***************
*** 0 ****
--- 1,313 ----
+ --
+ -- PGP sign
+ --
+ -- ensure consistent test output regardless of the default bytea format
+ SET bytea_output TO escape;
+ -- list keys
+ select pgp_sym_signature_keys.* from
+     (select pgp_sym_encrypt_sign_bytea('Secret.', 'key', dearmor(seckey)) as 
ciphertext
+     from keytbl where keytbl.name = 'rsa2048') encrypted,
+     lateral pgp_sym_signature_keys(encrypted.ciphertext, 'key')
+     ;
+       keyid       | digest | pubkeyalgo 
+ ------------------+--------+------------
+  9DCF8E9C9BD31F24 | sha512 | rsa
+ (1 row)
+ 
+ select pgp_pub_signature_keys.* from
+     (select seckey, pgp_pub_encrypt_sign_bytea('Secret.', dearmor(pubkey), 
dearmor(seckey)) as ciphertext
+     from keytbl where keytbl.name = 'rsaenc2048') encrypted,
+     lateral pgp_pub_signature_keys(encrypted.ciphertext, 
dearmor(encrypted.seckey))
+     ;
+       keyid       | digest | pubkeyalgo 
+ ------------------+--------+------------
+  C899EA9344195559 | sha512 | rsa
+ (1 row)
+ 
+ -- decrypt without verifying the signature
+ select pgp_sym_decrypt_bytea(pgp_sym_encrypt_sign_bytea('Secret.', 'key', 
dearmor(seckey)), 'key')
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_bytea 
+ -----------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_pub_decrypt_bytea(pgp_pub_encrypt_sign_bytea('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_bytea 
+ -----------------------
+  Secret.
+ (1 row)
+ 
+ -- decrypt and verify the signature
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign_bytea('Secret.', 
'key', dearmor(seckey)), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify_bytea 
+ ------------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign_bytea('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify_bytea 
+ ------------------------------
+  Secret.
+ (1 row)
+ 
+ -- decrypt and verify the signature, wrong key
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign_bytea('Secret.', 
'key', dearmor(keytbl1.seckey)), 'key', dearmor(keytbl2.pubkey))
+ from keytbl keytbl1, keytbl keytbl2 where keytbl1.name = 'rsa2048' and 
keytbl2.name = 'rsaenc2048';
+ ERROR:  No signature matching the key id present in the message
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign_bytea('Secret.', 
dearmor(keytbl2.pubkey), dearmor(keytbl1.seckey)), dearmor(keytbl2.seckey), 
dearmor(keytbl2.pubkey))
+ from keytbl keytbl1, keytbl keytbl2 where keytbl1.name = 'rsa2048' and 
keytbl2.name = 'rsaenc2048';
+ ERROR:  No signature matching the key id present in the message
+ -- complain if no signature is present
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_bytea('Secret.', 'key'), 
'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ ERROR:  No signature matching the key id present in the message
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_bytea('Secret.', 
dearmor(pubkey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ ERROR:  No signature matching the key id present in the message
+ -- multiple signers
+ insert into encdata(id, data) values (5, '
+ -----BEGIN PGP MESSAGE-----
+ Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
+ 
+ jA0ECQMCA7SEJlWfWYjUyekWNxFzQ/NFijc61eLTtHEqtxZ36f0XvgV2ZjIgUVq5
+ jSaGcly7rTfy6P9bCNMN+p1B86N+v6P+7zkzhtg4abM7RTbnXfj9VupQE+bTu++A
+ 9xTAOrM79cFlyVzVykkQUOcvw7kNRk2woepREbguRpqLytDwVf8tJKn2Yd00X/Lp
+ IsU5HfT+TcNngx8NFqhKedfAPcyQd0cS7NA0dcUyXcN/fO+PsPavp7iPGt0Q+/JN
+ exkjx4LmJPObkrgN7RYiOlA3vRUt4SuzJAIN6+GkKxveYrpQuaGr1t1M0HfPXw9n
+ gilqUtlwX36tHGfCOYYwlG64LaNsyuTRmXIvV0o8kYaaJtoVKeMGkZCPd6XZoAf9
+ Elluzf7Mxe+T44XRQ/VlO8P9aT0immSdOwGL6wywmV+kITpcVUcthCR3a2Yb2R4M
+ NE0efRop4arfdOGpLdysF32ymwAZgdqNCDHKLTuAKfDlnXl2Tm1QdOhXytILIe64
+ kkzt5YNjrAvw5qmn0ze3xZuUCTuEUbBh3T19o5jrF1oiZ4hqd6o3iUEPnYxWaHl0
+ r7W9BxHpVJexY7K3MGtAnnHKn8f+MmopGe4HDSHTRf+qDjZi7yg9psWChlii4PPs
+ YqmfxGBicnoHQy+GSauoDgVPNy4PPrH5yY4bAByt3op28/vkQ7bQH0tuc6x6J0Rm
+ GYG7s8HPpWFSzS7o25tALBmXIi+DZfdgQ8tQ4MLx5wZPJ1H68A3MTvinuQKiY5yE
+ YezsNH92tGilzM5E0iRA8UTluqhQIkX4apMJnnRT8RJ0by5pUbkYKokbmH4rKTCv
+ nOIu5RYb/9a4Nd4ijZOWM8AmNKVNsLP3cB7jJqupykWNpos=
+ =1JXB
+ -----END PGP MESSAGE-----
+ ');
+ insert into encdata(id, data) values (6, '
+ -----BEGIN PGP MESSAGE-----
+ Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
+ 
+ hQEMA/0CBsQJt0h1AQf/bAFXphI0ecP5Ba2gKnC9TXz7BWhHn07QBEBoWJ4CHMpp
+ ULwBJ4CgG6ED9QdtIPeteazrn490ORS8ut4mymf+ERolZGI7U4p2lJJIkvpS7Qyq
+ wAEjsZgl48mT6P8JQyp7Xf2MDrONVNS+rsp1+C5Fem8PGprlIu7RRUBYi1eg3lZO
+ Kjl8poBqU28PHT/HaZakccO/cOFKaXBAlq3wZGHgEwNa2LXwNlUOG66u2GrMKcAm
+ R2N68ve5clIa5cUWPB8uvvWkbCjBnf+re4L7hddRCAVNs98WC7ty1876xJLh5OyH
+ cGh8xa03LMOOnBseuOUx/dKVTjc5vFgsfTDJgf6SS9LpAeKts5nMscaVzqU2jgtd
+ YOyhocXn8+kA43iUX0YZvMzfep8vSoHqigV2VQ6OtxQBT1SA7inE/7l3t3xPaSRz
+ HeeXBDSg1BSwLr2p+l/PTvR158MZ4MQX5PvmPJ3M6f/1nDflHGR1pp8Qjv7BGiOz
+ XnjGSK+pRrzT4S2XSOIglcSNqEKa0B0iodv/R593E08/zZMeyGZQL7esJq9CWp4n
+ jT30ATpvIrZ6UvBpxj21G64/JfFSZa8a+v2biC/eOws3Dch/fCa0IU0RNlZaTXoN
+ 888am3HEKlObzst+7PvkRc4TgK91cfF46w311iD3bDi8lsv1LqDmBMqhIEWrg1Sc
+ ntAe+afUzHdUKg/StlMOSloTwU0oP+drj3h30UaR/t0/ykMDfCjW6peEmEB64vDx
+ 3FQk8phKket2EmKhC1pHWZpZsEgITficWwy42l43xAsLNp6cwuhZ5Sz874di73iE
+ oDhIqB5Mftvc1zUiZv/15KsIX9DwxGHWbaUIRro+xYmKj36ljJguTA74NwKljxbE
+ xQ6gLjMs81MCBkPbPjM4iNuF5AqVu78BSUqd7nOKauLm5/a2COr/5fh6Zigph57y
+ FTe54GqFzxlpP4JOqUiS2gd9lRlXujCWpVa8Cexxh99jpG1mF/xuHpnjJvCOPp4h
+ g0IBsNq67xYHubsX+goGnH2edeJsH5eXYwETFqYnUt5kQmKQKPZn4vH01TAidHco
+ Qv9O1DIyvwiwmgaoPT6JCuGfd8lFqmR6W4u+3NM6pbW19AguYIFXRcgzLIOX5t4K
+ E3JVgW8pkQcxBsycFxrwjf66hfaLTu39SsZrWkkaPRMo8kCt08K2jgf4alz/MyhH
+ uRScuAbB94gSEf/VmzTnilt2219be1w5zl35h1fjCbo=
+ =snSk
+ -----END PGP MESSAGE-----
+ ');
+ select * from pgp_pub_decrypt_bytea((select dearmor(data) from encdata where 
id=6), (select dearmor(seckey) from keytbl where keytbl.name = 'rsaenc2048'));
+  pgp_pub_decrypt_bytea 
+ -----------------------
+  hello world
+ (1 row)
+ 
+ select * from pgp_sym_signature_keys((select dearmor(data) from encdata where 
id=5), 'key');
+       keyid       | digest | pubkeyalgo 
+ ------------------+--------+------------
+  C899EA9344195559 | sha512 | rsa
+  9DCF8E9C9BD31F24 | sha512 | rsa
+ (2 rows)
+ 
+ select * from pgp_pub_signature_keys((select dearmor(data) from encdata where 
id=6), (select dearmor(seckey) from keytbl where keytbl.name = 'rsaenc2048'));
+       keyid       | digest | pubkeyalgo 
+ ------------------+--------+------------
+  C899EA9344195559 | sha1   | rsa
+  9DCF8E9C9BD31F24 | sha1   | rsa
+ (2 rows)
+ 
+ -- verify both signatures
+ select * from pgp_pub_decrypt_verify_bytea((select dearmor(data) from encdata 
where id=6), (select dearmor(seckey) from keytbl where keytbl.name = 
'rsaenc2048'), (select dearmor(pubkey) from keytbl where keytbl.name = 
'rsa2048'));
+  pgp_pub_decrypt_verify_bytea 
+ ------------------------------
+  hello world
+ (1 row)
+ 
+ select * from pgp_pub_decrypt_verify_bytea((select dearmor(data) from encdata 
where id=6), (select dearmor(seckey) from keytbl where keytbl.name = 
'rsaenc2048'), (select dearmor(pubkey) from keytbl where keytbl.name = 
'rsaenc2048'));
+  pgp_pub_decrypt_verify_bytea 
+ ------------------------------
+  hello world
+ (1 row)
+ 
+ -- test v3 signature headers
+ insert into encdata(id, data) values (7, '
+ -----BEGIN PGP MESSAGE-----
+ Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
+ Comment: GPGTools - http://gpgtools.org
+ 
+ hQEMA/0CBsQJt0h1AQf/TbgfgQgH8QxP6THfNFKOW39TvV+v9Sb2p5Q7JRF6/YxG
+ n2N2ADkO0S63wE9HRH2xHAbxvaxO9nCHX48mTTi6sj/6fRdg3nDn9yvQcE994JaS
+ Wumn3d+7Pe8AqpwAyk6Tn2YSrdv8K3AKB0DuQI0FsXvjET8x7uBvD272c665od4k
+ FhgOzJrgtin6DKCUSVc8UZgDw4ZI/TAHrbf6pxiIX2rLdn1EAcjuPALiQKGvQIyH
+ I/B+Yq7j8sLhL60k3DEKHSjFqHR16LG4wsCKnNjzBM+Dto3nkklTcuy1Qu6D8B38
+ b1yVWO6IoUPf1aKahrzdFfv3J9jnmt7CMbxIfjqeqdLAsAG2e+dtdDu/own6lI6T
+ AM8TqvSCyKpjz8IN6FELe4rJq2LgS+FKJPcuFJV2JJs+eOo4O2PzVfdv8yJklysH
+ epU5tfrpYdkbsrR9pLhsbKGDINDmqENydAhFLUII2xdichVkYvk+gye+GS3E2EPp
+ aniMP/CuetL6qDIht9ADBCstBih8VFE7d7bNB//ldKc8cXKMJ/h1CHJ788sV2QBO
+ RHgHdWFE02JoK8WsDf/Wg5422Yca1JXhfr3wvHUwAvmnnIGzOUBaHbMSTlrgqNsR
+ nerdZxLfaxUQ8CjJ2yobn9OIAj4TAuITipssUsEVypT8m1lwsW2CaTuWUBcE9oC7
+ ULIfPPt+McDf1EYNtp+0UxZASFLETVYsLIfhNQxf8YnXFuVcLzvhdVRQKZ7oMC17
+ +0non8pele5HURJO7e3ULQihtb1i9GPtPXRjhyuR5K3n35NoZJHt4SCQPuRxRJIB
+ I4toPKPYCrND+X25oKaTrTMC
+ =WWPD
+ -----END PGP MESSAGE-----
+ ');
+ select * from pgp_pub_decrypt_verify_bytea((select dearmor(data) from encdata 
where id=7), (select dearmor(seckey) from keytbl where keytbl.name = 
'rsaenc2048'), (select dearmor(pubkey) from keytbl where keytbl.name = 
'rsa2048'));
+  pgp_pub_decrypt_verify_bytea 
+ ------------------------------
+  hello world
+ (1 row)
+ 
+ -- pgp_main_key_id() should fail, even on signed data
+ select pgp_main_key_id(pgp_sym_encrypt_sign_bytea('Secret.', 'key', 
dearmor(seckey)))
+ from keytbl where keytbl.name = 'rsa2048';
+ ERROR:  No sign key found
+ -- text mode
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey)), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify 
+ ------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify 
+ ------------------------
+  Secret.
+ (1 row)
+ 
+ -- encrypt in binary, verify signature in text (doesn't work)
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign_bytea('Secret.', 'key', 
dearmor(seckey)), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ ERROR:  Not text data
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign_bytea('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ ERROR:  Not text data
+ -- encrypt in text, verify signature in binary (works)
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey)), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify_bytea 
+ ------------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify_bytea 
+ ------------------------------
+  Secret.
+ (1 row)
+ 
+ -- encrypt in text with convert-crlf, verify signature in binary (works)
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify_bytea 
+ ------------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify_bytea 
+ ------------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign(E'Secret.\n', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify_bytea 
+ ------------------------------
+  Secret.\015\012
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign(E'Secret.\n', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify_bytea 
+ ------------------------------
+  Secret.\015\012
+ (1 row)
+ 
+ -- encrypt in text with convert-crlf, verify with same (works)
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey), '', 
'convert-crlf=1')
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify 
+ ------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey), '', 'convert-crlf=1')
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify 
+ ------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign(E'Secret.\n', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey), '', 
'convert-crlf=1')
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify 
+ ------------------------
+  Secret.               +
+  
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign(E'Secret.\n', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey), '', 'convert-crlf=1')
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify 
+ ------------------------
+  Secret.               +
+  
+ (1 row)
+ 
+ -- encrypt in text with convert-crlf, verify in text without conversion 
(works)
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify 
+ ------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify 
+ ------------------------
+  Secret.
+ (1 row)
+ 
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign(E'Secret.\n', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+  pgp_sym_decrypt_verify 
+ ------------------------
+  Secret.\r             +
+  
+ (1 row)
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign(E'Secret.\n', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+  pgp_pub_decrypt_verify 
+ ------------------------
+  Secret.\r             +
+  
+ (1 row)
+ 
*** a/contrib/pgcrypto/mbuf.c
--- b/contrib/pgcrypto/mbuf.c
***************
*** 332,337 **** pullf_read_fixed(PullFilter *src, int len, uint8 *dst)
--- 332,379 ----
  }
  
  /*
+  * pullf_discard discards max bytes from src.  Reaching EOF before max bytes
+  * have been read will return PXE_MBUF_SHORT_READ.  If max is -1, all bytes
+  * until EOF are discarded.  Returns the number of bytes discarded on success,
+  * < 0 otherwise.
+  */
+ int
+ pullf_discard(PullFilter *src, int max)
+ {
+       int             res;
+       uint8  *tmp;
+       int             read = 0;
+ 
+       if (max == -1)
+       {
+               for (;;)
+               {
+                       res = pullf_read(src, 8192, &tmp);
+                       if (res == 0)
+                               return read;
+                       else if (res < 0)
+                               return res;
+                       read += res;
+               }
+       }
+       else
+       {
+               for (;;)
+               {
+                       if (read == max)
+                               return read;
+ 
+                       res = pullf_read(src, max - read, &tmp);
+                       if (res == 0)
+                               return PXE_MBUF_SHORT_READ;
+                       else if (res < 0)
+                               return res;
+                       read += res;
+               }
+       }
+ }
+ 
+ /*
   * read from MBuf
   */
  static int
***************
*** 353,358 **** pullf_create_mbuf_reader(PullFilter **mp_p, MBuf *src)
--- 395,477 ----
        return pullf_create(mp_p, &mbuf_reader, src, NULL);
  }
  
+ /*
+  * reader with a limit
+  */
+ 
+ static int
+ limited_reader_pull(void *arg, PullFilter *src, int len,
+                                       uint8 **data_p, uint8 *buf, int buflen)
+ {
+       int    *limit = arg;
+       int             res;
+ 
+       if (*limit == 0)
+               return 0;
+       if (len > *limit)
+               return PXE_MBUF_SHORT_READ;
+       res = pullf_read(src, len, data_p);
+       if (res > 0)
+       {
+               *limit -= res;
+               if (*limit < 0)
+                       return PXE_MBUF_SHORT_READ;
+       }
+       else if (res == 0)
+               return PXE_MBUF_SHORT_READ;
+       return res;
+ }
+ 
+ static const struct PullFilterOps limited_reader = {
+       NULL, limited_reader_pull, NULL
+ };
+ 
+ /*
+  * Creates a new PullFilter which reads *limit bytes from src.  The caller
+  * should make sure the memory limit points to stays alive until the reader is
+  * destroyed.  The value of *limit is updated after every read.  While 
reading,
+  * if an EOF is encountered before consuming *limit bytes from src or the
+  * caller tries to read more than *limit bytes in total, PXE_MBUF_SHORT_READ 
is
+  * returned.
+  */
+ int
+ pullf_create_limited_reader(PullFilter **mp_p, PullFilter *src, int *limit)
+ {
+       return pullf_create(mp_p, &limited_reader, limit, src);
+ }
+ 
+ /*
+  * reader which writes a copy to an mbuf
+  */
+ static int
+ tee_reader_pull(void *arg, PullFilter *src, int len,
+                               uint8 **data_p, uint8 *buf, int buflen)
+ {
+       MBuf   *mbuf = arg;
+       int             res;
+       int             res2;
+ 
+       res = pullf_read(src, len, data_p);
+       if (res <= 0)
+               return res;
+       res2 = mbuf_append(mbuf, *data_p, res);
+       if (res2 < 0)
+               return res2;
+       /* return the number of bytes read */
+       return res;
+ }
+ 
+ static const struct PullFilterOps tee_reader = {
+       NULL, tee_reader_pull, NULL
+ };
+ 
+ int
+ pullf_create_tee_reader(PullFilter **mp_p, PullFilter *src, MBuf *buf)
+ {
+       return pullf_create(mp_p, &tee_reader, buf, src);
+ }
+ 
+ 
  
  /*
   * PushFilter
*** a/contrib/pgcrypto/mbuf.h
--- b/contrib/pgcrypto/mbuf.h
***************
*** 109,115 **** int pullf_read_max(PullFilter *mp, int len,
--- 109,118 ----
                           uint8 **data_p, uint8 *tmpbuf);
  void          pullf_free(PullFilter *mp);
  int                   pullf_read_fixed(PullFilter *src, int len, uint8 *dst);
+ int                   pullf_discard(PullFilter *src, int max);
  
+ int                   pullf_create_limited_reader(PullFilter **pf_p, 
PullFilter *src, int *limit);
+ int                   pullf_create_tee_reader(PullFilter **mp_p, PullFilter 
*src, MBuf *buf);
  int                   pullf_create_mbuf_reader(PullFilter **pf_p, MBuf *mbuf);
  
  #define GETBYTE(pf, dst) \
*** /dev/null
--- b/contrib/pgcrypto/pgcrypto--1.1--1.2.sql
***************
*** 0 ****
--- 1,192 ----
+ /* contrib/pgcrypto/pgcrypto--1.1--1.2.sql */
+ 
+ -- complain if script is sourced in psql, rather than via ALTER EXTENSION
+ \echo Use "ALTER EXTENSION pgcrypto UPDATE TO '1.2'" to load this file. \quit
+ 
+ --
+ -- pgp_sym_encrypt_sign(data, key, sigkey)
+ --
+ CREATE FUNCTION pgp_sym_encrypt_sign(text, text, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_sym_encrypt_sign_bytea(bytea, text, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_sym_encrypt_sign(data, key, sigkey, psw)
+ --
+ CREATE FUNCTION pgp_sym_encrypt_sign(text, text, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_sym_encrypt_sign_bytea(bytea, text, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_sym_encrypt_sign(data, key, sigkey, psw, args)
+ --
+ CREATE FUNCTION pgp_sym_encrypt_sign(text, text, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_sym_encrypt_sign_bytea(bytea, text, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_sym_decrypt_verify(data, key, sigkey)
+ --
+ CREATE FUNCTION pgp_sym_decrypt_verify(bytea, text, bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_sym_decrypt_verify_bytea(bytea, text, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_sym_decrypt_verify(data, key, sigkey, psw)
+ --
+ CREATE FUNCTION pgp_sym_decrypt_verify(bytea, text, bytea, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_sym_decrypt_verify_bytea(bytea, text, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_sym_decrypt_verify(data, key, sigkey, psw, args)
+ --
+ CREATE FUNCTION pgp_sym_decrypt_verify(bytea, text, bytea, text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_sym_decrypt_verify_bytea(bytea, text, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_encrypt_sign(data, key, sigkey)
+ --
+ CREATE FUNCTION pgp_pub_encrypt_sign(text, bytea, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_pub_encrypt_sign_bytea(bytea, bytea, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_pub_encrypt_sign(data, key, sigkey, psw)
+ --
+ CREATE FUNCTION pgp_pub_encrypt_sign(text, bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_pub_encrypt_sign_bytea(bytea, bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_pub_encrypt_sign(data, key, sigkey, psw, args)
+ --
+ CREATE FUNCTION pgp_pub_encrypt_sign(text, bytea, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_pub_encrypt_sign_bytea(bytea, bytea, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_pub_decrypt_verify(data, key, sigkey)
+ --
+ CREATE FUNCTION pgp_pub_decrypt_verify(bytea, bytea, bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_verify_bytea(bytea, bytea, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_decrypt_verify(data, key, sigkey, psw)
+ --
+ CREATE FUNCTION pgp_pub_decrypt_verify(bytea, bytea, bytea, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_verify_bytea(bytea, bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_decrypt_verify(data, key, sigkey, psw, arg)
+ --
+ CREATE FUNCTION pgp_pub_decrypt_verify(bytea, bytea, bytea, text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_verify_bytea(bytea, bytea, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_main_key_id(key)
+ --
+ CREATE FUNCTION pgp_main_key_id(bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_main_key_id_w'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_sym_signature_keys(data, psw)
+ --
+ CREATE FUNCTION pgp_sym_signature_keys(bytea, text)
+ RETURNS TABLE (keyid text, digest text, pubkeyalgo text)
+ AS 'MODULE_PATHNAME', 'pgp_sym_signature_keys_w'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_signature_keys(data, key)
+ --
+ CREATE FUNCTION pgp_pub_signature_keys(bytea, bytea)
+ RETURNS TABLE (keyid text, digest text, pubkeyalgo text)
+ AS 'MODULE_PATHNAME', 'pgp_pub_signature_keys_w'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_signature_keys(data, key, psw)
+ --
+ CREATE FUNCTION pgp_pub_signature_keys(bytea, bytea, text)
+ RETURNS TABLE (keyid text, digest text, pubkeyalgo text)
+ AS 'MODULE_PATHNAME', 'pgp_pub_signature_keys_w'
+ LANGUAGE C IMMUTABLE STRICT;
*** /dev/null
--- b/contrib/pgcrypto/pgcrypto--1.2.sql
***************
*** 0 ****
--- 1,394 ----
+ /* contrib/pgcrypto/pgcrypto--1.2.sql */
+ 
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
+ \echo Use "CREATE EXTENSION pgcrypto" to load this file. \quit
+ 
+ CREATE FUNCTION digest(text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_digest'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION digest(bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_digest'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION hmac(text, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_hmac'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION hmac(bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_hmac'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION crypt(text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pg_crypt'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION gen_salt(text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pg_gen_salt'
+ LANGUAGE C VOLATILE STRICT;
+ 
+ CREATE FUNCTION gen_salt(text, int4)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pg_gen_salt_rounds'
+ LANGUAGE C VOLATILE STRICT;
+ 
+ CREATE FUNCTION encrypt(bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_encrypt'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION decrypt(bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_decrypt'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION encrypt_iv(bytea, bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_encrypt_iv'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION decrypt_iv(bytea, bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_decrypt_iv'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION gen_random_bytes(int4)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_random_bytes'
+ LANGUAGE C VOLATILE STRICT;
+ 
+ CREATE FUNCTION gen_random_uuid()
+ RETURNS uuid
+ AS 'MODULE_PATHNAME', 'pg_random_uuid'
+ LANGUAGE C VOLATILE;
+ 
+ --
+ -- pgp_sym_encrypt(data, key)
+ --
+ CREATE FUNCTION pgp_sym_encrypt(text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_sym_encrypt_bytea(bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_sym_encrypt(data, key, args)
+ --
+ CREATE FUNCTION pgp_sym_encrypt(text, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_sym_encrypt_bytea(bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_sym_encrypt_sign(data, key, sigkey)
+ --
+ CREATE FUNCTION pgp_sym_encrypt_sign(text, text, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_sym_encrypt_sign_bytea(bytea, text, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_sym_encrypt_sign(data, key, sigkey, psw)
+ --
+ CREATE FUNCTION pgp_sym_encrypt_sign(text, text, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_sym_encrypt_sign_bytea(bytea, text, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_sym_encrypt_sign(data, key, sigkey, psw, args)
+ --
+ CREATE FUNCTION pgp_sym_encrypt_sign(text, text, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_sym_encrypt_sign_bytea(bytea, text, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_sym_decrypt(data, key)
+ --
+ CREATE FUNCTION pgp_sym_decrypt(bytea, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_sym_decrypt_bytea(bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_sym_decrypt(data, key, args)
+ --
+ CREATE FUNCTION pgp_sym_decrypt(bytea, text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_sym_decrypt_bytea(bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_sym_decrypt_verify(data, key, sigkey)
+ --
+ CREATE FUNCTION pgp_sym_decrypt_verify(bytea, text, bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_sym_decrypt_verify_bytea(bytea, text, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_sym_decrypt_verify(data, key, sigkey, psw)
+ --
+ CREATE FUNCTION pgp_sym_decrypt_verify(bytea, text, bytea, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_sym_decrypt_verify_bytea(bytea, text, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_sym_decrypt_verify(data, key, sigkey, psw, args)
+ --
+ CREATE FUNCTION pgp_sym_decrypt_verify(bytea, text, bytea, text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_sym_decrypt_verify_bytea(bytea, text, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_encrypt(data, key)
+ --
+ CREATE FUNCTION pgp_pub_encrypt(text, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_pub_encrypt_bytea(bytea, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_pub_encrypt(data, key, args)
+ --
+ CREATE FUNCTION pgp_pub_encrypt(text, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_pub_encrypt_sign(data, key, sigkey)
+ --
+ CREATE FUNCTION pgp_pub_encrypt_sign(text, bytea, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_pub_encrypt_sign_bytea(bytea, bytea, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_pub_encrypt_sign(data, key, sigkey, psw)
+ --
+ CREATE FUNCTION pgp_pub_encrypt_sign(text, bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_pub_encrypt_sign_bytea(bytea, bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_pub_encrypt_sign(data, key, sigkey, psw, args)
+ --
+ CREATE FUNCTION pgp_pub_encrypt_sign(text, bytea, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_text'
+ LANGUAGE C STRICT;
+ 
+ CREATE FUNCTION pgp_pub_encrypt_sign_bytea(bytea, bytea, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_sign_bytea'
+ LANGUAGE C STRICT;
+ 
+ --
+ -- pgp_pub_decrypt(data, key)
+ --
+ CREATE FUNCTION pgp_pub_decrypt(bytea, bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_decrypt(data, key, psw)
+ --
+ CREATE FUNCTION pgp_pub_decrypt(bytea, bytea, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_decrypt(data, key, psw, arg)
+ --
+ CREATE FUNCTION pgp_pub_decrypt(bytea, bytea, text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_decrypt_verify(data, key, sigkey)
+ --
+ CREATE FUNCTION pgp_pub_decrypt_verify(bytea, bytea, bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_verify_bytea(bytea, bytea, bytea)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_decrypt_verify(data, key, sigkey, psw)
+ --
+ CREATE FUNCTION pgp_pub_decrypt_verify(bytea, bytea, bytea, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_verify_bytea(bytea, bytea, bytea, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_decrypt_verify(data, key, sigkey, psw, arg)
+ --
+ CREATE FUNCTION pgp_pub_decrypt_verify(bytea, bytea, bytea, text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_text'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_pub_decrypt_verify_bytea(bytea, bytea, bytea, text, text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_verify_bytea'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- PGP key ID
+ --
+ CREATE FUNCTION pgp_key_id(bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_key_id_w'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION pgp_main_key_id(bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_main_key_id_w'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ 
+ --
+ -- pgp_sym_signature_keys(data, psw)
+ --
+ CREATE FUNCTION pgp_sym_signature_keys(bytea, text)
+ RETURNS TABLE (keyid text, digest text, pubkeyalgo text)
+ AS 'MODULE_PATHNAME', 'pgp_sym_signature_keys_w'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_signature_keys(data, key)
+ --
+ CREATE FUNCTION pgp_pub_signature_keys(bytea, bytea)
+ RETURNS TABLE (keyid text, digest text, pubkeyalgo text)
+ AS 'MODULE_PATHNAME', 'pgp_pub_signature_keys_w'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- pgp_pub_signature_keys(data, key, psw)
+ --
+ CREATE FUNCTION pgp_pub_signature_keys(bytea, bytea, text)
+ RETURNS TABLE (keyid text, digest text, pubkeyalgo text)
+ AS 'MODULE_PATHNAME', 'pgp_pub_signature_keys_w'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ 
+ --
+ -- pgp armor
+ --
+ CREATE FUNCTION armor(bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pg_armor'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION dearmor(text)
+ RETURNS bytea
+ AS 'MODULE_PATHNAME', 'pg_dearmor'
+ LANGUAGE C IMMUTABLE STRICT;
*** a/contrib/pgcrypto/pgcrypto.control
--- b/contrib/pgcrypto/pgcrypto.control
***************
*** 1,5 ****
  # pgcrypto extension
  comment = 'cryptographic functions'
! default_version = '1.1'
  module_pathname = '$libdir/pgcrypto'
  relocatable = true
--- 1,5 ----
  # pgcrypto extension
  comment = 'cryptographic functions'
! default_version = '1.2'
  module_pathname = '$libdir/pgcrypto'
  relocatable = true
*** a/contrib/pgcrypto/pgp-decrypt.c
--- b/contrib/pgcrypto/pgp-decrypt.c
***************
*** 155,161 **** pgp_parse_pkt_hdr(PullFilter *src, uint8 *tag, int *len_p, int 
allow_ctx)
                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);
        }
--- 155,169 ----
                lentype = *p & 3;
                *tag = (*p >> 2) & 0x0F;
                if (lentype == 3)
!               {
!                       if (!allow_ctx)
!                       {
!                               px_debug("pgp_parse_pkt_hdr: lentype==3 but 
allow_context is false");
!                               res = PXE_PGP_CORRUPT_DATA;
!                       }
!                       else
!                               res = PKT_CONTEXT;
!               }
                else
                        res = parse_old_len(src, len_p, lentype);
        }
***************
*** 284,290 **** prefix_init(void **priv_p, void *arg, PullFilter *src)
        return 0;
  }
  
! static struct PullFilterOps prefix_filter = {
        prefix_init, NULL, NULL
  };
  
--- 292,298 ----
        return 0;
  }
  
! struct PullFilterOps pgp_prefix_filter = {
        prefix_init, NULL, NULL
  };
  
***************
*** 638,645 **** decrypt_key(PGP_Context *ctx, const uint8 *src, int len)
  /*
   * Handle key packet
   */
! static int
! parse_symenc_sesskey(PGP_Context *ctx, PullFilter *src)
  {
        uint8      *p;
        int                     res;
--- 646,653 ----
  /*
   * Handle key packet
   */
! int
! pgp_parse_symenc_sesskey(PGP_Context *ctx, PullFilter *src)
  {
        uint8      *p;
        int                     res;
***************
*** 805,810 **** parse_literal_data(PGP_Context *ctx, MBuf *dst, PullFilter 
*pkt)
--- 813,822 ----
  
        ctx->unicode_mode = (type == 'u') ? 1 : 0;
  
+       /* if verifying, a hashing context should have been set up for us */
+       if (ctx->sig_key && ctx->sig_digest_ctx == NULL)
+               return PXE_BUG;
+ 
        /* read data */
        while (1)
        {
***************
*** 812,817 **** parse_literal_data(PGP_Context *ctx, MBuf *dst, PullFilter 
*pkt)
--- 824,832 ----
                if (res <= 0)
                        break;
  
+               if (ctx->sig_digest_ctx)
+                       px_md_update(ctx->sig_digest_ctx, buf, res);
+ 
                if (ctx->text_mode && ctx->convert_crlf)
                        res = copy_crlf(dst, buf, res, &got_cr);
                else
***************
*** 869,874 **** parse_compressed_data(PGP_Context *ctx, MBuf *dst, PullFilter 
*pkt)
--- 884,973 ----
  }
  
  static int
+ parse_onepass_signature(PGP_Context *ctx, MBuf *dst, PullFilter *pkt)
+ {
+       int                             res;
+       PGP_Signature  *sig;
+ 
+       /* don't bother if we weren't asked to verify signatures */
+       if (!ctx->sig_key)
+               return pgp_skip_packet(pkt);
+ 
+       res = pgp_parse_onepass_signature(ctx, &sig, pkt);
+       if (res < 0)
+               return res;
+ 
+       if (memcmp(sig->keyid, ctx->sig_key->key_id, 8) == 0 &&
+               sig->algo == ctx->sig_key->algo &&
+               (sig->type == PGP_SIGTYP_BINARY ||
+                sig->type == PGP_SIGTYP_TEXT))
+       {
+               if (ctx->sig_onepass)
+                       res = PXE_PGP_MULTIPLE_SIGNATURES;
+               else if (ctx->sig_digest_ctx)
+                       res = PXE_BUG;
+               else
+                       res = pgp_load_digest(sig->digest_algo, 
&ctx->sig_digest_ctx);
+ 
+               if (res < 0)
+                       pgp_sig_free(sig);
+               else
+                       ctx->sig_onepass = sig;
+       }
+       else
+               res = pgp_sig_free(sig);
+       return res;
+ }
+ 
+ static int
+ parse_signature(PGP_Context *ctx, PullFilter *pkt)
+ {
+       int res;
+       PGP_Signature *sig;
+ 
+       /* don't bother if we weren't asked to verify signatures */
+       if (!ctx->sig_key)
+               return pgp_skip_packet(pkt);
+ 
+       res = pgp_parse_signature(ctx, &sig, pkt, ctx->sig_key->key_id);
+       if (res < 0)
+               return res;
+ 
+       if (memcmp(sig->keyid, ctx->sig_key->key_id, 8) == 0 &&
+               sig->algo == ctx->sig_key->algo &&
+               (sig->type == PGP_SIGTYP_BINARY ||
+                sig->type == PGP_SIGTYP_TEXT))
+       {
+               if (ctx->sig_expected)
+                       res = PXE_PGP_MULTIPLE_SIGNATURES;
+               else if (ctx->sig_onepass)
+               {
+                       if (ctx->sig_onepass->algo != sig->algo ||
+                               ctx->sig_onepass->digest_algo != 
sig->digest_algo)
+                               res = PXE_PGP_CONFLICTING_SIGNATURES;
+               }
+               else
+               {
+                       /* if there was no one-pass signature, load 
sig_digest_ctx now */
+                       if (ctx->sig_digest_ctx)
+                               res = PXE_BUG;
+                       else
+                               res = pgp_load_digest(sig->digest_algo, 
&ctx->sig_digest_ctx);
+               }
+ 
+               if (res < 0)
+                       pgp_sig_free(sig);
+               else
+                       ctx->sig_expected = sig;
+       }
+       else
+               pgp_sig_free(sig);
+ 
+       return res;
+ }
+ 
+ 
+ static int
  process_data_packets(PGP_Context *ctx, MBuf *dst, PullFilter *src,
                                         int allow_compr, int need_mdc)
  {
***************
*** 906,913 **** process_data_packets(PGP_Context *ctx, MBuf *dst, PullFilter 
*src,
                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)
--- 1005,1021 ----
                switch (tag)
                {
                        case PGP_PKT_LITERAL_DATA:
!                               if (ctx->sig_key && !ctx->sig_onepass && 
!ctx->sig_expected)
!                               {
!                                       px_debug("process_data_packets: no 
signature or one-pass "
!                                                        "signature before 
literal data");
!                                       res = PXE_PGP_NO_SIGNATURE;
!                               }
!                               else
!                               {
!                                       got_data = 1;
!                                       res = parse_literal_data(ctx, dst, pkt);
!                               }
                                break;
                        case PGP_PKT_COMPRESSED_DATA:
                                if (allow_compr == 0)
***************
*** 944,949 **** process_data_packets(PGP_Context *ctx, MBuf *dst, PullFilter 
*src,
--- 1052,1063 ----
                                if (res > 0)
                                        got_mdc = 1;
                                break;
+                       case PGP_PKT_ONEPASS_SIGNATURE:
+                               res = parse_onepass_signature(ctx, dst, pkt);
+                               break;
+                       case PGP_PKT_SIGNATURE:
+                               res = parse_signature(ctx, pkt);
+                               break;
                        default:
                                px_debug("process_data_packets: unexpected pkt 
tag=%d", tag);
                                res = PXE_PGP_CORRUPT_DATA;
***************
*** 992,998 **** parse_symenc_data(PGP_Context *ctx, PullFilter *pkt, MBuf *dst)
        if (res < 0)
                goto out;
  
!       res = pullf_create(&pf_prefix, &prefix_filter, ctx, pf_decrypt);
        if (res < 0)
                goto out;
  
--- 1106,1112 ----
        if (res < 0)
                goto out;
  
!       res = pullf_create(&pf_prefix, &pgp_prefix_filter, ctx, pf_decrypt);
        if (res < 0)
                goto out;
  
***************
*** 1039,1045 **** parse_symenc_mdc_data(PGP_Context *ctx, PullFilter *pkt, 
MBuf *dst)
        if (res < 0)
                goto out;
  
!       res = pullf_create(&pf_prefix, &prefix_filter, ctx, pf_mdc);
        if (res < 0)
                goto out;
  
--- 1153,1159 ----
        if (res < 0)
                goto out;
  
!       res = pullf_create(&pf_prefix, &pgp_prefix_filter, ctx, pf_mdc);
        if (res < 0)
                goto out;
  
***************
*** 1139,1145 **** pgp_decrypt(PGP_Context *ctx, MBuf *msrc, MBuf *mdst)
                                else
                                {
                                        got_key = 1;
!                                       res = parse_symenc_sesskey(ctx, pkt);
                                }
                                break;
                        case PGP_PKT_SYMENCRYPTED_DATA:
--- 1253,1259 ----
                                else
                                {
                                        got_key = 1;
!                                       res = pgp_parse_symenc_sesskey(ctx, 
pkt);
                                }
                                break;
                        case PGP_PKT_SYMENCRYPTED_DATA:
***************
*** 1187,1189 **** pgp_decrypt(PGP_Context *ctx, MBuf *msrc, MBuf *mdst)
--- 1301,1304 ----
  
        return res;
  }
+ 
*** a/contrib/pgcrypto/pgp-encrypt.c
--- b/contrib/pgcrypto/pgp-encrypt.c
***************
*** 144,149 **** static const PushFilterOps mdc_filter = {
--- 144,236 ----
        mdc_init, mdc_write, mdc_flush, mdc_free
  };
  
+ /*
+  * Signature writer filter
+  */
+ 
+ static int
+ sig_writer_flush(PushFilter *dst, void *priv)
+ {
+       int                             res;
+       int                             len;
+       PGP_Context    *ctx = priv;
+       MBuf               *buf = NULL;
+       PushFilter         *pf = NULL;
+       uint8              *data = NULL;
+ 
+       /*
+        * Capture all the data into an mbuf so we don't have to worry about the
+        * length of the packet.
+        */
+       buf = mbuf_create(0);
+       res = pushf_create_mbuf_writer(&pf, buf);
+       if (res < 0)
+               goto err;
+ 
+       res = pgp_write_signature(ctx, pf);
+       if (res < 0)
+               goto err;
+ 
+       len = mbuf_grab(buf, mbuf_avail(buf), &data);
+       res = write_normal_header(dst, PGP_PKT_SIGNATURE, len);
+       if (res < 0)
+               goto err;
+ 
+       res = pushf_write(dst, data, len);
+ 
+ err:
+       if (pf)
+               pushf_free(pf);
+       if (buf)
+               mbuf_free(buf);
+       return res;
+ }
+ 
+ static const PushFilterOps sig_writer_filter = {
+       NULL, NULL, sig_writer_flush, NULL
+ };
+ 
+ 
+ /*
+  * Signature computation filter
+  */
+ 
+ static int
+ sig_compute_init(PushFilter *dst, void *init_arg, void **priv_p)
+ {
+       int                             res;
+       PGP_Context        *ctx = init_arg;
+ 
+       res = pgp_load_digest(ctx->digest_algo, &ctx->sig_digest_ctx);
+       if (res < 0)
+               return res;
+ 
+       *priv_p = ctx->sig_digest_ctx;
+       return 0;
+ }
+ 
+ static int
+ sig_compute_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 void
+ sig_compute_free(void *priv)
+ {
+       PX_MD      *md = priv;
+ 
+       px_md_free(md);
+ }
+ 
+ static const PushFilterOps sig_compute_filter = {
+       sig_compute_init, sig_compute_write, NULL, sig_compute_free
+ };
+ 
+ 
  
  /*
   * Encrypted pkt writer
***************
*** 495,500 **** write_prefix(PGP_Context *ctx, PushFilter *dst)
--- 582,628 ----
  }
  
  /*
+  * Initializes one-pass signature state and writes the one-pass signature
+  * packet.  The packet contains enough information for the reader to decrypt
+  * and verify the signature in a single pass over the encrypted data.
+  */
+ static int
+ init_onepass_signature(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst)
+ {
+       int             res;
+       uint8   hdr[4];
+       uint8   ver = 3;
+       uint8   last = 1;
+ 
+       res = write_normal_header(dst, PGP_PKT_ONEPASS_SIGNATURE, 4 + 8 + 1);
+       if (res < 0)
+               return res;
+ 
+       hdr[0] = ver;
+ 
+       if (ctx->text_mode && ctx->convert_crlf)
+               hdr[1] = PGP_SIGTYP_TEXT;
+       else
+               hdr[1] = PGP_SIGTYP_BINARY;
+ 
+       hdr[2] = ctx->digest_algo;
+       hdr[3] = ctx->sig_key->algo;
+       res = pushf_write(dst, hdr, sizeof(hdr));
+       if (res < 0)
+               return res;
+ 
+       res = pushf_write(dst, ctx->sig_key->key_id, 8);
+       if (res < 0)
+               return res;
+       /* we only support one signature per message */
+       res = pushf_write(dst, &last, 1);
+       if (res < 0)
+               return res;
+       return pushf_create(pf_res, &sig_writer_filter, ctx, dst);
+ }
+ 
+ 
+ /*
   * write symmetrically encrypted session key packet
   */
  
***************
*** 678,689 **** pgp_encrypt(PGP_Context *ctx, MBuf *src, MBuf *dst)
--- 806,837 ----
                pf = pf_tmp;
        }
  
+       /* one-pass signature signature */
+       if (ctx->sig_key)
+       {
+               res = init_onepass_signature(&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;
  
+       /*
+        * If we're writing a signature, also add the signature computation 
filter
+        * right after the text mode canonicalization, if there is one.
+        */
+       if (ctx->sig_key)
+       {
+               res = pushf_create(&pf_tmp, &sig_compute_filter, ctx, pf);
+               if (res < 0)
+                       goto out;
+               pf = pf_tmp;
+       }
  
        /* text conversion? */
        if (ctx->text_mode && ctx->convert_crlf)
*** a/contrib/pgcrypto/pgp-info.c
--- b/contrib/pgcrypto/pgp-info.c
***************
*** 49,65 **** read_pubkey_keyid(PullFilter *pkt, uint8 *keyid_buf)
        if (res < 0)
                goto err;
  
-       /* is it encryption key */
        switch (pk->algo)
        {
                case PGP_PUB_ELG_ENCRYPT:
                case PGP_PUB_RSA_ENCRYPT:
                case PGP_PUB_RSA_ENCRYPT_SIGN:
                        memcpy(keyid_buf, pk->key_id, 8);
                        res = 1;
                        break;
                default:
!                       res = 0;
        }
  
  err:
--- 49,66 ----
        if (res < 0)
                goto err;
  
        switch (pk->algo)
        {
                case PGP_PUB_ELG_ENCRYPT:
                case PGP_PUB_RSA_ENCRYPT:
                case PGP_PUB_RSA_ENCRYPT_SIGN:
+               case PGP_PUB_RSA_SIGN:
+               case PGP_PUB_DSA_SIGN:
                        memcpy(keyid_buf, pk->key_id, 8);
                        res = 1;
                        break;
                default:
!                       res = PXE_PGP_UNSUPPORTED_PUBALGO;
        }
  
  err:
***************
*** 102,115 **** print_key(uint8 *keyid, char *dst)
        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;
--- 103,285 ----
        return 8 * 2;
  }
  
! typedef int (*sig_key_cb_type)(void *opaque, PGP_Signature *sig);
! 
! static int
! extract_signature_keys(PGP_Context *ctx, PullFilter *src, void *opaque,
!                                          sig_key_cb_type sig_key_cb, int 
allow_compr);
! 
! 
! static int
! read_signature_keys_from_compressed_data(PGP_Context *ctx, PullFilter *pkt,
!                                                                               
 void *opaque, sig_key_cb_type sig_key_cb)
! {
!       int res;
!       uint8 type;
!       PullFilter *pf_decompr;
! 
!       GETBYTE(pkt, type);
! 
!       ctx->compress_algo = type;
!       switch (type)
!       {
!               case PGP_COMPR_NONE:
!                       res = extract_signature_keys(ctx, pf_decompr, opaque, 
sig_key_cb, 0);
!                       break;
! 
!               case PGP_COMPR_ZIP:
!               case PGP_COMPR_ZLIB:
!                       res = pgp_decompress_filter(&pf_decompr, ctx, pkt);
!                       if (res >= 0)
!                       {
!                               res = extract_signature_keys(ctx, pf_decompr, 
opaque, sig_key_cb, 0);
!                               pullf_free(pf_decompr);
!                       }
!                       break;
! 
!               case PGP_COMPR_BZIP2:
!                       px_debug("read_signature_keys_from_compressed_data: 
bzip2 unsupported");
!                       res = PXE_PGP_UNSUPPORTED_COMPR;
!                       break;
! 
!               default:
!                       px_debug("read_signature_keys_from_compressed_data: 
unknown compr type");
!                       res = PXE_PGP_CORRUPT_DATA;
!       }
! 
!       return res;
! }
! 
! static int
! extract_signature_keys(PGP_Context *ctx, PullFilter *src, void *opaque,
!                                          sig_key_cb_type sig_key_cb, int 
allow_compr)
! {
!       int res;
!       PGP_Signature *sig = NULL;
!       PullFilter *pkt = NULL;
!       int len;
!       uint8 tag;
! 
!       while (1)
!       {
!               /*
!                * We don't need to care about the special handling for 
PKG_CONTEXT
!                * length in SYMENC_MDC packets because we skip over the data 
and never
!                * check the MDC.
!                */
!               res = pgp_parse_pkt_hdr(src, &tag, &len, 1);
!               if (res <= 0)
!                       break;
! 
!               res = pgp_create_pkt_reader(&pkt, src, len, res, NULL);
!               if (res < 0)
!                       break;
! 
!               switch (tag)
!               {
!                       case PGP_PKT_SIGNATURE:
!                               res = pgp_parse_signature(ctx, &sig, pkt, NULL);
!                               if (res >= 0)
!                                       res = sig_key_cb(opaque, sig);
!                               break;
!                       case PGP_PKT_COMPRESSED_DATA:
!                               if (!allow_compr)
!                               {
!                                       px_debug("extract_signature_keys: 
unexpected compression");
!                                       res = PXE_PGP_CORRUPT_DATA;
!                               }
!                               else
!                                       res = 
read_signature_keys_from_compressed_data(ctx, pkt, opaque, sig_key_cb);
!                               break;
!                       case PGP_PKT_ONEPASS_SIGNATURE:
!                       case PGP_PKT_LITERAL_DATA:
!                       case PGP_PKT_MDC:
!                       case PGP_PKT_TRUST:
!                               res = pgp_skip_packet(pkt);
!                               break;
!                       default:
!                               elog(WARNING, "broken tag %d", tag);
!                               res = PXE_PGP_CORRUPT_DATA;
!               }
! 
!               if (pkt)
!                       pullf_free(pkt);
!               pkt = NULL;
!               if (sig)
!                       pgp_sig_free(sig);
!               sig = NULL;
! 
!               if (res < 0)
!                       break;
!       }
! 
!       return res;
! }
! 
  
  /*
!  * Set up everything needed to decrypt the data and to extract out all the
!  * signatures.
   */
! static int
! read_signature_keys_from_data(PGP_Context *ctx, PullFilter *pkt, int tag,
!                                                         void *opaque, 
sig_key_cb_type sig_key_cb)
! {
!       int res;
!       PGP_CFB *cfb = NULL;
!       PullFilter *pf_decrypt = NULL;
!       PullFilter *pf_prefix = NULL;
!       PullFilter *pf_mdc = NULL;
!       int resync;
! 
!       if (tag == PGP_PKT_SYMENCRYPTED_DATA_MDC)
!       {
!               uint8 ver;
! 
!               GETBYTE(pkt, ver);
!               if (ver != 1)
!               {
!                       px_debug("extract_signature_keys: pkt ver != 1");
!                       return PXE_PGP_CORRUPT_DATA;
!               }
!               resync = 0;
!       }
!       else
!               resync = 1;
! 
!       res = pgp_cfb_create(&cfb, ctx->cipher_algo,
!                                                ctx->sess_key, 
ctx->sess_key_len, resync, 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, &pgp_prefix_filter, ctx, pf_decrypt);
!       if (res < 0)
!               goto out;
! 
!       res = extract_signature_keys(ctx, pf_prefix, opaque, sig_key_cb, 1);
! 
! 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;
! }
! 
! static int
! get_key_information(PGP_Context *ctx, MBuf *pgp_data, int want_main_key,
!                                       void *opaque,
!                                       int (*key_cb)(void *opaque, uint8 
keyid[8]),
!                                       sig_key_cb_type sig_key_cb)
  {
        int                     res;
        PullFilter *src;
***************
*** 122,127 **** pgp_get_keyid(MBuf *pgp_data, char *dst)
--- 292,298 ----
        int                     got_data = 0;
        uint8           keyid_buf[8];
        int                     got_main_key = 0;
+       PGP_Signature *sig = NULL;
  
  
        res = pullf_create_mbuf_reader(&src, pgp_data);
***************
*** 141,176 **** pgp_get_keyid(MBuf *pgp_data, char *dst)
                {
                        case PGP_PKT_SECRET_KEY:
                        case PGP_PKT_PUBLIC_KEY:
!                               /* main key is for signing, so ignore it */
!                               if (!got_main_key)
!                               {
!                                       got_main_key = 1;
!                                       res = pgp_skip_packet(pkt);
!                               }
!                               else
                                        res = PXE_PGP_MULTIPLE_KEYS;
                                break;
                        case PGP_PKT_SECRET_SUBKEY:
                        case PGP_PKT_PUBLIC_SUBKEY:
!                               res = read_pubkey_keyid(pkt, keyid_buf);
!                               if (res < 0)
!                                       break;
!                               if (res > 0)
!                                       got_pub_key++;
                                break;
                        case PGP_PKT_PUBENCRYPTED_SESSKEY:
                                got_pubenc_key++;
!                               res = read_pubenc_keyid(pkt, keyid_buf);
                                break;
                        case PGP_PKT_SYMENCRYPTED_DATA:
                        case PGP_PKT_SYMENCRYPTED_DATA_MDC:
!                               /* don't skip it, just stop */
                                got_data = 1;
                                break;
-                       case PGP_PKT_SYMENCRYPTED_SESSKEY:
-                               got_symenc_key++;
-                               /* fallthru */
                        case PGP_PKT_SIGNATURE:
                        case PGP_PKT_MARKER:
                        case PGP_PKT_TRUST:
                        case PGP_PKT_USER_ID:
--- 312,373 ----
                {
                        case PGP_PKT_SECRET_KEY:
                        case PGP_PKT_PUBLIC_KEY:
!                               if (got_main_key)
                                        res = PXE_PGP_MULTIPLE_KEYS;
+                               else
+                               {
+                                       got_main_key = 1;
+                                       if (want_main_key)
+                                               res = read_pubkey_keyid(pkt, 
keyid_buf);
+                                       else
+                                               res = pgp_skip_packet(pkt);
+                               }
                                break;
                        case PGP_PKT_SECRET_SUBKEY:
                        case PGP_PKT_PUBLIC_SUBKEY:
!                               if (want_main_key)
!                                       res = pgp_skip_packet(pkt);
!                               else
!                               {
!                                       res = read_pubkey_keyid(pkt, keyid_buf);
!                                       if (res > 0)
!                                               got_pub_key++;
!                               }
!                               break;
!                       case PGP_PKT_SYMENCRYPTED_SESSKEY:
!                               got_symenc_key++;
!                               if (sig_key_cb)
!                                       res = pgp_parse_symenc_sesskey(ctx, 
pkt);
!                               else
!                                       res = pgp_skip_packet(pkt);
                                break;
                        case PGP_PKT_PUBENCRYPTED_SESSKEY:
                                got_pubenc_key++;
!                               if (sig_key_cb)
!                                       res = pgp_parse_pubenc_sesskey(ctx, 
pkt);
!                               else
!                                       res = read_pubenc_keyid(pkt, keyid_buf);
                                break;
                        case PGP_PKT_SYMENCRYPTED_DATA:
                        case PGP_PKT_SYMENCRYPTED_DATA_MDC:
!                               /*
!                                * If there's a key callback, read all the keys 
from the
!                                * encrypted data.  Otherwise we're done.
!                                */
                                got_data = 1;
+                               if (sig_key_cb)
+                                       res = 
read_signature_keys_from_data(ctx, pkt, tag, opaque, sig_key_cb);
                                break;
                        case PGP_PKT_SIGNATURE:
+                               if (sig_key_cb)
+                               {
+                                       res = pgp_parse_signature(ctx, &sig, 
pkt, NULL);
+                                       if (res >= 0)
+                                               res = sig_key_cb(opaque, sig);
+                               }
+                               else
+                                       res = pgp_skip_packet(pkt);
+                               break;
                        case PGP_PKT_MARKER:
                        case PGP_PKT_TRUST:
                        case PGP_PKT_USER_ID:
***************
*** 185,190 **** pgp_get_keyid(MBuf *pgp_data, char *dst)
--- 382,390 ----
                if (pkt)
                        pullf_free(pkt);
                pkt = NULL;
+               if (sig)
+                       pgp_sig_free(sig);
+               sig = NULL;
  
                if (res < 0 || got_data)
                        break;
***************
*** 210,235 **** pgp_get_keyid(MBuf *pgp_data, char *dst)
        /*
         * 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;
  }
--- 410,497 ----
        /*
         * if still ok, look what we got
         */
!       if (res < 0)
!               return res;
! 
!       if (key_cb)
        {
!               if (want_main_key)
                {
!                       if (got_main_key)
!                               res = key_cb(opaque, keyid_buf);
                        else
!                               res = PXE_PGP_NO_SIGN_KEY;
                }
                else
!               {
!                       if (got_pubenc_key || got_pub_key)
!                               res = key_cb(opaque, keyid_buf);
!                       else if (got_symenc_key)
!                               res = key_cb(opaque, NULL);
!                       else
!                               res = PXE_PGP_NO_USABLE_KEY;
!               }
        }
  
        return res;
  }
+ 
+ static const uint8 any_key[] =
+ {0, 0, 0, 0, 0, 0, 0, 0};
+ 
+ static int
+ get_keyid_cb(void *opaque, uint8 keyid[8])
+ {
+       char *dst = (char *) opaque;
+       if (keyid == NULL)
+       {
+               memcpy(dst, "SYMKEY", 7);
+               return 6;
+       }
+       else if (memcmp(keyid, any_key, 8) == 0)
+       {
+               memcpy(dst, "ANYKEY", 7);
+               return 6;
+       }
+       else
+               return print_key(keyid, dst);
+ }
+ 
+ /*
+  * dst should have room for 17 bytes
+  */
+ int
+ pgp_get_keyid(int want_main_key, MBuf *pgp_data, char *dst)
+ {
+       return get_key_information(NULL, pgp_data, want_main_key, dst, 
get_keyid_cb, NULL);
+ }
+ 
+ struct GetSignatureKeyCtx
+ {
+       int (*cb)(void *opaque, PGP_Signature *sig, char *keyid);
+       void *opaque;
+ };
+ 
+ static int
+ get_signature_key_cb(void *opaque, PGP_Signature *sig)
+ {
+       char keyid[17];
+       struct GetSignatureKeyCtx *ctx = opaque;
+       if (memcmp(sig->keyid, any_key, 8) == 0)
+               memcpy(keyid, "ANYKEY", 7);
+       else
+               print_key(sig->keyid, keyid);
+       return ctx->cb(ctx->opaque, sig, keyid);
+ }
+ 
+ int
+ pgp_get_signature_keys(PGP_Context *ctx, MBuf *pgp_data, void *opaque,
+                                          int (*cb)(void *opaque, 
PGP_Signature *sig, char *keyid))
+ {
+       struct GetSignatureKeyCtx cbctx;
+ 
+       memset(&cbctx, 0, sizeof(cbctx));
+       cbctx.cb = cb;
+       cbctx.opaque = opaque;
+       return get_key_information(ctx, pgp_data, 0, &cbctx, NULL, 
get_signature_key_cb);
+ }
*** a/contrib/pgcrypto/pgp-pgsql.c
--- b/contrib/pgcrypto/pgp-pgsql.c
***************
*** 33,38 ****
--- 33,41 ----
  
  #include "mb/pg_wchar.h"
  #include "utils/builtins.h"
+ #include "funcapi.h"
+ #include "utils/memutils.h"
+ #include "miscadmin.h"
  
  #include "mbuf.h"
  #include "px.h"
***************
*** 43,57 ****
--- 46,71 ----
   */
  PG_FUNCTION_INFO_V1(pgp_sym_encrypt_bytea);
  PG_FUNCTION_INFO_V1(pgp_sym_encrypt_text);
+ PG_FUNCTION_INFO_V1(pgp_sym_encrypt_sign_bytea);
+ PG_FUNCTION_INFO_V1(pgp_sym_encrypt_sign_text);
  PG_FUNCTION_INFO_V1(pgp_sym_decrypt_bytea);
  PG_FUNCTION_INFO_V1(pgp_sym_decrypt_text);
+ PG_FUNCTION_INFO_V1(pgp_sym_decrypt_verify_bytea);
+ PG_FUNCTION_INFO_V1(pgp_sym_decrypt_verify_text);
  
  PG_FUNCTION_INFO_V1(pgp_pub_encrypt_bytea);
  PG_FUNCTION_INFO_V1(pgp_pub_encrypt_text);
+ PG_FUNCTION_INFO_V1(pgp_pub_encrypt_sign_bytea);
+ PG_FUNCTION_INFO_V1(pgp_pub_encrypt_sign_text);
  PG_FUNCTION_INFO_V1(pgp_pub_decrypt_bytea);
  PG_FUNCTION_INFO_V1(pgp_pub_decrypt_text);
+ PG_FUNCTION_INFO_V1(pgp_pub_decrypt_verify_bytea);
+ PG_FUNCTION_INFO_V1(pgp_pub_decrypt_verify_text);
  
  PG_FUNCTION_INFO_V1(pgp_key_id_w);
+ PG_FUNCTION_INFO_V1(pgp_main_key_id_w);
+ PG_FUNCTION_INFO_V1(pgp_sym_signature_keys_w);
+ PG_FUNCTION_INFO_V1(pgp_pub_signature_keys_w);
  
  PG_FUNCTION_INFO_V1(pg_armor);
  PG_FUNCTION_INFO_V1(pg_dearmor);
***************
*** 162,167 **** struct debug_expect
--- 176,182 ----
        int                     debug;
        int                     expect;
        int                     cipher_algo;
+       int                     digest_algo;
        int                     s2k_mode;
        int                     s2k_cipher_algo;
        int                     s2k_digest_algo;
***************
*** 177,182 **** fill_expect(struct debug_expect * ex, int text_mode)
--- 192,198 ----
        ex->debug = 0;
        ex->expect = 0;
        ex->cipher_algo = -1;
+       ex->digest_algo = -1;
        ex->s2k_mode = -1;
        ex->s2k_cipher_algo = -1;
        ex->s2k_digest_algo = -1;
***************
*** 199,204 **** static void
--- 215,221 ----
  check_expect(PGP_Context *ctx, struct debug_expect * ex)
  {
        EX_CHECK(cipher_algo);
+       EX_CHECK(digest_algo);
        EX_CHECK(s2k_mode);
        EX_CHECK(s2k_digest_algo);
        EX_CHECK(use_sess_key);
***************
*** 223,228 **** set_arg(PGP_Context *ctx, char *key, char *val,
--- 240,247 ----
  
        if (strcmp(key, "cipher-algo") == 0)
                res = pgp_set_cipher_algo(ctx, val);
+       else if (strcmp(key, "digest-algo") == 0)
+               res = pgp_set_digest_algo(ctx, val);
        else if (strcmp(key, "disable-mdc") == 0)
                res = pgp_disable_mdc(ctx, atoi(val));
        else if (strcmp(key, "sess-key") == 0)
***************
*** 249,254 **** set_arg(PGP_Context *ctx, char *key, char *val,
--- 268,278 ----
                ex->expect = 1;
                ex->cipher_algo = pgp_get_cipher_code(val);
        }
+       else if (ex != NULL && strcmp(key, "expect-digest-algo") == 0)
+       {
+               ex->expect = 1;
+               ex->digest_algo = pgp_get_digest_code(val);
+       }
        else if (ex != NULL && strcmp(key, "expect-disable-mdc") == 0)
        {
                ex->expect = 1;
***************
*** 414,420 **** init_work(PGP_Context **ctx_p, int is_text,
  
  static bytea *
  encrypt_internal(int is_pubenc, int is_text,
!                                text *data, text *key, text *args)
  {
        MBuf       *src,
                           *dst;
--- 438,445 ----
  
  static bytea *
  encrypt_internal(int is_pubenc, int is_text,
!                                text *data, text *key, text *sigkey,
!                                text *keypsw, text *args)
  {
        MBuf       *src,
                           *dst;
***************
*** 459,480 **** encrypt_internal(int is_pubenc, int is_text,
                MBuf       *kbuf = create_mbuf_from_vardata(key);
  
                err = pgp_set_pubkey(ctx, kbuf,
!                                                        NULL, 0, 0);
                mbuf_free(kbuf);
        }
        else
                err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key),
                                                         VARSIZE(key) - 
VARHDRSZ);
  
        /*
         * encrypt
         */
!       if (err >= 0)
!               err = pgp_encrypt(ctx, src, dst);
  
        /*
         * check for error
         */
        if (err)
        {
                if (ex.debug)
--- 484,529 ----
                MBuf       *kbuf = create_mbuf_from_vardata(key);
  
                err = pgp_set_pubkey(ctx, kbuf,
!                                                        NULL, 0, 0, 1);
                mbuf_free(kbuf);
+               if (err < 0)
+                       goto out;
        }
        else
+       {
                err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key),
                                                         VARSIZE(key) - 
VARHDRSZ);
+               if (err < 0)
+                       goto out;
+       }
+ 
+       if (sigkey)
+       {
+               uint8      *psw = NULL;
+               int                     psw_len = 0;
+               MBuf       *kbuf;
+ 
+               if (keypsw)
+               {
+                       psw = (uint8 *) VARDATA(keypsw);
+                       psw_len = VARSIZE(keypsw) - VARHDRSZ;
+               }
+               kbuf = create_mbuf_from_vardata(sigkey);
+               err = pgp_set_sigkey(ctx, kbuf, psw, psw_len, 1, 0);
+               mbuf_free(kbuf);
+               if (err < 0)
+                       goto out;
+       }
  
        /*
         * encrypt
         */
!       err = pgp_encrypt(ctx, src, dst);
  
        /*
         * check for error
         */
+ out:
        if (err)
        {
                if (ex.debug)
***************
*** 507,513 **** encrypt_internal(int is_pubenc, int is_text,
  
  static bytea *
  decrypt_internal(int is_pubenc, int need_text, text *data,
!                                text *key, text *keypsw, text *args)
  {
        int                     err;
        MBuf       *src = NULL,
--- 556,562 ----
  
  static bytea *
  decrypt_internal(int is_pubenc, int need_text, text *data,
!                                text *key, text *sigkey, text *keypsw, text 
*args)
  {
        int                     err;
        MBuf       *src = NULL,
***************
*** 547,571 **** decrypt_internal(int is_pubenc, int need_text, text *data,
                        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, (uint8 *) 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);
  
--- 596,642 ----
                        psw_len = VARSIZE(keypsw) - VARHDRSZ;
                }
                kbuf = create_mbuf_from_vardata(key);
!               err = pgp_set_pubkey(ctx, kbuf, psw, psw_len, 1, 1);
                mbuf_free(kbuf);
+ 
+               if (err < 0)
+                       goto out;
        }
        else
+       {
                err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key),
                                                         VARSIZE(key) - 
VARHDRSZ);
+               if (err < 0)
+                       goto out;
+       }
+ 
+       if (sigkey)
+       {
+               MBuf       *kbuf;
+ 
+               kbuf = create_mbuf_from_vardata(sigkey);
+               err = pgp_set_sigkey(ctx, kbuf, NULL, 0, 0, 0);
+               mbuf_free(kbuf);
+ 
+               if (err < 0)
+                       goto out;
+       }
+ 
  
        /*
         * decrypt
         */
!       err = pgp_decrypt(ctx, src, dst);
        if (err < 0)
                goto out;
  
+       if (ctx->sig_key)
+       {
+               err = pgp_verify_signature(ctx);
+               if (err < 0)
+                       goto out;
+       }
+ 
        if (ex.expect)
                check_expect(ctx, &ex);
  
***************
*** 615,620 **** out:
--- 686,826 ----
        return res;
  }
  
+ struct MaterializeSignatureKeyCtx
+ {
+       Tuplestorestate *tupstore;
+       TupleDesc tupdesc;
+ };
+ 
+ static int
+ materialize_signature_key_cb(void *opaque, PGP_Signature *sig, char *keyid)
+ {
+       struct MaterializeSignatureKeyCtx *ctx = opaque;
+       const char *digestalgo;
+       const char *pubkeyalgo;
+       Datum           values[3];
+       bool            isnull[3] = { false, false, false };
+ 
+       digestalgo = pgp_get_digest_name(sig->digest_algo);
+       if (!digestalgo)
+               return PXE_PGP_UNSUPPORTED_HASH;
+ 
+       switch (sig->algo)
+       {
+               case PGP_PUB_RSA_ENCRYPT_SIGN:
+               case PGP_PUB_RSA_SIGN:
+                       pubkeyalgo = "rsa";
+                       break;
+               case PGP_PUB_DSA_SIGN:
+                       pubkeyalgo = "dsa";
+                       break;
+               default:
+                       return PXE_PGP_UNSUPPORTED_PUBALGO;
+       }
+ 
+       values[0] = CStringGetTextDatum(keyid);
+       values[1] = CStringGetTextDatum(digestalgo);
+       values[2] = CStringGetTextDatum(pubkeyalgo);
+       tuplestore_putvalues(ctx->tupstore, ctx->tupdesc, values, isnull);
+       return 0;
+ }
+ 
+ static int
+ materialize_signature_keys(int is_pubenc, text *data, text *key, text *keypsw,
+                                                  Tuplestorestate *tupstore, 
TupleDesc tupdesc)
+ {
+       PGP_Context        *ctx = NULL;
+       MBuf               *src = NULL;
+       int                             err;
+       struct debug_expect ex;
+       struct MaterializeSignatureKeyCtx cbctx;
+ 
+       init_work(&ctx, 0, NULL, &ex);
+       if (is_pubenc)
+       {
+               MBuf *kbuf = create_mbuf_from_vardata(key);
+ 
+               err = pgp_set_pubkey(ctx, kbuf, NULL, 0, 1, 1);
+               mbuf_free(kbuf);
+               if (err < 0)
+                       goto out;
+       }
+       else
+       {
+               err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key),
+                                                        VARSIZE(key) - 
VARHDRSZ);
+               if (err < 0)
+                       goto out;
+       }
+ 
+       memset(&cbctx, 0, sizeof(cbctx));
+       cbctx.tupstore = tupstore;
+       cbctx.tupdesc = tupdesc;
+ 
+       src = create_mbuf_from_vardata(data);
+       err = pgp_get_signature_keys(ctx, src, &cbctx, 
materialize_signature_key_cb);
+ 
+ out:
+       if (src)
+               mbuf_free(src);
+       if (ctx)
+               pgp_free(ctx);
+       if (err < 0)
+       {
+               if (ex.debug)
+                       px_set_debug_handler(NULL);
+               ereport(ERROR,
+                               
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+                                errmsg("%s", px_strerror(err))));
+       }
+       return 0;
+ }
+ 
+ static int
+ signature_keys_internal(int is_pubenc, text *data, text *key, text *keypsw,
+                                               ReturnSetInfo *rsinfo)
+ {
+       MemoryContext   oldcxt;
+       int                             res;
+       Tuplestorestate *tupstore;
+       TupleDesc               tupdesc;
+ 
+       /* check to see if caller supports us returning a tuplestore */
+       if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                               errmsg("set-valued function called in context 
that cannot accept a set")));
+       if (!(rsinfo->allowedModes & SFRM_Materialize))
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("materialize mode required, but it is 
not " \
+                                               "allowed in this context")));
+ 
+       /* switch to long-lived memory context */
+       oldcxt = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
+ 
+       /* get the requested return tuple description */
+       tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+       if (tupdesc->natts != 3)
+               elog(ERROR, "unexpected natts %d", tupdesc->natts);
+ 
+       tupstore =
+               tuplestore_begin_heap(rsinfo->allowedModes & 
SFRM_Materialize_Random,
+                                                         false, work_mem);
+ 
+       res = materialize_signature_keys(is_pubenc, data, key, keypsw, 
tupstore, tupdesc);
+       if (res < 0)
+               return PXE_BUG;
+ 
+       MemoryContextSwitchTo(oldcxt);
+ 
+       rsinfo->returnMode = SFRM_Materialize;
+       rsinfo->setResult = tupstore;
+       rsinfo->setDesc = tupdesc;
+ 
+       return 0;
+       }
+ 
  /*
   * Wrappers for symmetric-key functions
   */
***************
*** 631,637 **** pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS)
        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);
--- 837,843 ----
        if (PG_NARGS() > 2)
                arg = PG_GETARG_BYTEA_P(2);
  
!       res = encrypt_internal(0, 0, data, key, NULL, NULL, arg);
  
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
***************
*** 653,659 **** pgp_sym_encrypt_text(PG_FUNCTION_ARGS)
        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);
--- 859,865 ----
        if (PG_NARGS() > 2)
                arg = PG_GETARG_BYTEA_P(2);
  
!       res = encrypt_internal(0, 1, data, key, NULL, NULL, arg);
  
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
***************
*** 662,667 **** pgp_sym_encrypt_text(PG_FUNCTION_ARGS)
--- 868,933 ----
        PG_RETURN_TEXT_P(res);
  }
  
+ Datum
+ pgp_sym_encrypt_sign_bytea(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key,
+                          *sigkey;
+       text       *psw = NULL,
+                          *arg = NULL;
+       text       *res;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       sigkey = PG_GETARG_BYTEA_P(2);
+       if (PG_NARGS() > 3)
+               psw = PG_GETARG_BYTEA_P(3);
+       if (PG_NARGS() > 4)
+               arg = PG_GETARG_BYTEA_P(4);
+ 
+       res = encrypt_internal(0, 0, data, key, sigkey, psw, arg);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       PG_FREE_IF_COPY(sigkey, 2);
+       if (PG_NARGS() > 3)
+               PG_FREE_IF_COPY(psw, 3);
+       if (PG_NARGS() > 4)
+               PG_FREE_IF_COPY(arg, 4);
+       PG_RETURN_TEXT_P(res);
+ }
+ 
+ Datum
+ pgp_sym_encrypt_sign_text(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key,
+                          *sigkey;
+       text       *psw = NULL,
+                          *arg = NULL;
+       text       *res;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       sigkey = PG_GETARG_BYTEA_P(2);
+       if (PG_NARGS() > 3)
+               psw = PG_GETARG_BYTEA_P(3);
+       if (PG_NARGS() > 4)
+               arg = PG_GETARG_BYTEA_P(4);
+ 
+       res = encrypt_internal(0, 1, data, key, sigkey, psw, arg);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       PG_FREE_IF_COPY(sigkey, 2);
+       if (PG_NARGS() > 3)
+               PG_FREE_IF_COPY(psw, 3);
+       if (PG_NARGS() > 4)
+               PG_FREE_IF_COPY(arg, 4);
+       PG_RETURN_TEXT_P(res);
+ }
+ 
  
  Datum
  pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS)
***************
*** 676,682 **** pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS)
        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);
--- 942,948 ----
        if (PG_NARGS() > 2)
                arg = PG_GETARG_BYTEA_P(2);
  
!       res = decrypt_internal(0, 0, data, key, NULL, NULL, arg);
  
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
***************
*** 698,704 **** pgp_sym_decrypt_text(PG_FUNCTION_ARGS)
        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);
--- 964,970 ----
        if (PG_NARGS() > 2)
                arg = PG_GETARG_BYTEA_P(2);
  
!       res = decrypt_internal(0, 1, data, key, NULL, NULL, arg);
  
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
***************
*** 707,712 **** pgp_sym_decrypt_text(PG_FUNCTION_ARGS)
--- 973,1038 ----
        PG_RETURN_TEXT_P(res);
  }
  
+ Datum
+ pgp_sym_decrypt_verify_bytea(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key,
+                          *sigkey;
+       text       *psw = NULL,
+                          *arg = NULL;
+       text       *res;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       sigkey = PG_GETARG_BYTEA_P(2);
+       if (PG_NARGS() > 3)
+               psw = PG_GETARG_BYTEA_P(3);
+       if (PG_NARGS() > 4)
+               arg = PG_GETARG_BYTEA_P(4);
+ 
+       res = decrypt_internal(0, 0, data, key, sigkey, psw, arg);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       PG_FREE_IF_COPY(sigkey, 2);
+       if (PG_NARGS() > 3)
+               PG_FREE_IF_COPY(arg, 3);
+       if (PG_NARGS() > 4)
+               PG_FREE_IF_COPY(arg, 4);
+       PG_RETURN_TEXT_P(res);
+ }
+ 
+ Datum
+ pgp_sym_decrypt_verify_text(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key,
+                          *sigkey;
+       text       *psw = NULL,
+                          *arg = NULL;
+       text       *res;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       sigkey = PG_GETARG_BYTEA_P(2);
+       if (PG_NARGS() > 3)
+               psw = PG_GETARG_BYTEA_P(3);
+       if (PG_NARGS() > 4)
+               arg = PG_GETARG_BYTEA_P(4);
+ 
+       res = decrypt_internal(0, 1, data, key, sigkey, psw, arg);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       PG_FREE_IF_COPY(sigkey, 2);
+       if (PG_NARGS() > 3)
+               PG_FREE_IF_COPY(arg, 3);
+       if (PG_NARGS() > 4)
+               PG_FREE_IF_COPY(arg, 4);
+       PG_RETURN_TEXT_P(res);
+ }
+ 
  /*
   * Wrappers for public-key functions
   */
***************
*** 724,730 **** pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS)
        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);
--- 1050,1056 ----
        if (PG_NARGS() > 2)
                arg = PG_GETARG_BYTEA_P(2);
  
!       res = encrypt_internal(1, 0, data, key, NULL, NULL, arg);
  
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
***************
*** 746,752 **** pgp_pub_encrypt_text(PG_FUNCTION_ARGS)
        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);
--- 1072,1078 ----
        if (PG_NARGS() > 2)
                arg = PG_GETARG_BYTEA_P(2);
  
!       res = encrypt_internal(1, 1, data, key, NULL, NULL, arg);
  
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
***************
*** 755,760 **** pgp_pub_encrypt_text(PG_FUNCTION_ARGS)
--- 1081,1145 ----
        PG_RETURN_TEXT_P(res);
  }
  
+ Datum
+ pgp_pub_encrypt_sign_bytea(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key,
+                          *sigkey;
+       text       *psw = NULL,
+                          *arg = NULL;
+       text       *res;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       sigkey = PG_GETARG_BYTEA_P(2);
+       if (PG_NARGS() > 3)
+               psw = PG_GETARG_BYTEA_P(3);
+       if (PG_NARGS() > 4)
+               arg = PG_GETARG_BYTEA_P(4);
+ 
+       res = encrypt_internal(1, 0, data, key, sigkey, psw, arg);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       PG_FREE_IF_COPY(sigkey, 2);
+       if (PG_NARGS() > 3)
+               PG_FREE_IF_COPY(psw, 3);
+       if (PG_NARGS() > 4)
+               PG_FREE_IF_COPY(arg, 4);
+       PG_RETURN_TEXT_P(res);
+ }
+ 
+ Datum
+ pgp_pub_encrypt_sign_text(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key,
+                          *sigkey;
+       text       *psw = NULL,
+                          *arg = NULL;
+       text       *res;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       sigkey = PG_GETARG_BYTEA_P(2);
+       if (PG_NARGS() > 3)
+               psw = PG_GETARG_BYTEA_P(3);
+       if (PG_NARGS() > 4)
+               arg = PG_GETARG_BYTEA_P(4);
+ 
+       res = encrypt_internal(1, 1, data, key, sigkey, psw, arg);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       PG_FREE_IF_COPY(sigkey, 2);
+       if (PG_NARGS() > 3)
+               PG_FREE_IF_COPY(psw, 3);
+       if (PG_NARGS() > 4)
+               PG_FREE_IF_COPY(arg, 4);
+       PG_RETURN_TEXT_P(res);
+ }
  
  Datum
  pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS)
***************
*** 772,778 **** pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS)
        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);
--- 1157,1163 ----
        if (PG_NARGS() > 3)
                arg = PG_GETARG_BYTEA_P(3);
  
!       res = decrypt_internal(1, 0, data, key, NULL, psw, arg);
  
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
***************
*** 799,805 **** pgp_pub_decrypt_text(PG_FUNCTION_ARGS)
        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);
--- 1184,1190 ----
        if (PG_NARGS() > 3)
                arg = PG_GETARG_BYTEA_P(3);
  
!       res = decrypt_internal(1, 1, data, key, NULL, psw, arg);
  
        PG_FREE_IF_COPY(data, 0);
        PG_FREE_IF_COPY(key, 1);
***************
*** 810,815 **** pgp_pub_decrypt_text(PG_FUNCTION_ARGS)
--- 1195,1259 ----
        PG_RETURN_TEXT_P(res);
  }
  
+ Datum
+ pgp_pub_decrypt_verify_bytea(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key,
+                          *sigkey;
+       text       *psw = NULL,
+                          *arg = NULL;
+       text       *res;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       sigkey = PG_GETARG_BYTEA_P(2);
+       if (PG_NARGS() > 3)
+               psw = PG_GETARG_BYTEA_P(3);
+       if (PG_NARGS() > 4)
+               arg = PG_GETARG_BYTEA_P(4);
+ 
+       res = decrypt_internal(1, 0, data, key, sigkey, psw, arg);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       PG_FREE_IF_COPY(sigkey, 2);
+       if (PG_NARGS() > 3)
+               PG_FREE_IF_COPY(psw, 3);
+       if (PG_NARGS() > 4)
+               PG_FREE_IF_COPY(arg, 4);
+       PG_RETURN_TEXT_P(res);
+ }
+ 
+ Datum
+ pgp_pub_decrypt_verify_text(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key,
+                          *sigkey;
+       text       *psw = NULL,
+                          *arg = NULL;
+       text       *res;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       sigkey = PG_GETARG_BYTEA_P(2);
+       if (PG_NARGS() > 3)
+               psw = PG_GETARG_BYTEA_P(3);
+       if (PG_NARGS() > 4)
+               arg = PG_GETARG_BYTEA_P(4);
+ 
+       res = decrypt_internal(1, 1, data, key, sigkey, psw, arg);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       PG_FREE_IF_COPY(sigkey, 2);
+       if (PG_NARGS() > 3)
+               PG_FREE_IF_COPY(psw, 3);
+       if (PG_NARGS() > 4)
+               PG_FREE_IF_COPY(arg, 4);
+       PG_RETURN_TEXT_P(res);
+ }
  
  /*
   * Wrappers for PGP ascii armor
***************
*** 874,880 **** pg_dearmor(PG_FUNCTION_ARGS)
  }
  
  /*
!  * Wrappers for PGP key id
   */
  
  Datum
--- 1318,1324 ----
  }
  
  /*
!  * Wrappers for PGP key ids
   */
  
  Datum
***************
*** 889,895 **** pgp_key_id_w(PG_FUNCTION_ARGS)
        buf = create_mbuf_from_vardata(data);
        res = palloc(VARHDRSZ + 17);
  
!       res_len = pgp_get_keyid(buf, VARDATA(res));
        mbuf_free(buf);
        if (res_len < 0)
                ereport(ERROR,
--- 1333,1339 ----
        buf = create_mbuf_from_vardata(data);
        res = palloc(VARHDRSZ + 17);
  
!       res_len = pgp_get_keyid(0, buf, VARDATA(res));
        mbuf_free(buf);
        if (res_len < 0)
                ereport(ERROR,
***************
*** 900,902 **** pgp_key_id_w(PG_FUNCTION_ARGS)
--- 1344,1423 ----
        PG_FREE_IF_COPY(data, 0);
        PG_RETURN_TEXT_P(res);
  }
+ 
+ Datum
+ pgp_main_key_id_w(PG_FUNCTION_ARGS)
+ {
+       bytea      *data;
+       text       *res;
+       int                     res_len;
+       MBuf       *buf;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       buf = create_mbuf_from_vardata(data);
+       res = palloc(VARHDRSZ + 17);
+ 
+       res_len = pgp_get_keyid(1, buf, VARDATA(res));
+       mbuf_free(buf);
+       if (res_len < 0)
+               ereport(ERROR,
+                               
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+                                errmsg("%s", px_strerror(res_len))));
+       SET_VARSIZE(res, VARHDRSZ + res_len);
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_RETURN_TEXT_P(res);
+ }
+ 
+ 
+ Datum
+ pgp_sym_signature_keys_w(PG_FUNCTION_ARGS)
+ {
+       bytea      *data;
+       text       *psw;
+       int err;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       psw = PG_GETARG_TEXT_P(1);
+ 
+       err = signature_keys_internal(0, data, psw, NULL,
+                                                                 
(ReturnSetInfo *) fcinfo->resultinfo);
+ 
+       if (err < 0)
+               ereport(ERROR,
+                               
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+                                errmsg("%s", px_strerror(err))));
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(psw, 1);
+       return (Datum) 0;
+ }
+ 
+ Datum
+ pgp_pub_signature_keys_w(PG_FUNCTION_ARGS)
+ {
+       bytea      *data,
+                          *key;
+       text       *keypsw = NULL;
+       int err;
+ 
+       data = PG_GETARG_BYTEA_P(0);
+       key = PG_GETARG_BYTEA_P(1);
+       if (PG_NARGS() > 2)
+               keypsw = PG_GETARG_BYTEA_P(2);
+ 
+       err = signature_keys_internal(1, data, key, keypsw,
+                                                                 
(ReturnSetInfo *) fcinfo->resultinfo);
+ 
+       if (err < 0)
+               ereport(ERROR,
+                               
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+                                errmsg("%s", px_strerror(err))));
+ 
+       PG_FREE_IF_COPY(data, 0);
+       PG_FREE_IF_COPY(key, 1);
+       if (PG_NARGS() > 2)
+               PG_FREE_IF_COPY(keypsw, 2);
+ 
+       return (Datum) 0;
+ }
*** a/contrib/pgcrypto/pgp-pubdec.c
--- b/contrib/pgcrypto/pgp-pubdec.c
***************
*** 161,167 **** pgp_parse_pubenc_sesskey(PGP_Context *ctx, PullFilter *pkt)
        pk = ctx->pub_key;
        if (pk == NULL)
        {
!               px_debug("no pubkey?");
                return PXE_BUG;
        }
  
--- 161,167 ----
        pk = ctx->pub_key;
        if (pk == NULL)
        {
!               px_debug("pgp_parse_pubenc_sesskey: no pubkey?");
                return PXE_BUG;
        }
  
*** a/contrib/pgcrypto/pgp-pubenc.c
--- b/contrib/pgcrypto/pgp-pubenc.c
***************
*** 29,38 ****
--- 29,42 ----
   * contrib/pgcrypto/pgp-pubenc.c
   */
  #include "postgres.h"
+ #include "c.h"
  
  #include "px.h"
  #include "pgp.h"
  
+ #define HASHED_SUBPKT_LENGTH          8
+ #define SIGNATURE_PKT_HEADER_LENGTH 4
+ 
  /*
   * padded msg: 02 || non-zero pad bytes || 00 || msg
   */
***************
*** 202,208 **** pgp_write_pubenc_sesskey(PGP_Context *ctx, PushFilter *dst)
  
        if (pk == NULL)
        {
!               px_debug("no pubkey?\n");
                return PXE_BUG;
        }
  
--- 206,212 ----
  
        if (pk == NULL)
        {
!               px_debug("pgp_write_pubenc_sesskey: no pubkey?\n");
                return PXE_BUG;
        }
  
*** a/contrib/pgcrypto/pgp-pubkey.c
--- b/contrib/pgcrypto/pgp-pubkey.c
***************
*** 457,476 **** process_secret_key(PullFilter *pkt, PGP_PubKey **pk_p,
  
  static int
  internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
!                                 const uint8 *psw, int psw_len, int pubtype)
  {
        PullFilter *pkt = NULL;
        int                     res;
        uint8           tag;
        int                     len;
        PGP_PubKey *enc_key = NULL;
        PGP_PubKey *pk = NULL;
        int                     got_main_key = 0;
  
        /*
!        * Search for encryption key.
!        *
!        * Error out on anything fancy.
         */
        while (1)
        {
--- 457,479 ----
  
  static int
  internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
!                                 const uint8 *psw, int psw_len, int pubtype,
!                                 int want_encrypt)
  {
        PullFilter *pkt = NULL;
        int                     res;
        uint8           tag;
        int                     len;
        PGP_PubKey *enc_key = NULL;
+       PGP_PubKey *sig_key = NULL;
        PGP_PubKey *pk = NULL;
        int                     got_main_key = 0;
  
        /*
!        * Find the key to use for encryption, decryption, signing or verifying
!        * from src, and place it into *pk_p.  An error is returned if the input
!        * has multiple main keys or if asked for an encryption key and there 
are
!        * multiple subkeys capable of encryption.
         */
        while (1)
        {
***************
*** 485,511 **** internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
                {
                        case PGP_PKT_PUBLIC_KEY:
                        case PGP_PKT_SECRET_KEY:
!                               if (got_main_key)
                                {
!                                       res = PXE_PGP_MULTIPLE_KEYS;
!                                       break;
                                }
-                               got_main_key = 1;
-                               res = pgp_skip_packet(pkt);
                                break;
  
                        case PGP_PKT_PUBLIC_SUBKEY:
!                               if (pubtype != 0)
!                                       res = PXE_PGP_EXPECT_SECRET_KEY;
                                else
!                                       res = _pgp_read_public_key(pkt, &pk);
                                break;
  
                        case PGP_PKT_SECRET_SUBKEY:
!                               if (pubtype != 1)
!                                       res = PXE_PGP_EXPECT_PUBLIC_KEY;
                                else
!                                       res = process_secret_key(pkt, &pk, psw, 
psw_len);
                                break;
  
                        case PGP_PKT_SIGNATURE:
--- 488,541 ----
                {
                        case PGP_PKT_PUBLIC_KEY:
                        case PGP_PKT_SECRET_KEY:
!                               if (want_encrypt)
                                {
!                                       if (got_main_key)
!                                       {
!                                               res = PXE_PGP_MULTIPLE_KEYS;
!                                               break;
!                                       }
!                                       got_main_key = 1;
!                                       res = pgp_skip_packet(pkt);
!                               }
!                               else if (tag == PGP_PKT_PUBLIC_KEY)
!                               {
!                                       if (pubtype != 0)
!                                               res = PXE_PGP_EXPECT_SECRET_KEY;
!                                       else
!                                               res = _pgp_read_public_key(pkt, 
&pk);
!                               }
!                               else
!                               {
!                                       if (pubtype != 1)
!                                               res = PXE_PGP_EXPECT_PUBLIC_KEY;
!                                       else
!                                               res = process_secret_key(pkt, 
&pk, psw, psw_len);
                                }
                                break;
  
                        case PGP_PKT_PUBLIC_SUBKEY:
!                               if (want_encrypt)
!                               {
!                                       if (pubtype != 0)
!                                               res = PXE_PGP_EXPECT_SECRET_KEY;
!                                       else
!                                               res = _pgp_read_public_key(pkt, 
&pk);
!                               }
                                else
!                                       res = pgp_skip_packet(pkt);
                                break;
  
                        case PGP_PKT_SECRET_SUBKEY:
!                               if (want_encrypt)
!                               {
!                                       if (pubtype != 1)
!                                               res = PXE_PGP_EXPECT_PUBLIC_KEY;
!                                       else
!                                               res = process_secret_key(pkt, 
&pk, psw, psw_len);
!                               }
                                else
!                                       res = pgp_skip_packet(pkt);
                                break;
  
                        case PGP_PKT_SIGNATURE:
***************
*** 525,531 **** internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
  
                if (pk != NULL)
                {
!                       if (res >= 0 && pk->can_encrypt)
                        {
                                if (enc_key == NULL)
                                {
--- 555,561 ----
  
                if (pk != NULL)
                {
!                       if (res >= 0 && want_encrypt && pk->can_encrypt)
                        {
                                if (enc_key == NULL)
                                {
***************
*** 535,540 **** internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
--- 565,580 ----
                                else
                                        res = PXE_PGP_MULTIPLE_SUBKEYS;
                        }
+                       else if (res >= 0 && !want_encrypt)
+                       {
+                               if (sig_key == NULL)
+                               {
+                                       sig_key = pk;
+                                       pk = NULL;
+                               }
+                               else
+                                       res = PXE_PGP_MULTIPLE_KEYS;
+                       }
  
                        if (pk)
                                pgp_key_free(pk);
***************
*** 552,570 **** internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
        {
                if (enc_key)
                        pgp_key_free(enc_key);
                return res;
        }
  
!       if (!enc_key)
!               res = PXE_PGP_NO_USABLE_KEY;
        else
!               *pk_p = enc_key;
        return res;
  }
  
! int
! pgp_set_pubkey(PGP_Context *ctx, MBuf *keypkt,
!                          const uint8 *key, int key_len, int pubtype)
  {
        int                     res;
        PullFilter *src;
--- 592,622 ----
        {
                if (enc_key)
                        pgp_key_free(enc_key);
+               if (sig_key)
+                       pgp_key_free(sig_key);
                return res;
        }
  
!       if (want_encrypt)
!       {
!               if (!enc_key)
!                       res = PXE_PGP_NO_USABLE_KEY;
!               else
!                       *pk_p = enc_key;
!       }
        else
!       {
!               if (!sig_key)
!                       res = PXE_PGP_NO_SIGN_KEY;
!               else
!                       *pk_p = sig_key;
!       }
        return res;
  }
  
! static int
! set_key(MBuf *keypkt, const uint8 *key, int key_len,
!               int pubtype, int encrypt, PGP_PubKey **pk_p)
  {
        int                     res;
        PullFilter *src;
***************
*** 574,584 **** pgp_set_pubkey(PGP_Context *ctx, MBuf *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;
  }
--- 626,663 ----
        if (res < 0)
                return res;
  
!       res = internal_read_key(src, &pk, key, key_len, pubtype, encrypt);
        pullf_free(src);
  
        if (res >= 0)
!       {
!               *pk_p = pk;
!               return 0;
!       }
!       return res;
! }
! 
! int
! pgp_set_sigkey(PGP_Context *ctx, MBuf *keypkt,
!                          const uint8 *key, int key_len, int pubtype,
!                          int encrypt)
! {
!       int res;
! 
!       res = set_key(keypkt, key, key_len, pubtype, encrypt, &ctx->sig_key);
!       if (res < 0)
!               return res;
!       if (ctx->sig_key->algo != PGP_PUB_RSA_ENCRYPT_SIGN &&
!               ctx->sig_key->algo != PGP_PUB_RSA_SIGN &&
!               ctx->sig_key->algo != PGP_PUB_DSA_SIGN)
!               return PXE_PGP_UNSUPPORTED_PUBALGO;
!       return 0;
! }
  
! int
! pgp_set_pubkey(PGP_Context *ctx, MBuf *keypkt,
!                          const uint8 *key, int key_len, int pubtype,
!                          int encrypt)
! {
!       return set_key(keypkt, key, key_len, pubtype, encrypt, &ctx->pub_key);
  }
*** /dev/null
--- b/contrib/pgcrypto/pgp-sig.c
***************
*** 0 ****
--- 1,807 ----
+ /*
+  * pgp-sig.c
+  *      Creating and verifying signatures.
+  *
+  * Copyright (c) 2005 Marko Kreen
+  * All rights reserved.
+  *
+  * Redistribution and use in source and binary forms, with or without
+  * modification, are permitted provided that the following conditions
+  * are met:
+  * 1. Redistributions of source code must retain the above copyright
+  *      notice, this list of conditions and the following disclaimer.
+  * 2. Redistributions in binary form must reproduce the above copyright
+  *      notice, this list of conditions and the following disclaimer in the
+  *      documentation and/or other materials provided with the distribution.
+  *
+  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  * SUCH DAMAGE.
+  *
+  * contrib/pgcrypto/pgp-sig.c
+  */
+ #include "postgres.h"
+ #include "c.h"
+ 
+ #include <time.h>
+ 
+ #include "px.h"
+ #include "pgp.h"
+ 
+ 
+ #define HASHED_SUBPKT_LENGTH          8
+ #define SIGNATURE_PKT_HEADER_LENGTH 4
+ 
+ /*
+  * padded msg: 01 || padded bytes (FF) || 00 || msg
+  */
+ static int
+ pad_emsa_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p)
+ {
+       uint8      *buf;
+       int                     pad_len = res_len - 2 - data_len;
+ 
+       if (pad_len < 8)
+               return PXE_BUG;
+ 
+       buf = px_alloc(res_len);
+       buf[0] = 0x01;
+       memset(buf+1, 0xFF, pad_len);
+       buf[pad_len + 1] = 0x00;
+       memcpy(buf + pad_len + 2, data, data_len);
+       *res_p = buf;
+ 
+       return 0;
+ }
+ 
+ /*
+  * padded msg = 01 || PS || 00 || M
+  * PS - pad bytes (FF)
+  * M - msg
+  */
+ static uint8 *
+ check_emsa_pkcs1_v15(uint8 *data, int len)
+ {
+       uint8      *data_end = data + len;
+       uint8      *p = data;
+       int                     pad = 0;
+ 
+       if (len < 1 + 8 + 1)
+               return NULL;
+ 
+       if (*p++ != 1)
+               return NULL;
+ 
+       while (p < data_end && *p == 0xFF)
+       {
+               p++;
+               pad++;
+       }
+ 
+       if (p == data_end)
+               return NULL;
+       if (*p != 0)
+               return NULL;
+       if (pad < 8)
+               return NULL;
+       return p + 1;
+ }
+ 
+ static int
+ create_signature_vessel(PGP_Context *ctx, uint8 *data, int klen, PGP_MPI 
**msg_p, int full_bytes)
+ {
+       uint8 asn1_prefix[PGP_MAX_DIGEST_ASN1_PREFIX];
+       int prefix_len;
+       uint8 *vessel;
+       uint8 *padded = NULL;
+       int res;
+       PGP_MPI *m = NULL;
+ 
+       prefix_len = pgp_get_digest_asn1_prefix(ctx->digest_algo, asn1_prefix);
+       if (prefix_len < 0)
+               return prefix_len;
+ 
+       vessel = px_alloc(klen + prefix_len);
+ 
+       memcpy(vessel, asn1_prefix, prefix_len);
+       memcpy(vessel + prefix_len, data, klen);
+ 
+       res = pad_emsa_pkcs1_v15(vessel, klen + prefix_len, full_bytes, 
&padded);
+       if (res >= 0)
+       {
+               int full_bits = full_bytes * 8 - 7;
+               res = pgp_mpi_create(padded, full_bits, &m);
+       }
+       if (padded)
+       {
+               px_memset(padded, 0, full_bytes);
+               px_free(padded);
+       }
+       px_memset(vessel, 0, klen + 1);
+       px_free(vessel);
+ 
+       if (res >= 0)
+               *msg_p = m;
+ 
+       return res;
+ }
+ 
+ static int
+ sign_and_write_rsa(PGP_Context *ctx, uint8 *digest, int digest_len, 
PGP_PubKey *pk, PushFilter *pkt)
+ {
+       int                     res;
+       PGP_MPI    *m = NULL,
+                          *c = NULL;
+ 
+       /* create padded msg */
+       res = create_signature_vessel(ctx, digest, digest_len, &m, 
pk->pub.rsa.n->bytes - 1);
+       if (res < 0)
+               goto err;
+ 
+       /* sign it */
+       res = pgp_rsa_decrypt(pk, m, &c);
+       if (res < 0)
+               goto err;
+ 
+       /* write out */
+       res = pgp_mpi_write(pkt, c);
+ 
+ err:
+       pgp_mpi_free(m);
+       pgp_mpi_free(c);
+       return res;
+ }
+ 
+ static int
+ decrypt_rsa_signature(PGP_PubKey *pk, PullFilter *pkt, PGP_MPI **m_p)
+ {
+       int                     res;
+       PGP_MPI *c;
+ 
+       if (pk->algo != PGP_PUB_RSA_ENCRYPT_SIGN
+               && pk->algo != PGP_PUB_RSA_SIGN)
+               return PXE_PGP_WRONG_KEY;
+ 
+       /* read rsa encrypted data */
+       res = pgp_mpi_read(pkt, &c);
+       if (res < 0)
+               return res;
+ 
+       /* encrypted using a private key */
+       res = pgp_rsa_encrypt(pk, c, m_p);
+ 
+       pgp_mpi_free(c);
+       return res;
+ }
+ 
+ 
+ /*
+  * Writes both the hashed and unhashed subpackets of the signature packet into
+  * pkt, and updates md accordingly.
+  */
+ static int
+ write_signature_subpackets(PGP_Context *ctx, PX_MD *md, PushFilter *pkt)
+ {
+       uint32    t;
+       uint8 hashed[HASHED_SUBPKT_LENGTH];
+       uint8 unhashed_hdr[4];
+       int res;
+ 
+       /* hashed subpkt length, two octets */
+       hashed[0] = 0x00;
+       hashed[1] = 0x06;
+       /* header: length 5, type Signature Creation Time */
+       hashed[2] = 0x05;
+       hashed[3] = 2;
+       /* creation time */
+       t = (uint32) time(NULL);
+       hashed[4] = (t >> 24) & 255;
+       hashed[5] = (t >> 16) & 255;
+       hashed[6] = (t >> 8) & 255;
+       hashed[7] = t & 255;
+ 
+       res = pushf_write(pkt, hashed, sizeof(hashed));
+       if (res < 0)
+               return res;
+       px_md_update(md, hashed, sizeof(hashed));
+ 
+       /* unhashed subpackets below; not part of the signature hash */
+ 
+       /* length, two octets */
+       unhashed_hdr[0] = 0x00;
+       unhashed_hdr[1] = 0x0A;
+       /* length 9, type Issuer */
+       unhashed_hdr[2] = 0x09;
+       unhashed_hdr[3] = 16;
+       res = pushf_write(pkt, unhashed_hdr, sizeof(unhashed_hdr));
+       if (res < 0)
+               return res;
+       return pushf_write(pkt, ctx->sig_key->key_id, 8);
+ }
+ 
+ /* Hashes the signature with the v4 "final trailer" */
+ static void
+ digest_v4_final_trailer(PX_MD *md, int trailer_len)
+ {
+       uint8 b;
+ 
+       /* two magic octets, per spec */
+       b = 0x04;
+       px_md_update(md, &b, 1);
+       b = 0xFF;
+       px_md_update(md, &b, 1);
+ 
+       /* length of trailer, four octets in big endian */
+       b = (trailer_len >> 24);
+       px_md_update(md, &b, 1);
+       b = (trailer_len >> 16) & 0xFF;
+       px_md_update(md, &b, 1);
+       b = (trailer_len >> 8) & 0xFF;
+       px_md_update(md, &b, 1);
+       b = trailer_len & 0xFF;
+       px_md_update(md, &b, 1);
+ }
+ 
+ int
+ pgp_write_signature(PGP_Context *ctx, PushFilter *dst)
+ {
+       int                     res;
+       PGP_PubKey *pk = ctx->sig_key;
+       uint8           ver = 4;
+       uint8           digest[PGP_MAX_DIGEST];
+       int                     digest_len;
+       uint8           hdr[SIGNATURE_PKT_HEADER_LENGTH];
+ 
+       if (pk == NULL)
+       {
+               px_debug("no private key?\n");
+               return PXE_BUG;
+       }
+       else if (ctx->sig_digest_ctx == NULL)
+       {
+               px_debug("no sig ctx?\n");
+               return PXE_BUG;
+       }
+ 
+       hdr[0] = ver;
+ 
+       if (ctx->text_mode && ctx->convert_crlf)
+               hdr[1] = PGP_SIGTYP_TEXT;
+       else
+               hdr[1] = PGP_SIGTYP_BINARY;
+ 
+       hdr[2] = pk->algo;
+       hdr[3] = ctx->digest_algo;
+       res = pushf_write(dst, hdr, sizeof(hdr));
+       if (res < 0)
+               return res;
+       px_md_update(ctx->sig_digest_ctx, hdr, sizeof(hdr));
+ 
+       res = write_signature_subpackets(ctx, ctx->sig_digest_ctx, dst);
+       if (res < 0)
+               return res;
+ 
+       digest_v4_final_trailer(ctx->sig_digest_ctx,
+                                                       
SIGNATURE_PKT_HEADER_LENGTH + HASHED_SUBPKT_LENGTH);
+ 
+       px_md_finish(ctx->sig_digest_ctx, digest);
+       digest_len = px_md_result_size(ctx->sig_digest_ctx);
+ 
+       /* write out the first two bytes of the digest */
+       res = pushf_write(dst, digest, 2);
+       if (res < 0)
+               return res;
+ 
+       switch (pk->algo)
+       {
+               case PGP_PUB_RSA_ENCRYPT_SIGN:
+               case PGP_PUB_RSA_SIGN:
+                       res = sign_and_write_rsa(ctx, digest, digest_len, pk, 
dst);
+                       break;
+               default:
+                       /* only RSA is currently supported */
+                       res = PXE_PGP_UNKNOWN_PUBALGO;
+       }
+ 
+       return res;
+ }
+ 
+ 
+ /*
+  * Parses a one, two or five-octet length from a packet.  Partial Body Lengths
+  * are not supported.  Returns 0 if EOF was reached when trying to read the
+  * first byte, 1 if the length was read successfully, or < 0 if something went
+  * wrong.
+  */
+ static int
+ parse_packet_len(PullFilter *src, int *len_p)
+ {
+       uint8           b;
+       uint8     *tmpbuf;
+       int                     len;
+       int              res;
+ 
+       res = pullf_read(src, 1, &tmpbuf);
+       if (res <= 0)
+               return res;
+       b = *tmpbuf;
+       if (b <= 191)
+               len = b;
+       else if (b >= 192 && b < 255)
+       {
+               len = ((unsigned) (b) - 192) << 8;
+               GETBYTE(src, b);
+               len += 192 + b;
+       }
+       else
+       {
+               /* 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;
+       }
+ 
+       *len_p = len;
+       return 1;
+ }
+ 
+ struct SigSubPktParserState {
+       bool hashed_done;
+       bool done;
+       int lr_len;
+       PullFilter *lr;
+       PullFilter *hashed_src;
+       PullFilter *unhashed_src;
+ };
+ 
+ struct SigSubPkt {
+       int len;
+       int type;
+       bool hashed;
+       PullFilter *body;
+ };
+ 
+ static int
+ start_section(struct SigSubPktParserState *pstate, bool hashed)
+ {
+       int res;
+       int len;
+       PullFilter *src;
+       uint8 b;
+ 
+       if (hashed)
+               src = pstate->hashed_src;
+       else
+               src = pstate->unhashed_src;
+ 
+       /* read the length of the section; two-octet big endian */
+       GETBYTE(src, b);
+       len = b;
+       GETBYTE(src, b);
+       len = (len << 8) | b;
+ 
+       /* hashed section MUST be present */
+       if (hashed && len == 0)
+               return PXE_PGP_CORRUPT_DATA;
+       pstate->lr_len = len;
+       res = pullf_create_limited_reader(&pstate->lr, src, &pstate->lr_len);
+       if (res < 0)
+               return res;
+       return 0;
+ }
+ 
+ /*
+  * Initializes a parser for parsing the subpackets in a version 4 signature
+  * packet.  hashed_src is used for parsing the hashed subpackets, and
+  * unhashed_src is used for reading the unhashed ones.  Returns < 0 on 
failure.
+  * The caller never has to worry about releasing the parse state.
+  */
+ static int
+ init_sigsubpkt_parser(PullFilter *hashed_src, PullFilter *unhashed_src, 
struct SigSubPktParserState *pstate)
+ {
+       pstate->hashed_done = false;
+       pstate->done = false;
+       pstate->lr = NULL;
+       pstate->hashed_src = hashed_src;
+       pstate->unhashed_src = unhashed_src;
+ 
+       return start_section(pstate, true);
+ }
+ 
+ /*
+  * Releases any memory allocated by the signature subpacket parser.  You only
+  * need to call this function if you want to stop reading before you've 
reached
+  * the last subpacket.
+  */
+ static void
+ destroy_sigsubpkt_parser(struct SigSubPktParserState *pstate)
+ {
+       if (pstate->lr)
+       {
+               pullf_free(pstate->lr);
+               pstate->lr = NULL;
+       }
+ }
+ 
+ /*
+  * Reads the next subpacket's header from state to subpkt.  Returns 1 if a
+  * packet was read, 0 if all subpackets have been successfully read from the
+  * signature packet, or < 0 on error.
+  */
+ static int
+ sigsubpkt_parser_next(struct SigSubPktParserState *pstate, struct SigSubPkt 
*subpkt)
+ {
+       uint8 typ;
+       int len;
+       int res;
+ 
+       if (pstate->done || pstate->lr == NULL)
+               return PXE_BUG;
+ 
+ again:
+       res = parse_packet_len(pstate->lr, &len);
+       if (res < 0)
+               goto err;
+       else if (res == 0)
+       {
+               /* no more subpackets in this section */
+ 
+               if (pstate->hashed_done)
+               {
+                       pstate->done = true;
+                       pullf_free(pstate->lr);
+                       pstate->lr = NULL;
+                       return 0;
+               }
+               pstate->hashed_done = true;
+               res = start_section(pstate, false);
+               if (res < 0)
+                       goto err;
+               else
+               {
+                       /* start again from the first packet of the unhashed 
section */
+                       goto again;
+               }
+       }
+ 
+       res = pullf_read_fixed(pstate->lr, 1, &typ);
+       if (res < 0)
+               goto err;
+       len--;
+ 
+       /* done; let the caller read the data */
+       subpkt->len = len;
+       subpkt->type = typ;
+       subpkt->hashed = !pstate->hashed_done;
+       subpkt->body = pstate->lr;
+ 
+ err:
+       if (res < 0)
+       {
+               pullf_free(pstate->lr);
+               pstate->lr = NULL;
+               return res;
+       }
+       return 1;
+ }
+ 
+ static int
+ parse_v3_signature_header(PGP_Context *ctx, PullFilter *pkt, PGP_Signature 
*sig)
+ {
+       int             res;
+       uint8   len;
+ 
+       /* one-octet length, must be 5 */
+       res = pullf_read_fixed(pkt, 1, &len);
+       if (res < 0)
+               return res;
+       if (len != 5)
+               return PXE_PGP_CORRUPT_DATA;
+ 
+       res = pullf_read_fixed(pkt, 1, &sig->type);
+       if (res < 0)
+               return res;
+       res = pullf_read_fixed(pkt, 4, sig->creation_time);
+       if (res < 0)
+               return res;
+       res = pullf_read_fixed(pkt, 8, sig->keyid);
+       if (res < 0)
+               return res;
+       res = pullf_read_fixed(pkt, 1, &sig->algo);
+       if (res < 0)
+               return res;
+       res = pullf_read_fixed(pkt, 1, &sig->digest_algo);
+       if (res < 0)
+               return res;
+ 
+       res = pullf_read_fixed(pkt, 2, sig->expected_digest_l16);
+ 
+       if (res >= 0)
+       {
+               /* write trailer */
+               mbuf_append(sig->trailer, &sig->type, 1);
+               mbuf_append(sig->trailer, sig->creation_time, 4);
+       }
+ 
+       return res;
+ }
+ 
+ static int
+ parse_v4_signature_header(PGP_Context *ctx, PullFilter *pkt, PGP_Signature 
*sig)
+ {
+       int res;
+ 
+       struct SigSubPktParserState pstate;
+       bool found_creation_time = false;
+       bool found_issuer = false;
+       PullFilter  *tr = NULL;
+ 
+       /*
+        * In a V4 header, we need to store everything up to the end of the 
hashed
+        * subpackets for the hash trailer.
+        */
+       mbuf_append(sig->trailer, &sig->version, 1);
+       res = pullf_create_tee_reader(&tr, pkt, sig->trailer);
+       if (res < 0)
+               return res;
+ 
+       res = pullf_read_fixed(tr, 1, &sig->type);
+       if (res < 0)
+               goto err;
+       res = pullf_read_fixed(tr, 1, &sig->algo);
+       if (res < 0)
+               goto err;
+       res = pullf_read_fixed(tr, 1, &sig->digest_algo);
+       if (res < 0)
+               goto err;
+ 
+       res = init_sigsubpkt_parser(tr, pkt, &pstate);
+       if (res < 0)
+               goto err;
+ 
+       for (;;)
+       {
+               struct SigSubPkt subpkt;
+ 
+               res = sigsubpkt_parser_next(&pstate, &subpkt);
+               if (res < 0)
+                       goto err;
+               else if (res == 0)
+                       break;
+ 
+               if (subpkt.hashed && subpkt.type == PGP_SIGNATURE_CREATION_TIME)
+               {
+                       if (found_creation_time || subpkt.len != 4)
+                       {
+                               res = PXE_PGP_CORRUPT_DATA;
+                               goto err;
+                       }
+                       found_creation_time = true;
+                       res = pullf_read_fixed(subpkt.body, 4, 
sig->creation_time);
+                       if (res < 0)
+                               goto err;
+               }
+               else if (subpkt.type == PGP_ISSUER_ID)
+               {
+                       if (found_issuer || subpkt.len != 8)
+                       {
+                               res = PXE_PGP_CORRUPT_DATA;
+                               goto err;
+                       }
+                       found_issuer = true;
+                       res = pullf_read_fixed(subpkt.body, 8, sig->keyid);
+                       if (res < 0)
+                               goto err;
+               }
+               else
+               {
+                       /* unknown subpacket; skip over the data */
+                       res = pullf_discard(subpkt.body, subpkt.len);
+                       if (res < 0)
+                               goto err;
+               }
+       }
+ 
+       if (!found_creation_time)
+       {
+               res = PXE_PGP_CORRUPT_DATA;
+               goto err;
+       }
+ 
+       res = pullf_read_fixed(pkt, 2, sig->expected_digest_l16);
+ 
+ err:
+       destroy_sigsubpkt_parser(&pstate);
+       if (tr)
+               pullf_free(tr);
+       if (res < 0)
+               return res;
+ 
+       return 0;
+ }
+ 
+ 
+ static int
+ parse_signature_payload(PGP_Context *ctx, PullFilter *pkt, PGP_Signature *sig)
+ {
+       int res;
+       PGP_PubKey *pk = ctx->sig_key;
+       PGP_MPI *m;
+       uint8      *msg;
+       int                     msglen;
+       uint8 asn1_prefix[PGP_MAX_DIGEST_ASN1_PREFIX];
+       int prefix_len;
+ 
+       if (pk == NULL)
+       {
+               px_debug("parse_signature_payload: no pubkey?");
+               return PXE_BUG;
+       }
+ 
+       switch (pk->algo)
+       {
+               case PGP_PUB_RSA_SIGN:
+               case PGP_PUB_RSA_ENCRYPT_SIGN:
+                       res = decrypt_rsa_signature(pk, pkt, &m);
+                       break;
+               default:
+                       /* only RSA is currently supported */
+                       res = PXE_PGP_UNKNOWN_PUBALGO;
+       }
+       if (res < 0)
+               return res;
+ 
+       /*
+        * extract message
+        */
+       msg = check_emsa_pkcs1_v15(m->data, m->bytes);
+       if (msg == NULL)
+       {
+               px_debug("check_emsa_pkcs1_v15 failed");
+               res = PXE_PGP_WRONG_KEY;
+               goto out;
+       }
+       msglen = m->bytes - (msg - m->data);
+ 
+       prefix_len = pgp_get_digest_asn1_prefix(sig->digest_algo, asn1_prefix);
+       if (prefix_len < 0)
+       {
+               px_debug("digest algo %d does not have an ASN1 prefix", 
sig->digest_algo);
+               res = PXE_PGP_UNSUPPORTED_HASH;
+               goto out;
+       }
+       if (msglen < prefix_len ||
+               memcmp(msg, asn1_prefix, prefix_len) != 0)
+       {
+               res = PXE_PGP_WRONG_KEY;
+               goto out;
+       }
+       msglen -= prefix_len;
+       if (msglen > PGP_MAX_DIGEST)
+       {
+               res = PXE_PGP_WRONG_KEY;
+               goto out;
+       }
+       memcpy(sig->expected_digest, msg + prefix_len, msglen);
+ 
+ out:
+       pgp_mpi_free(m);
+       if (res < 0)
+               return res;
+       return pgp_expect_packet_end(pkt);
+ }
+ 
+ int
+ pgp_parse_onepass_signature(PGP_Context *ctx, PGP_Signature **sig_p, 
PullFilter *pkt)
+ {
+       PGP_Signature *sig;
+       uint8   version;
+       uint8   type;
+       uint8   digestalgo;
+       uint8   pubkeyalgo;
+       uint8   last;
+       uint8   keyid[8];
+       int             res;
+ 
+       GETBYTE(pkt, version);
+       GETBYTE(pkt, type);
+       GETBYTE(pkt, digestalgo);
+       GETBYTE(pkt, pubkeyalgo);
+       res = pullf_read_fixed(pkt, 8, keyid);
+       if (res < 0)
+               return res;
+       GETBYTE(pkt, last);
+ 
+       res = pgp_sig_create(&sig);
+       if (res < 0)
+               return res;
+ 
+       sig->onepass = 1;
+       memcpy(sig->keyid, keyid, 8);
+       sig->version = version;
+       sig->type = type;
+       sig->digest_algo = digestalgo;
+       sig->algo = pubkeyalgo;
+       *sig_p = sig;
+       return 0;
+ }
+ 
+ int
+ pgp_parse_signature(PGP_Context *ctx, PGP_Signature **sig_p, PullFilter *pkt, 
uint8 *expected_keyid)
+ {
+       int             version;
+       int             res;
+       PGP_Signature *sig;
+ 
+       GETBYTE(pkt, version);
+ 
+       res = pgp_sig_create(&sig);
+       if (res < 0)
+               goto err;
+       sig->version = version;
+       if (version == 3)
+               res = parse_v3_signature_header(ctx, pkt, sig);
+       else if (version == 4)
+               res = parse_v4_signature_header(ctx, pkt, sig);
+       else
+               res = PXE_PGP_CORRUPT_DATA;
+ 
+       if (res < 0)
+               goto err;
+ 
+       if (expected_keyid &&
+               memcmp(expected_keyid, sig->keyid, 8) == 0)
+               res = parse_signature_payload(ctx, pkt, sig);
+       else
+               res = pullf_discard(pkt, -1);
+ 
+ err:
+       if (res < 0)
+               pgp_sig_free(sig);
+       else
+               *sig_p = sig;
+       return res;
+ }
+ 
+ 
+ int
+ pgp_verify_signature(PGP_Context *ctx)
+ {
+       int len;
+       uint8 *trailer;
+       uint8 digest[PGP_MAX_DIGEST];
+       PX_MD *md = ctx->sig_digest_ctx;
+       PGP_Signature *sig = ctx->sig_expected;
+ 
+       if (!md)
+               return PXE_BUG;
+       if (!sig)
+               return PXE_PGP_NO_SIGNATURE;
+       if (sig->version != 3 && sig->version != 4)
+               return PXE_BUG;
+ 
+       len = mbuf_grab(sig->trailer, mbuf_avail(sig->trailer), &trailer);
+       px_md_update(md, trailer, len);
+       if (sig->version == 4)
+               digest_v4_final_trailer(md, len);
+       px_md_finish(md, digest);
+ 
+       if (memcmp(digest, sig->expected_digest, px_md_result_size(md)) != 0)
+               return PXE_PGP_INVALID_SIGNATURE;
+ 
+       return 0;
+ }
+ 
*** a/contrib/pgcrypto/pgp.c
--- b/contrib/pgcrypto/pgp.c
***************
*** 38,43 ****
--- 38,44 ----
   * Defaults.
   */
  static int    def_cipher_algo = PGP_SYM_AES_128;
+ static int    def_digest_algo = PGP_DIGEST_SHA512;
  static int    def_s2k_cipher_algo = -1;
  static int    def_s2k_mode = PGP_S2K_ISALTED;
  static int    def_s2k_digest_algo = PGP_DIGEST_SHA1;
***************
*** 144,149 **** pgp_get_cipher_name(int code)
--- 145,208 ----
  }
  
  int
+ pgp_get_digest_asn1_prefix(int code, uint8 *data)
+ {
+       int len;
+ 
+       uint8 md5_prefix[18] =
+               {0x30, 0x20, 0x30, 0x0C, 0x06, 0x08, 0x2A, 0x86,
+                0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05, 0x05, 0x00,
+                0x04, 0x10};
+       uint8 ripemd160_prefix[15] =
+               {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24,
+                0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14};
+       uint8 sha1_prefix[15] =
+               {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0E,
+                0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14};
+       uint8 sha256_prefix[19] =
+               {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+                0x00, 0x04, 0x20};
+       uint8 sha384_prefix[19] =
+               {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
+                0x00, 0x04, 0x30};
+       uint8 sha512_prefix[19] =
+               {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+                0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
+                0x00, 0x04, 0x40};
+ 
+       switch (code)
+       {
+               case PGP_DIGEST_MD5:
+                       len = sizeof(md5_prefix);
+                       memcpy(data, md5_prefix, len);
+                       return len;
+               case PGP_DIGEST_RIPEMD160:
+                       len = sizeof(ripemd160_prefix);
+                       memcpy(data, ripemd160_prefix, len);
+                       return len;
+               case PGP_DIGEST_SHA1:
+                       len = sizeof(sha1_prefix);
+                       memcpy(data, sha1_prefix, len);
+                       return len;
+               case PGP_DIGEST_SHA256:
+                       len = sizeof(sha256_prefix);
+                       memcpy(data, sha256_prefix, len);
+                       return len;
+               case PGP_DIGEST_SHA384:
+                       len = sizeof(sha384_prefix);
+                       memcpy(data, sha384_prefix, len);
+                       return len;
+               case PGP_DIGEST_SHA512:
+                       len = sizeof(sha512_prefix);
+                       memcpy(data, sha512_prefix, len);
+                       return len;
+       }
+       return PXE_PGP_UNSUPPORTED_HASH;
+ }
+ 
+ int
  pgp_get_cipher_key_size(int code)
  {
        const struct cipher_info *i = get_cipher_info(code);
***************
*** 204,209 **** pgp_init(PGP_Context **ctx_p)
--- 263,269 ----
        memset(ctx, 0, sizeof *ctx);
  
        ctx->cipher_algo = def_cipher_algo;
+       ctx->digest_algo = def_digest_algo;
        ctx->s2k_cipher_algo = def_s2k_cipher_algo;
        ctx->s2k_mode = def_s2k_mode;
        ctx->s2k_digest_algo = def_s2k_digest_algo;
***************
*** 230,235 **** pgp_free(PGP_Context *ctx)
--- 290,317 ----
  }
  
  int
+ pgp_sig_create(PGP_Signature **sig_p)
+ {
+       PGP_Signature *sig;
+ 
+       sig = px_alloc(sizeof(PGP_Signature));
+       memset(sig, 0, sizeof(*sig));
+       sig->trailer = mbuf_create(256);
+       *sig_p = sig;
+       return 1;
+ }
+ 
+ int
+ pgp_sig_free(PGP_Signature *sig)
+ {
+       if (sig->trailer)
+               mbuf_free(sig->trailer);
+       px_memset(sig, 0, sizeof(*sig));
+       px_free(sig);
+       return 1;
+ }
+ 
+ int
  pgp_disable_mdc(PGP_Context *ctx, int disable)
  {
        ctx->disable_mdc = disable ? 1 : 0;
***************
*** 314,319 **** pgp_set_cipher_algo(PGP_Context *ctx, const char *name)
--- 396,412 ----
  }
  
  int
+ pgp_set_digest_algo(PGP_Context *ctx, const char *name)
+ {
+       int                     code = pgp_get_digest_code(name);
+ 
+       if (code < 0)
+               return code;
+       ctx->digest_algo = code;
+       return 0;
+ }
+ 
+ int
  pgp_set_s2k_cipher_algo(PGP_Context *ctx, const char *name)
  {
        int                     code = pgp_get_cipher_code(name);
*** a/contrib/pgcrypto/pgp.h
--- b/contrib/pgcrypto/pgp.h
***************
*** 45,50 **** enum PGP_PKT_TYPE
--- 45,51 ----
        PGP_PKT_PUBENCRYPTED_SESSKEY = 1,
        PGP_PKT_SIGNATURE = 2,
        PGP_PKT_SYMENCRYPTED_SESSKEY = 3,
+       PGP_PKT_ONEPASS_SIGNATURE = 4,
        PGP_PKT_SECRET_KEY = 5,
        PGP_PKT_PUBLIC_KEY = 6,
        PGP_PKT_SECRET_SUBKEY = 7,
***************
*** 107,121 **** enum PGP_DIGEST_TYPE
        PGP_DIGEST_SHA512 = 10
  };
  
! #define PGP_MAX_KEY    (256/8)
! #define PGP_MAX_BLOCK  (256/8)
! #define PGP_MAX_DIGEST (512/8)
! #define PGP_S2K_SALT   8
  
  typedef struct PGP_MPI PGP_MPI;
  typedef struct PGP_PubKey PGP_PubKey;
  typedef struct PGP_Context PGP_Context;
  typedef struct PGP_S2K PGP_S2K;
  
  struct PGP_S2K
  {
--- 108,136 ----
        PGP_DIGEST_SHA512 = 10
  };
  
! enum PGP_SIGNATURE_TYPE
! {
!       PGP_SIGTYP_BINARY = 0,
!       PGP_SIGTYP_TEXT = 1
! };
! 
! enum PGP_SIGNATURE_SUBPKT_TYPE
! {
!       PGP_SIGNATURE_CREATION_TIME = 2,
!       PGP_ISSUER_ID = 16
! };
! 
! #define PGP_MAX_KEY                                   (256/8)
! #define PGP_MAX_BLOCK                         (256/8)
! #define PGP_MAX_DIGEST                                (512/8)
! #define PGP_MAX_DIGEST_ASN1_PREFIX    20
! #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;
+ typedef struct PGP_Signature PGP_Signature;
  
  struct PGP_S2K
  {
***************
*** 139,144 **** struct PGP_Context
--- 154,160 ----
        int                     s2k_digest_algo;
        int                     s2k_cipher_algo;
        int                     cipher_algo;
+       int                     digest_algo;
        int                     compress_algo;
        int                     compress_level;
        int                     disable_mdc;
***************
*** 156,163 **** struct PGP_Context
        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;
  
        /*
--- 172,184 ----
        int                     use_mdcbuf_filter;
        PX_MD      *mdc_ctx;
  
!       PX_MD      *sig_digest_ctx;
!       PGP_Signature *sig_onepass;
!       PGP_Signature *sig_expected;
! 
!       PGP_PubKey *pub_key;            /* owned by ctx */
!       PGP_PubKey *sig_key;            /* owned by ctx */
!       const uint8 *sym_key;           /* not owned by ctx */
        int                     sym_key_len;
  
        /*
***************
*** 227,243 **** struct PGP_PubKey
--- 248,286 ----
        int                     can_encrypt;
  };
  
+ struct PGP_Signature
+ {
+       /* always present */
+       int             onepass;
+       uint8   keyid[8];
+       uint8   version;
+       uint8   type;
+       uint8   algo;
+       uint8   digest_algo;
+ 
+       /* only present if this is not a one-pass signature */
+       uint8   creation_time[4];
+       uint8   expected_digest[PGP_MAX_DIGEST];
+       uint8   expected_digest_l16[2];
+       MBuf   *trailer;
+ };
+ 
  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_sig_create(PGP_Signature **sig_p);
+ int                   pgp_sig_free(PGP_Signature *sig);
+ 
  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_get_digest_asn1_prefix(int code, uint8 *data);
  
  int                   pgp_set_cipher_algo(PGP_Context *ctx, const char *name);
+ int                   pgp_set_digest_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);
***************
*** 251,260 **** 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 */
  
--- 294,309 ----
  int                   pgp_get_unicode_mode(PGP_Context *ctx);
  
  int                   pgp_set_symkey(PGP_Context *ctx, const uint8 *key, int 
klen);
! int                   pgp_set_sigkey(PGP_Context *ctx, MBuf *keypkt,
!                                                  const uint8 *key, int klen, 
int pubtype,
!                                                  int encrypt);
! int                   pgp_set_pubkey(PGP_Context *ctx, MBuf *keypkt,
!                                                  const uint8 *key, int klen, 
int pubtype,
!                                                  int encrypt);
  
! int                   pgp_get_keyid(int want_main_key, MBuf *pgp_data, char 
*dst);
! int                   pgp_get_signature_keys(PGP_Context *ctx, MBuf 
*pgp_data, void *opaque,
!                                                                  int 
(*cb)(void *opaque, PGP_Signature *sig, char *keyid));
  
  /* internal functions */
  
***************
*** 286,291 **** int                    pgp_key_alloc(PGP_PubKey **pk_p);
--- 335,342 ----
  void          pgp_key_free(PGP_PubKey *pk);
  int                   _pgp_read_public_key(PullFilter *pkt, PGP_PubKey 
**pk_p);
  
+ int                   pgp_parse_symenc_sesskey(PGP_Context *ctx, PullFilter 
*src);
+ 
  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);
***************
*** 298,303 **** int                    pgp_expect_packet_end(PullFilter *pkt);
--- 349,362 ----
  int                   pgp_write_pubenc_sesskey(PGP_Context *ctx, PushFilter 
*dst);
  int                   pgp_create_pkt_writer(PushFilter *dst, int tag, 
PushFilter **res_p);
  
+ int                   pgp_write_signature(PGP_Context *ctx, PushFilter *dst);
+ int                   pgp_parse_onepass_signature(PGP_Context *ctx, 
PGP_Signature **sig_p,
+                                                                               
PullFilter *pkt);
+ int                   pgp_parse_signature(PGP_Context *ctx, PGP_Signature 
**sig_p,
+                                                               PullFilter 
*pkt, uint8 *expected_keyid);
+ int                   pgp_verify_signature(PGP_Context *ctx);
+ 
+ 
  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);
***************
*** 314,316 **** int                    pgp_rsa_encrypt(PGP_PubKey *pk, PGP_MPI 
*m, PGP_MPI **c);
--- 373,376 ----
  int                   pgp_rsa_decrypt(PGP_PubKey *pk, PGP_MPI *c, PGP_MPI 
**m);
  
  extern struct PullFilterOps pgp_decrypt_filter;
+ extern struct PullFilterOps pgp_prefix_filter;
*** a/contrib/pgcrypto/px.c
--- b/contrib/pgcrypto/px.c
***************
*** 86,91 **** static const struct error_desc px_err_list[] = {
--- 86,96 ----
        {PXE_PGP_BAD_S2K_MODE, "Bad S2K mode"},
        {PXE_PGP_UNSUPPORTED_PUBALGO, "Unsupported public key algorithm"},
        {PXE_PGP_MULTIPLE_SUBKEYS, "Several subkeys not supported"},
+       {PXE_PGP_NO_SIGNATURE, "No signature matching the key id present in the 
message"},
+       {PXE_PGP_INVALID_SIGNATURE, "Signature does not match"},
+       {PXE_PGP_MULTIPLE_SIGNATURES, "Multiple signatures with matching 
keyid"},
+       {PXE_PGP_CONFLICTING_SIGNATURES, "One-pass signature's options conflict 
with those of the actual signature"},
+       {PXE_PGP_NO_SIGN_KEY, "No sign key found"},
  
        /* fake this as PXE_PGP_CORRUPT_DATA */
        {PXE_MBUF_SHORT_READ, "Corrupt data"},
*** a/contrib/pgcrypto/px.h
--- b/contrib/pgcrypto/px.h
***************
*** 106,111 **** void           px_free(void *p);
--- 106,116 ----
  #define PXE_PGP_BAD_S2K_MODE          -121
  #define PXE_PGP_UNSUPPORTED_PUBALGO -122
  #define PXE_PGP_MULTIPLE_SUBKEYS      -123
+ #define PXE_PGP_NO_SIGNATURE          -124
+ #define PXE_PGP_INVALID_SIGNATURE     -125
+ #define PXE_PGP_MULTIPLE_SIGNATURES -126
+ #define PXE_PGP_CONFLICTING_SIGNATURES        -127
+ #define PXE_PGP_NO_SIGN_KEY                   -128
  
  
  typedef struct px_digest PX_MD;
*** a/contrib/pgcrypto/sql/pgp-encrypt.sql
--- b/contrib/pgcrypto/sql/pgp-encrypt.sql
***************
*** 13,19 **** select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
                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
--- 13,20 ----
                expect-sess-key=0,
                expect-s2k-mode=3,
                expect-s2k-digest-algo=sha1,
!               expect-compress-algo=0,
!               expect-digest-algo=sha512
                ');
  
  -- maybe the expect- stuff simply does not work
***************
*** 23,29 **** select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'),
                expect-sess-key=1,
                expect-s2k-mode=0,
                expect-s2k-digest-algo=md5,
!               expect-compress-algo=1
                ');
  
  -- bytea as text
--- 24,31 ----
                expect-sess-key=1,
                expect-s2k-mode=0,
                expect-s2k-digest-algo=md5,
!               expect-compress-algo=1,
!               expect-digest-algo=md5
                ');
  
  -- bytea as text
*** a/contrib/pgcrypto/sql/pgp-info.sql
--- b/contrib/pgcrypto/sql/pgp-info.sql
***************
*** 20,22 **** select pgp_key_id(dearmor(seckey)) from keytbl where id=6;
--- 20,38 ----
  
  select pgp_key_id(dearmor(data)) as data_key_id
  from encdata order by id;
+ 
+ -- pgp_main_key_id
+ 
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=1;
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=2;
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=3;
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=4;
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=5;
+ select pgp_main_key_id(dearmor(pubkey)) from keytbl where id=6;
+ 
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=1;
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=2;
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=3;
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=4;
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=5;
+ select pgp_main_key_id(dearmor(seckey)) from keytbl where id=6;
*** /dev/null
--- b/contrib/pgcrypto/sql/pgp-sign.sql
***************
*** 0 ****
--- 1,199 ----
+ --
+ -- PGP sign
+ --
+ -- ensure consistent test output regardless of the default bytea format
+ SET bytea_output TO escape;
+ 
+ -- list keys
+ select pgp_sym_signature_keys.* from
+     (select pgp_sym_encrypt_sign_bytea('Secret.', 'key', dearmor(seckey)) as 
ciphertext
+     from keytbl where keytbl.name = 'rsa2048') encrypted,
+     lateral pgp_sym_signature_keys(encrypted.ciphertext, 'key')
+     ;
+ select pgp_pub_signature_keys.* from
+     (select seckey, pgp_pub_encrypt_sign_bytea('Secret.', dearmor(pubkey), 
dearmor(seckey)) as ciphertext
+     from keytbl where keytbl.name = 'rsaenc2048') encrypted,
+     lateral pgp_pub_signature_keys(encrypted.ciphertext, 
dearmor(encrypted.seckey))
+     ;
+ 
+ -- decrypt without verifying the signature
+ select pgp_sym_decrypt_bytea(pgp_sym_encrypt_sign_bytea('Secret.', 'key', 
dearmor(seckey)), 'key')
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_bytea(pgp_pub_encrypt_sign_bytea('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ -- decrypt and verify the signature
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign_bytea('Secret.', 
'key', dearmor(seckey)), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign_bytea('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ -- decrypt and verify the signature, wrong key
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign_bytea('Secret.', 
'key', dearmor(keytbl1.seckey)), 'key', dearmor(keytbl2.pubkey))
+ from keytbl keytbl1, keytbl keytbl2 where keytbl1.name = 'rsa2048' and 
keytbl2.name = 'rsaenc2048';
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign_bytea('Secret.', 
dearmor(keytbl2.pubkey), dearmor(keytbl1.seckey)), dearmor(keytbl2.seckey), 
dearmor(keytbl2.pubkey))
+ from keytbl keytbl1, keytbl keytbl2 where keytbl1.name = 'rsa2048' and 
keytbl2.name = 'rsaenc2048';
+ 
+ -- complain if no signature is present
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_bytea('Secret.', 'key'), 
'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_bytea('Secret.', 
dearmor(pubkey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ -- multiple signers
+ insert into encdata(id, data) values (5, '
+ -----BEGIN PGP MESSAGE-----
+ Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
+ 
+ jA0ECQMCA7SEJlWfWYjUyekWNxFzQ/NFijc61eLTtHEqtxZ36f0XvgV2ZjIgUVq5
+ jSaGcly7rTfy6P9bCNMN+p1B86N+v6P+7zkzhtg4abM7RTbnXfj9VupQE+bTu++A
+ 9xTAOrM79cFlyVzVykkQUOcvw7kNRk2woepREbguRpqLytDwVf8tJKn2Yd00X/Lp
+ IsU5HfT+TcNngx8NFqhKedfAPcyQd0cS7NA0dcUyXcN/fO+PsPavp7iPGt0Q+/JN
+ exkjx4LmJPObkrgN7RYiOlA3vRUt4SuzJAIN6+GkKxveYrpQuaGr1t1M0HfPXw9n
+ gilqUtlwX36tHGfCOYYwlG64LaNsyuTRmXIvV0o8kYaaJtoVKeMGkZCPd6XZoAf9
+ Elluzf7Mxe+T44XRQ/VlO8P9aT0immSdOwGL6wywmV+kITpcVUcthCR3a2Yb2R4M
+ NE0efRop4arfdOGpLdysF32ymwAZgdqNCDHKLTuAKfDlnXl2Tm1QdOhXytILIe64
+ kkzt5YNjrAvw5qmn0ze3xZuUCTuEUbBh3T19o5jrF1oiZ4hqd6o3iUEPnYxWaHl0
+ r7W9BxHpVJexY7K3MGtAnnHKn8f+MmopGe4HDSHTRf+qDjZi7yg9psWChlii4PPs
+ YqmfxGBicnoHQy+GSauoDgVPNy4PPrH5yY4bAByt3op28/vkQ7bQH0tuc6x6J0Rm
+ GYG7s8HPpWFSzS7o25tALBmXIi+DZfdgQ8tQ4MLx5wZPJ1H68A3MTvinuQKiY5yE
+ YezsNH92tGilzM5E0iRA8UTluqhQIkX4apMJnnRT8RJ0by5pUbkYKokbmH4rKTCv
+ nOIu5RYb/9a4Nd4ijZOWM8AmNKVNsLP3cB7jJqupykWNpos=
+ =1JXB
+ -----END PGP MESSAGE-----
+ ');
+ insert into encdata(id, data) values (6, '
+ -----BEGIN PGP MESSAGE-----
+ Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
+ 
+ hQEMA/0CBsQJt0h1AQf/bAFXphI0ecP5Ba2gKnC9TXz7BWhHn07QBEBoWJ4CHMpp
+ ULwBJ4CgG6ED9QdtIPeteazrn490ORS8ut4mymf+ERolZGI7U4p2lJJIkvpS7Qyq
+ wAEjsZgl48mT6P8JQyp7Xf2MDrONVNS+rsp1+C5Fem8PGprlIu7RRUBYi1eg3lZO
+ Kjl8poBqU28PHT/HaZakccO/cOFKaXBAlq3wZGHgEwNa2LXwNlUOG66u2GrMKcAm
+ R2N68ve5clIa5cUWPB8uvvWkbCjBnf+re4L7hddRCAVNs98WC7ty1876xJLh5OyH
+ cGh8xa03LMOOnBseuOUx/dKVTjc5vFgsfTDJgf6SS9LpAeKts5nMscaVzqU2jgtd
+ YOyhocXn8+kA43iUX0YZvMzfep8vSoHqigV2VQ6OtxQBT1SA7inE/7l3t3xPaSRz
+ HeeXBDSg1BSwLr2p+l/PTvR158MZ4MQX5PvmPJ3M6f/1nDflHGR1pp8Qjv7BGiOz
+ XnjGSK+pRrzT4S2XSOIglcSNqEKa0B0iodv/R593E08/zZMeyGZQL7esJq9CWp4n
+ jT30ATpvIrZ6UvBpxj21G64/JfFSZa8a+v2biC/eOws3Dch/fCa0IU0RNlZaTXoN
+ 888am3HEKlObzst+7PvkRc4TgK91cfF46w311iD3bDi8lsv1LqDmBMqhIEWrg1Sc
+ ntAe+afUzHdUKg/StlMOSloTwU0oP+drj3h30UaR/t0/ykMDfCjW6peEmEB64vDx
+ 3FQk8phKket2EmKhC1pHWZpZsEgITficWwy42l43xAsLNp6cwuhZ5Sz874di73iE
+ oDhIqB5Mftvc1zUiZv/15KsIX9DwxGHWbaUIRro+xYmKj36ljJguTA74NwKljxbE
+ xQ6gLjMs81MCBkPbPjM4iNuF5AqVu78BSUqd7nOKauLm5/a2COr/5fh6Zigph57y
+ FTe54GqFzxlpP4JOqUiS2gd9lRlXujCWpVa8Cexxh99jpG1mF/xuHpnjJvCOPp4h
+ g0IBsNq67xYHubsX+goGnH2edeJsH5eXYwETFqYnUt5kQmKQKPZn4vH01TAidHco
+ Qv9O1DIyvwiwmgaoPT6JCuGfd8lFqmR6W4u+3NM6pbW19AguYIFXRcgzLIOX5t4K
+ E3JVgW8pkQcxBsycFxrwjf66hfaLTu39SsZrWkkaPRMo8kCt08K2jgf4alz/MyhH
+ uRScuAbB94gSEf/VmzTnilt2219be1w5zl35h1fjCbo=
+ =snSk
+ -----END PGP MESSAGE-----
+ ');
+ 
+ 
+ select * from pgp_pub_decrypt_bytea((select dearmor(data) from encdata where 
id=6), (select dearmor(seckey) from keytbl where keytbl.name = 'rsaenc2048'));
+ 
+ select * from pgp_sym_signature_keys((select dearmor(data) from encdata where 
id=5), 'key');
+ select * from pgp_pub_signature_keys((select dearmor(data) from encdata where 
id=6), (select dearmor(seckey) from keytbl where keytbl.name = 'rsaenc2048'));
+ 
+ -- verify both signatures
+ select * from pgp_pub_decrypt_verify_bytea((select dearmor(data) from encdata 
where id=6), (select dearmor(seckey) from keytbl where keytbl.name = 
'rsaenc2048'), (select dearmor(pubkey) from keytbl where keytbl.name = 
'rsa2048'));
+ select * from pgp_pub_decrypt_verify_bytea((select dearmor(data) from encdata 
where id=6), (select dearmor(seckey) from keytbl where keytbl.name = 
'rsaenc2048'), (select dearmor(pubkey) from keytbl where keytbl.name = 
'rsaenc2048'));
+ 
+ -- test v3 signature headers
+ insert into encdata(id, data) values (7, '
+ -----BEGIN PGP MESSAGE-----
+ Version: GnuPG/MacGPG2 v2.0.19 (Darwin)
+ Comment: GPGTools - http://gpgtools.org
+ 
+ hQEMA/0CBsQJt0h1AQf/TbgfgQgH8QxP6THfNFKOW39TvV+v9Sb2p5Q7JRF6/YxG
+ n2N2ADkO0S63wE9HRH2xHAbxvaxO9nCHX48mTTi6sj/6fRdg3nDn9yvQcE994JaS
+ Wumn3d+7Pe8AqpwAyk6Tn2YSrdv8K3AKB0DuQI0FsXvjET8x7uBvD272c665od4k
+ FhgOzJrgtin6DKCUSVc8UZgDw4ZI/TAHrbf6pxiIX2rLdn1EAcjuPALiQKGvQIyH
+ I/B+Yq7j8sLhL60k3DEKHSjFqHR16LG4wsCKnNjzBM+Dto3nkklTcuy1Qu6D8B38
+ b1yVWO6IoUPf1aKahrzdFfv3J9jnmt7CMbxIfjqeqdLAsAG2e+dtdDu/own6lI6T
+ AM8TqvSCyKpjz8IN6FELe4rJq2LgS+FKJPcuFJV2JJs+eOo4O2PzVfdv8yJklysH
+ epU5tfrpYdkbsrR9pLhsbKGDINDmqENydAhFLUII2xdichVkYvk+gye+GS3E2EPp
+ aniMP/CuetL6qDIht9ADBCstBih8VFE7d7bNB//ldKc8cXKMJ/h1CHJ788sV2QBO
+ RHgHdWFE02JoK8WsDf/Wg5422Yca1JXhfr3wvHUwAvmnnIGzOUBaHbMSTlrgqNsR
+ nerdZxLfaxUQ8CjJ2yobn9OIAj4TAuITipssUsEVypT8m1lwsW2CaTuWUBcE9oC7
+ ULIfPPt+McDf1EYNtp+0UxZASFLETVYsLIfhNQxf8YnXFuVcLzvhdVRQKZ7oMC17
+ +0non8pele5HURJO7e3ULQihtb1i9GPtPXRjhyuR5K3n35NoZJHt4SCQPuRxRJIB
+ I4toPKPYCrND+X25oKaTrTMC
+ =WWPD
+ -----END PGP MESSAGE-----
+ ');
+ select * from pgp_pub_decrypt_verify_bytea((select dearmor(data) from encdata 
where id=7), (select dearmor(seckey) from keytbl where keytbl.name = 
'rsaenc2048'), (select dearmor(pubkey) from keytbl where keytbl.name = 
'rsa2048'));
+ 
+ -- pgp_main_key_id() should fail, even on signed data
+ select pgp_main_key_id(pgp_sym_encrypt_sign_bytea('Secret.', 'key', 
dearmor(seckey)))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ -- text mode
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey)), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ -- encrypt in binary, verify signature in text (doesn't work)
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign_bytea('Secret.', 'key', 
dearmor(seckey)), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign_bytea('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ -- encrypt in text, verify signature in binary (works)
+ 
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey)), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey)), dearmor(seckey), dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ -- encrypt in text with convert-crlf, verify signature in binary (works)
+ 
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ select pgp_sym_decrypt_verify_bytea(pgp_sym_encrypt_sign(E'Secret.\n', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify_bytea(pgp_pub_encrypt_sign(E'Secret.\n', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ -- encrypt in text with convert-crlf, verify with same (works)
+ 
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey), '', 
'convert-crlf=1')
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey), '', 'convert-crlf=1')
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign(E'Secret.\n', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey), '', 
'convert-crlf=1')
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign(E'Secret.\n', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey), '', 'convert-crlf=1')
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ -- encrypt in text with convert-crlf, verify in text without conversion 
(works)
+ 
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign('Secret.', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign('Secret.', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
+ select pgp_sym_decrypt_verify(pgp_sym_encrypt_sign(E'Secret.\n', 'key', 
dearmor(seckey), '', 'convert-crlf=1'), 'key', dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsa2048';
+ 
+ select pgp_pub_decrypt_verify(pgp_pub_encrypt_sign(E'Secret.\n', 
dearmor(pubkey), dearmor(seckey), '', 'convert-crlf=1'), dearmor(seckey), 
dearmor(pubkey))
+ from keytbl where keytbl.name = 'rsaenc2048';
+ 
*** a/doc/src/sgml/pgcrypto.sgml
--- b/doc/src/sgml/pgcrypto.sgml
***************
*** 535,549 **** gen_salt(type text [, iter_count integer ]) returns text
--- 535,565 ----
      <primary>pgp_sym_encrypt_bytea</primary>
     </indexterm>
  
+    <indexterm>
+     <primary>pgp_sym_encrypt_sign</primary>
+    </indexterm>
+ 
+    <indexterm>
+     <primary>pgp_sym_encrypt_sign_bytea</primary>
+    </indexterm>
+ 
  <synopsis>
  pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea
  pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea
+ pgp_sym_encrypt_sign(data text, psw text [, options text ]) returns bytea
+ pgp_sym_encrypt_sign_bytea(data bytea, psw text [, options text ]) returns 
bytea
  </synopsis>
     <para>
      Encrypt <parameter>data</> with a symmetric PGP key <parameter>psw</>.
      The <parameter>options</> parameter can contain option settings,
      as described below.
     </para>
+    <para>
+     The <literal>sign</> versions also sign the encrypted data using the 
secret
+     key <parameter>sigkey</>.  If this key is password-protected, you must 
give
+     the password in <parameter>psw</>.  If there is no password, but you want
+     to specify options, you need to give an empty password.
+    </para>
    </sect3>
  
    <sect3>
***************
*** 557,565 **** pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) 
returns bytea
--- 573,591 ----
      <primary>pgp_sym_decrypt_bytea</primary>
     </indexterm>
  
+    <indexterm>
+     <primary>pgp_sym_decrypt_verify</primary>
+    </indexterm>
+ 
+    <indexterm>
+     <primary>pgp_sym_decrypt_verify_bytea</primary>
+    </indexterm>
+ 
  <synopsis>
  pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text
  pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea
+ pgp_sym_decrypt_verify(msg bytea, psw text, sigkey bytea [, options text ]) 
returns text
+ pgp_sym_decrypt_verify_bytea(msg bytea, psw text, sigkey bytea [, options 
text ]) returns bytea
  </synopsis>
     <para>
      Decrypt a symmetric-key-encrypted PGP message.
***************
*** 570,575 **** pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) 
returns bytea
--- 596,608 ----
      originally textual data with <function>pgp_sym_decrypt_bytea</> is fine.
     </para>
     <para>
+     The <literal>verify</> versions also verify the signature against the
+     main public key provided in <parameter>sigkey</>.  An exception is raised
+     if no signature matching <parameter>sigkey's</> key ID is found or the
+     signature does not match.  If multiple signatures matching the key ID are
+     present in the message, an error is raised.
+    </para>
+    <para>
      The <parameter>options</> parameter can contain option settings,
      as described below.
     </para>
***************
*** 586,598 **** pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) 
returns bytea
      <primary>pgp_pub_encrypt_bytea</primary>
     </indexterm>
  
  <synopsis>
  pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
  pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
  </synopsis>
     <para>
      Encrypt <parameter>data</> with a public PGP key <parameter>key</>.
!     Giving this function a secret key will produce a error.
     </para>
     <para>
      The <parameter>options</> parameter can contain option settings,
--- 619,648 ----
      <primary>pgp_pub_encrypt_bytea</primary>
     </indexterm>
  
+    <indexterm>
+     <primary>pgp_pub_encrypt_sign</primary>
+    </indexterm>
+ 
+    <indexterm>
+     <primary>pgp_pub_encrypt_sign_bytea</primary>
+    </indexterm>
+ 
  <synopsis>
  pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
  pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
+ pgp_pub_encrypt_sign(data text, key bytea, sigkey bytea [, psw text [, 
options text ]]) returns bytea
+ pgp_pub_encrypt_sign_bytea(data bytea, key bytea, sigkey bytea [, psw text [, 
options text ]]) returns bytea
  </synopsis>
     <para>
      Encrypt <parameter>data</> with a public PGP key <parameter>key</>.
!     If a secret key is provided as the <parameter>key</> parameter, the
!     functions will produce an error.
!    </para>
!    <para>
!     The <literal>sign</> versions also sign the encrypted data using the 
secret
!     key <parameter>sigkey</>.  If this key is password-protected, you must 
give
!     the password in <parameter>psw</>.  If there is no password, but you want
!     to specify options, you need to give an empty password.
     </para>
     <para>
      The <parameter>options</> parameter can contain option settings,
***************
*** 611,619 **** pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) 
returns bytea
--- 661,679 ----
      <primary>pgp_pub_decrypt_bytea</primary>
     </indexterm>
  
+    <indexterm>
+     <primary>pgp_pub_decrypt_verify</primary>
+    </indexterm>
+ 
+    <indexterm>
+     <primary>pgp_pub_decrypt_verify_bytea</primary>
+    </indexterm>
+ 
  <synopsis>
  pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns 
text
  pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) 
returns bytea
+ pgp_pub_decrypt_verify(msg bytea, key bytea, sigkey bytea [, psw text [, 
options text ]]) returns text
+ pgp_pub_decrypt_verify_bytea(msg bytea, key bytea, sigkey bytea [, psw text 
[, options text ]]) returns bytea
  </synopsis>
     <para>
      Decrypt a public-key-encrypted message.  <parameter>key</> must be the
***************
*** 623,628 **** pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, 
options text ]]) retur
--- 683,695 ----
      options, you need to give an empty password.
     </para>
     <para>
+     The <literal>verify</> versions also verify the signature against the
+     main public key provided in <parameter>sigkey</>.  An exception is raised
+     if no signature matching <parameter>sigkey's</> key ID is found or the
+     signature does not match.  If multiple signatures matching the key ID are
+     present in the message, an error is raised.
+    </para>
+    <para>
      Decrypting <type>bytea</> data with <function>pgp_pub_decrypt</> is 
disallowed.
      This is to avoid outputting invalid character data.  Decrypting
      originally textual data with <function>pgp_pub_decrypt_bytea</> is fine.
***************
*** 680,685 **** pgp_key_id(bytea) returns text
--- 747,808 ----
    </sect3>
  
    <sect3>
+    <title><function>pgp_main_key_id()</function></title>
+ 
+    <indexterm>
+     <primary>pgp_main_key_id</primary>
+    </indexterm>
+ 
+ <synopsis>
+ pgp_main_key_id(bytea) returns text
+ </synopsis>
+    <para>
+     <function>pgp_main_key_id</> extracts the key ID of the main key of a PGP
+     public or secret key.  Unlike <function>pgp_key_id</>, this function only
+     extracts key IDs from keys and not encrypted messages.  See
+     <function>pgp_sym_signature_keys</> and 
<function>pgp_pub_signature_keys</>
+     if you want to extract the keys used to sign encrypted data.
+    </para>
+   </sect3>
+ 
+   <sect3>
+    <title><function>pgp_sym_signature_keys()</function></title>
+ 
+    <indexterm>
+     <primary>pgp_sym_signature_keys</primary>
+    </indexterm>
+ 
+ <synopsis>
+ pgp_sym_signature_keys(data bytea, key text) returns setof (keyid text, 
digest text, pubkeyalgo text)
+ </synopsis>
+    <para>
+     <function>pgp_sym_signature_keys</> extracts the list of signatures 
present
+     in the encrypted data in bytea.  The symmetric PGP key used to encrypt the
+     data should be provided in <parameter>key</>
+    </para>
+   </sect3>
+ 
+   <sect3>
+    <title><function>pgp_pub_signature_keys()</function></title>
+ 
+    <indexterm>
+     <primary>pgp_pub_signature_keys</primary>
+    </indexterm>
+ 
+ <synopsis>
+ pgp_pub_signature_keys(data bytea, key bytea [ , psw text]) returns setof 
(keyid text, digest text, pubkeyalgo text)
+ </synopsis>
+    <para>
+     <function>pgp_pub_signature_keys</> extracts the list of signatures 
present
+     in the encrypted data in bytea.  The secret key corresponding to the 
public
+     key used to encrypt the data should be provided in <parameter>key</>.  If
+     key is password-protected, the password should be provided in
+     <parameter>psw</>.
+    </para>
+   </sect3>
+ 
+ 
+   <sect3>
     <title><function>armor()</function>, <function>dearmor()</function></title>
  
     <indexterm>
***************
*** 786,791 **** Applies to: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, 
pgp_pub_decrypt
--- 909,927 ----
    </sect4>
  
    <sect4>
+    <title>digest-algo</title>
+ 
+    <para>
+     Which digest algorithm to use for generating signatures.
+    </para>
+ <literallayout>
+ Values: md5, sha1, sha256, sha384, sha512
+ Default: sha512
+ Applies to: pgp_sym_encrypt, pgp_pub_encrypt
+ </literallayout>
+   </sect4>
+ 
+   <sect4>
     <title>disable-mdc</title>
  
     <para>
***************
*** 929,942 **** gpg -a --export-secret-keys KEYID > secret.key
    <itemizedlist>
     <listitem>
      <para>
!     No support for signing.  That also means that it is not checked
!     whether the encryption subkey belongs to the master key.
!     </para>
!    </listitem>
!    <listitem>
!     <para>
!     No support for encryption key as master key.  As such practice
!     is generally discouraged, this should not be a problem.
      </para>
     </listitem>
     <listitem>
--- 1065,1073 ----
    <itemizedlist>
     <listitem>
      <para>
!     No support for master key as encryption key.  As such practice
!     is generally discouraged, this should not be a problem.  Similarly,
!     subkeys are not supported for signing.
      </para>
     </listitem>
     <listitem>
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to