Hi,
Currently there's no way to generate or extract armor headers from the
PGP armored format in pgcrypto. I've written a patch to add the
support. For example:
local:marko=#* select armor('zooka', array['Version', 'Comment'],
array['Created by pgcrypto', 'PostgreSQL, the database']);
armor
-----------------------------------
-----BEGIN PGP MESSAGE----- +
Version: Created by pgcrypto +
Comment: PostgreSQL, the database+
+
em9va2E= +
=D5cR +
-----END PGP MESSAGE----- +
local:marko=#* select pgp_armor_header(armor('zooka', array['Version',
'Comment'], array['Created by pgcrypto', 'PostgreSQL, the database']),
'Comment');
pgp_armor_header
--------------------------
PostgreSQL, the database
(1 row)
.marko
*** 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.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,362 ----
-----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)
+
+ -- 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 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)
+
*** 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,210 **** crc24(const uint8 *data, unsigned len)
}
int
! pgp_armor_encode(const uint8 *src, unsigned len, uint8 *dst)
{
int n;
uint8 *pos = dst;
--- 204,211 ----
}
int
! pgp_armor_encode(const uint8 *src, unsigned len, uint8 *dst,
! int num_headers, char **keys, char **values)
{
int n;
uint8 *pos = dst;
***************
*** 214,219 **** pgp_armor_encode(const uint8 *src, unsigned len, uint8 *dst)
--- 215,236 ----
memcpy(pos, armor_header, n);
pos += n;
+ for (n = 0; n < num_headers; n++)
+ {
+ size_t keylen = strlen(keys[n]);
+ size_t valuelen = strlen(values[n]);
+
+ memcpy(pos, keys[n], keylen);
+ pos += keylen;
+ *pos++ = ':';
+ *pos++ = ' ';
+ memcpy(pos, values[n], valuelen);
+ pos += valuelen;
+ *pos++ = '\n';
+ }
+
+ *pos++ = '\n';
+
n = b64_encode(src, len, pos);
pos += n;
***************
*** 369,378 **** out:
return res;
}
unsigned
! pgp_armor_enc_len(unsigned len)
{
! return b64_enc_len(len) + strlen(armor_header) + strlen(armor_footer) +
16;
}
unsigned
--- 386,500 ----
return res;
}
+ int
+ pgp_armor_header(const uint8 *src, unsigned len, const char *key,
+ unsigned key_len, uint8 **dst, int *out_len)
+ {
+ const uint8 *p = src;
+ const uint8 *data_end = src + len;
+ const uint8 *armor_end;
+ const uint8 *eol,
+ *colon;
+ MBuf *buf = NULL;
+ int hlen;
+ int res;
+
+ /* armor start */
+ hlen = find_header(src, data_end, &p, 0);
+ if (hlen <= 0)
+ goto out;
+ p += hlen;
+
+ /* armor end */
+ hlen = find_header(p, data_end, &armor_end, 1);
+ if (hlen <= 0)
+ goto out;
+
+ /* skip comments - find empty line */
+ while (p < armor_end)
+ {
+ 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;
+ }
+
+ if (key_len == colon - p &&
+ memcmp(p, key, key_len) == 0)
+ {
+ size_t valuelen;
+ valuelen = eol - colon - 2;
+ if (!buf)
+ buf = mbuf_create(valuelen + 1);
+
+ res = mbuf_append(buf, colon + 2, (int) valuelen);
+ if (res < 0)
+ goto out;
+ }
+ /* step to start of next line */
+ p = eol + 1;
+ }
+
+ out:
+ if (res < 0)
+ {
+ if (buf)
+ px_free(buf);
+ *dst = NULL;
+ *out_len = -1;
+ return res;
+ }
+ else
+ {
+ if (buf)
+ {
+ res = mbuf_append(buf, (uint8 *) "\x00", 1);
+ if (res < 0)
+ return res;
+ *out_len = mbuf_steal_data(buf, dst) - 1;
+ }
+ else
+ {
+ *dst = NULL;
+ *out_len = 0;
+ }
+ return 0;
+ }
+ }
+
unsigned
! pgp_armor_enc_len(unsigned len, int num_headers, char **keys, char **values)
{
! int i;
! unsigned header_length = 0;
!
! for (i = 0; i < num_headers; i++)
! header_length += strlen(keys[i]) + strlen(values[i]) + 2 + 1;
!
! return strlen(armor_header) + header_length + 1 +
! b64_enc_len(len) + strlen(armor_footer) + 16;
}
unsigned
*** a/contrib/pgcrypto/pgp-pgsql.c
--- b/contrib/pgcrypto/pgp-pgsql.c
***************
*** 31,38 ****
--- 31,40 ----
#include "postgres.h"
+ #include "catalog/pg_type.h"
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
+ #include "utils/array.h"
#include "mbuf.h"
#include "px.h"
***************
*** 55,60 **** PG_FUNCTION_INFO_V1(pgp_key_id_w);
--- 57,63 ----
PG_FUNCTION_INFO_V1(pg_armor);
PG_FUNCTION_INFO_V1(pg_dearmor);
+ PG_FUNCTION_INFO_V1(pgp_armor_header_w);
/*
* Mix a block of data into RNG.
***************
*** 815,820 **** pgp_pub_decrypt_text(PG_FUNCTION_ARGS)
--- 818,885 ----
* 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,837 **** pg_armor(PG_FUNCTION_ARGS)
int data_len,
res_len,
guess_len;
data = PG_GETARG_BYTEA_P(0);
data_len = VARSIZE(data) - VARHDRSZ;
! guess_len = pgp_armor_enc_len(data_len);
res = palloc(VARHDRSZ + guess_len);
res_len = pgp_armor_encode((uint8 *) VARDATA(data), data_len,
! (uint8 *)
VARDATA(res));
if (res_len > guess_len)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
--- 888,914 ----
int data_len,
res_len,
guess_len;
+ 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());
! guess_len = pgp_armor_enc_len(data_len, num_headers, keys, values);
res = palloc(VARHDRSZ + guess_len);
res_len = pgp_armor_encode((uint8 *) VARDATA(data), data_len,
! (uint8 *)
VARDATA(res),
! num_headers, keys,
values);
if (res_len > guess_len)
ereport(ERROR,
(errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
***************
*** 873,878 **** pg_dearmor(PG_FUNCTION_ARGS)
--- 950,1001 ----
PG_RETURN_TEXT_P(res);
}
+ Datum
+ pgp_armor_header_w(PG_FUNCTION_ARGS)
+ {
+ bytea *data;
+ int data_len,
+ res;
+ char *buf;
+ char *key,
+ *utf8key;
+ int buflen;
+
+ data = PG_GETARG_BYTEA_P(0);
+ data_len = VARSIZE(data) - VARHDRSZ;
+
+ /* convert the key to UTF-8 */
+ key = text_to_cstring(PG_GETARG_TEXT_PP(1));
+ utf8key = pg_server_to_any(key, strlen(key), PG_UTF8);
+
+ res = pgp_armor_header((uint8 *) VARDATA(data), data_len,
+ utf8key,
strlen(utf8key),
+ (uint8 **) &buf,
&buflen);
+ 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);
+ pfree(key);
+ if (!buf)
+ PG_RETURN_NULL();
+ else
+ {
+ /* assume it's UTF-8 */
+ char *utf;
+ text *result;
+
+ utf = pg_any_to_server(buf, buflen, PG_UTF8);
+ result = cstring_to_text(utf);
+ PG_RETURN_TEXT_P(result);
+ }
+ }
+
+
+
/*
* Wrappers for PGP key id
*/
*** a/contrib/pgcrypto/pgp.h
--- b/contrib/pgcrypto/pgp.h
***************
*** 274,282 **** void pgp_cfb_free(PGP_CFB *ctx);
int pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int
len, uint8 *dst);
int pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int
len, uint8 *dst);
! int pgp_armor_encode(const uint8 *src, unsigned len, uint8
*dst);
int pgp_armor_decode(const uint8 *src, unsigned len, uint8
*dst);
! unsigned pgp_armor_enc_len(unsigned len);
unsigned pgp_armor_dec_len(unsigned len);
int pgp_compress_filter(PushFilter **res, PGP_Context *ctx,
PushFilter *dst);
--- 274,286 ----
int pgp_cfb_encrypt(PGP_CFB *ctx, const uint8 *data, int
len, uint8 *dst);
int pgp_cfb_decrypt(PGP_CFB *ctx, const uint8 *data, int
len, uint8 *dst);
! int pgp_armor_encode(const uint8 *src, unsigned len, uint8
*dst,
! int num_headers, char
**keys, char **values);
int pgp_armor_decode(const uint8 *src, unsigned len, uint8
*dst);
! int pgp_armor_header(const uint8 *src, unsigned len,
! const char *key,
unsigned key_len,
! uint8 **dst, int
*out_len);
! unsigned pgp_armor_enc_len(unsigned len, int num_headers, char **keys,
char **values);
unsigned pgp_armor_dec_len(unsigned len);
int pgp_compress_filter(PushFilter **res, PGP_Context *ctx,
PushFilter *dst);
*** a/contrib/pgcrypto/sql/pgp-armor.sql
--- b/contrib/pgcrypto/sql/pgp-armor.sql
***************
*** 56,58 **** em9va2E=
--- 56,216 ----
=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');
+
+ -- 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 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,706 **** pgp_key_id(bytea) returns text
</indexterm>
<synopsis>
! armor(data bytea) returns text
dearmor(data text) returns bytea
</synopsis>
<para>
These functions wrap/unwrap binary data into PGP ASCII-armor format,
which is basically Base64 with CRC and additional formatting.
</para>
</sect3>
<sect3>
<title>Options for PGP Functions</title>
<para>
--- 691,738 ----
</indexterm>
<synopsis>
! armor(data bytea [ , keys text[], values text[] ]) returns text
dearmor(data text) returns bytea
</synopsis>
<para>
These functions wrap/unwrap binary data into PGP ASCII-armor format,
which is basically Base64 with CRC and additional formatting.
</para>
+
+ <para>
+ For <function>armor</>, if the <parameter>keys</> and <parameter>values</>
+ arrays are specified, their members are written into the armored data as
+ <literal>armor headers</>. For each member in <parameter>keys</>, the
+ value in <parameter>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.
+ </para>
</sect3>
<sect3>
+ <title><function>pgp_armor_header</function></title>
+
+ <indexterm>
+ <primary>pgp_armor_header</primary>
+ </indexterm>
+
+ <synopsis>
+ pgp_armor_header(data text, key text) returns text
+ </synopsis>
+ <para>
+ <function>pgp_armor_header()</> extracts the <literal>armor header</> with
+ the key <parameter>key</> from <parameter>data</>. Before matching,
+ <parameter>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
+ <parameter>key</> can not be converted to UTF-8, an error is returned.
+ If the key <parameter>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.
+ </para>
+ </sect3>
+
+
+ <sect3>
<title>Options for PGP Functions</title>
<para>
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers