📄 pgpelgkey.c
字号:
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 + -