📄 pgprsakey.c
字号:
/*
* Return the algorithm and (symmetric) key size used for locking/unlocking
* the secret key.
*/
static PGPError
rsaLockingAlgorithm(
PGPSecKey const *seckey,
PGPCipherAlgorithm *pAlg,
PGPSize *pAlgKeySize
)
{
RSAsecPlus *sec = (RSAsecPlus *)seckey->priv;
PGPCipherVTBL const *cipher;
PGPByte alg;
int i;
ASSERTRSA(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, 2, 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
rsaS2KType(
PGPSecKey const *seckey,
PGPStringToKeyType *s2kType
)
{
RSAsecPlus *sec = (RSAsecPlus *)seckey->priv;
PGPByte alg;
int i;
ASSERTRSA(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, 2, 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 0xff 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
rsaConvertPassphrase(PGPSecKey *seckey, PGPEnv const *env,
char const *phrase, PGPSize plen, PGPByte *outbuf)
{
RSAsecPlus *sec = (RSAsecPlus *)seckey->priv;
PGPStringToKey *s2k;
PGPByte alg;
PGPBoolean hasS2K;
PGPCipherVTBL const *cipher;
int i;
PGPContextRef context = pgpenvGetContext( env );
ASSERTRSA(seckey->pkAlg);
pgpAssert (IsntNull( outbuf ) );
/* Check packet for basic consistency */
i = pgpBnParse(sec->cryptkey, sec->cklen, 2, 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 255 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, context, sec->cryptkey+i, sec->cklen-i);
} else {
s2k = pgpS2Ksimple(context, 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 RSA-specific part is:
*
* 0 2+u MPI for modulus
* 2+u 2+v MPI for exponent
* 4+u+v 1 Encryption algorithm (0 for none, 1 for IDEA)
* 5+u+v t Encryption IV: 0 or 8 bytes
* 5+t+u+v 2+w MPI for d
* 7+t+u+v+w 2+x MPI for p
* 9+t+u+v+w+x 2+y MPI for q
* 11+t+u+v+w+x+y 2+z MPI for u
* 13+t+u+v+w+x+y+z 2 Checksum
* 15+t+u+v+w+x+y+z
*
* Actually, that's the old-style, if pgpS2KoldVers is true.
* If it's false, the algorithm ('alg0') is 0xff or 0xfe, and is followed by the
* algorithm, then the (varaible-length, self-delimiting)
* string-to-key descriptor.
*/
static int
rsaUnlock(PGPSecKey *seckey,
char const *phrase, PGPSize plen, PGPBoolean hashedPhrase)
{
RSAsecPlus *sec = (RSAsecPlus *)seckey->priv;
BigNum d, p, q, u, bn;
PGPCFBContext *cfb = NULL; /* Necessary */
unsigned v, t, v_copy;
unsigned alg /* actual algorithm */, alg0 /* first algorithm descriptor */;
PGPHashContextRef checksumHash = NULL;
PGPHashVTBL const *hashEntry;
PGPByte checksumStored[20], checksum[20]; /* size no less then the max return value from pgpChecksumGet */
unsigned checksumSize;
int i;
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 );
ASSERTRSA(seckey->pkAlg);
bnInit();
if (sec->cklen < 5)
return kPGPError_KeyPacketTruncated;
v = ((unsigned)sec->cryptkey[0] << 8) + sec->cryptkey[1];
v = (v+7)/8;
if (sec->cklen < 5+v)
return kPGPError_KeyPacketTruncated;
if (bnInsertBigBytes(&sec->s.n, sec->cryptkey+2, 0, v) < 0)
return kPGPError_OutOfMemory;
t = ((unsigned)sec->cryptkey[2+v] << 8) + sec->cryptkey[3+v];
t = (t+7)/8;
if (sec->cklen < 4+v+t)
return kPGPError_KeyPacketTruncated;
if (bnInsertBigBytes(&sec->s.e, sec->cryptkey+4+v, 0, t) < 0)
return kPGPError_OutOfMemory;
v += t + 4;
if (sec->cklen < v+1)
return kPGPError_KeyPacketTruncated;
/* 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)
return 0;
hashEntry = pgpHashByNumberWithMask( alg0!=0xfe ? kPGPHashAlgorithm_Checksum16 : kPGPHashAlgorithm_SHA, 0xffffffff );
checksumHash = pgpHashCreate( mgr, hashEntry );
if( checksumHash == NULL )
return kPGPError_BadHashNumber;
bnBegin(&d, mgr, TRUE);
bnBegin(&p, mgr, TRUE);
bnBegin(&q, mgr, TRUE);
bnBegin(&u, mgr, TRUE);
bnBegin(&bn, mgr, TRUE);
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(&d, sec->cryptkey + v, sec->cklen - v, cfb, alg0, sec->v3, checksumHash);
if (i <= 0)
goto badpass;
v += i;
if (bnCmp(&d, &sec->s.n) >= 0)
goto badpass; /* Wrong passphrase: d must be < n */
i = pgpBnGet(&p, sec->cryptkey + v, sec->cklen - v, cfb, alg0, sec->v3, checksumHash);
if (i <= 0)
goto badpass;
if ((bnLSWord(&p) & 1) == 0)
goto badpass;
v += i;
i = pgpBnGet(&q, sec->cryptkey + v, sec->cklen - v, cfb, alg0, sec->v3, checksumHash);
if (i <= 0)
goto badpass;
if ((bnLSWord(&q) & 1) == 0)
goto badpass;
v += i;
/* Extremely high-powered check. Verify that p*q == n */
if (bnMul(&bn, &p, &q) < 0)
goto nomem;
if (bnCmp(&bn, &sec->s.n) != 0)
goto badpass;
/* Verify that d*e == 1 mod p-1 */
(void)bnSubQ(&p, 1);
if (bnMul(&bn, &d, &sec->s.e) < 0 || bnMod(&bn, &bn, &p) < 0)
goto nomem;
if (bnCmpQ(&bn, 1) != 0)
goto badpass;
(void)bnAddQ(&p, 1);
/* Verify that d*e == 1 mod q-1 */
(void)bnSubQ(&q, 1);
if (bnMul(&bn, &d, &sec->s.e) < 0 || bnMod(&bn, &bn, &q) < 0)
goto nomem;
if (bnCmpQ(&bn, 1) != 0)
goto badpass;
(void)bnAddQ(&q, 1);
i = pgpBnGet(&u, sec->cryptkey + v, sec->cklen - v, cfb, alg0, sec->v3, checksumHash);
if (i <= 0)
goto badpass;
v += i;
/* 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;
/* Bug in 7.0 stored checksum in V3 format for V4 keys, so check both */
PGPFinalizeHash( checksumHash, checksum );
/* check the right way first - encrypted */
if( ! sec->v3 ) /* v3 is always in clear */
pgpChecksumGet(sec->cryptkey+v, cfb, alg0, checksumHash, checksumStored);
if( sec->v3 || !pgpMemoryEqual( checksumStored, checksum, checksumSize ) ) {
if( alg0 == 0xfe ) /* 7.0 didn't understand this format */
goto badpass;
/* read checksum in clear */
pgpChecksumGet(sec->cryptkey+v, NULL, alg0, checksumHash, checksumStored);
if( !pgpMemoryEqual( checksumStored, checksum, checksumSize ) )
goto badpass;
}
/* Verify that u = p^-1 mod q is less than q */
if (bnCmp(&u, &q) >= 0)
goto badpass;
/* Verify that u * p == 1 mod q */
if (bnMul(&bn, &p, &u) < 0 || bnMod(&bn, &bn, &q) < 0)
goto nomem;
if (bnCmpQ(&bn, 1) != 0)
goto badpass;
/*
* Okay, we've verified every single value in the secret key,
* against the public key, so it is *definitely* the right
* secret key. Note that the "nomem" case calls bnEnd()
* more than once, but this is guaranteed harmless.
*/
bnEnd(&bn);
if (bnCopy(&sec->s.d, &d) < 0)
goto nomem;
bnEnd(&d);
if (bnCopy(&sec->s.p, &p) < 0)
goto nomem;
bnEnd(&p);
if (bnCopy(&sec->s.q, &q) < 0)
goto nomem;
bnEnd(&q);
if (bnCopy(&sec->s.u, &u) < 0)
goto nomem;
bnEnd(&u);
i = 1; /* Decrypted! */
sec->locked = 0;
if (cfb)
PGPFreeCFBContext (cfb);
return 1; /* Decrypted */
nomem:
i = kPGPError_OutOfMemory;
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;
}
i = 0; /* Incorrect passphrase */
goto done;
done:
if (cfb)
PGPFreeCFBContext (cfb);
if( checksumHash )
PGPFreeHashContext( checksumHash );
bnEnd(&bn);
bnEnd(&u);
bnEnd(&q);
bnEnd(&p);
bnEnd(&d);
return i;
}
/*
* Relock the key.
*/
static void
rsaLock(PGPSecKey *seckey)
{
RSAsecPlus *sec = (RSAsecPlus *)seckey->priv;
ASSERTRSA(seckey->pkAlg);
sec->locked = 1;
/* bnEnd is documented as also doing a bnBegin */
bnEnd(&sec->s.d);
bnEnd(&sec->s.p);
bnEnd(&sec->s.q);
bnEnd(&sec->s.u);
}
static PGPSize
rsaSecMaxdecrypted(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
rsaDecrypt(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 */ /* ] [ */
RSAsecPlus *sec = (RSAsecPlus *)seckey->priv;
BigNum bn;
int i, j;
unsigned t;
PGPSize max;
PGPMemoryMgrRef mgr = NULL;
mgr = PGPPeekContextMemoryMgr( seckey->context );
ASSERTRSAENC(seckey->pkAlg);
if (sec->locked) {
i = rsaUnlock(seckey, phrase, plen, FALSE);
if (i <= 0)
return i ? i : kPGPError_KeyIsLocked;
pgpAssert(!sec->locked);
}
if (esklen < 2)
return kPGPError_BadSessionKeySize;
bnBegin(&bn, mgr, TRUE);
i = pgpBnGetFormatted(&bn, esk, esklen, bnBytes(&sec->s.n), format);
if (i <= 0)
return kPGPError_BadSessionKeySize;
max = rsaSecMaxdecrypted(seckey, format);
i = rsaPrivateDecrypt(key, max, &bn, &sec->s);
bnEnd(&bn);
if (i < 0)
return i;
if ((PGPSize)i > max || i < 3)
return kPGPError_CorruptData;
if (format == kPGPPublicKeyMessageFormat_PGP) {
/* 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 kPGPError_CorruptData;
pgpClearMemory(key+i-2, 2);
/* The actual key */
if (keylen)
*keylen = (PGPSize)i-2;
} else {
/* The actual key */
if (keylen)
*keylen = (PGPSize)i;
}
return 0;
#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
rsaSecMaxdecrypted(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
RSAsecPlus const *sec = (RSAsecPlus *)seckey->priv;
PGPSize size;
(void) format;
ASSERTRSAENC(seckey->pkAlg);
size = bnBytes(&sec->s.n);
return size < 3 ? 0 : size-3;
}
/* Return the largest possible PGPESK size for a given key */
static PGPSize
rsaSecMaxesk(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
RSAsecPlus const *sec = (RSAsecPlus *)seckey->priv;
ASSERTRSAENC(seckey->pkAlg);
if (format == kPGPPublicKeyMessageFormat_PGP)
return 2 + bnBytes(&sec->s.n);
else if (format == kPGPPublicKeyMessageFormat_PKCS1 ||
format == kPGPPublicKeyMessageFormat_X509 ||
format == kPGPPublicKeyMessageFormat_IKE)
return bnBytes(&sec->s.n);
pgpAssert(0);
return 0;
}
static PGPSize
rsaSecMaxsig(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
RSAsecPlus const *sec = (RSAsecPlus *)seckey->priv;
ASSERTRSASIG(seckey->pkAlg);
if (format == kPGPPublicKeyMessageFormat_PGP)
return 2 + bnBytes(&sec->s.n);
else if (format == kPGPPublicKeyMessageFormat_PKCS1 ||
format == kPGPPublicKeyMessageFormat_IKE ||
format == kPGPPublicKeyMessageFormat_X509)
return bnBytes(&sec->s.n);
pgpAssert(0);
return 0;
}
static int
rsaSign(PGPSecKey *seckey, PGPHashVTBL const *h, PGPByte const *hash,
PGPByte *sig, PGPSize *siglen, PGPRandomContext const *rc,
PGPPublicKeyMessageFormat format)
{
#if PGP_SIGN_DISABLE /* [ */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -