LCOV - code coverage report
Current view: top level - contrib/pgcrypto - pgcrypto.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 176 190 92.6 %
Date: 2025-04-24 13:15:39 Functions: 24 27 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * pgcrypto.c
       3             :  *      Various cryptographic stuff for PostgreSQL.
       4             :  *
       5             :  * Copyright (c) 2001 Marko Kreen
       6             :  * All rights reserved.
       7             :  *
       8             :  * Redistribution and use in source and binary forms, with or without
       9             :  * modification, are permitted provided that the following conditions
      10             :  * are met:
      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.
      16             :  *
      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
      27             :  * SUCH DAMAGE.
      28             :  *
      29             :  * contrib/pgcrypto/pgcrypto.c
      30             :  */
      31             : 
      32             : #include "postgres.h"
      33             : 
      34             : #include <ctype.h>
      35             : 
      36             : #include "parser/scansup.h"
      37             : #include "pgcrypto.h"
      38             : #include "px-crypt.h"
      39             : #include "px.h"
      40             : #include "utils/builtins.h"
      41             : #include "utils/guc.h"
      42             : #include "varatt.h"
      43             : 
      44          48 : PG_MODULE_MAGIC_EXT(
      45             :                     .name = "pgcrypto",
      46             :                     .version = PG_VERSION
      47             : );
      48             : 
      49             : /* private stuff */
      50             : 
      51             : static const struct config_enum_entry builtin_crypto_options[] = {
      52             :     {"on", BC_ON, false},
      53             :     {"off", BC_OFF, false},
      54             :     {"fips", BC_FIPS, false},
      55             :     {NULL, 0, false}
      56             : };
      57             : 
      58             : typedef int (*PFN) (const char *name, void **res);
      59             : static void *find_provider(text *name, PFN provider_lookup, const char *desc,
      60             :                            int silent);
      61             : 
      62             : int         builtin_crypto_enabled = BC_ON;
      63             : 
      64             : /*
      65             :  * Entrypoint of this module.
      66             :  */
      67             : void
      68          48 : _PG_init(void)
      69             : {
      70          48 :     DefineCustomEnumVariable("pgcrypto.builtin_crypto_enabled",
      71             :                              "Sets if builtin crypto functions are enabled.",
      72             :                              "\"on\" enables builtin crypto, \"off\" unconditionally disables and \"fips\" "
      73             :                              "will disable builtin crypto if OpenSSL is in FIPS mode",
      74             :                              &builtin_crypto_enabled,
      75             :                              BC_ON,
      76             :                              builtin_crypto_options,
      77             :                              PGC_SUSET,
      78             :                              0,
      79             :                              NULL,
      80             :                              NULL,
      81             :                              NULL);
      82          48 :     MarkGUCPrefixReserved("pgcrypto");
      83          48 : }
      84             : 
      85             : /* SQL function: hash(bytea, text) returns bytea */
      86          16 : PG_FUNCTION_INFO_V1(pg_digest);
      87             : 
      88             : Datum
      89          88 : pg_digest(PG_FUNCTION_ARGS)
      90             : {
      91             :     bytea      *arg;
      92             :     text       *name;
      93             :     unsigned    len,
      94             :                 hlen;
      95             :     PX_MD      *md;
      96             :     bytea      *res;
      97             : 
      98          88 :     name = PG_GETARG_TEXT_PP(1);
      99             : 
     100             :     /* will give error if fails */
     101          88 :     md = find_provider(name, (PFN) px_find_digest, "Digest", 0);
     102             : 
     103          86 :     hlen = px_md_result_size(md);
     104             : 
     105          86 :     res = (text *) palloc(hlen + VARHDRSZ);
     106          86 :     SET_VARSIZE(res, hlen + VARHDRSZ);
     107             : 
     108          86 :     arg = PG_GETARG_BYTEA_PP(0);
     109          86 :     len = VARSIZE_ANY_EXHDR(arg);
     110             : 
     111          86 :     px_md_update(md, (uint8 *) VARDATA_ANY(arg), len);
     112          86 :     px_md_finish(md, (uint8 *) VARDATA(res));
     113          86 :     px_md_free(md);
     114             : 
     115          86 :     PG_FREE_IF_COPY(arg, 0);
     116          86 :     PG_FREE_IF_COPY(name, 1);
     117             : 
     118          86 :     PG_RETURN_BYTEA_P(res);
     119             : }
     120             : 
     121             : /* SQL function: hmac(data:bytea, key:bytea, type:text) returns bytea */
     122          14 : PG_FUNCTION_INFO_V1(pg_hmac);
     123             : 
     124             : Datum
     125          30 : pg_hmac(PG_FUNCTION_ARGS)
     126             : {
     127             :     bytea      *arg;
     128             :     bytea      *key;
     129             :     text       *name;
     130             :     unsigned    len,
     131             :                 hlen,
     132             :                 klen;
     133             :     PX_HMAC    *h;
     134             :     bytea      *res;
     135             : 
     136          30 :     name = PG_GETARG_TEXT_PP(2);
     137             : 
     138             :     /* will give error if fails */
     139          30 :     h = find_provider(name, (PFN) px_find_hmac, "HMAC", 0);
     140             : 
     141          28 :     hlen = px_hmac_result_size(h);
     142             : 
     143          28 :     res = (text *) palloc(hlen + VARHDRSZ);
     144          28 :     SET_VARSIZE(res, hlen + VARHDRSZ);
     145             : 
     146          28 :     arg = PG_GETARG_BYTEA_PP(0);
     147          28 :     key = PG_GETARG_BYTEA_PP(1);
     148          28 :     len = VARSIZE_ANY_EXHDR(arg);
     149          28 :     klen = VARSIZE_ANY_EXHDR(key);
     150             : 
     151          28 :     px_hmac_init(h, (uint8 *) VARDATA_ANY(key), klen);
     152          28 :     px_hmac_update(h, (uint8 *) VARDATA_ANY(arg), len);
     153          28 :     px_hmac_finish(h, (uint8 *) VARDATA(res));
     154          28 :     px_hmac_free(h);
     155             : 
     156          28 :     PG_FREE_IF_COPY(arg, 0);
     157          28 :     PG_FREE_IF_COPY(key, 1);
     158          28 :     PG_FREE_IF_COPY(name, 2);
     159             : 
     160          28 :     PG_RETURN_BYTEA_P(res);
     161             : }
     162             : 
     163             : 
     164             : /* SQL function: pg_gen_salt(text) returns text */
     165          10 : PG_FUNCTION_INFO_V1(pg_gen_salt);
     166             : 
     167             : Datum
     168          12 : pg_gen_salt(PG_FUNCTION_ARGS)
     169             : {
     170          12 :     text       *arg0 = PG_GETARG_TEXT_PP(0);
     171             :     int         len;
     172             :     char        buf[PX_MAX_SALT_LEN + 1];
     173             : 
     174          12 :     text_to_cstring_buffer(arg0, buf, sizeof(buf));
     175          12 :     len = px_gen_salt(buf, buf, 0);
     176          10 :     if (len < 0)
     177           2 :         ereport(ERROR,
     178             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     179             :                  errmsg("gen_salt: %s", px_strerror(len))));
     180             : 
     181           8 :     PG_FREE_IF_COPY(arg0, 0);
     182             : 
     183           8 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
     184             : }
     185             : 
     186             : /* SQL function: pg_gen_salt(text, int4) returns text */
     187           8 : PG_FUNCTION_INFO_V1(pg_gen_salt_rounds);
     188             : 
     189             : Datum
     190          16 : pg_gen_salt_rounds(PG_FUNCTION_ARGS)
     191             : {
     192          16 :     text       *arg0 = PG_GETARG_TEXT_PP(0);
     193          16 :     int         rounds = PG_GETARG_INT32(1);
     194             :     int         len;
     195             :     char        buf[PX_MAX_SALT_LEN + 1];
     196             : 
     197          16 :     text_to_cstring_buffer(arg0, buf, sizeof(buf));
     198          16 :     len = px_gen_salt(buf, buf, rounds);
     199          16 :     if (len < 0)
     200           8 :         ereport(ERROR,
     201             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     202             :                  errmsg("gen_salt: %s", px_strerror(len))));
     203             : 
     204           8 :     PG_FREE_IF_COPY(arg0, 0);
     205             : 
     206           8 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
     207             : }
     208             : 
     209             : /* SQL function: pg_crypt(psw:text, salt:text) returns text */
     210          12 : PG_FUNCTION_INFO_V1(pg_crypt);
     211             : 
     212             : Datum
     213         104 : pg_crypt(PG_FUNCTION_ARGS)
     214             : {
     215         104 :     text       *arg0 = PG_GETARG_TEXT_PP(0);
     216         104 :     text       *arg1 = PG_GETARG_TEXT_PP(1);
     217             :     char       *buf0,
     218             :                *buf1,
     219             :                *cres,
     220             :                *resbuf;
     221             :     text       *res;
     222             : 
     223         104 :     buf0 = text_to_cstring(arg0);
     224         104 :     buf1 = text_to_cstring(arg1);
     225             : 
     226         104 :     resbuf = palloc0(PX_MAX_CRYPT);
     227             : 
     228         104 :     cres = px_crypt(buf0, buf1, resbuf, PX_MAX_CRYPT);
     229             : 
     230          92 :     pfree(buf0);
     231          92 :     pfree(buf1);
     232             : 
     233          92 :     if (cres == NULL)
     234           4 :         ereport(ERROR,
     235             :                 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
     236             :                  errmsg("crypt(3) returned NULL")));
     237             : 
     238          88 :     res = cstring_to_text(cres);
     239             : 
     240          88 :     pfree(resbuf);
     241             : 
     242          88 :     PG_FREE_IF_COPY(arg0, 0);
     243          88 :     PG_FREE_IF_COPY(arg1, 1);
     244             : 
     245          88 :     PG_RETURN_TEXT_P(res);
     246             : }
     247             : 
     248             : /* SQL function: pg_encrypt(bytea, bytea, text) returns bytea */
     249          14 : PG_FUNCTION_INFO_V1(pg_encrypt);
     250             : 
     251             : Datum
     252         134 : pg_encrypt(PG_FUNCTION_ARGS)
     253             : {
     254             :     int         err;
     255             :     bytea      *data,
     256             :                *key,
     257             :                *res;
     258             :     text       *type;
     259             :     PX_Combo   *c;
     260             :     unsigned    dlen,
     261             :                 klen,
     262             :                 rlen;
     263             : 
     264         134 :     type = PG_GETARG_TEXT_PP(2);
     265         134 :     c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
     266             : 
     267         132 :     data = PG_GETARG_BYTEA_PP(0);
     268         132 :     key = PG_GETARG_BYTEA_PP(1);
     269         132 :     dlen = VARSIZE_ANY_EXHDR(data);
     270         132 :     klen = VARSIZE_ANY_EXHDR(key);
     271             : 
     272         132 :     rlen = px_combo_encrypt_len(c, dlen);
     273         132 :     res = palloc(VARHDRSZ + rlen);
     274             : 
     275         132 :     err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, NULL, 0);
     276         132 :     if (!err)
     277         132 :         err = px_combo_encrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
     278             :                                (uint8 *) VARDATA(res), &rlen);
     279         132 :     px_combo_free(c);
     280             : 
     281         132 :     PG_FREE_IF_COPY(data, 0);
     282         132 :     PG_FREE_IF_COPY(key, 1);
     283         132 :     PG_FREE_IF_COPY(type, 2);
     284             : 
     285         132 :     if (err)
     286             :     {
     287           2 :         pfree(res);
     288           2 :         ereport(ERROR,
     289             :                 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
     290             :                  errmsg("encrypt error: %s", px_strerror(err))));
     291             :     }
     292             : 
     293         130 :     SET_VARSIZE(res, VARHDRSZ + rlen);
     294         130 :     PG_RETURN_BYTEA_P(res);
     295             : }
     296             : 
     297             : /* SQL function: pg_decrypt(bytea, bytea, text) returns bytea */
     298          12 : PG_FUNCTION_INFO_V1(pg_decrypt);
     299             : 
     300             : Datum
     301          28 : pg_decrypt(PG_FUNCTION_ARGS)
     302             : {
     303             :     int         err;
     304             :     bytea      *data,
     305             :                *key,
     306             :                *res;
     307             :     text       *type;
     308             :     PX_Combo   *c;
     309             :     unsigned    dlen,
     310             :                 klen,
     311             :                 rlen;
     312             : 
     313          28 :     type = PG_GETARG_TEXT_PP(2);
     314          28 :     c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
     315             : 
     316          28 :     data = PG_GETARG_BYTEA_PP(0);
     317          28 :     key = PG_GETARG_BYTEA_PP(1);
     318          28 :     dlen = VARSIZE_ANY_EXHDR(data);
     319          28 :     klen = VARSIZE_ANY_EXHDR(key);
     320             : 
     321          28 :     rlen = px_combo_decrypt_len(c, dlen);
     322          28 :     res = palloc(VARHDRSZ + rlen);
     323             : 
     324          28 :     err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, NULL, 0);
     325          28 :     if (!err)
     326          28 :         err = px_combo_decrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
     327             :                                (uint8 *) VARDATA(res), &rlen);
     328             : 
     329          28 :     px_combo_free(c);
     330             : 
     331          28 :     if (err)
     332           2 :         ereport(ERROR,
     333             :                 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
     334             :                  errmsg("decrypt error: %s", px_strerror(err))));
     335             : 
     336          26 :     SET_VARSIZE(res, VARHDRSZ + rlen);
     337             : 
     338          26 :     PG_FREE_IF_COPY(data, 0);
     339          26 :     PG_FREE_IF_COPY(key, 1);
     340          26 :     PG_FREE_IF_COPY(type, 2);
     341             : 
     342          26 :     PG_RETURN_BYTEA_P(res);
     343             : }
     344             : 
     345             : /* SQL function: pg_encrypt_iv(bytea, bytea, bytea, text) returns bytea */
     346          12 : PG_FUNCTION_INFO_V1(pg_encrypt_iv);
     347             : 
     348             : Datum
     349          12 : pg_encrypt_iv(PG_FUNCTION_ARGS)
     350             : {
     351             :     int         err;
     352             :     bytea      *data,
     353             :                *key,
     354             :                *iv,
     355             :                *res;
     356             :     text       *type;
     357             :     PX_Combo   *c;
     358             :     unsigned    dlen,
     359             :                 klen,
     360             :                 ivlen,
     361             :                 rlen;
     362             : 
     363          12 :     type = PG_GETARG_TEXT_PP(3);
     364          12 :     c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
     365             : 
     366          12 :     data = PG_GETARG_BYTEA_PP(0);
     367          12 :     key = PG_GETARG_BYTEA_PP(1);
     368          12 :     iv = PG_GETARG_BYTEA_PP(2);
     369          12 :     dlen = VARSIZE_ANY_EXHDR(data);
     370          12 :     klen = VARSIZE_ANY_EXHDR(key);
     371          12 :     ivlen = VARSIZE_ANY_EXHDR(iv);
     372             : 
     373          12 :     rlen = px_combo_encrypt_len(c, dlen);
     374          12 :     res = palloc(VARHDRSZ + rlen);
     375             : 
     376          12 :     err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen,
     377             :                         (uint8 *) VARDATA_ANY(iv), ivlen);
     378          12 :     if (!err)
     379          12 :         err = px_combo_encrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
     380             :                                (uint8 *) VARDATA(res), &rlen);
     381             : 
     382          12 :     px_combo_free(c);
     383             : 
     384          12 :     if (err)
     385           0 :         ereport(ERROR,
     386             :                 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
     387             :                  errmsg("encrypt_iv error: %s", px_strerror(err))));
     388             : 
     389          12 :     SET_VARSIZE(res, VARHDRSZ + rlen);
     390             : 
     391          12 :     PG_FREE_IF_COPY(data, 0);
     392          12 :     PG_FREE_IF_COPY(key, 1);
     393          12 :     PG_FREE_IF_COPY(iv, 2);
     394          12 :     PG_FREE_IF_COPY(type, 3);
     395             : 
     396          12 :     PG_RETURN_BYTEA_P(res);
     397             : }
     398             : 
     399             : /* SQL function: pg_decrypt_iv(bytea, bytea, bytea, text) returns bytea */
     400          12 : PG_FUNCTION_INFO_V1(pg_decrypt_iv);
     401             : 
     402             : Datum
     403          16 : pg_decrypt_iv(PG_FUNCTION_ARGS)
     404             : {
     405             :     int         err;
     406             :     bytea      *data,
     407             :                *key,
     408             :                *iv,
     409             :                *res;
     410             :     text       *type;
     411             :     PX_Combo   *c;
     412             :     unsigned    dlen,
     413             :                 klen,
     414             :                 rlen,
     415             :                 ivlen;
     416             : 
     417          16 :     type = PG_GETARG_TEXT_PP(3);
     418          16 :     c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
     419             : 
     420          16 :     data = PG_GETARG_BYTEA_PP(0);
     421          16 :     key = PG_GETARG_BYTEA_PP(1);
     422          16 :     iv = PG_GETARG_BYTEA_PP(2);
     423          16 :     dlen = VARSIZE_ANY_EXHDR(data);
     424          16 :     klen = VARSIZE_ANY_EXHDR(key);
     425          16 :     ivlen = VARSIZE_ANY_EXHDR(iv);
     426             : 
     427          16 :     rlen = px_combo_decrypt_len(c, dlen);
     428          16 :     res = palloc(VARHDRSZ + rlen);
     429             : 
     430          16 :     err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen,
     431             :                         (uint8 *) VARDATA_ANY(iv), ivlen);
     432          16 :     if (!err)
     433          16 :         err = px_combo_decrypt(c, (uint8 *) VARDATA_ANY(data), dlen,
     434             :                                (uint8 *) VARDATA(res), &rlen);
     435             : 
     436          16 :     px_combo_free(c);
     437             : 
     438          16 :     if (err)
     439           2 :         ereport(ERROR,
     440             :                 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
     441             :                  errmsg("decrypt_iv error: %s", px_strerror(err))));
     442             : 
     443          14 :     SET_VARSIZE(res, VARHDRSZ + rlen);
     444             : 
     445          14 :     PG_FREE_IF_COPY(data, 0);
     446          14 :     PG_FREE_IF_COPY(key, 1);
     447          14 :     PG_FREE_IF_COPY(iv, 2);
     448          14 :     PG_FREE_IF_COPY(type, 3);
     449             : 
     450          14 :     PG_RETURN_BYTEA_P(res);
     451             : }
     452             : 
     453             : /* SQL function: pg_random_bytes(int4) returns bytea */
     454           2 : PG_FUNCTION_INFO_V1(pg_random_bytes);
     455             : 
     456             : Datum
     457           0 : pg_random_bytes(PG_FUNCTION_ARGS)
     458             : {
     459           0 :     int         len = PG_GETARG_INT32(0);
     460             :     bytea      *res;
     461             : 
     462           0 :     if (len < 1 || len > 1024)
     463           0 :         ereport(ERROR,
     464             :                 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
     465             :                  errmsg("Length not in range")));
     466             : 
     467           0 :     res = palloc(VARHDRSZ + len);
     468           0 :     SET_VARSIZE(res, VARHDRSZ + len);
     469             : 
     470             :     /* generate result */
     471           0 :     if (!pg_strong_random(VARDATA(res), len))
     472           0 :         px_THROW_ERROR(PXE_NO_RANDOM);
     473             : 
     474           0 :     PG_RETURN_BYTEA_P(res);
     475             : }
     476             : 
     477             : /* SQL function: gen_random_uuid() returns uuid */
     478           2 : PG_FUNCTION_INFO_V1(pg_random_uuid);
     479             : 
     480             : Datum
     481           0 : pg_random_uuid(PG_FUNCTION_ARGS)
     482             : {
     483             :     /* redirect to built-in function */
     484           0 :     return gen_random_uuid(fcinfo);
     485             : }
     486             : 
     487           2 : PG_FUNCTION_INFO_V1(pg_check_fipsmode);
     488             : 
     489             : Datum
     490           0 : pg_check_fipsmode(PG_FUNCTION_ARGS)
     491             : {
     492           0 :     PG_RETURN_BOOL(CheckFIPSMode());
     493             : }
     494             : 
     495             : static void *
     496         308 : find_provider(text *name,
     497             :               PFN provider_lookup,
     498             :               const char *desc, int silent)
     499             : {
     500             :     void       *res;
     501             :     char       *buf;
     502             :     int         err;
     503             : 
     504         308 :     buf = downcase_truncate_identifier(VARDATA_ANY(name),
     505         308 :                                        VARSIZE_ANY_EXHDR(name),
     506             :                                        false);
     507             : 
     508         308 :     err = provider_lookup(buf, &res);
     509             : 
     510         308 :     if (err && !silent)
     511           6 :         ereport(ERROR,
     512             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     513             :                  errmsg("Cannot use \"%s\": %s", buf, px_strerror(err))));
     514             : 
     515         302 :     pfree(buf);
     516             : 
     517         302 :     return err ? NULL : res;
     518             : }

Generated by: LCOV version 1.14