📄 pgpdsakey.c
字号:
* Yes, there *is* a reason that this is a function and not a variable.
* On a hardware device with an automatic timeout,
* it actually might need to do some work to find out.
*/
static int
dsaIslocked(PGPSecKey const *seckey)
{
DSAsecPlus const *sec = (DSAsecPlus *)seckey->priv;
ASSERTDSA(seckey->pkAlg);
return sec->locked;
}
/*
* Return the algorithm and (symmetric) key size used for locking/unlocking
* the secret key.
*/
static PGPError
dsaLockingAlgorithm(
PGPSecKey const *seckey,
PGPCipherAlgorithm *pAlg,
PGPSize *pAlgKeySize
)
{
DSAsecPlus *sec = (DSAsecPlus *)seckey->priv;
PGPCipherVTBL const *cipher;
PGPByte alg;
int i;
ASSERTDSA(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, 4, NULL, 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
dsaS2KType(
PGPSecKey const *seckey,
PGPStringToKeyType *s2kType
)
{
DSAsecPlus *sec = (DSAsecPlus *)seckey->priv;
PGPByte alg;
int i;
ASSERTDSA(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, 4, NULL, 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
dsaConvertPassphrase(PGPSecKey *seckey, PGPEnv const *env,
char const *phrase, PGPSize plen, PGPByte *outbuf)
{
DSAsecPlus *sec = (DSAsecPlus *)seckey->priv;
PGPStringToKey *s2k;
PGPByte alg;
PGPBoolean hasS2K;
PGPCipherVTBL const *cipher;
int i;
ASSERTDSA(seckey->pkAlg);
pgpAssert (IsntNull( outbuf ) );
/* Check packet for basic consistency */
i = pgpBnParse(sec->cryptkey, sec->cklen, 4, 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 DSA-specific part is:
*
* 0 2+u MPI for prime p
* 2+u 2+v MPI for order q
* 4+u+v 2+w MPI for generator g
* 6+u+v+w 2+x MPI for public key y
* 8+u+v+w+x 1 Encryption algorithm (0 for none, 1 for IDEA)
* 9+u+v+w+x t Encryption IV: 0 or 8 bytes
* 9+t+u+v+w+x 2+y MPI for x (discrete log of public key)
* 11+t+u+v+w+x+y 2 Checksum
* 13+t+u+v+w+x+y
*
* Actually, that's the old-style, if pgpS2KoldVers is true.
* If it's false, the algoruthm is 255, and is followed by the
* algorithm, then the (varaible-length, self-delimiting)
* string-to-key descriptor.
*/
static int
dsaUnlock(PGPSecKey *seckey, PGPEnv const *env,
char const *phrase, size_t plen, PGPBoolean hashedPhrase)
{
DSAsecPlus *sec = (DSAsecPlus *)seckey->priv;
BigNum x;
PGPCFBContext *cfb = NULL;
unsigned v;
unsigned alg;
unsigned checksum;
int i;
PGPMemoryMgrRef mgr = NULL;
mgr = PGPGetContextMemoryMgr( seckey->context );
bnBegin(&x, mgr, TRUE);
ASSERTDSA(seckey->pkAlg);
/* Check packet for basic consistency */
i = pgpBnParse(sec->cryptkey, sec->cklen, 4, &v, NULL, NULL, NULL);
if (i <= 0)
goto fail;
/* 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.q, 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 != !sec->cryptkey[v])
goto badpass;
i = pgpCipherSetup(sec->cryptkey + v, sec->cklen - v, phrase, plen,
hashedPhrase, env, &cfb);
if (i < 0)
goto done;
v += i;
checksum = 0;
i = pgpBnGetNew(&x, sec->cryptkey + v, sec->cklen - v, cfb, &checksum);
if (i <= 0)
goto badpass;
v += i;
if (bnCmp(&x, &sec->s.q) >= 0)
goto badpass; /* Wrong passphrase: x must be < q */
/* Check that we ended in the right place */
if (sec->cklen - v != 2) {
i = kPGPError_KEY_LONG;
goto fail;
}
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;
i = 1; /* Decrypted! */
sec->locked = 0;
goto done;
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
dsaLock(PGPSecKey *seckey)
{
DSAsecPlus *sec = (DSAsecPlus *)seckey->priv;
ASSERTDSA(seckey->pkAlg);
sec->locked = 1;
/* bnEnd is documented as also doing a bnBegin */
bnEnd(&sec->s.x);
}
/*
* 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
dsaDecrypt(PGPSecKey *seckey, PGPEnv const *env,
PGPByte const *esk, size_t esklen,
PGPByte *key, size_t *keylen, char const *phrase, size_t plen,
PGPPublicKeyMessageFormat format)
{
(void)seckey;
(void)env;
(void)esk;
(void)esklen;
(void)key;
(void)keylen;
(void)phrase;
(void)plen;
(void)format;
return kPGPError_PublicKeyUnimplemented;
}
/*
* Return the size of the buffer needed, worst-case, for the decrypted
* output.
*/
static size_t
dsaSecMaxdecrypted(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
(void)seckey;
(void)format;
return kPGPError_PublicKeyUnimplemented;
}
static size_t
dsaSecMaxesk(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
(void)seckey;
(void)format;
return kPGPError_PublicKeyUnimplemented;
}
static size_t
dsaSecMaxsig(PGPSecKey const *seckey, PGPPublicKeyMessageFormat format)
{
DSAsecPlus const *sec = (DSAsecPlus *)seckey->priv;
ASSERTDSA(seckey->pkAlg);
if (format == kPGPPublicKeyMessageFormat_PGP)
return 2*( 2 + bnBytes(&sec->s.q) );
else if (format == kPGPPublicKeyMessageFormat_PKCS1 ||
format == kPGPPublicKeyMessageFormat_IKE)
return 2*( bnBytes(&sec->s.q) );
else if (format == kPGPPublicKeyMessageFormat_X509) {
/* SEQUENCE, length, INT, INT */
PGPUInt32 len;
PGPUInt32 qbytes = bnBytes(&sec->s.q);
len = 2*(pgpBnX509LenLen(qbytes+1) + 1 + qbytes+1);
return 1 + pgpBnX509LenLen(len) + len;
}
pgpAssert(0);
return 0;
}
/*
* Helper function: seed a RandomContext from a BigNum.
* Be very sure to leave nothing in memory!
*/
static void
pgpRandomBnSeed(PGPRandomContext const *rc, BigNum const *bn)
{
PGPByte buf[32]; /* Big enough for 99.9% of all keys */
unsigned bytes = (bnBits(bn) + 7)/8;
unsigned off = 0;
while (bytes > sizeof(buf)) {
bnExtractLittleBytes(bn, buf, off, sizeof(buf));
pgpRandomAddBytes(rc, buf, sizeof(buf));
bytes -= sizeof(buf);
off += sizeof(buf);
}
bnExtractLittleBytes(bn, buf, off, bytes);
pgpRandomAddBytes(rc, buf, bytes);
pgpClearMemory( buf, sizeof(buf));
}
static int
dsaSign(PGPSecKey *seckey, PGPHashVTBL const *h, PGPByte const *hash,
PGPByte *sig, size_t *siglen, PGPRandomContext const *rc,
PGPPublicKeyMessageFormat format)
{
#if PGP_SIGN_DISABLE /* [ */
(void)seckey;
(void)h;
(void)hash;
(void)sig;
(void)siglen;
(void)rc;
(void)format;
return kPGPError_FeatureNotAvailable;
#else /* PGP_SIGN_DISABLE */ /* ] [ */
DSAsecPlus *sec = (DSAsecPlus *)seckey->priv;
BigNum r, s, bn, k;
unsigned t;
unsigned qbits;
unsigned qbytes;
int i;
PGPRandomContext *rc2;
PGPMemoryMgrRef mgr = NULL;
mgr = PGPGetContextMemoryMgr( seckey->context );
(void)h;
/* We don't need this argument, although other algorithms may... */
(void)format;
ASSERTDSA(seckey->pkAlg);
/* Allow generalizations of SHA as long as they are big enough */
#if 0
pgpAssert(h->algorithm == kPGPHashAlgorithm_SHA);
#else
pgpAssert(h->hashsize*8 >= bnBits(&sec->s.q));
/* Make sure that q is the right size of we are using regular SHA hash */
pgpAssert( ! (h->algorithm == kPGPHashAlgorithm_SHA
&& bnBits(&sec->s.q) != h->hashsize*8) );
#endif
if (sec->locked)
return kPGPError_KeyIsLocked;
/*
* DSA requires a secret k. This k is *very* important
* to keep secret. Consider, the DSA signing equations are:
* r = (g^k mod p) mod q, and
* s = k^-1 * (H(m) + x*r) mod q,
* so if you know k (and, the signature r, s and H), then
* x = r^-1 * (k*s - H(m))
* If we ever pick two k values the same, then
* r = (g^k mod p) mod q is the same for both signatures, and
* s1 = k^-1 * (H1 + x * r)
* s2 = k^-1 * (H2 + x * r)
* k = (H1-H2) / (s1-s2)
* and proceed from there.
*
* So we need to make *very* sure there's no problem. To make
* sure, we add a layer on top of the passed-in RNG. We assume
* the passed-in RNG is good enough to never repeat (not a
* difficult task), and apply an additional X9.17 generator on
* top of that, seeded with the secret x, which is destroyed
* before leaving this function.
*
* In addition, we add entropy from the hash to the original RNG.
* This will prevent us from using the same k value twice if the
* messages are different.
*/
pgpRandomAddBytes(rc, hash, bnBytes(&sec->s.q));
rc2 = pgpRandomCreateX9_17( rc->context, kPGPCipherAlgorithm_CAST5, rc);
if (!rc2)
return kPGPError_OutOfMemory;
pgpRandomBnSeed(rc2, &sec->s.x);
/*
* Of these values, only k is inherently sensitive, but others may
* hold some intermediate results we would prefer not to have leaked.
* So mark all as sensitive.
*/
bnBegin(&r, mgr, TRUE );
bnBegin(&s, mgr, TRUE );
bnBegin(&bn, mgr, TRUE );
bnBegin(&k, mgr, TRUE );
/*
* Choose the random k value to be used for this signature.
* Make it a bit bigger than q so it is fairly uniform mod q.
*/
qbits = bnBits(&sec->s.q);
qbytes = bnBytes(&sec->s.q);
if (pgpBnGenRand(&k, rc2, qbits+8, 0, 1, qbits) < 0 ||
bnMod(&k, &k, &sec->s.q) < 0)
goto nomem;
/* Raise g to k power mod p then mod q to get r */
if (bnExpMod(&r, &sec->s.g, &k, &sec->s.p) < 0 ||
bnMod(&r, &r, &sec->s.q) < 0)
goto nomem;
/* r*x mod q into s */
if (bnMul(&s, &r, &sec->s.x) < 0 ||
bnMod(&s, &s, &sec->s.q) < 0)
goto nomem;
/* Pack message hash M into buffer bn */
if (bnInsertBigBytes(&bn, hash, 0, bnBytes(&sec->s.q)) < 0)
goto nomem;
if (bnMod(&bn, &bn, &sec->s.q) < 0)
goto nomem;
/* Add into s */
if (bnAdd(&s, &bn) < 0 ||
bnMod(&s, &s, &sec->s.q) < 0)
goto nomem;
/* Divide by k, mod q (k inverse held in bn) */
if (bnInv(&bn, &k, &sec->s.q) < 0 ||
bnMul(&s, &s, &bn) < 0 ||
bnMod(&s, &s, &sec->s.q) < 0)
goto nomem;
/* That's it, now to pack r and then s into the buffer */
t = 0;
if (format == kPGPPublicKeyMessageFormat_X509) {
/* Put in SEQUENCE header for 509 sig data */
PGPUInt32 len_seq, lenlen_seq;
/* Count size of sequence, counting a 0 byte if hi bit is set */
if (8*qbytes == bnBits(&r))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -