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