📄 pgpelgkey.c
字号:
* 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 255 then algorithm value */
if (alg == 255)
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 == 255) {
/* New style has 255 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 == 255);
/* 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, env, sec->cryptkey+i, sec->cklen-i);
} else {
s2k = pgpS2Ksimple(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, PGPEnv const *env,
char const *phrase, size_t plen, PGPBoolean hashedPhrase)
{
ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;
BigNum x;
PGPCFBContext *cfb = NULL;
unsigned v;
unsigned alg;
unsigned checksum;
int i;
PGPMemoryMgrRef mgr = NULL;
mgr = PGPGetContextMemoryMgr( seckey->context );
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,
hashedPhrase, env, &cfb);
if (i < 0)
goto done;
v += i;
checksum = 0;
bnBegin(&x, mgr, TRUE );
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 = kPGPError_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)
PGPFreeCFBContext(cfb);
return 1; /* Decrypted */
nomem:
i = kPGPError_OutOfMemory;
goto done;
fail:
if (!i)
i = kPGPError_KeyPacketTruncated;
goto done;
badpass:
i = 0; /* Incorrect passphrase */
goto done;
done:
bnEnd(&x);
if (cfb)
PGPFreeCFBContext(cfb);
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 size_t
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, PGPEnv const *env,
PGPByte const *esk, size_t esklen,
PGPByte *key, size_t *keylen,
char const *phrase, size_t plen,
PGPPublicKeyMessageFormat format)
{
#if PGP_DECRYPT_DISABLE /* [ */
(void)seckey;
(void)env;
(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;
size_t len;
size_t off;
PGPMemoryMgrRef mgr = NULL;
mgr = PGPGetContextMemoryMgr( seckey->context );
ASSERTELG(seckey->pkAlg);
if (sec->locked) {
i = elgUnlock(seckey, env, 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;
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 = (size_t)i-2;
} else {
if( keylen)
*keylen = (size_t)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 size_t
elgSecMaxdecrypted(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
ELGsecPlus const *sec = (ELGsecPlus *)seckey->priv;
size_t size;
ASSERTELG(seckey->pkAlg);
(void) format;
size = bnBytes(&sec->s.p);
return size < 3 ? 0 : size-3;
}
static size_t
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) );
pgpAssert(0);
return 0;
}
static size_t
elgSecMaxsig(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
(void)seckey;
(void)format;
return kPGPError_PublicKeyUnimplemented;
}
static int
elgSign(PGPSecKey *seckey, PGPHashVTBL const *h, PGPByte const *hash,
PGPByte *sig, size_t *siglen, PGPRandomContext const *rc,
PGPPublicKeyMessageFormat format)
{
(void)seckey;
(void)h;
(void)hash;
(void)sig;
(void)siglen;
(void)rc;
(void)format;
return kPGPError_PublicKeyUnimplemented;
}
/*
* 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 (=kPGPPublicKeyAlgorithm_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
* 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
*
* 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),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -