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