*** a/contrib/pgcrypto/Makefile
--- b/contrib/pgcrypto/Makefile
***************
*** 26,32 **** 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 \
--- 26,32 ----
OBJS = $(SRCS:.c=.o) $(WIN32RES)
EXTENSION = pgcrypto
! DATA = pgcrypto--1.2.sql pgcrypto--1.1--1.2.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 \
*** a/contrib/pgcrypto/expected/pgp-armor.out
--- b/contrib/pgcrypto/expected/pgp-armor.out
***************
*** 102,104 **** em9va2E=
--- 102,423 ----
-----END PGP MESSAGE-----
');
ERROR: Corrupt ascii-armor
+ -- corrupt
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ foo:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+ ERROR: Corrupt ascii-armor
+ -- empty
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ foo:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+ pgp_armor_header
+ ------------------
+
+ (1 row)
+
+ -- simple
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ foo: bar
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+ pgp_armor_header
+ ------------------
+ bar
+ (1 row)
+
+ -- uninteresting keys, part 1
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ foo: found
+ bar: ignored
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+ pgp_armor_header
+ ------------------
+ found
+ (1 row)
+
+ -- uninteresting keys, part 2
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ bar: ignored
+ foo: found
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+ pgp_armor_header
+ ------------------
+ found
+ (1 row)
+
+ -- uninteresting keys, part 3
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ bar: ignored
+ foo: found
+ bar: ignored
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+ pgp_armor_header
+ ------------------
+ found
+ (1 row)
+
+ -- insane keys, part 1
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ insane:key :
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'insane:key ');
+ pgp_armor_header
+ ------------------
+
+ (1 row)
+
+ -- insane keys, part 2
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ insane:key : text value here
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'insane:key ');
+ pgp_armor_header
+ ------------------
+ text value here
+ (1 row)
+
+ -- long value
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ long: this value is more than 76 characters long, but it should still parse correctly as that''s permitted by RFC 4880
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'long');
+ pgp_armor_header
+ -----------------------------------------------------------------------------------------------------------------
+ this value is more than 76 characters long, but it should still parse correctly as that's permitted by RFC 4880
+ (1 row)
+
+ -- long value, split up
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ long: this value is more than 76 characters long, but it should still
+ long: parse correctly as that''s permitted by RFC 4880
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'long');
+ pgp_armor_header
+ -----------------------------------------------------------------------------------------------------------------
+ this value is more than 76 characters long, but it should still parse correctly as that's permitted by RFC 4880
+ (1 row)
+
+ -- long value, split up, part 2
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ long: this value is more than
+ long: 76 characters long, but it should still
+ long: parse correctly as that''s permitted by RFC 4880
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'long');
+ pgp_armor_header
+ -----------------------------------------------------------------------------------------------------------------
+ this value is more than 76 characters long, but it should still parse correctly as that's permitted by RFC 4880
+ (1 row)
+
+ -- long value, split up, part 3
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ ignored:
+ long: this value is more than
+ ignored:
+ long: 76 characters long, but it should still
+ ignored:
+ long: parse correctly as that''s permitted by RFC 4880
+ ignored:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'long');
+ pgp_armor_header
+ -----------------------------------------------------------------------------------------------------------------
+ this value is more than 76 characters long, but it should still parse correctly as that's permitted by RFC 4880
+ (1 row)
+
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ Comment: dat1.blowfish.sha1.mdc.s2k3.z0
+
+ jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
+ yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
+ =JcP+
+ -----END PGP MESSAGE-----
+ ', 'Comment');
+ pgp_armor_header
+ --------------------------------
+ dat1.blowfish.sha1.mdc.s2k3.z0
+ (1 row)
+
+ -- corrupt
+ select pgp_armor_header_keys('
+ -----BEGIN PGP MESSAGE-----
+ foo:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ');
+ ERROR: Corrupt ascii-armor
+ -- empty
+ select pgp_armor_header_keys('
+ -----BEGIN PGP MESSAGE-----
+ foo:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ');
+ pgp_armor_header_keys
+ -----------------------
+ foo
+ (1 row)
+
+ -- simple
+ select pgp_armor_header_keys('
+ -----BEGIN PGP MESSAGE-----
+ foo: bar
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ');
+ pgp_armor_header_keys
+ -----------------------
+ foo
+ (1 row)
+
+ -- duplicates should be eliminated
+ select pgp_armor_header_keys('
+ -----BEGIN PGP MESSAGE-----
+ nodups:
+ long: this value is more than
+ nodups:
+ long: 76 characters long, but it should still
+ nodups:
+ long: parse correctly as that''s permitted by RFC 4880
+ nodups:
+ reallynodups:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ');
+ pgp_armor_header_keys
+ -----------------------
+ nodups
+ long
+ reallynodups
+ (3 rows)
+
+ -- test header generation
+ select armor('zooka', array['foo'], array['bar']);
+ armor
+ -----------------------------
+ -----BEGIN PGP MESSAGE-----+
+ foo: bar +
+ +
+ em9va2E= +
+ =D5cR +
+ -----END PGP MESSAGE----- +
+
+ (1 row)
+
+ select armor('zooka', array['Version', 'Comment'], array['Created by pgcrypto', 'PostgreSQL, the world''s most most advanced open source database']);
+ armor
+ --------------------------------------------------------------------------
+ -----BEGIN PGP MESSAGE----- +
+ Version: Created by pgcrypto +
+ Comment: PostgreSQL, the world's most most advanced open source database+
+ +
+ em9va2E= +
+ =D5cR +
+ -----END PGP MESSAGE----- +
+
+ (1 row)
+
+ select pgp_armor_header(armor('zooka', array['Version', 'Comment'], array['Created by pgcrypto', 'PostgreSQL, the world''s most most advanced open source database']), 'Comment');
+ pgp_armor_header
+ -----------------------------------------------------------------
+ PostgreSQL, the world's most most advanced open source database
+ (1 row)
+
+ -- error/corner cases
+ select armor('', array['foo'], array['too', 'many']);
+ ERROR: mismatched array dimensions
+ select armor('', array['too', 'many'], array['foo']);
+ ERROR: mismatched array dimensions
+ select armor('', array[['']], array['foo']);
+ ERROR: wrong number of array subscripts
+ select armor('', array['foo'], array[['']]);
+ ERROR: wrong number of array subscripts
+ select armor('', array[null], array['foo']);
+ ERROR: null value not allowed for header key
+ select armor('', array['foo'], array[null]);
+ ERROR: null value not allowed for header value
+ select armor('', '[0:0]={"foo"}', array['foo']);
+ armor
+ -----------------------------
+ -----BEGIN PGP MESSAGE-----+
+ foo: foo +
+ +
+ =twTO +
+ -----END PGP MESSAGE----- +
+
+ (1 row)
+
+ select armor('', array['foo'], '[0:0]={"foo"}');
+ armor
+ -----------------------------
+ -----BEGIN PGP MESSAGE-----+
+ foo: foo +
+ +
+ =twTO +
+ -----END PGP MESSAGE----- +
+
+ (1 row)
+
*** /dev/null
--- b/contrib/pgcrypto/pgcrypto--1.1--1.2.sql
***************
*** 0 ****
--- 1,19 ----
+ /* contrib/pgcrypto/pgcrypto--1.1--1.2.sql */
+
+ -- complain if script is sourced in psql, rather than via ALTER EXTENSION
+ \echo Use "ALTER EXTENSION pgcrypto UPDATE TO '1.2'" to load this file. \quit
+
+ CREATE FUNCTION armor(bytea, text[], text[])
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pg_armor'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION pgp_armor_header(text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_armor_header'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION pgp_armor_header_keys(text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_armor_header_keys'
+ LANGUAGE C IMMUTABLE STRICT;
*** /dev/null
--- b/contrib/pgcrypto/pgcrypto--1.2.sql
***************
*** 0 ****
--- 1,222 ----
+ /* contrib/pgcrypto/pgcrypto--1.1.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_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_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_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 key ID
+ --
+ CREATE FUNCTION pgp_key_id(bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_key_id_w'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ --
+ -- pgp armor
+ --
+ CREATE FUNCTION armor(bytea)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pg_armor'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION armor(bytea, text[], text[])
+ 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;
+
+ CREATE FUNCTION pgp_armor_header(text, text)
+ RETURNS text
+ AS 'MODULE_PATHNAME', 'pgp_armor_header'
+ LANGUAGE C IMMUTABLE STRICT;
+
+ CREATE FUNCTION pgp_armor_header_keys(text)
+ RETURNS SETOF text
+ AS 'MODULE_PATHNAME', 'pgp_armor_header_keys'
+ 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-armor.c
--- b/contrib/pgcrypto/pgp-armor.c
***************
*** 178,184 **** b64_dec_len(unsigned srclen)
* PGP armor
*/
! static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n\n";
static const char *armor_footer = "\n-----END PGP MESSAGE-----\n";
/* CRC24 implementation from rfc2440 */
--- 178,184 ----
* PGP armor
*/
! static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n";
static const char *armor_footer = "\n-----END PGP MESSAGE-----\n";
/* CRC24 implementation from rfc2440 */
***************
*** 204,217 **** crc24(const uint8 *data, unsigned len)
}
void
! pgp_armor_encode(const uint8 *src, int len, StringInfo dst)
{
int res;
unsigned b64len;
unsigned crc = crc24(src, len);
appendStringInfoString(dst, armor_header);
/* make sure we have enough room to b64_encode() */
b64len = b64_enc_len(len);
enlargeStringInfo(dst, (int) b64len);
--- 204,229 ----
}
void
! pgp_armor_encode(const uint8 *src, unsigned len, StringInfo dst,
! int num_headers, char **keys, char **values)
{
+ int n;
int res;
unsigned b64len;
unsigned crc = crc24(src, len);
appendStringInfoString(dst, armor_header);
+ for (n = 0; n < num_headers; n++)
+ {
+ appendStringInfoString(dst, keys[n]);
+ appendStringInfoChar(dst, ':');
+ appendStringInfoChar(dst, ' ');
+ appendStringInfoString(dst, values[n]);
+ appendStringInfoChar(dst, '\n');
+ }
+ appendStringInfoChar(dst, '\n');
+
/* make sure we have enough room to b64_encode() */
b64len = b64_enc_len(len);
enlargeStringInfo(dst, (int) b64len);
***************
*** 371,373 **** pgp_armor_decode(const uint8 *src, int len, StringInfo dst)
--- 383,516 ----
out:
return res;
}
+
+ /*
+ * pgp_extract_armor_headers can be used in two different ways:
+ *
+ * 1) If key and valuedst are not NULL, this function finds the value for the
+ * key specified by *key, and copies it to valuedst. key_len should be
+ * set to the length of the string in *key, which need not be null
+ * terminated. The return value is 1 if the key was found, 0 if it was
+ * not found, or negative if an error occurred.
+ * 2) If keylistdst is not NULL, *keylistdst is set to a list containing the
+ * set of armor header keys present in the armor. The return value is 0,
+ * or negative if an error occurred.
+ */
+ int
+ pgp_extract_armor_headers(const uint8 *src, unsigned len, const char *key,
+ unsigned key_len, StringInfo valuedst,
+ List **keylistdst)
+ {
+ const uint8 *p = src;
+ const uint8 *data_end = src + len;
+ const uint8 *armor_end;
+ const uint8 *eol,
+ *colon;
+ int hlen;
+ int res = PXE_PGP_CORRUPT_ARMOR;
+ bool found = false;
+ List *keys = NIL;
+
+ Assert((valuedst != NULL) != (keylistdst != NULL));
+ Assert((key == NULL) == (valuedst == NULL));
+
+ /* armor start */
+ hlen = find_header(src, data_end, &p, 0);
+ if (hlen <= 0)
+ goto out;
+ p += hlen;
+
+ /* armor end */
+ hlen = find_header(p, data_end, &armor_end, 1);
+ if (hlen <= 0)
+ goto out;
+
+ /* read comments until an empty line or the end of data */
+ while (p < armor_end)
+ {
+ res = PXE_PGP_CORRUPT_ARMOR;
+
+ if (*p == '\n' || *p == '\r')
+ {
+ res = 0;
+ break;
+ }
+
+ eol = memchr(p, '\n', armor_end - p);
+ if (!eol)
+ goto out;
+
+ /* find the next key */
+ colon = p;
+ while (1)
+ {
+ colon = memchr(colon, ':', eol - colon);
+ if (!colon)
+ goto out;
+ if (colon == eol)
+ goto out;
+
+ /* if it's not followed by a space, this isn't the full key */
+ if (*(colon + 1) == ' ')
+ break;
+ colon = colon + 1;
+ }
+
+ /*
+ * See if this the key we're looking for. Note that even if it is, we
+ * need to keep scanning the headers since it might be split into
+ * multiple lines.
+ */
+ if (valuedst != NULL &&
+ key_len == colon - p &&
+ memcmp(p, key, key_len) == 0)
+ {
+ appendBinaryStringInfo(valuedst, (const char *) colon + 2, (int) (eol - colon - 2));
+ found = true;
+ }
+ else if (keylistdst != NULL)
+ {
+ char *key;
+ size_t len = colon - p;
+ ListCell *lc;
+
+ key = palloc(len + 1);
+ memcpy(key, p, len);
+ key[len] = '\0';
+
+ /* check for duplicates */
+ found = false;
+ foreach(lc, keys)
+ {
+ if (strcmp(key, lfirst(lc)) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ keys = lappend(keys, key);
+ else
+ pfree(key);
+ }
+ /* step to start of next line */
+ p = eol + 1;
+ }
+
+ out:
+ if (res < 0)
+ {
+ if (keys != NIL)
+ list_free_deep(keys);
+ return res;
+ }
+ if (keylistdst != NULL)
+ {
+ *keylistdst = keys;
+ return 0;
+ }
+ else if (valuedst != NULL)
+ return found ? 1 : 0;
+ else
+ elog(ERROR, "unexpected input arguments to pgp_extract_armor_headers");
+ }
*** a/contrib/pgcrypto/pgp-pgsql.c
--- b/contrib/pgcrypto/pgp-pgsql.c
***************
*** 32,39 ****
--- 32,42 ----
#include "postgres.h"
#include "lib/stringinfo.h"
+ #include "catalog/pg_type.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
+ #include "utils/array.h"
+ #include "funcapi.h"
#include "mbuf.h"
#include "px.h"
***************
*** 56,61 **** PG_FUNCTION_INFO_V1(pgp_key_id_w);
--- 59,66 ----
PG_FUNCTION_INFO_V1(pg_armor);
PG_FUNCTION_INFO_V1(pg_dearmor);
+ PG_FUNCTION_INFO_V1(pgp_armor_header);
+ PG_FUNCTION_INFO_V1(pgp_armor_header_keys);
/*
* Mix a block of data into RNG.
***************
*** 816,821 **** pgp_pub_decrypt_text(PG_FUNCTION_ARGS)
--- 821,888 ----
* Wrappers for PGP ascii armor
*/
+ static int
+ parse_key_value_arrays(ArrayType *key_array, ArrayType *val_array,
+ char ***p_keys, char ***p_values)
+ {
+ int nkdims = ARR_NDIM(key_array);
+ int nvdims = ARR_NDIM(val_array);
+ char **keys,
+ **values;
+ Datum *key_datums,
+ *val_datums;
+ bool *key_nulls,
+ *val_nulls;
+ int key_count,
+ val_count;
+ int i;
+
+ if (nkdims > 1 || nkdims != nvdims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("wrong number of array subscripts")));
+ if (nkdims == 0)
+ return 0;
+
+ deconstruct_array(key_array,
+ TEXTOID, -1, false, 'i',
+ &key_datums, &key_nulls, &key_count);
+
+ deconstruct_array(val_array,
+ TEXTOID, -1, false, 'i',
+ &val_datums, &val_nulls, &val_count);
+
+ if (key_count != val_count)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("mismatched array dimensions")));
+
+ keys = (char **) palloc(sizeof(char *) * key_count);
+ values = (char **) palloc(sizeof(char *) * val_count);
+
+ for (i = 0; i < key_count; i++)
+ {
+ char *v;
+
+ if (key_nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value not allowed for header key")));
+ if (val_nulls[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value not allowed for header value")));
+ v = TextDatumGetCString(key_datums[i]);
+ keys[i] = pg_server_to_any(v, strlen(v), PG_UTF8);
+ v = TextDatumGetCString(val_datums[i]);
+ values[i] = pg_server_to_any(v, strlen(v), PG_UTF8);
+ }
+
+ *p_keys = keys;
+ *p_values = values;
+ return key_count;
+ }
+
Datum
pg_armor(PG_FUNCTION_ARGS)
{
***************
*** 823,835 **** pg_armor(PG_FUNCTION_ARGS)
text *res;
int data_len;
StringInfoData buf;
data = PG_GETARG_BYTEA_P(0);
data_len = VARSIZE(data) - VARHDRSZ;
initStringInfo(&buf);
! pgp_armor_encode((uint8 *) VARDATA(data), data_len, &buf);
res = palloc(VARHDRSZ + buf.len);
SET_VARSIZE(res, VARHDRSZ + buf.len);
--- 890,914 ----
text *res;
int data_len;
StringInfoData buf;
+ int num_headers = 0;
+ char **keys = NULL,
+ **values = NULL;
data = PG_GETARG_BYTEA_P(0);
data_len = VARSIZE(data) - VARHDRSZ;
+ if (PG_NARGS() == 3)
+ {
+ num_headers = parse_key_value_arrays(PG_GETARG_ARRAYTYPE_P(1),
+ PG_GETARG_ARRAYTYPE_P(2),
+ &keys, &values);
+ }
+ else if (PG_NARGS() != 1)
+ elog(ERROR, "unexpected number of arguments %d", PG_NARGS());
initStringInfo(&buf);
! pgp_armor_encode((uint8 *) VARDATA(data), data_len, &buf,
! num_headers, keys, values);
res = palloc(VARHDRSZ + buf.len);
SET_VARSIZE(res, VARHDRSZ + buf.len);
***************
*** 868,873 **** pg_dearmor(PG_FUNCTION_ARGS)
--- 947,1052 ----
PG_RETURN_TEXT_P(res);
}
+ Datum
+ pgp_armor_header(PG_FUNCTION_ARGS)
+ {
+ bytea *data;
+ int data_len,
+ res;
+ text *key,
+ *utf8key;
+ StringInfoData buf;
+
+ data = PG_GETARG_BYTEA_P(0);
+ data_len = VARSIZE(data) - VARHDRSZ;
+
+ key = PG_GETARG_TEXT_P(1);
+ utf8key = convert_to_utf8(key);
+
+ initStringInfo(&buf);
+ res = pgp_extract_armor_headers((uint8 *) VARDATA(data), data_len,
+ VARDATA(utf8key), VARSIZE(utf8key) - VARHDRSZ,
+ &buf, NULL);
+ if (res < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("%s", px_strerror(res))));
+
+ PG_FREE_IF_COPY(data, 0);
+ if (utf8key != key)
+ pfree(utf8key);
+ PG_FREE_IF_COPY(key, 1);
+ if (res == 0)
+ {
+ pfree(buf.data);
+ PG_RETURN_NULL();
+ }
+ else
+ {
+ /* assume it's UTF-8 */
+ char *utf;
+ text *result;
+
+ /* 0-terminate the string for cstring_to_text */
+ appendStringInfoChar(&buf, '\x00');
+ utf = pg_any_to_server(buf.data, buf.len - 1, PG_UTF8);
+ result = cstring_to_text(utf);
+ pfree(buf.data);
+ PG_RETURN_TEXT_P(result);
+ }
+ }
+
+ Datum
+ pgp_armor_header_keys(PG_FUNCTION_ARGS)
+ {
+ FuncCallContext *funcctx;
+ List *keys;
+
+ if (SRF_IS_FIRSTCALL())
+ {
+ bytea *data;
+ int data_len,
+ res;
+ MemoryContext oldcontext;
+
+ data = PG_GETARG_BYTEA_P(0);
+ data_len = VARSIZE(data) - VARHDRSZ;
+
+ funcctx = SRF_FIRSTCALL_INIT();
+
+ /* we need the resulting list allocated in the multi call context */
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+ res = pgp_extract_armor_headers((uint8 *) VARDATA(data), data_len,
+ NULL, 0, NULL, &keys);
+ if (res < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
+ errmsg("%s", px_strerror(res))));
+
+ MemoryContextSwitchTo(oldcontext);
+ funcctx->user_fctx = keys;
+ }
+
+ funcctx = SRF_PERCALL_SETUP();
+ keys = (List *) funcctx->user_fctx;
+ if (keys == NIL)
+ SRF_RETURN_DONE(funcctx);
+ else
+ {
+ /* we assume the keys are UTF-8 */
+ char *utf;
+ text *result;
+
+ utf = pg_any_to_server(linitial(keys), strlen(linitial(keys)), PG_UTF8);
+ result = cstring_to_text(utf);
+ funcctx->user_fctx = list_delete_first(keys);
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(result));
+ }
+ }
+
+
+
/*
* Wrappers for PGP key id
*/
*** a/contrib/pgcrypto/pgp.h
--- b/contrib/pgcrypto/pgp.h
***************
*** 30,35 ****
--- 30,36 ----
*/
#include "lib/stringinfo.h"
+ #include "nodes/pg_list.h"
#include "mbuf.h"
#include "px.h"
***************
*** 276,283 **** void pgp_cfb_free(PGP_CFB *ctx);
int pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
int pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
! void pgp_armor_encode(const uint8 *src, int len, StringInfo dst);
int pgp_armor_decode(const uint8 *src, int len, StringInfo dst);
int pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst);
int pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src);
--- 277,288 ----
int pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
int pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int len, uint8 *dst);
! void pgp_armor_encode(const uint8 *src, unsigned len, StringInfo dst,
! int num_headers, char **keys, char **values);
int pgp_armor_decode(const uint8 *src, int len, StringInfo dst);
+ int pgp_extract_armor_headers(const uint8 *src, unsigned len,
+ const char *key, unsigned key_len,
+ StringInfo valuedst, List **keylistdst);
int pgp_compress_filter(PushFilter **res, PGP_Context *ctx, PushFilter *dst);
int pgp_decompress_filter(PullFilter **res, PGP_Context *ctx, PullFilter *src);
*** a/contrib/pgcrypto/sql/pgp-armor.sql
--- b/contrib/pgcrypto/sql/pgp-armor.sql
***************
*** 56,58 **** em9va2E=
--- 56,263 ----
=ZZZZ
-----END PGP MESSAGE-----
');
+
+ -- corrupt
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ foo:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+
+ -- empty
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ foo:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+
+ -- simple
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ foo: bar
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+
+ -- uninteresting keys, part 1
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ foo: found
+ bar: ignored
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+
+ -- uninteresting keys, part 2
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ bar: ignored
+ foo: found
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+
+ -- uninteresting keys, part 3
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ bar: ignored
+ foo: found
+ bar: ignored
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'foo');
+
+ -- insane keys, part 1
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ insane:key :
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'insane:key ');
+
+ -- insane keys, part 2
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ insane:key : text value here
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'insane:key ');
+
+ -- long value
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ long: this value is more than 76 characters long, but it should still parse correctly as that''s permitted by RFC 4880
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'long');
+
+ -- long value, split up
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ long: this value is more than 76 characters long, but it should still
+ long: parse correctly as that''s permitted by RFC 4880
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'long');
+
+ -- long value, split up, part 2
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ long: this value is more than
+ long: 76 characters long, but it should still
+ long: parse correctly as that''s permitted by RFC 4880
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'long');
+
+ -- long value, split up, part 3
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ ignored:
+ long: this value is more than
+ ignored:
+ long: 76 characters long, but it should still
+ ignored:
+ long: parse correctly as that''s permitted by RFC 4880
+ ignored:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ', 'long');
+
+ select pgp_armor_header('
+ -----BEGIN PGP MESSAGE-----
+ Comment: dat1.blowfish.sha1.mdc.s2k3.z0
+
+ jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS
+ yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE=
+ =JcP+
+ -----END PGP MESSAGE-----
+ ', 'Comment');
+
+ -- corrupt
+ select pgp_armor_header_keys('
+ -----BEGIN PGP MESSAGE-----
+ foo:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ');
+
+ -- empty
+ select pgp_armor_header_keys('
+ -----BEGIN PGP MESSAGE-----
+ foo:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ');
+
+ -- simple
+ select pgp_armor_header_keys('
+ -----BEGIN PGP MESSAGE-----
+ foo: bar
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ');
+
+ -- duplicates should be eliminated
+ select pgp_armor_header_keys('
+ -----BEGIN PGP MESSAGE-----
+ nodups:
+ long: this value is more than
+ nodups:
+ long: 76 characters long, but it should still
+ nodups:
+ long: parse correctly as that''s permitted by RFC 4880
+ nodups:
+ reallynodups:
+
+ em9va2E=
+ =ZZZZ
+ -----END PGP MESSAGE-----
+ ');
+
+ -- test header generation
+ select armor('zooka', array['foo'], array['bar']);
+ select armor('zooka', array['Version', 'Comment'], array['Created by pgcrypto', 'PostgreSQL, the world''s most most advanced open source database']);
+ select pgp_armor_header(armor('zooka', array['Version', 'Comment'], array['Created by pgcrypto', 'PostgreSQL, the world''s most most advanced open source database']), 'Comment');
+
+ -- error/corner cases
+ select armor('', array['foo'], array['too', 'many']);
+ select armor('', array['too', 'many'], array['foo']);
+ select armor('', array[['']], array['foo']);
+ select armor('', array['foo'], array[['']]);
+ select armor('', array[null], array['foo']);
+ select armor('', array['foo'], array[null]);
+ select armor('', '[0:0]={"foo"}', array['foo']);
+ select armor('', array['foo'], '[0:0]={"foo"}');
*** a/doc/src/sgml/pgcrypto.sgml
--- b/doc/src/sgml/pgcrypto.sgml
***************
*** 691,703 **** pgp_key_id(bytea) returns text
! armor(data bytea) returns text
dearmor(data text) returns bytea
These functions wrap/unwrap binary data into PGP ASCII-armor format,
which is basically Base64 with CRC and additional formatting.
--- 691,751 ----
! armor(data bytea [ , keys text[], values text[] ]) returns text
dearmor(data text) returns bytea
These functions wrap/unwrap binary data into PGP ASCII-armor format,
which is basically Base64 with CRC and additional formatting.
+
+
+ For armor>, if the keys> and values>
+ arrays are specified, their members are written into the armored data as
+ armor headers>. For each member in keys>, the
+ value in values> with the corresponding ordinal is used as
+ the value for that key. Both arrays must be single-dimensional, and they
+ must be of the same length. All text is converted into UTF-8.
+
+
+
+
+ pgp_armor_header
+
+
+ pgp_armor_header
+
+
+
+ pgp_armor_header(data text, key text) returns text
+
+
+ pgp_armor_header()> extracts the armor header> with
+ the key key> from data>. Before matching,
+ key> is converted into UTF-8. Also all data in the armored
+ text is assumed to be UTF-8. If part of the data is not valid UTF-8 or
+ key> can not be converted to UTF-8, an error is raised.
+ If the key key> appears multiple times in the armored text,
+ all values are concatenated into the return value. If the key does not
+ appear in the armored text, the return value is NULL.
+
+
+
+
+ pgp_armor_header_keys
+
+
+ pgp_armor_header_keys
+
+
+
+ pgp_armor_header_keys(data text) returns setof text
+
+
+ pgp_armor_header_keys()> extracts the list of armor
+ header> keys from data>. The keys are all assumed to be in
+ UTF-8. If any of the keys is not valid UTF-8, an error is raised.
+