pgprsakey.c
来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 1,105 行 · 第 1/2 页
C
1,105 行
/*
* pgpRSAKey.c
*
* Copyright (C) 1996,1997 Pretty Good Privacy, Inc. All rights reserved.
*
* $Id: pgpRSAKey.c,v 1.9.2.3 1997/06/09 23:46:35 quark Exp $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if (!NO_RSA)
#include "pgpDebug.h"
#include "pgpKeyMisc.h"
#include "pgpRSAKey.h"
#include "pgpRSAGlue.h"
#include "bn.h"
#include "pgpCFB.h"
#include "pgpCipher.h"
#include "pgpHash.h"
#include "pgpMem.h"
#include "pgpErr.h"
#include "bnprime.h"
#include "pgpPubKey.h"
#include "pgpRndom.h"
#include "pgpStr2Key.h"
#include "pgpUsuals.h"
#ifndef NULL
#define NULL 0
#endif
#define ASSERTRSA(alg) pgpAssert((ALGMASK(alg))==PGP_PKALG_RSA || \
(ALGMASK(alg))==PGP_PKALG_RSA_ENC || \
(ALGMASK(alg))==PGP_PKALG_RSA_SIG)
#define ASSERTRSASIG(alg) pgpAssert((ALGMASK(alg))==PGP_PKALG_RSA || \
(ALGMASK(alg))==PGP_PKALG_RSA_SIG)
#define ASSERTRSAENC(alg) pgpAssert((ALGMASK(alg))==PGP_PKALG_RSA || \
(ALGMASK(alg))==PGP_PKALG_RSA_ENC)
/** Public key functions **/
static void
rsaPubDestroy(struct PgpPubKey *pubkey)
{
struct RSApub *pub = (struct RSApub *)pubkey->priv;
ASSERTRSA(pubkey->pkAlg);
bnEnd(&pub->n);
bnEnd(&pub->e);
memset(pub, 0, sizeof(pub));
pgpMemFree(pub);
memset(pubkey, 0, sizeof(pubkey));
pgpMemFree(pubkey);
}
#if 0
static void
rsaPubId8(struct PgpPubKey const *pubkey, byte *buf)
{
struct RSApub const *pub = (struct RSApub *)pubkey->priv;
ASSERTRSA(pubkey->pkAlg);
bnExtractBigBytes(&pub->n, buf, 0, 8);
}
#endif
/* Return the largest possible PgpESK size for a given key */
static size_t
rsaMaxesk(struct PgpPubKey const *pubkey, PgpVersion version)
{
struct RSApub const *pub = (struct RSApub *)pubkey->priv;
(void)version;
ASSERTRSAENC(pubkey->pkAlg);
return 2 + (bnBits(&pub->n)+7)/8;
}
/*
* Given a buffer of at least "maxesk" bytes, make an PgpESK
* into it and return the size of the PgpESK, or <0.
*/
static int
rsaEncrypt(struct PgpPubKey const *pubkey, byte const *key,
size_t keylen, byte *esk, size_t *esklen,
struct PgpRandomContext const *rc, PgpVersion version)
{
struct RSApub const *pub = (struct RSApub *)pubkey->priv;
struct BigNum bn;
unsigned t;
int i;
/* We don't need these arguments, although other algorithms may... */
(void)rc;
(void)version;
ASSERTRSAENC(pubkey->pkAlg);
t = bnBits(&pub->n);
if (t > 0xffff)
return PGPERR_PUBKEY_TOOBIG;
if (keylen > t)
return PGPERR_PUBKEY_TOOSMALL; /* data too big for pubkey */
/* 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] = (byte)(t >> 8 & 255);
esk[keylen+1] = (byte)(t & 255);
bnBegin(&bn);
i = rsaPublicEncrypt(&bn, esk, keylen+2, pub, rc);
if (i < 0) {
bnEnd(&bn);
memset(esk, 0, keylen+2);
return i;
}
t = pgpBnPutPlain(&bn, esk);
bnEnd(&bn);
if (esklen)
*esklen = (size_t)t;
return 0;
}
/*
* Return 1 if (sig,siglen) is a valid MPI which signs
* hash, of type h. Check the DER-encoded prefix and the
* hash itself.
*/
static int
rsaVerify(struct PgpPubKey const *pubkey, int sigtype, byte const *sig,
size_t siglen, struct PgpHash const *h, byte const *hash)
{
struct RSApub const *pub = (struct RSApub *)pubkey->priv;
struct BigNum bn;
byte buf[64]; /* largest hash size + DER prefix */
int i;
unsigned t;
(void)sigtype;
ASSERTRSASIG(pubkey->pkAlg);
t = ((unsigned)sig[0]<<8) + sig[1];
t = (t+7)/8;
if (siglen != t+2)
return siglen < t+2 ? PGPERR_SIG_TOOSHORT
: PGPERR_SIG_TOOLONG;
bnBegin(&bn);
if (bnInsertBigBytes(&bn, sig+2, 0, t) < 0) {
bnEnd(&bn);
return PGPERR_NOMEM;
}
i = rsaPublicDecrypt(buf, sizeof(buf), &bn, pub);
bnEnd(&bn);
if (i >= 0) {
/* Check that the returned data is correct */
t = h->DERprefixsize;
i = (size_t)i <= sizeof(buf)
&& (unsigned)i == h->hashsize + t
&& memcmp(buf, h->DERprefix, t) == 0
&& memcmp(buf+t, hash, h->hashsize) == 0;
}
memset(buf, 0, sizeof(buf));
return i;
}
/*
* Turn a PgpPubKey into the algorithm-specific parts of a public key.
* A public key's RSA-specific part is:
*
* 0 2+i MPI for modulus
* 2+i 2+t MPI for exponent
* 4+i+t
*/
static size_t
rsaPubBufferLength(struct PgpPubKey const *pubkey)
{
struct RSApub const *pub = (struct RSApub *)pubkey->priv;
return 4 + (bnBits(&pub->n)+7)/8 + (bnBits(&pub->e)+7)/8;
}
static void
rsaPubToBuffer(struct PgpPubKey const *pubkey, byte *buf)
{
struct RSApub const *pub = (struct RSApub *)pubkey->priv;
unsigned i, t;
i = bnBits(&pub->n);
pgpAssert(i <= 0xffff);
buf[0] = (byte)(i >> 8);
buf[1] = (byte)i;
i = (i+7)/8;
bnExtractBigBytes(&pub->n, buf+2, 0, i);
t = bnBits(&pub->e);
pgpAssert(t <= 0xffff);
buf[2+i] = (byte)(t >> 8);
buf[3+i] = (byte)t;
t = (t+7)/8;
bnExtractBigBytes(&pub->e, buf+4+i, 0, t);
}
/* A little helper function that's used twice */
static void
rsaFillPubkey(struct PgpPubKey *pubkey, struct RSApub *pub)
{
pubkey->next = NULL;
pubkey->pkAlg = PGP_PKALG_RSA;
pubkey->priv = pub;
pubkey->destroy = rsaPubDestroy;
#if 0
pubkey->id8 = rsaPubId8;
#endif
pubkey->maxesk = rsaMaxesk;
pubkey->encrypt = rsaEncrypt;
pubkey->verify = rsaVerify;
pubkey->bufferLength = rsaPubBufferLength;
pubkey->toBuffer = rsaPubToBuffer;
}
/*
* Turn the algorithm-specific parts of a public key into a PgpPubKey
* structure. A public key's RSA-specific part is:
*
* 0 2+i MPI for modulus
* 2+i 2+t MPI for exponent
* 4+i+t
*/
struct PgpPubKey *
rsaPubFromBuf(byte const *buf, size_t size, int *error)
{
struct PgpPubKey *pubkey;
struct RSApub *pub;
int err = PGPERR_NOMEM;
unsigned i, t;
bnInit();
if (size < 4)
return NULL;
i = ((unsigned)buf[0] << 8) + buf[1];
if (!i || buf[2] >> ((i-1) & 7) != 1) {
*error = PGPERR_KEY_MODMPI;
return NULL; /* Bad bit length */
}
i = (i+7)/8;
if (size < 4+i) {
*error = PGPERR_KEY_SHORT;
return NULL;
}
if ((buf[1+i] & 1) == 0) { /* Too small or even modulus */
*error = PGPERR_KEY_MODEVEN;
return NULL;
}
t = ((unsigned)buf[2+i] << 8) + buf[3+i];
if (!t || buf[4+i] >> ((t-1) & 7) != 1) {
*error = PGPERR_KEY_EXPMPI;
return NULL; /* Bad bit length */
}
t = (t+7)/8;
if (size < 4+i+t) {
*error = PGPERR_KEY_SHORT;
return NULL;
}
pub = (struct RSApub *)pgpMemAlloc(sizeof(*pub));
if (pub) {
pubkey = (struct PgpPubKey *)pgpMemAlloc(sizeof(*pubkey));
if (pubkey) {
bnBegin(&pub->n);
bnBegin(&pub->e);
if (bnInsertBigBytes(&pub->n, buf+2, 0, i) >= 0
&& bnInsertBigBytes(&pub->e, buf+4+i, 0, t) >= 0)
{
if (rsaKeyTooBig (pub, NULL)) {
err = PGPERR_KEY_UNSUPP;
} else {
rsaFillPubkey(pubkey, pub);
*error = 0;
return pubkey;
}
}
/* Failed = clean up and return NULL */
bnEnd(&pub->n);
bnEnd(&pub->e);
pgpMemFree(pubkey);
}
pgpMemFree(pub);
}
*error = err;
return NULL;
}
/*
* Return the size of the public portion of a key buffer.
*/
int
rsaPubKeyPrefixSize(byte const *buf, size_t size)
{
return pgpBnParse(buf, size, 2, NULL, NULL);
}
/** Secret key functions **/
static void
rsaSecDestroy(struct PgpSecKey *seckey)
{
struct RSAsecPlus *sec = (struct RSAsecPlus *)seckey->priv;
ASSERTRSA(seckey->pkAlg);
bnEnd(&sec->s.n);
bnEnd(&sec->s.e);
bnEnd(&sec->s.d);
bnEnd(&sec->s.p);
bnEnd(&sec->s.q);
bnEnd(&sec->s.u);
memset(sec->cryptkey, 0, sec->ckalloc);
pgpMemFree(sec->cryptkey);
memset(sec, 0, sizeof(sec));
pgpMemFree(sec);
memset(seckey, 0, sizeof(seckey));
pgpMemFree(seckey);
}
#if 0
static void
rsaSecId8(struct PgpSecKey const *seckey, byte *buf)
{
struct RSAsecPlus const *sec = (struct RSAsecPlus *)seckey->priv;
ASSERTRSA(seckey->pkAlg);
bnExtractBigBytes(&sec->s.n, buf, 0, 8);
}
#endif
/*
* Generate a PgpPubKey from a PgpSecKey
*/
static struct PgpPubKey *
rsaPubkey(struct PgpSecKey const *seckey)
{
struct RSAsecPlus const *sec = (struct RSAsecPlus *)seckey->priv;
struct PgpPubKey *pubkey;
struct RSApub *pub;
ASSERTRSA(seckey->pkAlg);
pub = (struct RSApub *)pgpMemAlloc(sizeof(*pub));
if (pub) {
pubkey = (struct PgpPubKey *)pgpMemAlloc(sizeof(*pubkey));
if (pubkey) {
bnBegin(&pub->n);
bnBegin(&pub->e);
if (bnCopy(&pub->n, &sec->s.n) >= 0
&& bnCopy(&pub->e, &sec->s.e) >= 0)
{
rsaFillPubkey(pubkey, pub);
pubkey->pkAlg = seckey->pkAlg;
memcpy(pubkey->keyID, seckey->keyID,
sizeof(pubkey->keyID));
return pubkey;
}
/* Failed = clean up and return NULL */
bnEnd(&pub->n);
bnEnd(&pub->e);
pgpMemFree(pubkey);
}
pgpMemFree(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
rsaIslocked(struct PgpSecKey const *seckey)
{
struct RSAsecPlus const *sec = (struct RSAsecPlus *)seckey->priv;
ASSERTRSA(seckey->pkAlg);
return sec->locked;
}
/*
* Try to decrypt the secret key wih the given passphrase. Returns >0
* if it was the correct passphrase. =0 if it was not, and <0 on error.
* Does not alter the key even if it's the wrong passphrase and already
* unlocked. A NULL passphrae will work if the key is unencrypted.
*
* A (secret) key's RSA-specific part is:
*
* 0 2+u MPI for modulus
* 2+u 2+v MPI for exponent
* 4+u+v 1 Encryption algorithm (0 for none, 1 for IDEA)
* 5+u+v t Encryption IV: 0 or 8 bytes
* 5+t+u+v 2+w MPI for d
* 7+t+u+v+w 2+x MPI for p
* 9+t+u+v+w+x 2+y MPI for q
* 11+t+u+v+w+x+y 2+z MPI for u
* 13+t+u+v+w+x+y+z 2 Checksum
* 15+t+u+v+w+x+y+z
*
* Actually, that's the old-style, if pgpS2KoldVers is true.
* If it's false, the algoruthm is 255, and is followed by the
* algorithm, then the (varaible-length, self-delimiting)
* string-to-key descriptor.
*/
static int
rsaUnlock(struct PgpSecKey *seckey, struct PgpEnv const *env,
char const *phrase, size_t plen)
{
struct RSAsecPlus *sec = (struct RSAsecPlus *)seckey->priv;
struct BigNum d, p, q, u, bn;
struct PgpCipher const *cipher;
struct PgpCfbContext *cfb = NULL; /* Necessary */
struct PgpStringToKey *s2k;
byte key[PGP_CIPHER_MAXKEYSIZE];
unsigned v, t;
unsigned alg;
unsigned checksum;
int i;
ASSERTRSA(seckey->pkAlg);
bnInit();
if (sec->cklen < 5)
return PGPERR_KEY_SHORT;
v = ((unsigned)sec->cryptkey[0] << 8) + sec->cryptkey[1];
v = (v+7)/8;
if (sec->cklen < 5+v)
return PGPERR_KEY_SHORT;
if (bnInsertBigBytes(&sec->s.n, sec->cryptkey+2, 0, v) < 0)
return PGPERR_NOMEM;
t = ((unsigned)sec->cryptkey[2+v] << 8) + sec->cryptkey[3+v];
t = (t+7)/8;
if (sec->cklen < 4+v+t)
return PGPERR_KEY_SHORT;
if (bnInsertBigBytes(&sec->s.e, sec->cryptkey+4+v, 0, t) < 0)
return PGPERR_NOMEM;
v += t + 5;
if (sec->cklen < v)
return PGPERR_KEY_SHORT;
/* Get the encryption algorithm (cipher number). 0 == no encryption */
alg = sec->cryptkey[v-1];
/* If the phrase is empty, set it to NULL */
if (!phrase || plen == 0)
phrase = NULL;
/*
* We need a pass if it is encrypted, and we cannot have a
* password if it is NOT encrypted. I.e., this is a logical
* xor (^^)
*/
if (!phrase != !alg)
return 0;
if (!alg) {
/* The key isn't encrypted; just read it in */
cfb = NULL;
} else {
if (alg == 255) {
/* New style, with a separate string-to-key */
if (sec->cklen == v)
return PGPERR_KEY_SHORT;
alg = sec->cryptkey[v++];
i = pgpS2Kdecode(&s2k, env, sec->cryptkey+v,
sec->cklen-v);
if (i < 0)
return i;
v += i;
if (sec->cklen < v)
return PGPERR_KEY_SHORT;
} else {
/* Old-style string-to-key */
s2k = pgpS2Ksimple(env, pgpHashByNumber(PGP_HASH_MD5));
if (!s2k)
return PGPERR_NOMEM;
}
/* Okay now, do the conversion */
cipher = pgpCipherByNumber(alg);
if (!cipher) {
pgpS2Kdestroy(s2k);
return PGPERR_BAD_CIPHERNUM;
}
if (sec->cklen < v+cipher->blocksize) {
pgpS2Kdestroy(s2k);
return PGPERR_KEY_SHORT;
}
cfb = pgpCfbCreate(cipher);
if (!cfb) {
pgpS2Kdestroy(s2k);
return PGPERR_NOMEM;
}
pgpAssert(cipher->keysize <= sizeof(key));
pgpStringToKey(s2k, phrase, plen, key, cipher->keysize);
pgpCfbInit(cfb, key, sec->cryptkey+v);
memset(key, 0, sizeof(key));
pgpS2Kdestroy(s2k);
v += cipher->blocksize; /* Skip over IV */
}
#if 0
} else { /* Old-style */
/* The key is encrypted; create a cfb context */
cipher = pgpCipherByNumber(alg);
if (!cipher)
return PGPERR_BAD_CIPHERNUM;
if (sec->cklen < v+cipher->blocksize)
return PGPERR_KEY_SHORT;
cfb = pgpCfbCreate(cipher);
if (!cfb)
return PGPERR_NOMEM;
s2k = pgpS2Ksimple(pgpHashByNumber(PGP_HASH_MD5));
if (!s2k) {
pgpCfbDestroy(cfb);
return PGPERR_NOMEM;
}
hc = pgpHashCreate(pgpHashByNumber(PGP_HASH_MD5));
if (!hc) {
pgpCfbDestroy(cfb);
return PGPERR_NOMEM;
}
if (cipher->keysize > hc->hash->hashsize) {
pgpCfbDestroy(cfb);
return PGPERR_BAD_CIPHERNUM;
}
pgpHashUpdate(hc, (byte const *)phrase, plen);
pgpCfbInit(cfb, pgpHashFinal(hc), sec->cryptkey+v);
pgpHashDestroy(hc);
v += cipher->blocksize;
}
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?