3 * PostgreSQL wrappers for pgp.
5 * Copyright (c) 2005 Marko Kreen
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * contrib/pgcrypto/pgp-pgsql.c
34 #include "catalog/pg_type.h"
35 #include "common/string.h"
37 #include "lib/stringinfo.h"
38 #include "mb/pg_wchar.h"
42 #include "utils/array.h"
43 #include "utils/builtins.h"
48 PG_FUNCTION_INFO_V1(pgp_sym_encrypt_bytea
);
49 PG_FUNCTION_INFO_V1(pgp_sym_encrypt_text
);
50 PG_FUNCTION_INFO_V1(pgp_sym_decrypt_bytea
);
51 PG_FUNCTION_INFO_V1(pgp_sym_decrypt_text
);
53 PG_FUNCTION_INFO_V1(pgp_pub_encrypt_bytea
);
54 PG_FUNCTION_INFO_V1(pgp_pub_encrypt_text
);
55 PG_FUNCTION_INFO_V1(pgp_pub_decrypt_bytea
);
56 PG_FUNCTION_INFO_V1(pgp_pub_decrypt_text
);
58 PG_FUNCTION_INFO_V1(pgp_key_id_w
);
60 PG_FUNCTION_INFO_V1(pg_armor
);
61 PG_FUNCTION_INFO_V1(pg_dearmor
);
62 PG_FUNCTION_INFO_V1(pgp_armor_headers
);
65 * returns src in case of no conversion or error
68 convert_charset(text
*src
, int cset_from
, int cset_to
)
70 int src_len
= VARSIZE_ANY_EXHDR(src
);
72 unsigned char *csrc
= (unsigned char *) VARDATA_ANY(src
);
75 dst
= pg_do_encoding_conversion(csrc
, src_len
, cset_from
, cset_to
);
79 res
= cstring_to_text((char *) dst
);
85 convert_from_utf8(text
*src
)
87 return convert_charset(src
, PG_UTF8
, GetDatabaseEncoding());
91 convert_to_utf8(text
*src
)
93 return convert_charset(src
, GetDatabaseEncoding(), PG_UTF8
);
97 clear_and_pfree(text
*p
)
99 px_memset(p
, 0, VARSIZE_ANY(p
));
104 * expect-* arguments storage
122 fill_expect(struct debug_expect
*ex
, int text_mode
)
126 ex
->cipher_algo
= -1;
129 ex
->s2k_cipher_algo
= -1;
130 ex
->s2k_digest_algo
= -1;
131 ex
->compress_algo
= -1;
132 ex
->use_sess_key
= -1;
133 ex
->disable_mdc
= -1;
134 ex
->unicode_mode
= -1;
137 #define EX_MSG(arg) \
138 ereport(NOTICE, (errmsg( \
139 "pgp_decrypt: unexpected %s: expected %d got %d", \
140 CppAsString(arg), ex->arg, ctx->arg)))
142 #define EX_CHECK(arg) do { \
143 if (ex->arg >= 0 && ex->arg != ctx->arg) EX_MSG(arg); \
147 check_expect(PGP_Context
*ctx
, struct debug_expect
*ex
)
149 EX_CHECK(cipher_algo
);
152 EX_CHECK(s2k_digest_algo
);
153 EX_CHECK(use_sess_key
);
154 if (ctx
->use_sess_key
)
155 EX_CHECK(s2k_cipher_algo
);
156 EX_CHECK(disable_mdc
);
157 EX_CHECK(compress_algo
);
158 EX_CHECK(unicode_mode
);
162 show_debug(const char *msg
)
164 ereport(NOTICE
, (errmsg("dbg: %s", msg
)));
168 set_arg(PGP_Context
*ctx
, char *key
, char *val
,
169 struct debug_expect
*ex
)
173 if (strcmp(key
, "cipher-algo") == 0)
174 res
= pgp_set_cipher_algo(ctx
, val
);
175 else if (strcmp(key
, "disable-mdc") == 0)
176 res
= pgp_disable_mdc(ctx
, atoi(val
));
177 else if (strcmp(key
, "sess-key") == 0)
178 res
= pgp_set_sess_key(ctx
, atoi(val
));
179 else if (strcmp(key
, "s2k-mode") == 0)
180 res
= pgp_set_s2k_mode(ctx
, atoi(val
));
181 else if (strcmp(key
, "s2k-count") == 0)
182 res
= pgp_set_s2k_count(ctx
, atoi(val
));
183 else if (strcmp(key
, "s2k-digest-algo") == 0)
184 res
= pgp_set_s2k_digest_algo(ctx
, val
);
185 else if (strcmp(key
, "s2k-cipher-algo") == 0)
186 res
= pgp_set_s2k_cipher_algo(ctx
, val
);
187 else if (strcmp(key
, "compress-algo") == 0)
188 res
= pgp_set_compress_algo(ctx
, atoi(val
));
189 else if (strcmp(key
, "compress-level") == 0)
190 res
= pgp_set_compress_level(ctx
, atoi(val
));
191 else if (strcmp(key
, "convert-crlf") == 0)
192 res
= pgp_set_convert_crlf(ctx
, atoi(val
));
193 else if (strcmp(key
, "unicode-mode") == 0)
194 res
= pgp_set_unicode_mode(ctx
, atoi(val
));
197 * The remaining options are for debugging/testing and are therefore not
198 * documented in the user-facing docs.
200 else if (ex
!= NULL
&& strcmp(key
, "debug") == 0)
201 ex
->debug
= atoi(val
);
202 else if (ex
!= NULL
&& strcmp(key
, "expect-cipher-algo") == 0)
205 ex
->cipher_algo
= pgp_get_cipher_code(val
);
207 else if (ex
!= NULL
&& strcmp(key
, "expect-disable-mdc") == 0)
210 ex
->disable_mdc
= atoi(val
);
212 else if (ex
!= NULL
&& strcmp(key
, "expect-sess-key") == 0)
215 ex
->use_sess_key
= atoi(val
);
217 else if (ex
!= NULL
&& strcmp(key
, "expect-s2k-mode") == 0)
220 ex
->s2k_mode
= atoi(val
);
222 else if (ex
!= NULL
&& strcmp(key
, "expect-s2k-count") == 0)
225 ex
->s2k_count
= atoi(val
);
227 else if (ex
!= NULL
&& strcmp(key
, "expect-s2k-digest-algo") == 0)
230 ex
->s2k_digest_algo
= pgp_get_digest_code(val
);
232 else if (ex
!= NULL
&& strcmp(key
, "expect-s2k-cipher-algo") == 0)
235 ex
->s2k_cipher_algo
= pgp_get_cipher_code(val
);
237 else if (ex
!= NULL
&& strcmp(key
, "expect-compress-algo") == 0)
240 ex
->compress_algo
= atoi(val
);
242 else if (ex
!= NULL
&& strcmp(key
, "expect-unicode-mode") == 0)
245 ex
->unicode_mode
= atoi(val
);
248 res
= PXE_ARGUMENT_ERROR
;
254 * Find next word. Handle ',' and '=' as words. Skip whitespace.
255 * Put word info into res_p, res_len.
256 * Returns ptr to next word.
259 getword(char *p
, char **res_p
, int *res_len
)
261 /* whitespace at start */
262 while (*p
&& (*p
== ' ' || *p
== '\t' || *p
== '\n'))
267 if (*p
== '=' || *p
== ',')
270 while (*p
&& !(*p
== ' ' || *p
== '\t' || *p
== '\n'
271 || *p
== '=' || *p
== ','))
275 *res_len
= p
- *res_p
;
277 /* whitespace at end */
278 while (*p
&& (*p
== ' ' || *p
== '\t' || *p
== '\n'))
285 * Convert to lowercase asciiz string.
288 downcase_convert(const uint8
*s
, int len
)
292 char *res
= palloc(len
+ 1);
294 for (i
= 0; i
< len
; i
++)
297 if (c
>= 'A' && c
<= 'Z')
306 parse_args(PGP_Context
*ctx
, uint8
*args
, int arg_len
,
307 struct debug_expect
*ex
)
309 char *str
= downcase_convert(args
, arg_len
);
319 res
= PXE_ARGUMENT_ERROR
;
320 p
= getword(p
, &key
, &key_len
);
323 p
= getword(p
, &val
, &val_len
);
326 else if (*p
++ != ',')
329 if (*key
== 0 || *val
== 0 || val_len
== 0)
335 res
= set_arg(ctx
, key
, val
, ex
);
344 create_mbuf_from_vardata(text
*data
)
346 return mbuf_create_from_data((uint8
*) VARDATA_ANY(data
),
347 VARSIZE_ANY_EXHDR(data
));
351 init_work(PGP_Context
**ctx_p
, int is_text
,
352 text
*args
, struct debug_expect
*ex
)
354 int err
= pgp_init(ctx_p
);
356 fill_expect(ex
, is_text
);
358 if (err
== 0 && args
!= NULL
)
359 err
= parse_args(*ctx_p
, (uint8
*) VARDATA_ANY(args
),
360 VARSIZE_ANY_EXHDR(args
), ex
);
366 px_set_debug_handler(show_debug
);
368 pgp_set_text_mode(*ctx_p
, is_text
);
372 encrypt_internal(int is_pubenc
, int is_text
,
373 text
*data
, text
*key
, text
*args
)
383 struct debug_expect ex
;
384 text
*tmp_data
= NULL
;
386 init_work(&ctx
, is_text
, args
, &ex
);
388 if (is_text
&& pgp_get_unicode_mode(ctx
))
390 tmp_data
= convert_to_utf8(data
);
391 if (tmp_data
== data
)
397 src
= create_mbuf_from_vardata(data
);
398 dst
= mbuf_create(VARSIZE_ANY(data
) + 128);
401 * reserve room for header
403 mbuf_append(dst
, tmp
, VARHDRSZ
);
410 MBuf
*kbuf
= create_mbuf_from_vardata(key
);
412 err
= pgp_set_pubkey(ctx
, kbuf
,
417 err
= pgp_set_symkey(ctx
, (uint8
*) VARDATA_ANY(key
),
418 VARSIZE_ANY_EXHDR(key
));
424 err
= pgp_encrypt(ctx
, src
, dst
);
432 px_set_debug_handler(NULL
);
434 clear_and_pfree(tmp_data
);
441 /* res_len includes VARHDRSZ */
442 res_len
= mbuf_steal_data(dst
, &restmp
);
443 res
= (bytea
*) restmp
;
444 SET_VARSIZE(res
, res_len
);
447 clear_and_pfree(tmp_data
);
452 px_set_debug_handler(NULL
);
458 decrypt_internal(int is_pubenc
, int need_text
, text
*data
,
459 text
*key
, text
*keypsw
, text
*args
)
468 PGP_Context
*ctx
= NULL
;
469 struct debug_expect ex
;
473 init_work(&ctx
, need_text
, args
, &ex
);
475 src
= mbuf_create_from_data((uint8
*) VARDATA_ANY(data
),
476 VARSIZE_ANY_EXHDR(data
));
477 dst
= mbuf_create(VARSIZE_ANY(data
) + 2048);
480 * reserve room for header
482 mbuf_append(dst
, tmp
, VARHDRSZ
);
495 psw
= (uint8
*) VARDATA_ANY(keypsw
);
496 psw_len
= VARSIZE_ANY_EXHDR(keypsw
);
498 kbuf
= create_mbuf_from_vardata(key
);
499 err
= pgp_set_pubkey(ctx
, kbuf
, psw
, psw_len
, 1);
503 err
= pgp_set_symkey(ctx
, (uint8
*) VARDATA_ANY(key
),
504 VARSIZE_ANY_EXHDR(key
));
509 err
= pgp_decrypt(ctx
, src
, dst
);
512 check_expect(ctx
, &ex
);
514 /* remember the setting */
515 got_unicode
= pgp_get_unicode_mode(ctx
);
523 px_set_debug_handler(NULL
);
528 res_len
= mbuf_steal_data(dst
, &restmp
);
531 /* res_len includes VARHDRSZ */
532 res
= (bytea
*) restmp
;
533 SET_VARSIZE(res
, res_len
);
535 if (need_text
&& got_unicode
)
537 text
*utf
= convert_from_utf8(res
);
541 clear_and_pfree(res
);
545 px_set_debug_handler(NULL
);
551 * Wrappers for symmetric-key functions
554 pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS
)
561 data
= PG_GETARG_BYTEA_PP(0);
562 key
= PG_GETARG_TEXT_PP(1);
564 arg
= PG_GETARG_TEXT_PP(2);
566 res
= encrypt_internal(0, 0, data
, key
, arg
);
568 PG_FREE_IF_COPY(data
, 0);
569 PG_FREE_IF_COPY(key
, 1);
571 PG_FREE_IF_COPY(arg
, 2);
572 PG_RETURN_TEXT_P(res
);
576 pgp_sym_encrypt_text(PG_FUNCTION_ARGS
)
583 data
= PG_GETARG_TEXT_PP(0);
584 key
= PG_GETARG_TEXT_PP(1);
586 arg
= PG_GETARG_TEXT_PP(2);
588 res
= encrypt_internal(0, 1, data
, key
, arg
);
590 PG_FREE_IF_COPY(data
, 0);
591 PG_FREE_IF_COPY(key
, 1);
593 PG_FREE_IF_COPY(arg
, 2);
594 PG_RETURN_TEXT_P(res
);
599 pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS
)
606 data
= PG_GETARG_BYTEA_PP(0);
607 key
= PG_GETARG_TEXT_PP(1);
609 arg
= PG_GETARG_TEXT_PP(2);
611 res
= decrypt_internal(0, 0, data
, key
, NULL
, arg
);
613 PG_FREE_IF_COPY(data
, 0);
614 PG_FREE_IF_COPY(key
, 1);
616 PG_FREE_IF_COPY(arg
, 2);
617 PG_RETURN_TEXT_P(res
);
621 pgp_sym_decrypt_text(PG_FUNCTION_ARGS
)
628 data
= PG_GETARG_BYTEA_PP(0);
629 key
= PG_GETARG_TEXT_PP(1);
631 arg
= PG_GETARG_TEXT_PP(2);
633 res
= decrypt_internal(0, 1, data
, key
, NULL
, arg
);
635 PG_FREE_IF_COPY(data
, 0);
636 PG_FREE_IF_COPY(key
, 1);
638 PG_FREE_IF_COPY(arg
, 2);
639 PG_RETURN_TEXT_P(res
);
643 * Wrappers for public-key functions
647 pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS
)
654 data
= PG_GETARG_BYTEA_PP(0);
655 key
= PG_GETARG_BYTEA_PP(1);
657 arg
= PG_GETARG_TEXT_PP(2);
659 res
= encrypt_internal(1, 0, data
, key
, arg
);
661 PG_FREE_IF_COPY(data
, 0);
662 PG_FREE_IF_COPY(key
, 1);
664 PG_FREE_IF_COPY(arg
, 2);
665 PG_RETURN_TEXT_P(res
);
669 pgp_pub_encrypt_text(PG_FUNCTION_ARGS
)
676 data
= PG_GETARG_TEXT_PP(0);
677 key
= PG_GETARG_BYTEA_PP(1);
679 arg
= PG_GETARG_TEXT_PP(2);
681 res
= encrypt_internal(1, 1, data
, key
, arg
);
683 PG_FREE_IF_COPY(data
, 0);
684 PG_FREE_IF_COPY(key
, 1);
686 PG_FREE_IF_COPY(arg
, 2);
687 PG_RETURN_TEXT_P(res
);
692 pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS
)
700 data
= PG_GETARG_BYTEA_PP(0);
701 key
= PG_GETARG_BYTEA_PP(1);
703 psw
= PG_GETARG_TEXT_PP(2);
705 arg
= PG_GETARG_TEXT_PP(3);
707 res
= decrypt_internal(1, 0, data
, key
, psw
, arg
);
709 PG_FREE_IF_COPY(data
, 0);
710 PG_FREE_IF_COPY(key
, 1);
712 PG_FREE_IF_COPY(psw
, 2);
714 PG_FREE_IF_COPY(arg
, 3);
715 PG_RETURN_TEXT_P(res
);
719 pgp_pub_decrypt_text(PG_FUNCTION_ARGS
)
727 data
= PG_GETARG_BYTEA_PP(0);
728 key
= PG_GETARG_BYTEA_PP(1);
730 psw
= PG_GETARG_TEXT_PP(2);
732 arg
= PG_GETARG_TEXT_PP(3);
734 res
= decrypt_internal(1, 1, data
, key
, psw
, arg
);
736 PG_FREE_IF_COPY(data
, 0);
737 PG_FREE_IF_COPY(key
, 1);
739 PG_FREE_IF_COPY(psw
, 2);
741 PG_FREE_IF_COPY(arg
, 3);
742 PG_RETURN_TEXT_P(res
);
747 * Wrappers for PGP ascii armor
751 * Helper function for pg_armor. Converts arrays of keys and values into
752 * plain C arrays, and checks that they don't contain invalid characters.
755 parse_key_value_arrays(ArrayType
*key_array
, ArrayType
*val_array
,
756 char ***p_keys
, char ***p_values
)
758 int nkdims
= ARR_NDIM(key_array
);
759 int nvdims
= ARR_NDIM(val_array
);
770 if (nkdims
> 1 || nkdims
!= nvdims
)
772 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
773 errmsg("wrong number of array subscripts")));
777 deconstruct_array_builtin(key_array
, TEXTOID
, &key_datums
, &key_nulls
, &key_count
);
778 deconstruct_array_builtin(val_array
, TEXTOID
, &val_datums
, &val_nulls
, &val_count
);
780 if (key_count
!= val_count
)
782 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR
),
783 errmsg("mismatched array dimensions")));
785 keys
= (char **) palloc(sizeof(char *) * key_count
);
786 values
= (char **) palloc(sizeof(char *) * val_count
);
788 for (i
= 0; i
< key_count
; i
++)
792 /* Check that the key doesn't contain anything funny */
795 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
796 errmsg("null value not allowed for header key")));
798 v
= TextDatumGetCString(key_datums
[i
]);
802 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
803 errmsg("header key must not contain non-ASCII characters")));
806 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
807 errmsg("header key must not contain \": \"")));
810 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
811 errmsg("header key must not contain newlines")));
814 /* And the same for the value */
817 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED
),
818 errmsg("null value not allowed for header value")));
820 v
= TextDatumGetCString(val_datums
[i
]);
824 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
825 errmsg("header value must not contain non-ASCII characters")));
828 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
829 errmsg("header value must not contain newlines")));
840 pg_armor(PG_FUNCTION_ARGS
)
850 data
= PG_GETARG_BYTEA_PP(0);
851 data_len
= VARSIZE_ANY_EXHDR(data
);
854 num_headers
= parse_key_value_arrays(PG_GETARG_ARRAYTYPE_P(1),
855 PG_GETARG_ARRAYTYPE_P(2),
858 else if (PG_NARGS() == 1)
861 elog(ERROR
, "unexpected number of arguments %d", PG_NARGS());
863 initStringInfo(&buf
);
865 pgp_armor_encode((uint8
*) VARDATA_ANY(data
), data_len
, &buf
,
866 num_headers
, keys
, values
);
868 res
= palloc(VARHDRSZ
+ buf
.len
);
869 SET_VARSIZE(res
, VARHDRSZ
+ buf
.len
);
870 memcpy(VARDATA(res
), buf
.data
, buf
.len
);
873 PG_FREE_IF_COPY(data
, 0);
874 PG_RETURN_TEXT_P(res
);
878 pg_dearmor(PG_FUNCTION_ARGS
)
886 data
= PG_GETARG_TEXT_PP(0);
887 data_len
= VARSIZE_ANY_EXHDR(data
);
889 initStringInfo(&buf
);
891 ret
= pgp_armor_decode((uint8
*) VARDATA_ANY(data
), data_len
, &buf
);
894 res
= palloc(VARHDRSZ
+ buf
.len
);
895 SET_VARSIZE(res
, VARHDRSZ
+ buf
.len
);
896 memcpy(VARDATA(res
), buf
.data
, buf
.len
);
899 PG_FREE_IF_COPY(data
, 0);
900 PG_RETURN_TEXT_P(res
);
903 /* cross-call state for pgp_armor_headers */
909 } pgp_armor_headers_state
;
912 pgp_armor_headers(PG_FUNCTION_ARGS
)
914 FuncCallContext
*funcctx
;
915 pgp_armor_headers_state
*state
;
920 AttInMetadata
*attinmeta
;
922 if (SRF_IS_FIRSTCALL())
924 text
*data
= PG_GETARG_TEXT_PP(0);
926 MemoryContext oldcontext
;
928 funcctx
= SRF_FIRSTCALL_INIT();
930 /* we need the state allocated in the multi call context */
931 oldcontext
= MemoryContextSwitchTo(funcctx
->multi_call_memory_ctx
);
933 /* Build a tuple descriptor for our result type */
934 if (get_call_result_type(fcinfo
, NULL
, &tupdesc
) != TYPEFUNC_COMPOSITE
)
935 elog(ERROR
, "return type must be a row type");
937 attinmeta
= TupleDescGetAttInMetadata(tupdesc
);
938 funcctx
->attinmeta
= attinmeta
;
940 state
= (pgp_armor_headers_state
*) palloc(sizeof(pgp_armor_headers_state
));
942 res
= pgp_extract_armor_headers((uint8
*) VARDATA_ANY(data
),
943 VARSIZE_ANY_EXHDR(data
),
944 &state
->nheaders
, &state
->keys
,
949 MemoryContextSwitchTo(oldcontext
);
950 funcctx
->user_fctx
= state
;
953 funcctx
= SRF_PERCALL_SETUP();
954 state
= (pgp_armor_headers_state
*) funcctx
->user_fctx
;
956 if (funcctx
->call_cntr
>= state
->nheaders
)
957 SRF_RETURN_DONE(funcctx
);
962 /* we assume that the keys (and values) are in UTF-8. */
963 utf8key
= state
->keys
[funcctx
->call_cntr
];
964 utf8val
= state
->values
[funcctx
->call_cntr
];
966 values
[0] = pg_any_to_server(utf8key
, strlen(utf8key
), PG_UTF8
);
967 values
[1] = pg_any_to_server(utf8val
, strlen(utf8val
), PG_UTF8
);
970 tuple
= BuildTupleFromCStrings(funcctx
->attinmeta
, values
);
971 SRF_RETURN_NEXT(funcctx
, HeapTupleGetDatum(tuple
));
978 * Wrappers for PGP key id
982 pgp_key_id_w(PG_FUNCTION_ARGS
)
989 data
= PG_GETARG_BYTEA_PP(0);
990 buf
= create_mbuf_from_vardata(data
);
991 res
= palloc(VARHDRSZ
+ 17);
993 res_len
= pgp_get_keyid(buf
, VARDATA(res
));
996 px_THROW_ERROR(res_len
);
997 SET_VARSIZE(res
, VARHDRSZ
+ res_len
);
999 PG_FREE_IF_COPY(data
, 0);
1000 PG_RETURN_TEXT_P(res
);