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

📄 pgpelgkey.c

📁 可以实现对邮件的加密解密以及签名
💻 C
📖 第 1 页 / 共 3 页
字号:
elgIslocked(PGPSecKey const *seckey)
{
	ELGsecPlus const *sec = (ELGsecPlus *)seckey->priv;

	ASSERTELG(seckey->pkAlg);
	return sec->locked;
}


/*
 * Return the algorithm and (symmetric) key size used for locking/unlocking
 * the secret key.
 */
static PGPError
elgLockingAlgorithm(
	PGPSecKey const *seckey,
	PGPCipherAlgorithm *pAlg,
	PGPSize *pAlgKeySize
	)
{
	ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;
	PGPCipherVTBL const *cipher;
	PGPByte alg;
	int i;

	ASSERTELG(seckey->pkAlg);

	if( IsntNull( pAlg ) )
		*pAlg = (PGPCipherAlgorithm) 0;
	if( IsntNull( pAlgKeySize ) )
		*pAlgKeySize = (PGPSize) 0;

	/* Check packet for basic consistency */
	i = pgpBnParse(sec->cryptkey, sec->cklen, 3, NULL, NULL, NULL);
	if (i < 0)
		return (PGPError)i;

	/* Get the encryption algorithm (cipher number).  0 == no encryption */
	alg = sec->cryptkey[i] & 255;

	/* New style has 0xff or 0xfe then algorithm value */
	if (alg == 0xff || alg == 0xfe)
		alg = sec->cryptkey[i+1] & 255;

	cipher = pgpCipherGetVTBL( (PGPCipherAlgorithm)alg);
	if (!cipher)
		return kPGPError_BadCipherNumber;

	/* Success */
	if( IsntNull( pAlg ) )
		*pAlg = (PGPCipherAlgorithm) alg;
	if( IsntNull( pAlgKeySize ) )
		*pAlgKeySize = cipher->keysize;

	return kPGPError_NoErr;
}


/*
 * Return the StringToKey type for unlocking the given key.  We use
 * kPGPStringToKey_Literal to flag a secret split unlocking buffer.
 * Returns kPGPStringToKey_Simple if key has no passphrase.
 */
static PGPError
elgS2KType(
	PGPSecKey const *seckey,
	PGPStringToKeyType *s2kType
	)
{
	ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;
	PGPByte alg;
	int i;

	ASSERTELG(seckey->pkAlg);

	/* note that 0 is a valid type, but use it as default anyway */
	if( IsntNull( s2kType ) )
		*s2kType = (PGPStringToKeyType) 0;

	/* Check packet for basic consistency */
	i = pgpBnParse(sec->cryptkey, sec->cklen, 3, NULL, NULL, NULL);
	if (i < 0)
		return (PGPError)i;

	/* Get the encryption algorithm (cipher number).  0 == no encryption */
	alg = sec->cryptkey[i] & 255;

	if (alg == 0xff || alg == 0xfe) {
		/* New style has oxff or 0xfe then algorithm value then S2K */
		*s2kType = (PGPStringToKeyType) sec->cryptkey[i+2];
	} else {
		/* Unencrypted or old-style simple encryption */
		*s2kType = kPGPStringToKey_Simple;
	}


	return kPGPError_NoErr;
}

/*
 * Convert a passphrase into a s2k literal buffer for the key.
 * Returns error code.  Output buffer will be size of the *pAlgKeySize
 * parameter from pgpSecKeyLockingalgorithm.
 */
static PGPError
elgConvertPassphrase(PGPSecKey *seckey, PGPEnv const *env,
	  char const *phrase, PGPSize plen, PGPByte *outbuf)
{
	ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;
	PGPStringToKey *s2k;
	PGPByte alg;
	PGPBoolean hasS2K;
	PGPCipherVTBL const *cipher;
	int i;

	ASSERTELG(seckey->pkAlg);
	pgpAssert (IsntNull( outbuf ) );

	/* Check packet for basic consistency */
	i = pgpBnParse(sec->cryptkey, sec->cklen, 3, NULL, NULL, NULL, NULL);
	if (i < 0)
		return (PGPError)i;

	/* Get the encryption algorithm (cipher number).  0 == no encryption */
	alg = sec->cryptkey[i++] & 255;

	hasS2K = (alg == 0xff || alg == 0xfe);

	/* New style has 0xff or 0xfe then algorithm value */
	if (hasS2K)
		alg = sec->cryptkey[i++] & 255;

	/* Now we are looking at the s2k object if there is one. */
	if (alg == 0) {
		/* Key is not locked */
		return kPGPError_BadParams;
	}
	cipher = pgpCipherGetVTBL( (PGPCipherAlgorithm)alg);
	if( IsNull( cipher ) )
		return kPGPError_BadCipherNumber;

	if (hasS2K) {
		pgpS2Kdecode(&s2k, pgpenvGetContext( env ), sec->cryptkey+i, sec->cklen-i);
	} else {
		s2k = pgpS2Ksimple(pgpenvGetContext( env ), pgpHashByNumber(kPGPHashAlgorithm_MD5));
	}
	if (IsNull( s2k ) )
		return kPGPError_OutOfMemory;
	pgpStringToKey(s2k, phrase, plen, outbuf, cipher->keysize);
	pgpS2Kdestroy(s2k);

	return kPGPError_NoErr;
}


/*
 * 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(PGPSecKey *seckey,
	  char const *phrase, PGPSize plen, PGPBoolean hashedPhrase)
{
	ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;
	BigNum x;
	BigNum tmp1;
	PGPCFBContext *cfb = NULL;
	unsigned v, v_copy;
	unsigned alg /* actual algorithm */, alg0 /* first algorithm descriptor */;
	PGPHashContextRef checksumHash = NULL;
	PGPHashVTBL const *hashEntry = NULL;
	PGPByte checksumStored[20], checksum[20];       /* size no less then the max return value from pgpChecksumGetEx */
	unsigned checksumSize;
	int i;
	PGPBoolean validityChecked;
	PGPBoolean twofishRetry = FALSE;
	PGPMemoryMgrRef		mgr	= NULL;

	if( pgpFIPSModeEnabled() )
	{
		/* FIPS does not allow using keys with NULL passphrases */
		if( IsNull( phrase ) || plen == 0 )
			return( 0 );
	}
	
	mgr	= PGPPeekContextMemoryMgr( seckey->context );

	ASSERTELG(seckey->pkAlg);

	/* Check packet for basic consistency */
	i = pgpBnParse(sec->cryptkey, sec->cklen, 3, &v, NULL, NULL, NULL);
	if (i < 0)
		return i;

	bnBegin(&x, mgr, TRUE );
	bnBegin(&tmp1, mgr, TRUE );

	/* 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;

	/* Fast consistency checks (more below) */
	if (bnCmp(&sec->s.y, &sec->s.p) >= 0) {
		i = kPGPError_CorruptPrivateKey;
		goto done;
	}

	/* Check for reasonable size; y size check is somewhat arbitrary */
	if( bnBits(&sec->s.p) < 512 || bnBits(&sec->s.y) < 100 ) {
		i = kPGPError_CorruptPrivateKey;
		goto done;
	}

	/* Get the encryption algorithm (cipher number).  0 == no encryption */
	alg0 = alg = sec->cryptkey[v];
	if( alg0 == 0xff || alg0 == 0xfe )
		alg = sec->cryptkey[v+1];

	/* 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 != !alg0)
		goto badpass1;

	hashEntry = pgpHashByNumberWithMask( alg0!=0xfe ? kPGPHashAlgorithm_Checksum16 : kPGPHashAlgorithm_SHA, 0xffffffff );
	checksumHash = pgpHashCreate( mgr, hashEntry );
	if( checksumHash == NULL )  {
		i = kPGPError_BadHashNumber;
		goto done;      
	}

	v_copy = v;
twofishRetry:
	i = pgpCipherSetup(sec->cryptkey + v, sec->cklen - v, phrase, plen,
					   hashedPhrase, twofishRetry, seckey->context, &cfb);
	if (i < 0)
		goto done;
	v += i;

	PGPResetHash( checksumHash );
	i = pgpBnGet(&x, sec->cryptkey + v, sec->cklen - v, cfb, alg0, FALSE/*old*/, checksumHash);
	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 */
	checksumSize = pgpChecksumGet( sec->cryptkey+v, NULL, alg0, checksumHash, NULL );
	pgpAssert( checksumSize <= sizeof(checksumStored) );    /* programmer's error */
	if (sec->cklen - v != checksumSize)
		goto badpass;
		     
        pgpChecksumGet(sec->cryptkey+v, cfb, alg0, checksumHash, checksumStored);
	PGPFinalizeHash( checksumHash, checksum );
	if( !pgpMemoryEqual( checksumStored, checksum, checksumSize ) )
		goto badpass;
			
	validityChecked = pgpKeyDBObjIsValid( seckey->keyDBObj )
					  && pgpSecIsValidated( seckey->keyDBObj );
	if( !validityChecked )
	{
		/* Do careful checks of validity */
		if (bnCmp(&x, &sec->s.p) >= 0) {
			if( alg == kPGPCipherAlgorithm_Twofish256 && !twofishRetry )
				goto badpass;
			i = kPGPError_CorruptPrivateKey;
			goto done;
		}

		/* Calculate g^x mod p in tmp1; should be y */
		bnExpMod( &tmp1, &sec->s.g, &x, &sec->s.p );
		if( bnCmp( &tmp1, &sec->s.y ) != 0 ) {
			if( alg == kPGPCipherAlgorithm_Twofish256 && !twofishRetry )
				goto badpass;
			i = kPGPError_CorruptPrivateKey;
			goto done;
		}

		if( pgpKeyDBObjIsValid( seckey->keyDBObj ) )
			pgpSecSetValidated( seckey->keyDBObj );
	}

	/*
	 * 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 = kPGPError_OutOfMemory;
	goto done;
fail:
	if (!i)
		i = kPGPError_KeyPacketTruncated;
	goto done;
badpass:
	if( alg == kPGPCipherAlgorithm_Twofish256 && !twofishRetry )
	{
		/* Had a mis-implementation of Twofish on bigendian machines, so for
		 * backwards compatibility we must retry unlocking failures using
		 * the old, bad algorithm
		 */
		twofishRetry = TRUE;
		v = v_copy;
		if (cfb)
			PGPFreeCFBContext(cfb);
		goto twofishRetry;
	}
badpass1:
	i = 0;	/* Incorrect passphrase */
	goto done;
done:
	bnEnd(&x);
	bnEnd(&tmp1);
	if (cfb)
		PGPFreeCFBContext(cfb);
	if( checksumHash != NULL )
		PGPFreeHashContext( checksumHash );
	return i;
}

/*
 * Relock the key.
 */
static void
elgLock(PGPSecKey *seckey)
{
	ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;

	ASSERTELG(seckey->pkAlg);
	sec->locked = 1;
	/* bnEnd is documented as also doing a bnBegin */
	bnEnd(&sec->s.x);
}

static PGPSize
elgSecMaxdecrypted(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format);



/*
 * 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(PGPSecKey *seckey,
		   PGPByte const *esk, PGPSize esklen,
		   PGPByte *key, PGPSize *keylen,
		   char const *phrase, PGPSize plen,
		   PGPPublicKeyMessageFormat format)
{
#if PGP_DECRYPT_DISABLE /* [ */

	(void)seckey;
	(void)esk;
	(void)esklen;
	(void)key;
	(void)keylen;
	(void)phrase;
	(void)plen;
	(void)format;
	return kPGPError_FeatureNotAvailable;

#else /* PGP_DECRYPT_DISABLE */  /* ]  [ */

	ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;
	BigNum k, yy;
	int i, j;
	unsigned t, pbytes;
	PGPSize len;
	PGPSize off;
	PGPMemoryMgrRef		mgr	= NULL;
	mgr	= PGPPeekContextMemoryMgr( seckey->context );

	ASSERTELG(seckey->pkAlg);
	if (sec->locked) {
		i = elgUnlock(seckey, phrase, plen, FALSE);
		if (i <= 0)
			return i ? i : kPGPError_KeyIsLocked;
		pgpAssert(!sec->locked);
	}

	bnBegin(&k, mgr, TRUE );
	bnBegin(&yy, mgr, TRUE );
	pbytes = (bnBits(&sec->s.p) + 7) / 8;

	/* ESK holds two values.  Get first, yy, from ESK. */
	off = 0;
	if (format == kPGPPublicKeyMessageFormat_X509) {
		/* Parse SEQUENCE header for 509 encrypted data */
		PGPByte const *eskp = esk + off;
		PGPUInt32 len;
		if (pgpBnX509TagLen(&eskp, &len) != X509_TAG_SEQUENCE) {
			i = kPGPError_MalformedKeyComponent;
			goto done;
		}
		off += eskp - esk;
		if (len != esklen - off) {
			i = kPGPError_BadSessionKeySize;
			goto done;
		}
	}
	i = pgpBnGetFormatted(&yy, esk+off, esklen-off, pbytes, format);
	if (i <= 0)
		goto fail;
	/* Get 2nd value, k, from ESK */
	off += i;
	i = pgpBnGetFormatted(&k, esk+off, esklen-off, pbytes, format);
	if (i <= 0)
		goto fail;
	off += i;
	if (off != esklen) {
		i = kPGPError_BadSessionKeySize;
		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 */
	len = elgSecMaxdecrypted(seckey, format);
	i = pgpPKCSUnpack (key, len, &k, PKCS_PAD_ENCRYPTED, pbytes);
	bnEnd(&k);
	if (i < 0)
		return i;

	if (format == kPGPPublicKeyMessageFormat_PGP) {
		/* Check checksum */
		t = 0;
		for (j = 1; j < i-2; j++)
			t += key[j];
		if (t != ((unsigned)key[i-2]<<8) + key[i-1])
			return kPGPError_CorruptData;
		pgpClearMemory(key+i-2, 2);

		/* The actual key */
		if (keylen)
			*keylen = (PGPSize)i-2;
	} else {
		if( keylen)
			*keylen = (PGPSize)i;
	}
	
	return 0;

fail:
	if (!i)
		i = kPGPError_BadSessionKeySize;
	goto done;
nomem:
	i = kPGPError_OutOfMemory;
	goto done;
done:
	bnEnd(&yy);
	bnEnd(&k);
	return i;

#endif /* PGP_DECRYPT_DISABLE */ /* ] */
}



/*
 * 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 PGPSize
elgSecMaxdecrypted(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
	ELGsecPlus const *sec = (ELGsecPlus *)seckey->priv;
	PGPSize size;

	ASSERTELG(seckey->pkAlg);
	(void) format;

	size = bnBytes(&sec->s.p);
	return size < 3 ? 0 : size-3;
}

static PGPSize
elgSecMaxesk(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
	ELGsecPlus const *sec = (ELGsecPlus *)seckey->priv;

	ASSERTELG(seckey->pkAlg);
	if (format == kPGPPublicKeyMessageFormat_PGP)
		return 2*( 2 + bnBytes(&sec->s.p) );
	else if (format == kPGPPublicKeyMessageFormat_PKCS1 ||
			 format == kPGPPublicKeyMessageFormat_IKE)
		return 2*( bnBytes(&sec->s.p) );
	else if (format == kPGPPublicKeyMessageFormat_X509) {
		/* SEQUENCE, length, INT, INT */
		PGPUInt32 len;
		PGPUInt32 pbytes = bnBytes(&sec->s.p);
		len = 2*(pgpBnX509LenLen(pbytes+1) + 1 + pbytes+1);
		return 1 + pgpBnX509LenLen(len) + len;
	}

	pgpAssert(0);
	return 0;
}

static PGPSize

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -