Add the ability to perform Elgamal encryption to netpgp. Some of this

code is inspired by the (BSD-licensed) Elgamal crypto code in
Postgresql by Marko Kreen, but netpgp uses BIGNUM numbers instead of
MPIs, and its keys have a completely different structure, so much has
changed.

% cp config.h f
% netpgp -e f
netpgp: default key set to "d4a643c5"
% gpg -d f.gpg > f2

You need a passphrase to unlock the secret key for
user: "Alistair Crooks (DSA TEST KEY - DO NOT USE) <agc@netbsd.org>"
2048-bit ELG-E key, ID D727BC1E, created 2010-05-19 (main key ID D4A643C5)

gpg: encrypted with 2048-bit ELG-E key, ID D727BC1E, created 2010-05-19
      "Alistair Crooks (DSA TEST KEY - DO NOT USE) <agc@netbsd.org>"
% diff f f2
% ls -al f*
-rw-r--r--  1 agc  agc  5730 Nov  6 05:40 f
-rw-------  1 agc  agc  1727 Nov  6 05:40 f.gpg
-rw-r--r--  1 agc  agc  5730 Nov  6 05:41 f2
%
This commit is contained in:
agc 2010-11-07 02:29:28 +00:00
parent 3ca57ae182
commit 37d8b79b30
5 changed files with 186 additions and 27 deletions

View File

@ -12,9 +12,6 @@
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <dmalloc.h> header file. */
#undef HAVE_DMALLOC_H
/* Define to 1 if you have the <errno.h> header file. */
#undef HAVE_ERRNO_H

View File

@ -57,7 +57,7 @@
#if defined(__NetBSD__)
__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD: create.c,v 1.35 2010/11/04 15:38:45 agc Exp $");
__RCSID("$NetBSD: create.c,v 1.36 2010/11/07 02:29:28 agc Exp $");
#endif
#include <sys/types.h>
@ -881,41 +881,39 @@ encode_m_buf(const uint8_t *M, size_t mLen, const __ops_pubkey_t * pubkey,
/* implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC */
switch (pubkey->alg) {
case OPS_PKA_RSA:
k = (unsigned)BN_num_bytes(pubkey->key.rsa.n);
if (mLen > k - 11) {
(void) fprintf(stderr, "encode_m_buf: message too long\n");
return 0;
}
break;
case OPS_PKA_DSA:
case OPS_PKA_ELGAMAL:
(void) fprintf(stderr, "encode_m_buf: DSA/Elgamal encryption not implemented yet\n");
k = (unsigned)BN_num_bytes(pubkey->key.elgamal.p);
if (mLen > k - 11) {
(void) fprintf(stderr, "encode_m_buf: message too long\n");
return 0;
}
break;
default:
(void) fprintf(stderr, "encode_m_buf: pubkey algorithm\n");
return 0;
}
k = (unsigned)BN_num_bytes(pubkey->key.rsa.n);
if (mLen > k - 11) {
(void) fprintf(stderr, "encode_m_buf: message too long\n");
return 0;
}
/* these two bytes defined by RFC */
EM[0] = 0x00;
EM[1] = 0x02;
/* add non-zero random bytes of length k - mLen -3 */
for (i = 2; i < (k - mLen) - 1; ++i) {
do {
__ops_random(EM + i, 1);
} while (EM[i] == 0);
}
if (i < 8 + 2) {
(void) fprintf(stderr, "encode_m_buf: bad i len\n");
return 0;
}
EM[i++] = 0;
(void) memcpy(EM + i, M, mLen);
if (__ops_get_debug_level(__FILE__)) {
hexdump(stderr, "Encoded Message:", EM, mLen);
}
@ -967,7 +965,18 @@ __ops_create_pk_sesskey(const __ops_key_t *key, const char *ciphername)
"__ops_create_pk_sesskey: can't allocate\n");
return NULL;
}
sz_encoded_m_buf = BN_num_bytes(pubkey->key.rsa.n);
switch(pubkey->alg) {
case OPS_PKA_RSA:
sz_encoded_m_buf = BN_num_bytes(pubkey->key.rsa.n);
break;
case OPS_PKA_DSA:
case OPS_PKA_ELGAMAL:
sz_encoded_m_buf = BN_num_bytes(pubkey->key.elgamal.p);
break;
default:
sz_encoded_m_buf = 0;
break;
}
if ((encoded_m_buf = calloc(1, sz_encoded_m_buf)) == NULL) {
(void) fprintf(stderr,
"__ops_create_pk_sesskey: can't allocate\n");
@ -993,7 +1002,7 @@ __ops_create_pk_sesskey(const __ops_key_t *key, const char *ciphername)
(void) memcpy(sesskey->key_id, id, sizeof(sesskey->key_id));
if (__ops_get_debug_level(__FILE__)) {
hexdump(stderr, "Encrypting for RSA keyid", id, sizeof(sesskey->key_id));
hexdump(stderr, "Encrypting for keyid", id, sizeof(sesskey->key_id));
}
switch (pubkey->alg) {
case OPS_PKA_RSA:
@ -1041,11 +1050,14 @@ __ops_create_pk_sesskey(const __ops_key_t *key, const char *ciphername)
break;
case OPS_PKA_DSA:
case OPS_PKA_ELGAMAL:
(void) fprintf(stderr, "DSA/Elgamal encryption not supported yet\n");
free(unencoded_m_buf);
free(encoded_m_buf);
free(sesskey);
return NULL;
if (!__ops_elgamal_encrypt_mpi(encoded_m_buf, sz_encoded_m_buf, pubkey,
&sesskey->params)) {
free(unencoded_m_buf);
free(encoded_m_buf);
free(sesskey);
return NULL;
}
break;
default:
/* will not get here - for lint only */
break;
@ -1084,8 +1096,17 @@ __ops_write_pk_sesskey(__ops_output_t *output, __ops_pk_sesskey_t *pksk)
;
case OPS_PKA_DSA:
case OPS_PKA_ELGAMAL:
(void) fprintf(stderr, "__ops_write_pk_sesskey: DSA/Elgamal encryption not implemented yet\n");
return 0;
return __ops_write_ptag(output, OPS_PTAG_CT_PK_SESSION_KEY) &&
__ops_write_length(output, (unsigned)(1 + 8 + 1 +
BN_num_bytes(pksk->params.elgamal.g_to_k) + 2 +
BN_num_bytes(pksk->params.elgamal.encrypted_m) + 2)) &&
__ops_write_scalar(output, (unsigned)pksk->version, 1) &&
__ops_write(output, pksk->key_id, 8) &&
__ops_write_scalar(output, (unsigned)pksk->alg, 1) &&
__ops_write_mpi(output, pksk->params.elgamal.g_to_k) &&
__ops_write_mpi(output, pksk->params.elgamal.encrypted_m)
/* ?? && __ops_write_scalar(output, 0, 2); */
;
default:
(void) fprintf(stderr,
"__ops_write_pk_sesskey: bad algorithm\n");

View File

@ -54,7 +54,7 @@
#if defined(__NetBSD__)
__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD: crypto.c,v 1.29 2010/11/04 15:38:45 agc Exp $");
__RCSID("$NetBSD: crypto.c,v 1.30 2010/11/07 02:29:28 agc Exp $");
#endif
#include <sys/types.h>
@ -223,6 +223,45 @@ __ops_rsa_encrypt_mpi(const uint8_t *encoded_m_buf,
return 1;
}
/**
\ingroup Core_MPI
\brief Elgamal-encrypt an MPI
*/
unsigned
__ops_elgamal_encrypt_mpi(const uint8_t *encoded_m_buf,
const size_t sz_encoded_m_buf,
const __ops_pubkey_t * pubkey,
__ops_pk_sesskey_params_t * skp)
{
uint8_t encmpibuf[NETPGP_BUFSIZ];
uint8_t g_to_k[NETPGP_BUFSIZ];
int n;
if (sz_encoded_m_buf != (size_t)BN_num_bytes(pubkey->key.elgamal.p)) {
(void) fprintf(stderr, "sz_encoded_m_buf wrong\n");
return 0;
}
n = __ops_elgamal_public_encrypt(g_to_k, encmpibuf, encoded_m_buf,
sz_encoded_m_buf, &pubkey->key.elgamal);
if (n == -1) {
(void) fprintf(stderr, "__ops_elgamal_public_encrypt failure\n");
return 0;
}
if (n <= 0)
return 0;
skp->elgamal.g_to_k = BN_bin2bn(g_to_k, n / 2, NULL);
skp->elgamal.encrypted_m = BN_bin2bn(encmpibuf, n / 2, NULL);
if (__ops_get_debug_level(__FILE__)) {
hexdump(stderr, "encrypted mpi", encmpibuf, 16);
}
return 1;
}
static __ops_cb_ret_t
write_parsed_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
{

View File

@ -129,6 +129,8 @@ int __ops_rsa_private_encrypt(uint8_t *, const uint8_t *, size_t,
int __ops_rsa_private_decrypt(uint8_t *, const uint8_t *, size_t,
const __ops_rsa_seckey_t *, const __ops_rsa_pubkey_t *);
int __ops_elgamal_public_encrypt(uint8_t *, uint8_t *, const uint8_t *, size_t,
const __ops_elgamal_pubkey_t *);
int __ops_elgamal_private_decrypt(uint8_t *, const uint8_t *, size_t,
const __ops_elgamal_seckey_t *, const __ops_elgamal_pubkey_t *);
@ -161,6 +163,9 @@ int __ops_decrypt_decode_mpi(uint8_t *, unsigned, const BIGNUM *,
unsigned __ops_rsa_encrypt_mpi(const uint8_t *, const size_t,
const __ops_pubkey_t *,
__ops_pk_sesskey_params_t *);
unsigned __ops_elgamal_encrypt_mpi(const uint8_t *, const size_t,
const __ops_pubkey_t *,
__ops_pk_sesskey_params_t *);
/* Encrypt everything that's written */
struct __ops_key_data;

View File

@ -57,7 +57,7 @@
#if defined(__NetBSD__)
__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD: openssl_crypto.c,v 1.30 2010/11/04 06:45:28 agc Exp $");
__RCSID("$NetBSD: openssl_crypto.c,v 1.31 2010/11/07 02:29:28 agc Exp $");
#endif
#ifdef HAVE_OPENSSL_DSA_H
@ -878,6 +878,103 @@ openssl_read_pem_seckey(const char *f, __ops_key_t *key, const char *type, int v
return ok;
}
/*
* Decide the number of bits in the random componont k
*
* It should be in the same range as p for signing (which
* is deprecated), but can be much smaller for encrypting.
*
* Until I research it further, I just mimic gpg behaviour.
* It has a special mapping table, for values <= 5120,
* above that it uses 'arbitrary high number'. Following
* algorihm hovers 10-70 bits above gpg values. And for
* larger p, it uses gpg's algorihm.
*
* The point is - if k gets large, encryption will be
* really slow. It does not matter for decryption.
*/
static int
decide_k_bits(int p_bits)
{
return (p_bits <= 5120) ? p_bits / 10 + 160 : (p_bits / 8 + 200) * 3 / 2;
}
int
__ops_elgamal_public_encrypt(uint8_t *g_to_k, uint8_t *encm,
const uint8_t *in,
size_t size,
const __ops_elgamal_pubkey_t *pubkey)
{
int ret = 0;
int k_bits;
BIGNUM *m;
BIGNUM *p;
BIGNUM *g;
BIGNUM *y;
BIGNUM *k;
BIGNUM *yk;
BIGNUM *c1;
BIGNUM *c2;
BN_CTX *tmp;
m = BN_bin2bn(in, size, NULL);
p = pubkey->p;
g = pubkey->g;
y = pubkey->y;
k = BN_new();
yk = BN_new();
c1 = BN_new();
c2 = BN_new();
tmp = BN_CTX_new();
if (!m || !p || !g || !y || !k || !yk || !c1 || !c2 || !tmp) {
goto done;
}
/*
* generate k
*/
k_bits = decide_k_bits(BN_num_bits(p));
if (!BN_rand(k, k_bits, 0, 0)) {
goto done;
}
/*
* c1 = g^k c2 = m * y^k
*/
if (!BN_mod_exp(c1, g, k, p, tmp)) {
goto done;
}
if (!BN_mod_exp(yk, y, k, p, tmp)) {
goto done;
}
if (!BN_mod_mul(c2, m, yk, p, tmp)) {
goto done;
}
/* result */
BN_bn2bin(c1, g_to_k);
ret = BN_num_bytes(c1); /* c1 = g^k */
BN_bn2bin(c2, encm);
ret += BN_num_bytes(c2); /* c2 = m * y^k */
done:
if (tmp) {
BN_CTX_free(tmp);
}
if (c2) {
BN_clear_free(c2);
}
if (c1) {
BN_clear_free(c1);
}
if (yk) {
BN_clear_free(yk);
}
if (k) {
BN_clear_free(k);
}
if (g) {
BN_clear_free(g);
}
return ret;
}
int
__ops_elgamal_private_decrypt(uint8_t *out,
const uint8_t *in,