⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pgpelgkey.c

📁 著名的加密软件的应用于电子邮件中
💻 C
📖 第 1 页 / 共 3 页
字号:
/** 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 + -