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 + -
显示快捷键?