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 "utils/builtins.h"
38 #include "utils/uuid.h"
39
40 #include "px.h"
41 #include "px-crypt.h"
42 #include "pgcrypto.h"
43
44 PG_MODULE_MAGIC;
45
46 /* private stuff */
47
48 typedef int (*PFN) (const char *name, void **res);
49 static void *find_provider(text *name, PFN pf, char *desc, int silent);
50
51 /* SQL function: hash(bytea, text) returns bytea */
52 PG_FUNCTION_INFO_V1(pg_digest);
53
54 Datum
pg_digest(PG_FUNCTION_ARGS)55 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 name = PG_GETARG_TEXT_P(1);
65
66 /* will give error if fails */
67 md = find_provider(name, (PFN) px_find_digest, "Digest", 0);
68
69 hlen = px_md_result_size(md);
70
71 res = (text *) palloc(hlen + VARHDRSZ);
72 SET_VARSIZE(res, hlen + VARHDRSZ);
73
74 arg = PG_GETARG_BYTEA_P(0);
75 len = VARSIZE(arg) - VARHDRSZ;
76
77 px_md_update(md, (uint8 *) VARDATA(arg), len);
78 px_md_finish(md, (uint8 *) VARDATA(res));
79 px_md_free(md);
80
81 PG_FREE_IF_COPY(arg, 0);
82 PG_FREE_IF_COPY(name, 1);
83
84 PG_RETURN_BYTEA_P(res);
85 }
86
87 /* SQL function: hmac(data:bytea, key:bytea, type:text) returns bytea */
88 PG_FUNCTION_INFO_V1(pg_hmac);
89
90 Datum
pg_hmac(PG_FUNCTION_ARGS)91 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 name = PG_GETARG_TEXT_P(2);
103
104 /* will give error if fails */
105 h = find_provider(name, (PFN) px_find_hmac, "HMAC", 0);
106
107 hlen = px_hmac_result_size(h);
108
109 res = (text *) palloc(hlen + VARHDRSZ);
110 SET_VARSIZE(res, hlen + VARHDRSZ);
111
112 arg = PG_GETARG_BYTEA_P(0);
113 key = PG_GETARG_BYTEA_P(1);
114 len = VARSIZE(arg) - VARHDRSZ;
115 klen = VARSIZE(key) - VARHDRSZ;
116
117 px_hmac_init(h, (uint8 *) VARDATA(key), klen);
118 px_hmac_update(h, (uint8 *) VARDATA(arg), len);
119 px_hmac_finish(h, (uint8 *) VARDATA(res));
120 px_hmac_free(h);
121
122 PG_FREE_IF_COPY(arg, 0);
123 PG_FREE_IF_COPY(key, 1);
124 PG_FREE_IF_COPY(name, 2);
125
126 PG_RETURN_BYTEA_P(res);
127 }
128
129
130 /* SQL function: pg_gen_salt(text) returns text */
131 PG_FUNCTION_INFO_V1(pg_gen_salt);
132
133 Datum
pg_gen_salt(PG_FUNCTION_ARGS)134 pg_gen_salt(PG_FUNCTION_ARGS)
135 {
136 text *arg0 = PG_GETARG_TEXT_PP(0);
137 int len;
138 char buf[PX_MAX_SALT_LEN + 1];
139
140 text_to_cstring_buffer(arg0, buf, sizeof(buf));
141 len = px_gen_salt(buf, buf, 0);
142 if (len < 0)
143 ereport(ERROR,
144 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
145 errmsg("gen_salt: %s", px_strerror(len))));
146
147 PG_FREE_IF_COPY(arg0, 0);
148
149 PG_RETURN_TEXT_P(cstring_to_text_with_len(buf, len));
150 }
151
152 /* SQL function: pg_gen_salt(text, int4) returns text */
153 PG_FUNCTION_INFO_V1(pg_gen_salt_rounds);
154
155 Datum
pg_gen_salt_rounds(PG_FUNCTION_ARGS)156 pg_gen_salt_rounds(PG_FUNCTION_ARGS)
157 {
158 text *arg0 = PG_GETARG_TEXT_PP(0);
159 int rounds = PG_GETARG_INT32(1);
160 int len;
161 char buf[PX_MAX_SALT_LEN + 1];
162
163 text_to_cstring_buffer(arg0, buf, sizeof(buf));
164 len = px_gen_salt(buf, buf, rounds);
165 if (len < 0)
166 ereport(ERROR,
167 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
168 errmsg("gen_salt: %s", px_strerror(len))));
169
170 PG_FREE_IF_COPY(arg0, 0);
171
172 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 PG_FUNCTION_INFO_V1(pg_crypt);
177
178 Datum
pg_crypt(PG_FUNCTION_ARGS)179 pg_crypt(PG_FUNCTION_ARGS)
180 {
181 text *arg0 = PG_GETARG_TEXT_PP(0);
182 text *arg1 = PG_GETARG_TEXT_PP(1);
183 char *buf0,
184 *buf1,
185 *cres,
186 *resbuf;
187 text *res;
188
189 buf0 = text_to_cstring(arg0);
190 buf1 = text_to_cstring(arg1);
191
192 resbuf = palloc0(PX_MAX_CRYPT);
193
194 cres = px_crypt(buf0, buf1, resbuf, PX_MAX_CRYPT);
195
196 pfree(buf0);
197 pfree(buf1);
198
199 if (cres == NULL)
200 ereport(ERROR,
201 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
202 errmsg("crypt(3) returned NULL")));
203
204 res = cstring_to_text(cres);
205
206 pfree(resbuf);
207
208 PG_FREE_IF_COPY(arg0, 0);
209 PG_FREE_IF_COPY(arg1, 1);
210
211 PG_RETURN_TEXT_P(res);
212 }
213
214 /* SQL function: pg_encrypt(bytea, bytea, text) returns bytea */
215 PG_FUNCTION_INFO_V1(pg_encrypt);
216
217 Datum
pg_encrypt(PG_FUNCTION_ARGS)218 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 type = PG_GETARG_TEXT_P(2);
231 c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
232
233 data = PG_GETARG_BYTEA_P(0);
234 key = PG_GETARG_BYTEA_P(1);
235 dlen = VARSIZE(data) - VARHDRSZ;
236 klen = VARSIZE(key) - VARHDRSZ;
237
238 rlen = px_combo_encrypt_len(c, dlen);
239 res = palloc(VARHDRSZ + rlen);
240
241 err = px_combo_init(c, (uint8 *) VARDATA(key), klen, NULL, 0);
242 if (!err)
243 err = px_combo_encrypt(c, (uint8 *) VARDATA(data), dlen,
244 (uint8 *) VARDATA(res), &rlen);
245 px_combo_free(c);
246
247 PG_FREE_IF_COPY(data, 0);
248 PG_FREE_IF_COPY(key, 1);
249 PG_FREE_IF_COPY(type, 2);
250
251 if (err)
252 {
253 pfree(res);
254 ereport(ERROR,
255 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
256 errmsg("encrypt error: %s", px_strerror(err))));
257 }
258
259 SET_VARSIZE(res, VARHDRSZ + rlen);
260 PG_RETURN_BYTEA_P(res);
261 }
262
263 /* SQL function: pg_decrypt(bytea, bytea, text) returns bytea */
264 PG_FUNCTION_INFO_V1(pg_decrypt);
265
266 Datum
pg_decrypt(PG_FUNCTION_ARGS)267 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 type = PG_GETARG_TEXT_P(2);
280 c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
281
282 data = PG_GETARG_BYTEA_P(0);
283 key = PG_GETARG_BYTEA_P(1);
284 dlen = VARSIZE(data) - VARHDRSZ;
285 klen = VARSIZE(key) - VARHDRSZ;
286
287 rlen = px_combo_decrypt_len(c, dlen);
288 res = palloc(VARHDRSZ + rlen);
289
290 err = px_combo_init(c, (uint8 *) VARDATA(key), klen, NULL, 0);
291 if (!err)
292 err = px_combo_decrypt(c, (uint8 *) VARDATA(data), dlen,
293 (uint8 *) VARDATA(res), &rlen);
294
295 px_combo_free(c);
296
297 if (err)
298 ereport(ERROR,
299 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
300 errmsg("decrypt error: %s", px_strerror(err))));
301
302 SET_VARSIZE(res, VARHDRSZ + rlen);
303
304 PG_FREE_IF_COPY(data, 0);
305 PG_FREE_IF_COPY(key, 1);
306 PG_FREE_IF_COPY(type, 2);
307
308 PG_RETURN_BYTEA_P(res);
309 }
310
311 /* SQL function: pg_encrypt_iv(bytea, bytea, bytea, text) returns bytea */
312 PG_FUNCTION_INFO_V1(pg_encrypt_iv);
313
314 Datum
pg_encrypt_iv(PG_FUNCTION_ARGS)315 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 type = PG_GETARG_TEXT_P(3);
330 c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
331
332 data = PG_GETARG_BYTEA_P(0);
333 key = PG_GETARG_BYTEA_P(1);
334 iv = PG_GETARG_BYTEA_P(2);
335 dlen = VARSIZE(data) - VARHDRSZ;
336 klen = VARSIZE(key) - VARHDRSZ;
337 ivlen = VARSIZE(iv) - VARHDRSZ;
338
339 rlen = px_combo_encrypt_len(c, dlen);
340 res = palloc(VARHDRSZ + rlen);
341
342 err = px_combo_init(c, (uint8 *) VARDATA(key), klen,
343 (uint8 *) VARDATA(iv), ivlen);
344 if (!err)
345 err = px_combo_encrypt(c, (uint8 *) VARDATA(data), dlen,
346 (uint8 *) VARDATA(res), &rlen);
347
348 px_combo_free(c);
349
350 if (err)
351 ereport(ERROR,
352 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
353 errmsg("encrypt_iv error: %s", px_strerror(err))));
354
355 SET_VARSIZE(res, VARHDRSZ + rlen);
356
357 PG_FREE_IF_COPY(data, 0);
358 PG_FREE_IF_COPY(key, 1);
359 PG_FREE_IF_COPY(iv, 2);
360 PG_FREE_IF_COPY(type, 3);
361
362 PG_RETURN_BYTEA_P(res);
363 }
364
365 /* SQL function: pg_decrypt_iv(bytea, bytea, bytea, text) returns bytea */
366 PG_FUNCTION_INFO_V1(pg_decrypt_iv);
367
368 Datum
pg_decrypt_iv(PG_FUNCTION_ARGS)369 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 type = PG_GETARG_TEXT_P(3);
384 c = find_provider(type, (PFN) px_find_combo, "Cipher", 0);
385
386 data = PG_GETARG_BYTEA_P(0);
387 key = PG_GETARG_BYTEA_P(1);
388 iv = PG_GETARG_BYTEA_P(2);
389 dlen = VARSIZE(data) - VARHDRSZ;
390 klen = VARSIZE(key) - VARHDRSZ;
391 ivlen = VARSIZE(iv) - VARHDRSZ;
392
393 rlen = px_combo_decrypt_len(c, dlen);
394 res = palloc(VARHDRSZ + rlen);
395
396 err = px_combo_init(c, (uint8 *) VARDATA(key), klen,
397 (uint8 *) VARDATA(iv), ivlen);
398 if (!err)
399 err = px_combo_decrypt(c, (uint8 *) VARDATA(data), dlen,
400 (uint8 *) VARDATA(res), &rlen);
401
402 px_combo_free(c);
403
404 if (err)
405 ereport(ERROR,
406 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
407 errmsg("decrypt_iv error: %s", px_strerror(err))));
408
409 SET_VARSIZE(res, VARHDRSZ + rlen);
410
411 PG_FREE_IF_COPY(data, 0);
412 PG_FREE_IF_COPY(key, 1);
413 PG_FREE_IF_COPY(iv, 2);
414 PG_FREE_IF_COPY(type, 3);
415
416 PG_RETURN_BYTEA_P(res);
417 }
418
419 /* SQL function: pg_random_bytes(int4) returns bytea */
420 PG_FUNCTION_INFO_V1(pg_random_bytes);
421
422 Datum
pg_random_bytes(PG_FUNCTION_ARGS)423 pg_random_bytes(PG_FUNCTION_ARGS)
424 {
425 int err;
426 int len = PG_GETARG_INT32(0);
427 bytea *res;
428
429 if (len < 1 || len > 1024)
430 ereport(ERROR,
431 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
432 errmsg("Length not in range")));
433
434 res = palloc(VARHDRSZ + len);
435 SET_VARSIZE(res, VARHDRSZ + len);
436
437 /* generate result */
438 err = px_get_random_bytes((uint8 *) VARDATA(res), len);
439 if (err < 0)
440 ereport(ERROR,
441 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
442 errmsg("Random generator error: %s", px_strerror(err))));
443
444 PG_RETURN_BYTEA_P(res);
445 }
446
447 /* SQL function: gen_random_uuid() returns uuid */
448 PG_FUNCTION_INFO_V1(pg_random_uuid);
449
450 Datum
pg_random_uuid(PG_FUNCTION_ARGS)451 pg_random_uuid(PG_FUNCTION_ARGS)
452 {
453 uint8 *buf = (uint8 *) palloc(UUID_LEN);
454 int err;
455
456 /* generate random bits */
457 err = px_get_random_bytes(buf, UUID_LEN);
458 if (err < 0)
459 ereport(ERROR,
460 (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION),
461 errmsg("Random generator error: %s", px_strerror(err))));
462
463 /*
464 * Set magic numbers for a "version 4" (pseudorandom) UUID, see
465 * http://tools.ietf.org/html/rfc4122#section-4.4
466 */
467 buf[6] = (buf[6] & 0x0f) | 0x40; /* "version" field */
468 buf[8] = (buf[8] & 0x3f) | 0x80; /* "variant" field */
469
470 PG_RETURN_UUID_P((pg_uuid_t *) buf);
471 }
472
473 static void *
find_provider(text * name,PFN provider_lookup,char * desc,int silent)474 find_provider(text *name,
475 PFN provider_lookup,
476 char *desc, int silent)
477 {
478 void *res;
479 char *buf;
480 int err;
481
482 buf = downcase_truncate_identifier(VARDATA(name),
483 VARSIZE(name) - VARHDRSZ,
484 false);
485
486 err = provider_lookup(buf, &res);
487
488 if (err && !silent)
489 ereport(ERROR,
490 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
491 errmsg("Cannot use \"%s\": %s", buf, px_strerror(err))));
492
493 pfree(buf);
494
495 return err ? NULL : res;
496 }
497