pgpdsakey.c
来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 1,290 行 · 第 1/3 页
C
1,290 行
ASSERTDSA(seckey->pkAlg);
pub = (struct DSApub *)pgpMemAlloc(sizeof(*pub));
if (pub) {
pubkey = (struct PgpPubKey *)pgpMemAlloc(sizeof(*pubkey));
if (pubkey) {
bnBegin(&pub->p);
bnBegin(&pub->q);
bnBegin(&pub->g);
bnBegin(&pub->y);
if (bnCopy(&pub->p, &sec->s.p) >= 0
&& bnCopy(&pub->q, &sec->s.q) >= 0
&& bnCopy(&pub->g, &sec->s.g) >= 0
&& bnCopy(&pub->y, &sec->s.y) >= 0)
{
dsaFillPubkey(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->q);
bnEnd(&pub->g);
bnEnd(&pub->y);
pgpMemFree(pubkey);
}
pgpMemFree(pub);
}
return NULL;
}
/*
* Yes, there *is* a reason that this is a function and not a variable.
* On a hardware device with an automatic timeout,
* it actually might need to do some work to find out.
*/
static int
dsaIslocked(struct PgpSecKey const *seckey)
{
struct DSAsecPlus const *sec = (struct DSAsecPlus *)seckey->priv;
ASSERTDSA(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 DSA-specific part is:
*
* 0 2+u MPI for prime p
* 2+u 2+v MPI for order q
* 4+u+v 2+w MPI for generator g
* 6+u+v+w 2+x MPI for public key y
* 8+u+v+w+x 1 Encryption algorithm (0 for none, 1 for IDEA)
* 9+u+v+w+x t Encryption IV: 0 or 8 bytes
* 9+t+u+v+w+x 2+y MPI for x (discrete log of public key)
* 11+t+u+v+w+x+y 2 Checksum
* 13+t+u+v+w+x+y
*
* 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
dsaUnlock(struct PgpSecKey *seckey, struct PgpEnv const *env,
char const *phrase, size_t plen)
{
struct DSAsecPlus *sec = (struct DSAsecPlus *)seckey->priv;
struct BigNum x;
struct PgpCfbContext *cfb = NULL;
unsigned v;
unsigned alg;
unsigned checksum;
int i;
bnBegin(&x);
ASSERTDSA(seckey->pkAlg);
/* Check packet for basic consistency */
i = pgpBnParse(sec->cryptkey, sec->cklen, 4, &v, NULL, NULL, NULL);
if (i <= 0)
goto fail;
/* 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.q, 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 != !sec->cryptkey[v])
goto badpass;
i = pgpCipherSetup(sec->cryptkey + v, sec->cklen - v, phrase, plen,
env, &cfb);
if (i < 0)
goto done;
v += i;
checksum = 0;
i = pgpBnGetNew(&x, sec->cryptkey + v, sec->cklen - v, cfb, &checksum);
if (i <= 0)
goto badpass;
v += i;
if (bnCmp(&x, &sec->s.q) >= 0)
goto badpass; /* Wrong passphrase: x must be < q */
/* Check that we ended in the right place */
if (sec->cklen - v != 2) {
i = PGPERR_KEY_LONG;
goto fail;
}
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;
i = 1; /* Decrypted! */
sec->locked = 0;
goto done;
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
dsaLock(struct PgpSecKey *seckey)
{
struct DSAsecPlus *sec = (struct DSAsecPlus *)seckey->priv;
ASSERTDSA(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.
*/
static size_t
dsaMaxdecrypted(struct PgpSecKey const *seckey)
{
(void)seckey;
return PGPERR_PUBKEY_UNIMP;
}
/*
* 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
dsaDecrypt(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)
{
(void)seckey;
(void)env;
(void)esktype;
(void)esk;
(void)esklen;
(void)key;
(void)keylen;
(void)phrase;
(void)plen;
return PGPERR_PUBKEY_UNIMP;
}
static size_t
dsaMaxsig(struct PgpSecKey const *seckey, PgpVersion version)
{
struct DSAsecPlus const *sec = (struct DSAsecPlus *)seckey->priv;
(void)version;
ASSERTDSA(seckey->pkAlg);
return 2*((bnBits(&sec->s.q)+7)/8 + 2);
}
/*
* Helper function: seed a RandomContext from a BigNum.
* Be very sure to leave nothing in memory!
*/
static void
pgpRandomBnSeed(struct PgpRandomContext const *rc, struct BigNum const *bn)
{
byte buf[32]; /* Big enough for 99.9% of all keys */
unsigned bytes = (bnBits(bn) + 7)/8;
unsigned off = 0;
while (bytes > sizeof(buf)) {
bnExtractLittleBytes(bn, buf, off, sizeof(buf));
pgpRandomAddBytes(rc, buf, sizeof(buf));
bytes -= sizeof(buf);
off += sizeof(buf);
}
bnExtractLittleBytes(bn, buf, off, bytes);
pgpRandomAddBytes(rc, buf, bytes);
memset(buf, 0, sizeof(buf));
}
static int
dsaSign(struct PgpSecKey *seckey, struct PgpHash const *h, byte const *hash,
byte *sig, size_t *siglen, struct PgpRandomContext const *rc,
PgpVersion version)
{
/* Calculate a DSA signature */
struct DSAsecPlus *sec = (struct DSAsecPlus *)seckey->priv;
struct BigNum r, s, bn, k;
unsigned t;
unsigned qbits;
int i;
struct PgpCipher const *ciph;
struct PgpRandomContext *rc2;
/* We don't need this argument, although other algorithms may... */
(void)version;
ASSERTDSA(seckey->pkAlg);
/* We *only* sign SHA hashes */
pgpAssert(h->type == PGP_HASH_SHA);
if (sec->locked)
return PGPERR_KEY_ISLOCKED;
/*
* DSA requires a secret k. This k is *very* important
* to keep secret. Consider, the DSA signing equations are:
* r = (g^k mod p) mod q, and
* s = k^-1 * (H(m) + x*r) mod q,
* so if you know k (and, the signature r, s and H), then
* x = r^-1 * (k*s - H(m))
* If we ever pick two k values the same, then
* r = (g^k mod p) mod q is the same for both signatures, and
* s1 = k^-1 * (H1 + x * r)
* s2 = k^-1 * (H2 + x * r)
* k = (H1-H2) / (s1-s2)
* and proceed from there.
*
* So we need to make *very* sure there's no problem. To make
* sure, we add a layer on top of the passed-in RNG. We assume
* the passed-in RNG is good enough to never repeat (not a
* difficult task), and apply an additional X9.17 generator on
* top of that, seeded with the secret x, which is destroyed
* before leaving this function.
*
* In addition, we add entropy from the hash to the original RNG.
* This will prevent us from using the same k value twice if the
* messages are different.
*/
pgpRandomAddBytes(rc, hash, h->hashsize);
ciph = pgpCipherByNumber(PGP_CIPHER_CAST5);
if (!ciph)
return PGPERR_BAD_CIPHERNUM;
rc2 = pgpRandomCreateX9_17(ciph, rc);
if (!rc2)
return PGPERR_NOMEM;
pgpRandomBnSeed(rc2, &sec->s.x);
bnBegin(&r);
bnBegin(&s);
bnBegin(&bn);
bnBegin(&k);
/*
* Choose the random k value to be used for this signature.
* Make it a bit bigger than q so it is fairly uniform mod q.
*/
qbits = bnBits(&sec->s.q);
if (pgpBnGenRand(&k, rc2, qbits+8, 0, 1, qbits) < 0 ||
bnMod(&k, &k, &sec->s.q) < 0)
goto nomem;
/* Raise g to k power mod p then mod q to get r */
if (bnExpMod(&r, &sec->s.g, &k, &sec->s.p) < 0 ||
bnMod(&r, &r, &sec->s.q) < 0)
goto nomem;
/* r*x mod q into s */
if (bnMul(&s, &r, &sec->s.x) < 0 ||
bnMod(&s, &s, &sec->s.q) < 0)
goto nomem;
/* Pack message hash M into buffer bn */
/* TODO: complicated issues re packing, 160 bits is tight... */
if (bnInsertBigBytes(&bn, hash, 0, h->hashsize) < 0)
goto nomem;
if (bnMod(&bn, &bn, &sec->s.q) < 0)
goto nomem;
/* Add into s */
if (bnAdd(&s, &bn) < 0 ||
bnMod(&s, &s, &sec->s.q) < 0)
goto nomem;
/* Divide by k, mod q (k inverse held in bn) */
if (bnInv(&bn, &k, &sec->s.q) < 0 ||
bnMul(&s, &s, &bn) < 0 ||
bnMod(&s, &s, &sec->s.q) < 0)
goto nomem;
/* That's it, now to pack r and then s into the buffer */
t = pgpBnPutPlain(&r, sig);
t += pgpBnPutPlain(&s, sig+t);
if (siglen)
*siglen = (size_t)t;
i = 0;
goto done;
nomem:
i = PGPERR_NOMEM;
/* fall through */
done:
pgpRandomDestroy(rc2);
bnEnd(&k);
bnEnd(&bn);
bnEnd(&s);
bnEnd(&r);
return i;
}
/*
* Re-encrypt a PgpSecKey with a new urn a PgpSecKey into a secret key.
* 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_DSA for DSA)
* The following:
* 0 2+u MPI for prime p
* 2+u 2+v MPI for order q
* 4+u+v 2+w MPI for generator g
* 6+u+v+w 2+x MPI for public key y
* 8+u+v+w+x 1 Encryption algorithm (0 for none, 1 for IDEA)
* 9+u+v+w+x t Encryption IV: 0 or 8 bytes
* 9+t+u+v+w+x 2+y MPI for x (discrete log of public key)
* 11+t+u+v+w+x+y 2 Checksum
* 13+t+u+v+w+x+y
*
* The Encryption algorithm is the cipher algorithm for the old-style
* string-to-key conversion. For the new type, it's 255, then a cipher
* algorithm, then a string-to-key algorithm (variable-length),
* then the encryption IV. That's 16 bytes plus the string-to-key
* conversion length.
*
* On initial key generation we rely on calling this with env=NULL being
* OK if phrase=NULL.
*/
static int
dsaChangeLock(struct PgpSecKey *seckey, struct PgpEnv const *env,
struct PgpRandomContext const *rc, char const *phrase, size_t plen)
{
struct DSAsecPlus *sec = (struct DSAsecPlus *)seckey->priv;
struct PgpStringToKey *s2k = NULL; /* Shut up warnings */
struct PgpCipher const *cipher = NULL; /* Shut up warnings */
struct PgpCfbContext *cfb = NULL; /* This is realy needed */
byte *p;
int oldf = 0; /* Shut up warnings */
unsigned len;
unsigned checksum;
ASSERTDSA(seckey->pkAlg);
if (sec->locked)
return PGPERR_KEY_ISLOCKED;
len = bnBytes(&sec->s.p) + bnBytes(&sec->s.q) + bnBytes(&sec->s.g) +
bnBytes(&sec->s.y) + bnBytes(&sec->s.x) + 13;
if (phrase) {
s2k = pgpS2Kdefault(env, rc);
if (!s2k)
return PGPERR_NOMEM;
cipher = pgpCipherDefaultKey(env);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?