| /* |
| * pgp-pgsql.c |
| * PostgreSQL wrappers for pgp. |
| * |
| * Copyright (c) 2005 Marko Kreen |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * contrib/pgcrypto/pgp-pgsql.c |
| */ |
| |
| #include "postgres.h" |
| |
| #include "mb/pg_wchar.h" |
| #include "utils/builtins.h" |
| |
| #include "mbuf.h" |
| #include "px.h" |
| #include "pgp.h" |
| |
| /* |
| * public functions |
| */ |
| Datum pgp_sym_encrypt_text(PG_FUNCTION_ARGS); |
| Datum pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS); |
| Datum pgp_sym_decrypt_text(PG_FUNCTION_ARGS); |
| Datum pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS); |
| |
| Datum pgp_pub_encrypt_text(PG_FUNCTION_ARGS); |
| Datum pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS); |
| Datum pgp_pub_decrypt_text(PG_FUNCTION_ARGS); |
| Datum pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS); |
| |
| Datum pgp_key_id_w(PG_FUNCTION_ARGS); |
| |
| Datum pg_armor(PG_FUNCTION_ARGS); |
| Datum pg_dearmor(PG_FUNCTION_ARGS); |
| |
| /* function headers */ |
| |
| PG_FUNCTION_INFO_V1(pgp_sym_encrypt_bytea); |
| PG_FUNCTION_INFO_V1(pgp_sym_encrypt_text); |
| PG_FUNCTION_INFO_V1(pgp_sym_decrypt_bytea); |
| PG_FUNCTION_INFO_V1(pgp_sym_decrypt_text); |
| |
| PG_FUNCTION_INFO_V1(pgp_pub_encrypt_bytea); |
| PG_FUNCTION_INFO_V1(pgp_pub_encrypt_text); |
| PG_FUNCTION_INFO_V1(pgp_pub_decrypt_bytea); |
| PG_FUNCTION_INFO_V1(pgp_pub_decrypt_text); |
| |
| PG_FUNCTION_INFO_V1(pgp_key_id_w); |
| |
| PG_FUNCTION_INFO_V1(pg_armor); |
| PG_FUNCTION_INFO_V1(pg_dearmor); |
| |
| /* |
| * Mix a block of data into RNG. |
| */ |
| static void |
| add_block_entropy(PX_MD *md, text *data) |
| { |
| uint8 sha1[20]; |
| |
| px_md_reset(md); |
| px_md_update(md, (uint8 *) VARDATA(data), VARSIZE(data) - VARHDRSZ); |
| px_md_finish(md, sha1); |
| |
| px_add_entropy(sha1, 20); |
| |
| memset(sha1, 0, 20); |
| } |
| |
| /* |
| * Mix user data into RNG. It is for user own interests to have |
| * RNG state shuffled. |
| */ |
| static void |
| add_entropy(text *data1, text *data2, text *data3) |
| { |
| PX_MD *md; |
| uint8 rnd[3]; |
| |
| if (!data1 && !data2 && !data3) |
| return; |
| |
| if (px_get_random_bytes(rnd, 3) < 0) |
| return; |
| |
| if (px_find_digest("sha1", &md) < 0) |
| return; |
| |
| /* |
| * Try to make the feeding unpredictable. |
| * |
| * Prefer data over keys, as it's rather likely that key is same in |
| * several calls. |
| */ |
| |
| /* chance: 7/8 */ |
| if (data1 && rnd[0] >= 32) |
| add_block_entropy(md, data1); |
| |
| /* chance: 5/8 */ |
| if (data2 && rnd[1] >= 160) |
| add_block_entropy(md, data2); |
| |
| /* chance: 5/8 */ |
| if (data3 && rnd[2] >= 160) |
| add_block_entropy(md, data3); |
| |
| px_md_free(md); |
| memset(rnd, 0, sizeof(rnd)); |
| } |
| |
| /* |
| * returns src in case of no conversion or error |
| */ |
| static text * |
| convert_charset(text *src, int cset_from, int cset_to) |
| { |
| int src_len = VARSIZE(src) - VARHDRSZ; |
| unsigned char *dst; |
| unsigned char *csrc = (unsigned char *) VARDATA(src); |
| text *res; |
| |
| dst = pg_do_encoding_conversion(csrc, src_len, cset_from, cset_to); |
| if (dst == csrc) |
| return src; |
| |
| res = cstring_to_text((char *) dst); |
| pfree(dst); |
| return res; |
| } |
| |
| static text * |
| convert_from_utf8(text *src) |
| { |
| return convert_charset(src, PG_UTF8, GetDatabaseEncoding()); |
| } |
| |
| static text * |
| convert_to_utf8(text *src) |
| { |
| return convert_charset(src, GetDatabaseEncoding(), PG_UTF8); |
| } |
| |
| static void |
| clear_and_pfree(text *p) |
| { |
| memset(p, 0, VARSIZE(p)); |
| pfree(p); |
| } |
| |
| /* |
| * expect-* arguments storage |
| */ |
| struct debug_expect |
| { |
| int debug; |
| int expect; |
| int cipher_algo; |
| int s2k_mode; |
| int s2k_cipher_algo; |
| int s2k_digest_algo; |
| int compress_algo; |
| int use_sess_key; |
| int disable_mdc; |
| int unicode_mode; |
| }; |
| |
| static void |
| fill_expect(struct debug_expect * ex, int text_mode) |
| { |
| ex->debug = 0; |
| ex->expect = 0; |
| ex->cipher_algo = -1; |
| ex->s2k_mode = -1; |
| ex->s2k_cipher_algo = -1; |
| ex->s2k_digest_algo = -1; |
| ex->compress_algo = -1; |
| ex->use_sess_key = -1; |
| ex->disable_mdc = -1; |
| ex->unicode_mode = -1; |
| } |
| |
| #define EX_MSG(arg) \ |
| ereport(NOTICE, (errmsg( \ |
| "pgp_decrypt: unexpected %s: expected %d got %d", \ |
| CppAsString(arg), ex->arg, ctx->arg))) |
| |
| #define EX_CHECK(arg) do { \ |
| if (ex->arg >= 0 && ex->arg != ctx->arg) EX_MSG(arg); \ |
| } while (0) |
| |
| static void |
| check_expect(PGP_Context *ctx, struct debug_expect * ex) |
| { |
| EX_CHECK(cipher_algo); |
| EX_CHECK(s2k_mode); |
| EX_CHECK(s2k_digest_algo); |
| EX_CHECK(use_sess_key); |
| if (ctx->use_sess_key) |
| EX_CHECK(s2k_cipher_algo); |
| EX_CHECK(disable_mdc); |
| EX_CHECK(compress_algo); |
| EX_CHECK(unicode_mode); |
| } |
| |
| static void |
| show_debug(const char *msg) |
| { |
| ereport(NOTICE, (errmsg("dbg: %s", msg))); |
| } |
| |
| static int |
| set_arg(PGP_Context *ctx, char *key, char *val, |
| struct debug_expect * ex) |
| { |
| int res = 0; |
| |
| if (strcmp(key, "cipher-algo") == 0) |
| res = pgp_set_cipher_algo(ctx, val); |
| else if (strcmp(key, "disable-mdc") == 0) |
| res = pgp_disable_mdc(ctx, atoi(val)); |
| else if (strcmp(key, "sess-key") == 0) |
| res = pgp_set_sess_key(ctx, atoi(val)); |
| else if (strcmp(key, "s2k-mode") == 0) |
| res = pgp_set_s2k_mode(ctx, atoi(val)); |
| else if (strcmp(key, "s2k-digest-algo") == 0) |
| res = pgp_set_s2k_digest_algo(ctx, val); |
| else if (strcmp(key, "s2k-cipher-algo") == 0) |
| res = pgp_set_s2k_cipher_algo(ctx, val); |
| else if (strcmp(key, "compress-algo") == 0) |
| res = pgp_set_compress_algo(ctx, atoi(val)); |
| else if (strcmp(key, "compress-level") == 0) |
| res = pgp_set_compress_level(ctx, atoi(val)); |
| else if (strcmp(key, "convert-crlf") == 0) |
| res = pgp_set_convert_crlf(ctx, atoi(val)); |
| else if (strcmp(key, "unicode-mode") == 0) |
| res = pgp_set_unicode_mode(ctx, atoi(val)); |
| /* decrypt debug */ |
| else if (ex != NULL && strcmp(key, "debug") == 0) |
| ex->debug = atoi(val); |
| else if (ex != NULL && strcmp(key, "expect-cipher-algo") == 0) |
| { |
| ex->expect = 1; |
| ex->cipher_algo = pgp_get_cipher_code(val); |
| } |
| else if (ex != NULL && strcmp(key, "expect-disable-mdc") == 0) |
| { |
| ex->expect = 1; |
| ex->disable_mdc = atoi(val); |
| } |
| else if (ex != NULL && strcmp(key, "expect-sess-key") == 0) |
| { |
| ex->expect = 1; |
| ex->use_sess_key = atoi(val); |
| } |
| else if (ex != NULL && strcmp(key, "expect-s2k-mode") == 0) |
| { |
| ex->expect = 1; |
| ex->s2k_mode = atoi(val); |
| } |
| else if (ex != NULL && strcmp(key, "expect-s2k-digest-algo") == 0) |
| { |
| ex->expect = 1; |
| ex->s2k_digest_algo = pgp_get_digest_code(val); |
| } |
| else if (ex != NULL && strcmp(key, "expect-s2k-cipher-algo") == 0) |
| { |
| ex->expect = 1; |
| ex->s2k_cipher_algo = pgp_get_cipher_code(val); |
| } |
| else if (ex != NULL && strcmp(key, "expect-compress-algo") == 0) |
| { |
| ex->expect = 1; |
| ex->compress_algo = atoi(val); |
| } |
| else if (ex != NULL && strcmp(key, "expect-unicode-mode") == 0) |
| { |
| ex->expect = 1; |
| ex->unicode_mode = atoi(val); |
| } |
| else |
| res = PXE_ARGUMENT_ERROR; |
| |
| return res; |
| } |
| |
| /* |
| * Find next word. Handle ',' and '=' as words. Skip whitespace. |
| * Put word info into res_p, res_len. |
| * Returns ptr to next word. |
| */ |
| static char * |
| getword(char *p, char **res_p, int *res_len) |
| { |
| /* whitespace at start */ |
| while (*p && (*p == ' ' || *p == '\t' || *p == '\n')) |
| p++; |
| |
| /* word data */ |
| *res_p = p; |
| if (*p == '=' || *p == ',') |
| p++; |
| else |
| while (*p && !(*p == ' ' || *p == '\t' || *p == '\n' |
| || *p == '=' || *p == ',')) |
| p++; |
| |
| /* word end */ |
| *res_len = p - *res_p; |
| |
| /* whitespace at end */ |
| while (*p && (*p == ' ' || *p == '\t' || *p == '\n')) |
| p++; |
| |
| return p; |
| } |
| |
| /* |
| * Convert to lowercase asciiz string. |
| */ |
| static char * |
| downcase_convert(const uint8 *s, int len) |
| { |
| int c, |
| i; |
| char *res = palloc(len + 1); |
| |
| for (i = 0; i < len; i++) |
| { |
| c = s[i]; |
| if (c >= 'A' && c <= 'Z') |
| c += 'a' - 'A'; |
| res[i] = c; |
| } |
| res[len] = 0; |
| return res; |
| } |
| |
| static int |
| parse_args(PGP_Context *ctx, uint8 *args, int arg_len, |
| struct debug_expect * ex) |
| { |
| char *str = downcase_convert(args, arg_len); |
| char *key, |
| *val; |
| int key_len, |
| val_len; |
| int res = 0; |
| char *p = str; |
| |
| while (*p) |
| { |
| res = PXE_ARGUMENT_ERROR; |
| p = getword(p, &key, &key_len); |
| if (*p++ != '=') |
| break; |
| p = getword(p, &val, &val_len); |
| if (*p == '\0') |
| ; |
| else if (*p++ != ',') |
| break; |
| |
| if (*key == 0 || *val == 0 || val_len == 0) |
| break; |
| |
| key[key_len] = 0; |
| val[val_len] = 0; |
| |
| res = set_arg(ctx, key, val, ex); |
| if (res < 0) |
| break; |
| } |
| pfree(str); |
| return res; |
| } |
| |
| static MBuf * |
| create_mbuf_from_vardata(text *data) |
| { |
| return mbuf_create_from_data((uint8 *) VARDATA(data), |
| VARSIZE(data) - VARHDRSZ); |
| } |
| |
| static void |
| init_work(PGP_Context **ctx_p, int is_text, |
| text *args, struct debug_expect * ex) |
| { |
| int err = pgp_init(ctx_p); |
| |
| fill_expect(ex, is_text); |
| |
| if (err == 0 && args != NULL) |
| err = parse_args(*ctx_p, (uint8 *) VARDATA(args), |
| VARSIZE(args) - VARHDRSZ, ex); |
| |
| if (err) |
| { |
| ereport(ERROR, |
| (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), |
| errmsg("%s", px_strerror(err)))); |
| } |
| |
| if (ex->debug) |
| px_set_debug_handler(show_debug); |
| |
| pgp_set_text_mode(*ctx_p, is_text); |
| } |
| |
| static bytea * |
| encrypt_internal(int is_pubenc, int is_text, |
| text *data, text *key, text *args) |
| { |
| MBuf *src, |
| *dst; |
| uint8 tmp[VARHDRSZ]; |
| uint8 *restmp; |
| bytea *res; |
| int res_len; |
| PGP_Context *ctx; |
| int err; |
| struct debug_expect ex; |
| text *tmp_data = NULL; |
| |
| /* |
| * Add data and key info RNG. |
| */ |
| add_entropy(data, key, NULL); |
| |
| init_work(&ctx, is_text, args, &ex); |
| |
| if (is_text && pgp_get_unicode_mode(ctx)) |
| { |
| tmp_data = convert_to_utf8(data); |
| if (tmp_data == data) |
| tmp_data = NULL; |
| else |
| data = tmp_data; |
| } |
| |
| src = create_mbuf_from_vardata(data); |
| dst = mbuf_create(VARSIZE(data) + 128); |
| |
| /* |
| * reserve room for header |
| */ |
| mbuf_append(dst, tmp, VARHDRSZ); |
| |
| /* |
| * set key |
| */ |
| if (is_pubenc) |
| { |
| MBuf *kbuf = create_mbuf_from_vardata(key); |
| |
| err = pgp_set_pubkey(ctx, kbuf, |
| NULL, 0, 0); |
| mbuf_free(kbuf); |
| } |
| else |
| err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key), |
| VARSIZE(key) - VARHDRSZ); |
| |
| /* |
| * encrypt |
| */ |
| if (err >= 0) |
| err = pgp_encrypt(ctx, src, dst); |
| |
| /* |
| * check for error |
| */ |
| if (err) |
| { |
| if (ex.debug) |
| px_set_debug_handler(NULL); |
| if (tmp_data) |
| clear_and_pfree(tmp_data); |
| pgp_free(ctx); |
| mbuf_free(src); |
| mbuf_free(dst); |
| ereport(ERROR, |
| (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), |
| errmsg("%s", px_strerror(err)))); |
| } |
| |
| /* res_len includes VARHDRSZ */ |
| res_len = mbuf_steal_data(dst, &restmp); |
| res = (bytea *) restmp; |
| SET_VARSIZE(res, res_len); |
| |
| if (tmp_data) |
| clear_and_pfree(tmp_data); |
| pgp_free(ctx); |
| mbuf_free(src); |
| mbuf_free(dst); |
| |
| px_set_debug_handler(NULL); |
| |
| return res; |
| } |
| |
| static bytea * |
| decrypt_internal(int is_pubenc, int need_text, text *data, |
| text *key, text *keypsw, text *args) |
| { |
| int err; |
| MBuf *src = NULL, |
| *dst = NULL; |
| uint8 tmp[VARHDRSZ]; |
| uint8 *restmp; |
| bytea *res; |
| int res_len; |
| PGP_Context *ctx = NULL; |
| struct debug_expect ex; |
| int got_unicode = 0; |
| |
| |
| init_work(&ctx, need_text, args, &ex); |
| |
| src = mbuf_create_from_data((uint8 *) VARDATA(data), |
| VARSIZE(data) - VARHDRSZ); |
| dst = mbuf_create(VARSIZE(data) + 2048); |
| |
| /* |
| * reserve room for header |
| */ |
| mbuf_append(dst, tmp, VARHDRSZ); |
| |
| /* |
| * set key |
| */ |
| if (is_pubenc) |
| { |
| uint8 *psw = NULL; |
| int psw_len = 0; |
| MBuf *kbuf; |
| |
| if (keypsw) |
| { |
| psw = (uint8 *) VARDATA(keypsw); |
| psw_len = VARSIZE(keypsw) - VARHDRSZ; |
| } |
| kbuf = create_mbuf_from_vardata(key); |
| err = pgp_set_pubkey(ctx, kbuf, psw, psw_len, 1); |
| mbuf_free(kbuf); |
| } |
| else |
| err = pgp_set_symkey(ctx, (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); |
| |
| /* remember the setting */ |
| got_unicode = pgp_get_unicode_mode(ctx); |
| |
| out: |
| if (src) |
| mbuf_free(src); |
| if (ctx) |
| pgp_free(ctx); |
| |
| if (err) |
| { |
| px_set_debug_handler(NULL); |
| if (dst) |
| mbuf_free(dst); |
| ereport(ERROR, |
| (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), |
| errmsg("%s", px_strerror(err)))); |
| } |
| |
| res_len = mbuf_steal_data(dst, &restmp); |
| mbuf_free(dst); |
| |
| /* res_len includes VARHDRSZ */ |
| res = (bytea *) restmp; |
| SET_VARSIZE(res, res_len); |
| |
| if (need_text && got_unicode) |
| { |
| text *utf = convert_from_utf8(res); |
| |
| if (utf != res) |
| { |
| clear_and_pfree(res); |
| res = utf; |
| } |
| } |
| px_set_debug_handler(NULL); |
| |
| /* |
| * add successful decryptions also into RNG |
| */ |
| add_entropy(res, key, keypsw); |
| |
| return res; |
| } |
| |
| /* |
| * Wrappers for symmetric-key functions |
| */ |
| Datum |
| pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS) |
| { |
| bytea *data, |
| *key; |
| text *arg = NULL; |
| text *res; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| key = PG_GETARG_BYTEA_P(1); |
| if (PG_NARGS() > 2) |
| arg = PG_GETARG_BYTEA_P(2); |
| |
| res = encrypt_internal(0, 0, data, key, arg); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_FREE_IF_COPY(key, 1); |
| if (PG_NARGS() > 2) |
| PG_FREE_IF_COPY(arg, 2); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| Datum |
| pgp_sym_encrypt_text(PG_FUNCTION_ARGS) |
| { |
| bytea *data, |
| *key; |
| text *arg = NULL; |
| text *res; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| key = PG_GETARG_BYTEA_P(1); |
| if (PG_NARGS() > 2) |
| arg = PG_GETARG_BYTEA_P(2); |
| |
| res = encrypt_internal(0, 1, data, key, arg); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_FREE_IF_COPY(key, 1); |
| if (PG_NARGS() > 2) |
| PG_FREE_IF_COPY(arg, 2); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| |
| Datum |
| pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS) |
| { |
| bytea *data, |
| *key; |
| text *arg = NULL; |
| text *res; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| key = PG_GETARG_BYTEA_P(1); |
| if (PG_NARGS() > 2) |
| arg = PG_GETARG_BYTEA_P(2); |
| |
| res = decrypt_internal(0, 0, data, key, NULL, arg); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_FREE_IF_COPY(key, 1); |
| if (PG_NARGS() > 2) |
| PG_FREE_IF_COPY(arg, 2); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| Datum |
| pgp_sym_decrypt_text(PG_FUNCTION_ARGS) |
| { |
| bytea *data, |
| *key; |
| text *arg = NULL; |
| text *res; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| key = PG_GETARG_BYTEA_P(1); |
| if (PG_NARGS() > 2) |
| arg = PG_GETARG_BYTEA_P(2); |
| |
| res = decrypt_internal(0, 1, data, key, NULL, arg); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_FREE_IF_COPY(key, 1); |
| if (PG_NARGS() > 2) |
| PG_FREE_IF_COPY(arg, 2); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| /* |
| * Wrappers for public-key functions |
| */ |
| |
| Datum |
| pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS) |
| { |
| bytea *data, |
| *key; |
| text *arg = NULL; |
| text *res; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| key = PG_GETARG_BYTEA_P(1); |
| if (PG_NARGS() > 2) |
| arg = PG_GETARG_BYTEA_P(2); |
| |
| res = encrypt_internal(1, 0, data, key, arg); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_FREE_IF_COPY(key, 1); |
| if (PG_NARGS() > 2) |
| PG_FREE_IF_COPY(arg, 2); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| Datum |
| pgp_pub_encrypt_text(PG_FUNCTION_ARGS) |
| { |
| bytea *data, |
| *key; |
| text *arg = NULL; |
| text *res; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| key = PG_GETARG_BYTEA_P(1); |
| if (PG_NARGS() > 2) |
| arg = PG_GETARG_BYTEA_P(2); |
| |
| res = encrypt_internal(1, 1, data, key, arg); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_FREE_IF_COPY(key, 1); |
| if (PG_NARGS() > 2) |
| PG_FREE_IF_COPY(arg, 2); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| |
| Datum |
| pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS) |
| { |
| bytea *data, |
| *key; |
| text *psw = NULL, |
| *arg = NULL; |
| text *res; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| key = PG_GETARG_BYTEA_P(1); |
| if (PG_NARGS() > 2) |
| psw = PG_GETARG_BYTEA_P(2); |
| if (PG_NARGS() > 3) |
| arg = PG_GETARG_BYTEA_P(3); |
| |
| res = decrypt_internal(1, 0, data, key, psw, arg); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_FREE_IF_COPY(key, 1); |
| if (PG_NARGS() > 2) |
| PG_FREE_IF_COPY(psw, 2); |
| if (PG_NARGS() > 3) |
| PG_FREE_IF_COPY(arg, 3); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| Datum |
| pgp_pub_decrypt_text(PG_FUNCTION_ARGS) |
| { |
| bytea *data, |
| *key; |
| text *psw = NULL, |
| *arg = NULL; |
| text *res; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| key = PG_GETARG_BYTEA_P(1); |
| if (PG_NARGS() > 2) |
| psw = PG_GETARG_BYTEA_P(2); |
| if (PG_NARGS() > 3) |
| arg = PG_GETARG_BYTEA_P(3); |
| |
| res = decrypt_internal(1, 1, data, key, psw, arg); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_FREE_IF_COPY(key, 1); |
| if (PG_NARGS() > 2) |
| PG_FREE_IF_COPY(psw, 2); |
| if (PG_NARGS() > 3) |
| PG_FREE_IF_COPY(arg, 3); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| |
| /* |
| * Wrappers for PGP ascii armor |
| */ |
| |
| Datum |
| pg_armor(PG_FUNCTION_ARGS) |
| { |
| bytea *data; |
| text *res; |
| int data_len, |
| res_len, |
| guess_len; |
| |
| 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), |
| errmsg("Overflow - encode estimate too small"))); |
| SET_VARSIZE(res, VARHDRSZ + res_len); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| Datum |
| pg_dearmor(PG_FUNCTION_ARGS) |
| { |
| text *data; |
| bytea *res; |
| int data_len, |
| res_len, |
| guess_len; |
| |
| data = PG_GETARG_TEXT_P(0); |
| data_len = VARSIZE(data) - VARHDRSZ; |
| |
| guess_len = pgp_armor_dec_len(data_len); |
| res = palloc(VARHDRSZ + guess_len); |
| |
| res_len = pgp_armor_decode((uint8 *) VARDATA(data), data_len, |
| (uint8 *) VARDATA(res)); |
| if (res_len < 0) |
| ereport(ERROR, |
| (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), |
| errmsg("%s", px_strerror(res_len)))); |
| if (res_len > guess_len) |
| ereport(ERROR, |
| (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), |
| errmsg("Overflow - decode estimate too small"))); |
| SET_VARSIZE(res, VARHDRSZ + res_len); |
| |
| PG_FREE_IF_COPY(data, 0); |
| PG_RETURN_TEXT_P(res); |
| } |
| |
| /* |
| * Wrappers for PGP key id |
| */ |
| |
| Datum |
| pgp_key_id_w(PG_FUNCTION_ARGS) |
| { |
| bytea *data; |
| text *res; |
| int res_len; |
| MBuf *buf; |
| |
| data = PG_GETARG_BYTEA_P(0); |
| 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, |
| (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); |
| } |