📄 pgpelgkey.c
字号:
/** Secret key functions **/
static void
elgSecDestroy(struct PgpSecKey *seckey)
{
struct ELGsecPlus *sec = (struct ELGsecPlus *)seckey->priv;
ASSERTELG(seckey->pkAlg);
bnEnd(&sec->s.p);
bnEnd(&sec->s.g);
bnEnd(&sec->s.y);
bnEnd(&sec->s.x);
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
elgSecId8(struct PgpSecKey const *seckey, byte *buf)
{
struct ELGsecPlus const *sec = (struct ELGsecPlus *)seckey->priv;
ASSERT (seckey->pkAlg);
bnExtractBigBytes(&sec->s.p, buf, 0, 8);
}
#endif
/*
* Generate a PgpPubKey from a PgpSecKey
*/
static struct PgpPubKey *
elgPubkey(struct PgpSecKey const *seckey)
{
struct ELGsecPlus const *sec = (struct ELGsecPlus *)seckey->priv;
struct PgpPubKey *pubkey;
struct ELGpub *pub;
ASSERTELG(seckey->pkAlg);
pub = (struct ELGpub *)pgpMemAlloc(sizeof(*pub));
if (pub) {
pubkey = (struct PgpPubKey *)pgpMemAlloc(sizeof(*pubkey));
if (pubkey) {
bnBegin(&pub->p);
bnBegin(&pub->g);
bnBegin(&pub->y);
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);
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
elgIslocked(struct PgpSecKey const *seckey)
{
struct ELGsecPlus const *sec = (struct ELGsecPlus *)seckey->priv;
ASSERTELG(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 ELG-specific part is:
*
* 0 2+u MPI for prime
* 2+u 2+v MPI for generator
* 4+u+v 2+w MPI for public key
* 6+u+v+w 1 Encryption algorithm (0 for none, 1 for IDEA)
* 7+u+v+w t Encryption IV: 0 or 8 bytes
* 7+t+u+v+w 2+x MPI for x (discrete log of public key)
* 9+t+u+v+w+x 2 Checksum
* 11+t+u+v+w+x
*
* Actually, that's the old-style, if pgpS2KoldVers is true.
* If it's false, the algorithm is 255, and is followed by the
* algorithm, then the (varaible-length, self-delimiting)
* string-to-key descriptor.
*/
static int
elgUnlock(struct PgpSecKey *seckey, struct PgpEnv const *env,
char const *phrase, size_t plen)
{
struct ELGsecPlus *sec = (struct ELGsecPlus *)seckey->priv;
struct BigNum x;
struct PgpCfbContext *cfb = NULL;
unsigned v;
unsigned alg;
unsigned checksum;
int i;
ASSERTELG(seckey->pkAlg);
bnInit();
/* Check packet for basic consistency */
i = pgpBnParse(sec->cryptkey, sec->cklen, 3, &v, NULL, NULL, NULL);
if (i < 0)
return i;
/* OK, read the public data */
i = pgpBnGetPlain(&sec->s.p, sec->cryptkey+v, sec->cklen-v);
if (i <= 0)
goto fail;
v += i;
i = pgpBnGetPlain(&sec->s.g, sec->cryptkey+v, sec->cklen-v);
if (i <= 0)
goto fail;
v += i;
i = pgpBnGetPlain(&sec->s.y, sec->cryptkey+v, sec->cklen-v);
if (i <= 0)
goto fail;
v += i;
/* Get the encryption algorithm (cipher number). 0 == no encryption */
alg = sec->cryptkey[v];
/* If the phrase is empty, set it to NULL */
if (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;
i = pgpCipherSetup(sec->cryptkey + v, sec->cklen - v, phrase, plen,
env, &cfb);
if (i < 0)
goto done;
v += i;
checksum = 0;
bnBegin(&x);
i = pgpBnGetNew(&x, sec->cryptkey + v, sec->cklen - v, cfb, &checksum);
if (i <= 0)
goto badpass;
v += i;
if (bnCmp(&x, &sec->s.p) >= 0)
goto badpass; /* Wrong passphrase: x must be < p */
/* Check that we ended in the right place */
if (sec->cklen - v != 2) {
i = PGPERR_KEY_LONG;
goto done;
}
checksum &= 0xffff;
if (checksum != pgpChecksumGetNew(sec->cryptkey + v, cfb))
goto badpass;
/*
* Note that the "nomem" case calls bnEnd()
* more than once, but this is guaranteed harmless.
*/
if (bnCopy(&sec->s.x, &x) < 0)
goto nomem;
bnEnd(&x);
i = 1; /* Decrypted! */
sec->locked = 0;
if (cfb)
pgpCfbDestroy (cfb);
return 1; /* Decrypted */
nomem:
i = PGPERR_NOMEM;
goto done;
fail:
if (!i)
i = PGPERR_KEY_SHORT;
goto done;
badpass:
i = 0; /* Incorrect passphrase */
goto done;
done:
bnEnd(&x);
if (cfb)
pgpCfbDestroy (cfb);
return i;
}
/*
* Relock the key.
*/
static void
elgLock(struct PgpSecKey *seckey)
{
struct ELGsecPlus *sec = (struct ELGsecPlus *)seckey->priv;
ASSERTELG(seckey->pkAlg);
sec->locked = 1;
/* bnEnd is documented as also doing a bnBegin */
bnEnd(&sec->s.x);
}
/*
* 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
elgMaxdecrypted(struct PgpSecKey const *seckey)
{
struct ELGsecPlus const *sec = (struct ELGsecPlus *)seckey->priv;
size_t size;
ASSERTELG(seckey->pkAlg);
size = (bnBits(&sec->s.p)+7)/8;
return size < 3 ? 0 : size-3;
}
/*
* Try to decrypt the given esk. If the key is locked, try the given
* passphrase. It may or may not leave the key unlocked in such a case.
* (Some hardware implementations may insist on a password per usage.)
*/
static int
elgDecrypt(struct PgpSecKey *seckey, struct PgpEnv const *env,
int esktype, byte const *esk, size_t esklen,
byte *key, size_t *keylen, char const *phrase,
size_t plen)
{
struct ELGsecPlus *sec = (struct ELGsecPlus *)seckey->priv;
struct BigNum k, yy;
int i, j;
unsigned t, pbytes;
size_t len;
size_t off;
(void)esktype;
ASSERTELG(seckey->pkAlg);
if (sec->locked) {
i = elgUnlock(seckey, env, phrase, plen);
if (i <= 0)
return i ? i : PGPERR_KEY_ISLOCKED;
pgpAssert(!sec->locked);
}
bnBegin(&k);
bnBegin(&yy);
/* ESK holds two values. Get first, yy, from ESK. */
off = 0;
i = pgpBnGetPlain(&yy, esk+off, esklen-off);
if (i <= 0)
goto fail;
/* Get 2nd value, k, from ESK */
off += i;
i = pgpBnGetPlain(&k, esk+off, esklen-off);
if (i <= 0)
goto fail;
off += i;
if (off != esklen) {
i = PGPERR_ESK_TOOLONG;
goto done;
}
/* Calculate yy = yy**x mod p, which is what is multiplied by k */
if (bnExpMod(&yy, &yy, &sec->s.x, &sec->s.p) < 0)
goto nomem;
/* Set k = k / (new)yy , revealing PKCS padded session key */
if (bnInv(&yy, &yy, &sec->s.p) < 0 ||
bnMul(&k, &k, &yy) < 0 ||
bnMod(&k, &k, &sec->s.p) < 0) {
goto nomem;
}
bnEnd(&yy);
/* k is now suitable for unpacking */
pbytes = (bnBits(&sec->s.p) + 7) / 8;
len = elgMaxdecrypted(seckey);
i = pgpPKCSUnpack (key, len, &k, PKCS_PAD_ENCRYPTED, pbytes);
bnEnd(&k);
if (i < 0)
return i;
/* Check checksum (should this be here?) */
t = 0;
for (j = 1; j < i-2; j++)
t += key[j];
if (t != ((unsigned)key[i-2]<<8) + key[i-1])
return PGPERR_PK_CORRUPT;
memset(key+i-2, 0, 2);
/* The actual key */
if (keylen)
*keylen = (size_t)i-2;
return 0;
fail:
if (!i)
i = PGPERR_ESK_TOOSHORT;
goto done;
nomem:
i = PGPERR_NOMEM;
goto done;
done:
bnEnd(&yy);
bnEnd(&k);
return 0;
}
static size_t
elgMaxsig(struct PgpSecKey const *seckey, PgpVersion version)
{
#ifndef ELGSIGS
(void)seckey;
(void)version;
return PGPERR_PUBKEY_UNIMP;
#else
struct ELGsecPlus const *sec = (struct ELGsecPlus *)seckey->priv;
(void)version;
ASSERTELG(seckey->pkAlg);
return 2*((bnBits(&sec->s.p)+7)/8 + 2);
#endif
}
static int
elgSign(struct PgpSecKey *seckey, struct PgpHash const *h, byte const *hash,
byte *sig, size_t *siglen, struct PgpRandomContext const *rc,
PgpVersion version)
{
#ifndef ELGSIGS
(void)seckey;
(void)h;
(void)hash;
(void)sig;
(void)siglen;
(void)rc;
(void)version;
return PGPERR_PUBKEY_UNIMP;
#else
/* Calculate an El Gamal signature */
struct ELGsecPlus *sec = (struct ELGsecPlus *)seckey->priv;
struct BigNum pm1, x1, a, bn, xk, xa;
unsigned t;
unsigned x1bits;
int i;
/* We don't need this argument, although other algorithms may... */
(void)version;
ASSERTELG(seckey->pkAlg);
if (sec->locked)
return PGPERR_KEY_ISLOCKED;
if (h->DERprefixsize + h->hashsize > elgMaxsig(seckey, version))
return PGPERR_PUBKEY_TOOSMALL;
bnBegin(&pm1);
bnBegin(&x1);
bnBegin(&a);
bnBegin(&bn);
bnBegin(&xk);
bnBegin(&xa);
/* Some calculations done mod p-1 */
if (bnCopy (&pm1, &sec->s.p) < 0 ||
bnSubQ (&pm1, 1) < 0)
goto nomem;
/*
* Choose the random x1 value to be used for this signature.
* Make sure it is relatively prime to p-1
*/
x1bits = pgpDiscreteLogExponentBits(bnBits(&sec->s.p))*3/2;
do {
if (pgpBnGenRand(&x1, rc, x1bits, 0, 1, x1bits-1) < 0 ||
bnGcd(&a, &x1, &pm1) < 0)
goto nomem;
} while (bnBits(&a) > 1);
/* Raise g to x1 power to get a */
if (bnExpMod(&a, &sec->s.g, &x1, &sec->s.p) < 0)
goto nomem;
/* Calculate x**-1 mod p-1, keep in x1 */
if (bnInv(&x1, &x1, &pm1) < 0)
goto nomem;
/* Pack message hash M into buffer bn (use sig temporarily) */
t = elgPack(sig, elgMaxsig(seckey, version), h, hash);
if (pgpPKCSPack(&bn, sig, t, PKCS_PAD_SIGNED,
(bnBits(&sec->s.p)+7)/8, rc) < 0)
goto nomem;
/* Calculate b = (M-xa)/k mod p-1, put it in bn */
if (bnMul(&xa, &sec->s.x, &a) < 0 ||
bnMod(&xa, &xa, &pm1) < 0)
goto nomem;
if (bnCmp(&bn, &xa) < 0) {
if (bnAdd(&bn, &pm1) < 0)
goto nomem;
}
bnSub(&bn, &xa);
if (bnMul(&bn, &bn, &x1) < 0 ||
bnMod(&bn, &bn, &pm1) < 0)
goto nomem;
/* Success, now just pack into sig buffer */
t = pgpBnPutPlain(&a, sig);
t += pgpBnPutPlain(&bn, sig+t);
if (siglen)
*siglen = (size_t)t;
i = 0;
goto done;
nomem:
i = PGPERR_NOMEM;
/* fall through */
done:
bnEnd(&xa);
bnEnd(&bn);
bnEnd(&pm1);
bnEnd(&x1);
bnEnd(&a);
bnEnd(&bn);
bnEnd(&xk);
return i;
#endif
}
/*
* Re-encrypt a PgpSecKey with a new passphrase.
* A secret key is, after a non-specific prefix:
* 0 1 Version (= 2 or 3)
* 1 4 Timestamp
* 5 2 Validity (=0 at present)
* 7 1 Algorithm (=PGP_PKALG_ELGAMAL for ELG)
* The following:
* 0 2+u MPI for prime
* 2+u 2+v MPI for generator
* 4+u+v 2+w MPI for public key
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -