📄 pgpelgkey.c
字号:
/*
* pgpElGKey.c
* El Gamal encryption
*
* $Id: pgpElGKey.c,v 1.56.6.1 1999/06/04 00:28:55 heller Exp $
*/
#include "pgpConfig.h"
#include <string.h>
#include <stddef.h>
#include "pgpSDKBuildFlags.h"
#include "pgpDebug.h"
#include "pgpElGKey.h"
#include "pgpKeyMisc.h"
#include "bn.h"
#include "bnprime.h"
#include "pgpCFBPriv.h"
#include "pgpSymmetricCipherPriv.h"
#include "pgpHashPriv.h"
#include "pgpFixedKey.h"
#include "bngermain.h"
#include "pgpHash.h"
#include "pgpMem.h"
#include "pgpErrors.h"
#include "bnprime.h"
#include "pgpPubKey.h"
#include "pgpRandomX9_17.h"
#include "pgpStr2Key.h"
#include "pgpContext.h"
#include "pgpEnv.h"
#ifndef NULL
#define NULL 0
#endif
#define ASSERTELG(alg) pgpAssert((ALGMASK(alg))==kPGPPublicKeyAlgorithm_ElGamal)
typedef struct ELGpub
{
BigNum p; /* Public prime */
BigNum g; /* Public generator */
BigNum y; /* Public key, g**x mod p */
} ELGpub;
typedef struct ELGsec
{
BigNum p; /* Copy of public parameters */
BigNum g;
BigNum y;
BigNum x; /* Secret key, discrete log of y */
} ELGsec;
/* A PGPSecKey's priv points to this, an ELGsec plus the encrypted form... */
/* This struct is always allocated using PGPNewSecureData */
typedef struct ELGsecPlus
{
PGPContextRef context;
ELGsec s;
PGPByte *cryptkey;
size_t ckalloc, cklen;
int locked;
} ELGsecPlus;
/** Public key functions **/
static void
elgPubDestroy(PGPPubKey *pubkey)
{
ELGpub *pub = (ELGpub *)pubkey->priv;
PGPContextRef context = pubkey->context;
ASSERTELG(pubkey->pkAlg);
bnEnd(&pub->p);
bnEnd(&pub->g);
bnEnd(&pub->y);
pgpClearMemory( pub, sizeof(pub));
pgpContextMemFree( context, pub);
pgpClearMemory( pubkey, sizeof(pubkey));
pgpContextMemFree( context, pubkey);
}
/*
* Return the largest possible PGPESK size for a given key.
* Must hold two numbers up to p in size.
*/
static size_t
elgPubMaxesk(PGPPubKey const *pubkey, PGPPublicKeyMessageFormat format)
{
ELGpub const *pub = (ELGpub *)pubkey->priv;
ASSERTELG(pubkey->pkAlg);
if (format == kPGPPublicKeyMessageFormat_PGP)
return 2*( 2 + bnBytes(&pub->p) );
else if (format == kPGPPublicKeyMessageFormat_PKCS1 ||
format == kPGPPublicKeyMessageFormat_IKE)
return 2*( bnBytes(&pub->p) );
pgpAssert(0);
return 0;
}
/*
* Return the size of the buffer needed, worst-case, for the decrypted
* output. A trivially padded key (random padding length = 0)
* can just be 0 2 0 <key>.
*/
static size_t
elgPubMaxdecrypted(PGPPubKey const *pubkey, PGPPublicKeyMessageFormat format)
{
ELGpub const *pub = (ELGpub *)pubkey->priv;
size_t size;
ASSERTELG(pubkey->pkAlg);
(void) format;
size = bnBytes(&pub->p);
return size < 3 ? 0 : size-3;
}
static size_t
elgPubMaxsig(PGPPubKey const *pubkey, PGPPublicKeyMessageFormat format)
{
(void)pubkey;
(void)format;
return kPGPError_PublicKeyUnimplemented;
}
/*
* Given a buffer of at least "maxesk" bytes, make an PGPESK
* into it and return the size of the PGPESK, or <0.
*
* ElGamal encryption is a simple variant on non-interactove
* Diffie-Hellman. The recipient publishes g, p, and y = g**x mod p.
* the sender picks a random xx, computes yy = g**xx mod p, and
* the shared secret z = y**xx mod p = yy**x mod p = g**(x*xx) mod p, then
* then sends z*m, where m is the message. (Padded in the usual
* PKCS manner.)
*/
static int
elgEncrypt(PGPPubKey const *pubkey, PGPByte const *key,
size_t keylen, PGPByte *esk, size_t *esklen,
PGPRandomContext const *rc, PGPPublicKeyMessageFormat format)
{
#if PGP_ENCRYPT_DISABLE /* [ */
(void)pubkey;
(void)key;
(void)keylen;
(void)esk;
(void)esklen;
(void)rc;
(void)format;
return kPGPError_FeatureNotAvailable;
#else /* PGP_ENCRYPT_DISABLE */ /* ] [ */
ELGpub const *pub = (ELGpub *)pubkey->priv;
BigNum xx; /* Random exponent */
BigNum yy; /* g**xx mod p */
BigNum z; /* y**xx mod p */
unsigned t;
unsigned xxbits, pbytes;
int i;
PGPMemoryMgrRef mgr = NULL;
mgr = PGPGetContextMemoryMgr( pubkey->context );
/* We don't need this argument, although other algorithms may... */
(void)format;
/*
* Of these values, only xx is inherently sensitive, but others may
* hold some intermediate results we would prefer not to have leaked.
* So mark all as sensitive.
*/
bnBegin(&xx, mgr, TRUE );
bnBegin(&yy, mgr, TRUE );
bnBegin(&z, mgr, TRUE );
ASSERTELG(pubkey->pkAlg);
t = bnBits(&pub->p);
if (t > 0xffff)
return kPGPError_PublicKeyTooLarge;
pbytes = (t + 7) / 8;
if (keylen > pbytes)
return kPGPError_PublicKeyTooSmall; /* data too big for pubkey */
if( format == kPGPPublicKeyMessageFormat_PGP ) {
/* Add checksum to key, place temporarily in esk buffer */
t = 0;
esk[0] = key[0];
for (i = 1; i < (int)keylen; i++)
t += esk[i] = key[i];
esk[keylen] = (PGPByte)(t >> 8 & 255);
esk[keylen+1] = (PGPByte)(t & 255);
keylen += 2;
key = esk;
}
/* Choose the random xx value to be used for this encryption */
xxbits = pgpDiscreteLogExponentBits(bnBits(&pub->p))*3/2;
if (pgpBnGenRand(&xx, rc, xxbits, 0, 0, xxbits) < 0)
goto nomem;
/* Do the two exponentiations necessary to compute yy and z */
if (bnExpMod(&yy, &pub->g, &xx, &pub->p) < 0 ||
bnExpMod(&z, &pub->y, &xx, &pub->p) < 0)
goto nomem;
/* Re-use xx to hold the PKCS-padded conventional key */
if (pgpPKCSPack(&xx, key, keylen, PKCS_PAD_ENCRYPTED, pbytes, rc)<0)
goto nomem;
pgpAssert (bnCmp(&xx, &pub->p) < 0);
/* Compute xx*z mod p, the encrypted session key we will transmit */
if (bnMul(&z, &z, &xx) < 0 ||
bnMod(&z, &z, &pub->p) < 0)
goto nomem;
/* Pack the two values into the esk, first yy and then k+z */
t = pgpBnPutFormatted(&yy, esk, pbytes, format);
t += pgpBnPutFormatted(&z, esk+t, pbytes, format);
if (esklen)
*esklen = (size_t)t;
i = 0;
goto done;
nomem:
i = kPGPError_OutOfMemory;
done:
bnEnd(&z);
bnEnd(&yy);
bnEnd(&xx);
return i;
#endif /* PGP_ENCRYPT_DISABLE */ /* ] */
}
/*
* Return 1 if (sig,siglen) is a valid MPI which signs
* (hash, hashlen).
* Not implementing ElGamal signatures, using DSS.
*/
static int
elgVerify(PGPPubKey const *pubkey, PGPByte const *sig,
size_t siglen, PGPHashVTBL const *h, PGPByte const *hash,
PGPPublicKeyMessageFormat format)
{
(void)pubkey;
(void)sig;
(void)siglen;
(void)h;
(void)hash;
(void)format;
return kPGPError_PublicKeyUnimplemented;
}
/*
* Turn a PGPPubKey into the algorithm-specific parts of a public key.
* A public key's ELG-specific part is:
*
* 0 2+i MPI for prime
* 2+i 2+t MPI for generator
* 4+i+t 2+u MPI for public key
* 6+i+t+u
*/
static size_t
elgPubBufferLength(PGPPubKey const *pubkey)
{
ELGpub const *pub = (ELGpub *)pubkey->priv;
return 6 + bnBytes(&pub->p) + bnBytes(&pub->g) +
bnBytes(&pub->y);
}
static void
elgPubToBuffer(PGPPubKey const *pubkey, PGPByte *buf)
{
ELGpub const *pub = (ELGpub *)pubkey->priv;
unsigned off;
off = 0;
off += pgpBnPutPlain(&pub->p, buf+off);
off += pgpBnPutPlain(&pub->g, buf+off);
off += pgpBnPutPlain(&pub->y, buf+off);
}
/* A little helper function that's used twice */
static void
elgFillPubkey(PGPPubKey *pubkey, ELGpub *pub)
{
pubkey->next = NULL;
pubkey->pkAlg = kPGPPublicKeyAlgorithm_ElGamal;
pubkey->priv = pub;
pubkey->destroy = elgPubDestroy;
pubkey->maxesk = elgPubMaxesk;
pubkey->maxdecrypted = elgPubMaxdecrypted;
pubkey->maxsig = elgPubMaxsig;
pubkey->encrypt = elgEncrypt;
pubkey->verify = elgVerify;
pubkey->bufferLength = elgPubBufferLength;
pubkey->toBuffer = elgPubToBuffer;
}
/*
* Turn the algorithm-specific parts of a public key into a PGPPubKey
* structure. A public key's ELG-specific part is:
*
* 0 2+i MPI for prime
* 2+i 2+t MPI for generator
* 4+i+t 2+u MPI for public key
* 6+i+t+u
*/
PGPPubKey *
elgPubFromBuf(
PGPContextRef context,
PGPByte const *buf, size_t size, PGPError *error)
{
PGPPubKey *pubkey;
ELGpub *pub;
unsigned i, t, u;
int v;
PGPError err = kPGPError_OutOfMemory;
PGPMemoryMgrRef mgr = PGPGetContextMemoryMgr( context );
bnInit();
v = pgpBnParse(buf, size, 3, &i, &t, &u);
if (v < 0) {
*error = (PGPError)v;
return NULL;
}
if ((buf[t-1] & 1) == 0) { /* Too small or even prime p */
*error = kPGPError_MalformedKeyComponent;
return NULL;
}
pub = (ELGpub *)pgpContextMemAlloc( context,
sizeof(*pub), kPGPMemoryMgrFlags_Clear);
if (pub) {
pubkey = (PGPPubKey *)pgpContextMemAlloc( context,
sizeof(*pubkey), kPGPMemoryMgrFlags_Clear);
if (pubkey) {
pubkey->context = context;
bnBegin(&pub->p, mgr, FALSE );
bnBegin(&pub->g, mgr, FALSE );
bnBegin(&pub->y, mgr, FALSE );
if (bnInsertBigBytes(&pub->p, buf+i+2, 0, t-i-2) >= 0
&& bnInsertBigBytes(&pub->g, buf+t+2, 0,
u-t-2) >= 0
&& bnInsertBigBytes(&pub->y, buf+u+2, 0,
v-u-2) >= 0)
{
elgFillPubkey(pubkey, pub);
*error = kPGPError_NoErr;
return pubkey;
}
else
{
err = kPGPError_UnknownError;
}
/* Failed = clean up and return NULL */
bnEnd(&pub->p);
bnEnd(&pub->g);
bnEnd(&pub->y);
pgpContextMemFree( context, pubkey);
}
pgpContextMemFree( context, pub);
}
*error = err;
return NULL;
}
/*
* Return the size of the public portion of a key buffer.
*/
int
elgPubKeyPrefixSize(PGPByte const *buf, size_t size)
{
return pgpBnParse(buf, size, 3, NULL, NULL, NULL);
}
/** Secret key functions **/
static void
elgSecDestroy(PGPSecKey *seckey)
{
ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;
PGPContextRef context;
pgpAssertAddrValid( seckey, PGPSecKey );
context = seckey->context;
pgpAssert( pgpContextIsValid( context ) );
ASSERTELG(seckey->pkAlg);
bnEnd(&sec->s.p);
bnEnd(&sec->s.g);
bnEnd(&sec->s.y);
bnEnd(&sec->s.x);
pgpClearMemory(sec->cryptkey, sec->ckalloc);
pgpContextMemFree( context, sec->cryptkey);
PGPFreeData( sec ); /* Wipes as it frees */
pgpClearMemory( seckey, sizeof(seckey));
pgpContextMemFree( context, seckey);
}
/*
* Generate a PGPPubKey from a PGPSecKey
*/
static PGPPubKey *
elgPubkey(
PGPSecKey const *seckey)
{
ELGsecPlus const *sec = (ELGsecPlus *)seckey->priv;
PGPPubKey *pubkey;
ELGpub *pub;
PGPContextRef context;
PGPMemoryMgrRef mgr = NULL;
pgpAssertAddrValid( seckey, PGPSecKey );
context = seckey->context;
mgr = PGPGetContextMemoryMgr( context );
ASSERTELG(seckey->pkAlg);
pub = (ELGpub *)pgpContextMemAlloc( context,
sizeof(*pub), kPGPMemoryMgrFlags_Clear);
if (pub) {
pubkey = (PGPPubKey *)pgpContextMemAlloc( context,
sizeof(*pubkey), kPGPMemoryMgrFlags_Clear);
if (pubkey) {
pubkey->context = context;
bnBegin(&pub->p, mgr, FALSE );
bnBegin(&pub->g, mgr, FALSE );
bnBegin(&pub->y, mgr, FALSE );
if (bnCopy(&pub->p, &sec->s.p) >= 0
&& bnCopy(&pub->g, &sec->s.g) >= 0
&& bnCopy(&pub->y, &sec->s.y) >= 0)
{
elgFillPubkey(pubkey, pub);
pubkey->pkAlg = seckey->pkAlg;
memcpy(pubkey->keyID, seckey->keyID,
sizeof(pubkey->keyID));
return pubkey;
}
/* Failed = clean up and return NULL */
bnEnd(&pub->p);
bnEnd(&pub->g);
bnEnd(&pub->y);
pgpContextMemFree( context, pubkey);
}
pgpContextMemFree( context, pub);
}
return NULL;
}
/*
* Yes, there *is* a reason that this is a function and no a variable.
* On a hardware device with an automatic timeout,
* it actually might need to do some work to find out.
*/
static int
elgIslocked(PGPSecKey const *seckey)
{
ELGsecPlus const *sec = (ELGsecPlus *)seckey->priv;
ASSERTELG(seckey->pkAlg);
return sec->locked;
}
/*
* Return the algorithm and (symmetric) key size used for locking/unlocking
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -