| /* |
| * pgp-s2k.c |
| * OpenPGP string2key functions. |
| * |
| * Copyright (c) 2005 Marko Kreen |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * contrib/pgcrypto/pgp-s2k.c |
| */ |
| |
| #include "postgres.h" |
| |
| #include "px.h" |
| #include "mbuf.h" |
| #include "pgp.h" |
| |
| static int |
| calc_s2k_simple(PGP_S2K *s2k, PX_MD *md, const uint8 *key, |
| unsigned key_len) |
| { |
| unsigned md_rlen; |
| uint8 buf[PGP_MAX_DIGEST]; |
| unsigned preload; |
| unsigned remain; |
| uint8 *dst = s2k->key; |
| |
| md_rlen = px_md_result_size(md); |
| |
| remain = s2k->key_len; |
| preload = 0; |
| while (remain > 0) |
| { |
| px_md_reset(md); |
| |
| if (preload) |
| { |
| memset(buf, 0, preload); |
| px_md_update(md, buf, preload); |
| } |
| preload++; |
| |
| px_md_update(md, key, key_len); |
| px_md_finish(md, buf); |
| |
| if (remain > md_rlen) |
| { |
| memcpy(dst, buf, md_rlen); |
| dst += md_rlen; |
| remain -= md_rlen; |
| } |
| else |
| { |
| memcpy(dst, buf, remain); |
| remain = 0; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| calc_s2k_salted(PGP_S2K *s2k, PX_MD *md, const uint8 *key, unsigned key_len) |
| { |
| unsigned md_rlen; |
| uint8 buf[PGP_MAX_DIGEST]; |
| unsigned preload = 0; |
| uint8 *dst; |
| unsigned remain; |
| |
| md_rlen = px_md_result_size(md); |
| |
| dst = s2k->key; |
| remain = s2k->key_len; |
| while (remain > 0) |
| { |
| px_md_reset(md); |
| |
| if (preload > 0) |
| { |
| memset(buf, 0, preload); |
| px_md_update(md, buf, preload); |
| } |
| preload++; |
| |
| px_md_update(md, s2k->salt, PGP_S2K_SALT); |
| px_md_update(md, key, key_len); |
| px_md_finish(md, buf); |
| |
| if (remain > md_rlen) |
| { |
| memcpy(dst, buf, md_rlen); |
| remain -= md_rlen; |
| dst += md_rlen; |
| } |
| else |
| { |
| memcpy(dst, buf, remain); |
| remain = 0; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| calc_s2k_iter_salted(PGP_S2K *s2k, PX_MD *md, const uint8 *key, |
| unsigned key_len) |
| { |
| unsigned md_rlen; |
| uint8 buf[PGP_MAX_DIGEST]; |
| uint8 *dst; |
| unsigned preload = 0; |
| unsigned remain, |
| c, |
| cval, |
| curcnt, |
| count; |
| |
| cval = s2k->iter; |
| count = ((unsigned) 16 + (cval & 15)) << ((cval >> 4) + 6); |
| |
| md_rlen = px_md_result_size(md); |
| |
| remain = s2k->key_len; |
| dst = s2k->key; |
| while (remain > 0) |
| { |
| px_md_reset(md); |
| |
| if (preload) |
| { |
| memset(buf, 0, preload); |
| px_md_update(md, buf, preload); |
| } |
| preload++; |
| |
| px_md_update(md, s2k->salt, PGP_S2K_SALT); |
| px_md_update(md, key, key_len); |
| curcnt = PGP_S2K_SALT + key_len; |
| |
| while (curcnt < count) |
| { |
| if (curcnt + PGP_S2K_SALT < count) |
| c = PGP_S2K_SALT; |
| else |
| c = count - curcnt; |
| px_md_update(md, s2k->salt, c); |
| curcnt += c; |
| |
| if (curcnt + key_len < count) |
| c = key_len; |
| else if (curcnt < count) |
| c = count - curcnt; |
| else |
| break; |
| px_md_update(md, key, c); |
| curcnt += c; |
| } |
| px_md_finish(md, buf); |
| |
| if (remain > md_rlen) |
| { |
| memcpy(dst, buf, md_rlen); |
| remain -= md_rlen; |
| dst += md_rlen; |
| } |
| else |
| { |
| memcpy(dst, buf, remain); |
| remain = 0; |
| } |
| } |
| return 0; |
| } |
| |
| /* |
| * Decide S2K_ISALTED iteration count |
| * |
| * Too small: weak |
| * Too big: slow |
| * gpg defaults to 96 => 65536 iters |
| * let it float a bit: 96 + 32 => 262144 iters |
| */ |
| static int |
| decide_count(unsigned rand_byte) |
| { |
| return 96 + (rand_byte & 0x1F); |
| } |
| |
| int |
| pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo) |
| { |
| int res = 0; |
| uint8 tmp; |
| |
| s2k->mode = mode; |
| s2k->digest_algo = digest_algo; |
| |
| switch (s2k->mode) |
| { |
| case 0: |
| break; |
| case 1: |
| res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT); |
| break; |
| case 3: |
| res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT); |
| if (res < 0) |
| break; |
| res = px_get_pseudo_random_bytes(&tmp, 1); |
| if (res < 0) |
| break; |
| s2k->iter = decide_count(tmp); |
| break; |
| default: |
| res = PXE_PGP_BAD_S2K_MODE; |
| } |
| return res; |
| } |
| |
| int |
| pgp_s2k_read(PullFilter *src, PGP_S2K *s2k) |
| { |
| int res = 0; |
| |
| GETBYTE(src, s2k->mode); |
| GETBYTE(src, s2k->digest_algo); |
| switch (s2k->mode) |
| { |
| case 0: |
| break; |
| case 1: |
| res = pullf_read_fixed(src, 8, s2k->salt); |
| break; |
| case 3: |
| res = pullf_read_fixed(src, 8, s2k->salt); |
| if (res < 0) |
| break; |
| GETBYTE(src, s2k->iter); |
| break; |
| default: |
| res = PXE_PGP_BAD_S2K_MODE; |
| } |
| return res; |
| } |
| |
| int |
| pgp_s2k_process(PGP_S2K *s2k, int cipher, const uint8 *key, int key_len) |
| { |
| int res; |
| PX_MD *md; |
| |
| s2k->key_len = pgp_get_cipher_key_size(cipher); |
| if (s2k->key_len <= 0) |
| return PXE_PGP_UNSUPPORTED_CIPHER; |
| |
| res = pgp_load_digest(s2k->digest_algo, &md); |
| if (res < 0) |
| return res; |
| |
| switch (s2k->mode) |
| { |
| case 0: |
| res = calc_s2k_simple(s2k, md, key, key_len); |
| break; |
| case 1: |
| res = calc_s2k_salted(s2k, md, key, key_len); |
| break; |
| case 3: |
| res = calc_s2k_iter_salted(s2k, md, key, key_len); |
| break; |
| default: |
| res = PXE_PGP_BAD_S2K_MODE; |
| } |
| px_md_free(md); |
| return res; |
| } |