postgresql/contrib/pgcrypto/pgp-pubdec.c

236 lines
4.8 KiB
C

/*
* pgp-pubdec.c
* Decrypt public-key encrypted session 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.
*
* contrib/pgcrypto/pgp-pubdec.c
*/
#include "postgres.h"
#include "pgp.h"
#include "px.h"
/*
* padded msg = 02 || PS || 00 || M
* PS - pad bytes
* M - msg
*/
static uint8 *
check_eme_pkcs1_v15(uint8 *data, int len)
{
uint8 *data_end = data + len;
uint8 *p = data;
int rnd = 0;
if (len < 1 + 8 + 1)
return NULL;
if (*p++ != 2)
return NULL;
while (p < data_end && *p)
{
p++;
rnd++;
}
if (p == data_end)
return NULL;
if (*p != 0)
return NULL;
if (rnd < 8)
return NULL;
return p + 1;
}
/*
* secret message: 1 byte algo, sesskey, 2 byte cksum
* ignore algo in cksum
*/
static int
control_cksum(uint8 *msg, int msglen)
{
int i;
unsigned my_cksum,
got_cksum;
if (msglen < 3)
return PXE_PGP_WRONG_KEY;
my_cksum = 0;
for (i = 1; i < msglen - 2; i++)
my_cksum += msg[i];
my_cksum &= 0xFFFF;
got_cksum = ((unsigned) (msg[msglen - 2]) << 8) + msg[msglen - 1];
if (my_cksum != got_cksum)
{
px_debug("pubenc cksum failed");
return PXE_PGP_WRONG_KEY;
}
return 0;
}
static int
decrypt_elgamal(PGP_PubKey *pk, PullFilter *pkt, PGP_MPI **m_p)
{
int res;
PGP_MPI *c1 = NULL;
PGP_MPI *c2 = NULL;
if (pk->algo != PGP_PUB_ELG_ENCRYPT)
return PXE_PGP_WRONG_KEY;
/* read elgamal encrypted data */
res = pgp_mpi_read(pkt, &c1);
if (res < 0)
goto out;
res = pgp_mpi_read(pkt, &c2);
if (res < 0)
goto out;
/* decrypt */
res = pgp_elgamal_decrypt(pk, c1, c2, m_p);
out:
pgp_mpi_free(c1);
pgp_mpi_free(c2);
return res;
}
static int
decrypt_rsa(PGP_PubKey *pk, PullFilter *pkt, PGP_MPI **m_p)
{
int res;
PGP_MPI *c;
if (pk->algo != PGP_PUB_RSA_ENCRYPT
&& pk->algo != PGP_PUB_RSA_ENCRYPT_SIGN)
return PXE_PGP_WRONG_KEY;
/* read rsa encrypted data */
res = pgp_mpi_read(pkt, &c);
if (res < 0)
return res;
/* decrypt */
res = pgp_rsa_decrypt(pk, c, m_p);
pgp_mpi_free(c);
return res;
}
/* key id is missing - user is expected to try all keys */
static const uint8
any_key[] = {0, 0, 0, 0, 0, 0, 0, 0};
int
pgp_parse_pubenc_sesskey(PGP_Context *ctx, PullFilter *pkt)
{
int ver;
int algo;
int res;
uint8 key_id[8];
PGP_PubKey *pk;
uint8 *msg;
int msglen;
PGP_MPI *m;
pk = ctx->pub_key;
if (pk == NULL)
{
px_debug("no pubkey?");
return PXE_BUG;
}
GETBYTE(pkt, ver);
if (ver != 3)
{
px_debug("unknown pubenc_sesskey pkt ver=%d", ver);
return PXE_PGP_CORRUPT_DATA;
}
/*
* check if keyid's match - user-friendly msg
*/
res = pullf_read_fixed(pkt, 8, key_id);
if (res < 0)
return res;
if (memcmp(key_id, any_key, 8) != 0
&& memcmp(key_id, pk->key_id, 8) != 0)
{
px_debug("key_id's does not match");
return PXE_PGP_WRONG_KEY;
}
/*
* Decrypt
*/
GETBYTE(pkt, algo);
switch (algo)
{
case PGP_PUB_ELG_ENCRYPT:
res = decrypt_elgamal(pk, pkt, &m);
break;
case PGP_PUB_RSA_ENCRYPT:
case PGP_PUB_RSA_ENCRYPT_SIGN:
res = decrypt_rsa(pk, pkt, &m);
break;
default:
res = PXE_PGP_UNKNOWN_PUBALGO;
}
if (res < 0)
return res;
/*
* extract message
*/
msg = check_eme_pkcs1_v15(m->data, m->bytes);
if (msg == NULL)
{
px_debug("check_eme_pkcs1_v15 failed");
res = PXE_PGP_WRONG_KEY;
goto out;
}
msglen = m->bytes - (msg - m->data);
res = control_cksum(msg, msglen);
if (res < 0)
goto out;
/*
* got sesskey
*/
ctx->cipher_algo = *msg;
ctx->sess_key_len = msglen - 3;
memcpy(ctx->sess_key, msg + 1, ctx->sess_key_len);
out:
pgp_mpi_free(m);
if (res < 0)
return res;
return pgp_expect_packet_end(pkt);
}