postgres/contrib/pgcrypto/pgp-pubkey.c
2005-07-10 13:46:29 +00:00

449 lines
9.3 KiB
C

/*
* pgp-pubkey.c
* Read public or secret key.
*
* 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.
*
* $PostgreSQL: pgsql/contrib/pgcrypto/pgp-pubkey.c,v 1.1 2005/07/10 13:46:29 momjian Exp $
*/
#include <postgres.h>
#include "px.h"
#include "mbuf.h"
#include "pgp.h"
#define PXE_PGP_BAD_KEY -90
int pgp_key_alloc(PGP_PubKey **pk_p)
{
PGP_PubKey *pk;
pk = px_alloc(sizeof(*pk));
memset(pk, 0, sizeof(*pk));
*pk_p = pk;
return 0;
}
void pgp_key_free(PGP_PubKey *pk)
{
if (pk->elg_p)
pgp_mpi_free(pk->elg_p);
if (pk->elg_g)
pgp_mpi_free(pk->elg_g);
if (pk->elg_y)
pgp_mpi_free(pk->elg_y);
if (pk->elg_x)
pgp_mpi_free(pk->elg_x);
memset(pk, 0, sizeof(*pk));
px_free(pk);
}
static int
calc_key_id(PGP_PubKey *pk)
{
int res;
PX_MD *md;
int len;
uint8 hdr[3];
uint8 hash[20];
res = pgp_load_digest(PGP_DIGEST_SHA1, &md);
if (res < 0)
return res;
len = 1 + 4 + 1;
switch (pk->algo)
{
case PGP_PUB_ELG_ENCRYPT:
len += 2 + pk->elg_p->bytes;
len += 2 + pk->elg_g->bytes;
len += 2 + pk->elg_y->bytes;
break;
}
hdr[0] = 0x99;
hdr[1] = len >> 8;
hdr[2] = len & 0xFF;
px_md_update(md, hdr, 3);
px_md_update(md, &pk->ver, 1);
px_md_update(md, pk->time, 4);
px_md_update(md, &pk->algo, 1);
switch (pk->algo)
{
case PGP_PUB_ELG_ENCRYPT:
pgp_mpi_hash(md, pk->elg_p);
pgp_mpi_hash(md, pk->elg_g);
pgp_mpi_hash(md, pk->elg_y);
break;
}
px_md_finish(md, hash);
px_md_free(md);
memcpy(pk->key_id, hash + 12, 8);
memset(hash, 0, 20);
return 0;
}
int _pgp_read_public_key(PullFilter *pkt, PGP_PubKey *pk)
{
int res;
/* get version */
GETBYTE(pkt, pk->ver);
if (pk->ver != 4) {
px_debug("\tunsupported version: %d", pk->ver);
return PXE_PGP_NOT_V4_KEYPKT;
}
/* read time */
res = pullf_read_fixed(pkt, 4, pk->time);
if (res < 0)
return res;
/* pubkey algorithm */
GETBYTE(pkt, pk->algo);
switch (pk->algo) {
case PGP_PUB_RSA_ENCRYPT_SIGN:
case PGP_PUB_RSA_ENCRYPT:
case PGP_PUB_RSA_SIGN:
case PGP_PUB_DSA_SIGN:
res = pgp_skip_packet(pkt);
break;
case PGP_PUB_ELG_ENCRYPT:
res = pgp_mpi_read(pkt, &pk->elg_p);
if (res < 0) break;
res = pgp_mpi_read(pkt, &pk->elg_g);
if (res < 0) break;
res = pgp_mpi_read(pkt, &pk->elg_y);
if (res < 0) break;
res = calc_key_id(pk);
break;
default:
px_debug("unknown public algo: %d", pk->algo);
res = PXE_PGP_UNKNOWN_PUBALGO;
}
return res;
}
#define HIDE_CLEAR 0
#define HIDE_CKSUM 255
#define HIDE_SHA1 254
static int
check_key_sha1(PullFilter *src, PGP_PubKey *pk)
{
int res;
uint8 got_sha1[20];
uint8 my_sha1[20];
PX_MD *md;
res = pullf_read_fixed(src, 20, got_sha1);
if (res < 0)
return res;
res = pgp_load_digest(PGP_DIGEST_SHA1, &md);
if (res < 0)
goto err;
switch (pk->algo)
{
case PGP_PUB_ELG_ENCRYPT:
pgp_mpi_hash(md, pk->elg_x);
break;
}
px_md_finish(md, my_sha1);
px_md_free(md);
if (memcmp(my_sha1, got_sha1, 20) != 0)
{
px_debug("key sha1 check failed");
res = PXE_PGP_KEYPKT_CORRUPT;
}
err:
memset(got_sha1, 0, 20);
memset(my_sha1, 0, 20);
return res;
}
static int
check_key_cksum(PullFilter *src, PGP_PubKey *pk)
{
int res;
unsigned got_cksum, my_cksum = 0;
uint8 buf[2];
res = pullf_read_fixed(src, 2, buf);
if (res < 0)
return res;
got_cksum = ((unsigned)buf[0] << 8) + buf[1];
switch (pk->algo)
{
case PGP_PUB_ELG_ENCRYPT:
my_cksum = pgp_mpi_cksum(0, pk->elg_x);
break;
}
if (my_cksum != got_cksum)
{
px_debug("key cksum check failed");
return PXE_PGP_KEYPKT_CORRUPT;
}
return 0;
}
static int process_secret_key(PullFilter *pkt, PGP_PubKey *pk,
const uint8 *key, int key_len)
{
int res;
int hide_type;
int cipher_algo;
int bs;
uint8 iv[512];
PullFilter *pf_decrypt = NULL, *pf_key;
PGP_CFB *cfb = NULL;
PGP_S2K s2k;
/* first read public key part */
res = _pgp_read_public_key(pkt, pk);
if (res < 0)
return res;
/* skip key? */
if (pk->algo != PGP_PUB_ELG_ENCRYPT)
return 0;
/*
* is secret key encrypted?
*/
GETBYTE(pkt, hide_type);
if (hide_type == HIDE_SHA1 || hide_type == HIDE_CKSUM) {
if (key == NULL)
return PXE_PGP_NEED_SECRET_PSW;
GETBYTE(pkt, cipher_algo);
res = pgp_s2k_read(pkt, &s2k);
if (res < 0)
return res;
res = pgp_s2k_process(&s2k, cipher_algo, key, key_len);
if (res < 0)
return res;
bs = pgp_get_cipher_block_size(cipher_algo);
if (bs == 0) {
px_debug("unknown cipher algo=%d", cipher_algo);
return PXE_PGP_UNSUPPORTED_CIPHER;
}
res = pullf_read_fixed(pkt, bs, iv);
if (res < 0)
return res;
/*
* create decrypt filter
*/
res = pgp_cfb_create(&cfb, cipher_algo, s2k.key, s2k.key_len, 0, iv);
if (res < 0)
return res;
res = pullf_create(&pf_decrypt, &pgp_decrypt_filter, cfb, pkt);
if (res < 0)
return res;
pf_key = pf_decrypt;
} else if (hide_type == HIDE_CLEAR) {
pf_key = pkt;
} else {
px_debug("unknown hide type");
return PXE_PGP_KEYPKT_CORRUPT;
}
/* read secret key */
switch (pk->algo) {
case PGP_PUB_RSA_ENCRYPT_SIGN:
case PGP_PUB_RSA_ENCRYPT:
case PGP_PUB_RSA_SIGN:
case PGP_PUB_DSA_SIGN:
px_debug("unsupported public algo: %d", pk->algo);
res = PXE_PGP_UNSUPPORTED_PUBALGO;
break;
case PGP_PUB_ELG_ENCRYPT:
res = pgp_mpi_read(pf_key, &pk->elg_x);
break;
default:
px_debug("unknown public algo: %d", pk->algo);
res = PXE_PGP_KEYPKT_CORRUPT;
}
/* read checksum / sha1 */
if (res >= 0)
{
if (hide_type == HIDE_SHA1)
res = check_key_sha1(pf_key, pk);
else
res = check_key_cksum(pf_key, pk);
}
if (res >= 0)
res = pgp_expect_packet_end(pf_key);
if (pf_decrypt)
pullf_free(pf_decrypt);
if (cfb)
pgp_cfb_free(cfb);
return res;
}
static int
internal_read_key(PullFilter *src, PGP_PubKey **pk_p,
const uint8 *key, int key_len, int pubtype)
{
PullFilter *pkt = NULL;
int res;
uint8 tag;
int len;
PGP_PubKey *pk = NULL;
int got_key = 0;
int n_subkey = 0;
res = pgp_key_alloc(&pk);
if (res < 0)
return res;
/*
* Search for Elgamal key.
*
* Error out on anything fancy.
*/
res = PXE_PGP_KEYPKT_CORRUPT;
while (1) {
res = pgp_parse_pkt_hdr(src, &tag, &len, 0);
if (res <= 0)
break;
res = pgp_create_pkt_reader(&pkt, src, len, res, NULL);
if (res < 0)
break;
switch (tag) {
case PGP_PKT_SECRET_KEY:
if (got_key)
{
res = PXE_PGP_MULTIPLE_KEYS;
break;
}
got_key = 1;
n_subkey = 0;
/* fallthru */
case PGP_PKT_SECRET_SUBKEY:
if (tag == PGP_PKT_SECRET_SUBKEY)
n_subkey++;
if (n_subkey > 1)
res = PXE_PGP_MULTIPLE_SUBKEYS;
else if (pubtype == 1)
res = process_secret_key(pkt, pk, key, key_len);
else
res = PXE_PGP_EXPECT_PUBLIC_KEY;
break;
case PGP_PKT_PUBLIC_KEY:
if (got_key)
{
res = PXE_PGP_MULTIPLE_KEYS;
break;
}
got_key = 1;
n_subkey = 0;
/* fallthru */
case PGP_PKT_PUBLIC_SUBKEY:
if (tag == PGP_PKT_PUBLIC_SUBKEY)
n_subkey++;
if (n_subkey > 1)
res = PXE_PGP_MULTIPLE_SUBKEYS;
else if (pubtype == 0)
res = _pgp_read_public_key(pkt, pk);
else
res = PXE_PGP_EXPECT_SECRET_KEY;
break;
case PGP_PKT_SIGNATURE:
case PGP_PKT_MARKER:
case PGP_PKT_TRUST:
case PGP_PKT_USER_ID:
case PGP_PKT_USER_ATTR:
case PGP_PKT_PRIV_61:
res = pgp_skip_packet(pkt);
break;
default:
px_debug("unknown/unexpected packet: %d", tag);
res = PXE_PGP_UNEXPECTED_PKT;
}
pullf_free(pkt);
pkt = NULL;
if (res < 0)
break;
if (pk->algo == PGP_PUB_ELG_ENCRYPT)
break;
}
if (pkt)
pullf_free(pkt);
if (res < 0)
pgp_key_free(pk);
else {
if (pk->algo == PGP_PUB_ELG_ENCRYPT)
*pk_p = pk;
else {
pgp_key_free(pk);
px_debug("non-elg");
res = PXE_PGP_NO_USABLE_KEY;
}
}
return res < 0 ? res : 0;
}
int
pgp_set_pubkey(PGP_Context *ctx, MBuf *keypkt,
const uint8 *key, int key_len, int pubtype)
{
int res;
PullFilter *src;
PGP_PubKey *pk = NULL;
res = pullf_create_mbuf_reader(&src, keypkt);
if (res < 0)
return res;
res = internal_read_key(src, &pk, key, key_len, pubtype);
pullf_free(src);
if (res >= 0)
ctx->pub_key = pk;
return res < 0 ? res : 0;
}