📄 pgpelgkey.c
字号:
* then the encryption IV. That's 16 bytes plus the string-to-key
* conversion length.
*/
static int
elgChangeLock(PGPSecKey *seckey, PGPEnv const *env,
PGPRandomContext const *rc, char const *phrase, size_t plen,
PGPStringToKeyType s2ktype)
{
ELGsecPlus *sec = (ELGsecPlus *)seckey->priv;
PGPStringToKey *s2k = NULL; /* Shut up warnings */
PGPCipherVTBL const *cipher = NULL; /* Shut up warnings */
PGPCFBContext *cfb = NULL; /* This is realy needed */
PGPByte *p;
PGPByte key[PGP_CIPHER_MAXKEYSIZE];
int oldf = 0; /* Shut up warnings */
unsigned len;
unsigned checksum;
PGPContextRef context = pgpenvGetContext( env );
ASSERTELG(seckey->pkAlg);
if (sec->locked)
return kPGPError_KeyIsLocked;
len = bnBytes(&sec->s.p) + bnBytes(&sec->s.g) +
bnBytes(&sec->s.y) + bnBytes(&sec->s.x) + 11;
if (phrase) {
s2k = pgpS2Kcreate(env, rc, s2ktype);
if (!s2k)
return kPGPError_OutOfMemory;
cipher = pgpCipherDefaultKey(env);
pgpAssert(cipher);
if (!cipher) {
pgpS2Kdestroy(s2k);
return kPGPError_OutOfMemory;
}
len += cipher->blocksize;
cfb = pgpCFBCreate( PGPGetContextMemoryMgr( context ), cipher);
if (!cfb) {
pgpS2Kdestroy(s2k);
return kPGPError_OutOfMemory;
}
oldf = pgpS2KisOldVers(s2k);
if (!oldf)
len += 1 + s2k->encodelen;
}
if (len > sec->ckalloc) {
PGPError err = kPGPError_NoErr;
if( IsNull( sec->cryptkey ) ) {
sec->cryptkey = (PGPByte *)
pgpContextMemAlloc( sec->context, len,
0 );
if( IsNull( sec->cryptkey ) ) {
err = kPGPError_OutOfMemory;
}
} else {
err = pgpContextMemRealloc( context,
(void **)&sec->cryptkey, len, 0 );
}
if( IsPGPError( err ) ) {
PGPFreeCFBContext(cfb);
pgpS2Kdestroy(s2k);
return err;
}
sec->ckalloc = (size_t)len;
}
sec->cklen = len;
p = sec->cryptkey;
/* Okay, no more errors possible! Start installing data */
p += pgpBnPutPlain(&sec->s.p, p);
p += pgpBnPutPlain(&sec->s.g, p);
p += pgpBnPutPlain(&sec->s.y, p);
/* Encryption parameters */
if (!phrase) {
*p++ = 0; /* Unencrypted */
} else {
if (oldf) {
*p++ = cipher->algorithm;
} else {
*p++ = 255;
*p++ = cipher->algorithm;
memcpy(p, s2k->encoding, s2k->encodelen);
p += s2k->encodelen;
}
/* Create IV */
pgpRandomGetBytes(rc, p, cipher->blocksize);
pgpStringToKey(s2k, phrase, plen, key, cipher->keysize);
PGPInitCFB(cfb, key, p);
pgpS2Kdestroy(s2k);
p += cipher->blocksize;
/* Wipe key *immediately* */
pgpClearMemory( key, cipher->keysize);
}
/* Now install x, encrypted */
checksum = 0;
p += pgpBnPutNew(&sec->s.x, p, cfb, &checksum);
pgpChecksumPutNew(checksum, p, cfb);
p += 2;
pgpAssert((ptrdiff_t)len == p - sec->cryptkey);
if (cfb)
PGPFreeCFBContext(cfb);
return 0; /* Success */
}
static size_t
elgSecBufferLength(PGPSecKey const *seckey)
{
ELGsecPlus const *sec = (ELGsecPlus *)seckey->priv;
return sec->cklen;
}
static void
elgSecToBuffer(PGPSecKey const *seckey, PGPByte *buf)
{
ELGsecPlus const *sec = (ELGsecPlus *)seckey->priv;
memcpy(buf, sec->cryptkey, sec->cklen);
/* Return only algorithm-dependent portion */
}
/* Fill in secret key structure */
static void
elgFillSecKey(PGPSecKey *seckey, ELGsecPlus *sec)
{
seckey->pkAlg = kPGPPublicKeyAlgorithm_ElGamal;
seckey->priv = sec;
seckey->destroy = elgSecDestroy;
seckey->pubkey = elgPubkey;
seckey->islocked = elgIslocked;
seckey->lockingalgorithm = elgLockingAlgorithm;
seckey->s2ktype = elgS2KType;
seckey->convertpassphrase = elgConvertPassphrase;
seckey->unlock = elgUnlock;
seckey->lock = elgLock;
seckey->decrypt = elgDecrypt;
seckey->maxdecrypted = elgSecMaxdecrypted;
seckey->maxesk = elgSecMaxesk;
seckey->maxsig = elgSecMaxsig;
seckey->sign = elgSign;
seckey->changeLock = elgChangeLock;
seckey->bufferLength = elgSecBufferLength;
seckey->toBuffer = elgSecToBuffer;
}
PGPSecKey *
elgSecFromBuf(
PGPContextRef context,
PGPByte const * buf,
size_t size,
PGPError * error)
{
PGPSecKey *seckey;
ELGsecPlus *sec;
PGPByte *cryptk;
PGPError err = kPGPError_OutOfMemory;
PGPMemoryMgrRef mgr = PGPGetContextMemoryMgr( context );
PGPEnv * pgpEnv = pgpContextGetEnvironment( context );
bnInit();
cryptk = (PGPByte *)pgpContextMemAlloc( context,
size, kPGPMemoryMgrFlags_Clear);
if (cryptk) {
sec = (ELGsecPlus *)PGPNewSecureData( mgr, sizeof(*sec), 0 );
if (sec) {
pgpClearMemory( sec, sizeof(*sec) );
sec->context = context;
seckey = (PGPSecKey *) pgpContextMemAlloc( context,
sizeof(*seckey), kPGPMemoryMgrFlags_Clear);
if (seckey) {
seckey->context = context;
memcpy(cryptk, buf, size);
bnBegin(&sec->s.p, mgr, FALSE );
bnBegin(&sec->s.g, mgr, FALSE );
bnBegin(&sec->s.y, mgr, FALSE );
bnBegin(&sec->s.x, mgr, TRUE );
sec->cryptkey = cryptk;
sec->cklen = sec->ckalloc = size;
sec->locked = 1;
/* We only need this to try unlocking... */
seckey->pkAlg = kPGPPublicKeyAlgorithm_ElGamal;
seckey->priv = sec;
if (elgUnlock(seckey, pgpEnv, NULL, 0, FALSE) >= 0) {
elgFillSecKey(seckey, sec);
*error = kPGPError_NoErr;
return seckey; /* Success! */
}
else
{
err = kPGPError_UnknownError;
}
/* Ka-boom. Delete and free everything. */
pgpClearMemory( cryptk, size);
pgpContextMemFree( context, seckey);
}
PGPFreeData( sec ); /* Wipes as it frees */
}
pgpContextMemFree( context, cryptk);
}
*error = err;
return NULL;
}
/* Generate super-strong primes? (Warning: slow!) */
#ifndef ELG_GERMAIN
#define ELG_GERMAIN 0
#endif
/*
* Generate an ELG secret key with prime of the specified number of bits.
* Make callbacks to progress function periodically.
* Secret key is returned in the unlocked form, with no passphrase set.
* fastgen tells us to use canned primes if available.
*
* If ELG_GERMAIN is set to 1, we generate p such that (p-1)/2 is also
* prime. This takes long time. (Pseudoprimality tests take time
* cubic in the number of bits, and the number of tests needed is
* linear in the number of bits, so it's quartic overall. Searching
* for Sophier Germain primes makes it quintic(!), although the constant
* factor improves to make up for a lot of that.
*
* The alternative (which is really just as safe, really) is to generate
* primes which have a large prime factor q about 10 bits shorter than
* the requested length. We will guarantee that 2 is a generator of a
* subgroup with period at least q.
*
* If it is OK, we could speed it up more by generating multiple primes
* qi such that p = 2*k*q1*q2*q3*...*qn + 1, and where each qi is greater
* than 2**160 (or whatever exponent value we are using). Again, once we
* verify that 2's period is > 2k we know it as at least min(qi), which
* should be long enough for prevention of discrete log attacks.
*
* A bit of theory: the average density of primes around n is 1/ln(n),
* so the average gap between primes is ln(n). However, the maximum
* gap is ln(n)^2. The fact that we're searching in steps other than
* 1 doesn't matter - it'll take an average of ln(n) steps.
*
* So to produce a prime of the desired size, we should have p/q at
* least ln(p) and to guarantee it, we need p/q = ln(p)^2. This
* means that we want
* log2(ln(p)) < log2(p/q) < log2(ln(p)^2)
* log2(0.693*log2(p)) < log2(p) - log2(q) < 2 * log2(0.693 * log2(p))
* log2(log2(p)) - 0.529 < log2(p) - log2(q) < 2 * log2(log2(p)) - 1.058
*
* At this point, it's safe to start getting crude, because if we're
* only counting bits, 2^(bits(x)-1) <= x < 2^bits(x) <= 2*x, or
* bits(x)-1 <= log2(x) < bits(x) <= log2(x)+1. Another way of looking
* at all this is that log2(x) is bits(x) - 0.5 +/- 0.5.
* So let's split the difference and use 1.5 * bits(bits(p)) - 1 as the
* difference in bits between p and q.
*
* See the discussion preceding the DSA keygen routine dsaSecGenerate
* in pgpDSAKey.c for an explanation of the rcdummy random number
* generator below. It serves to limit leakage of the state of the
* randpool into the public values generated as part of the key.
*/
PGPSecKey *
elgSecGenerate(
PGPContextRef context,
unsigned bits, PGPBoolean fastgen,
PGPRandomContext const *rc,
int progress(void *arg, int c), void *arg, PGPError *error)
{
PGPSecKey *seckey;
ELGsecPlus *sec;
PGPRandomContext *rcdummy = NULL;
unsigned bits2;
#if !ELG_GERMAIN
unsigned lengthdiff;
BigNum q, h, e;
int i;
#endif
PGPByte dummyseed[ELGDUMMYBITS/8];
PGPMemoryMgrRef mgr = PGPGetContextMemoryMgr( context );
PGPEnv * pgpEnv = pgpContextGetEnvironment( context );
*error = kPGPError_NoErr;
/* Initialize local pointers (simplify cleanup below) */
seckey = NULL;
sec = NULL;
/* Allocate data structures */
seckey = (PGPSecKey *)pgpContextMemAlloc( context,
sizeof(*seckey), kPGPMemoryMgrFlags_Clear);
if (!seckey)
goto memerror;
seckey->context = context;
sec = (ELGsecPlus *)PGPNewSecureData( mgr, sizeof(*sec), 0 );
sec->context = context;
if (!sec)
goto memerror;
bnBegin(&sec->s.p, mgr, FALSE );
bnBegin(&sec->s.g, mgr, FALSE );
bnBegin(&sec->s.y, mgr, FALSE );
bnBegin(&sec->s.x, mgr, TRUE );
/* Use a fixed prime and generator if in our table */
if (fastgen) {
PGPByte const *fixedp, *fixedg;
size_t fixedplen, fixedglen;
if (pgpElGfixed (bits, &fixedp, &fixedplen, &fixedg, &fixedglen) > 0) {
if (progress != NULL)
progress(arg, ' ');
bnInsertBigBytes (&sec->s.p, fixedp, 0, fixedplen);
if (progress != NULL)
progress(arg, ' ');
bnInsertBigBytes (&sec->s.g, fixedg, 0, fixedglen);
goto choose_x;
}
}
/* Set up local random number generator for p and q */
rcdummy = pgpPseudoRandomCreate ( rc->context );
if (!rcdummy)
goto memerror;
pgpRandomGetBytes (rc, dummyseed, sizeof(dummyseed));
pgpRandomAddBytes (rcdummy, dummyseed, sizeof(dummyseed));
#if ELG_GERMAIN
/* Strong ("sophie germain") prime search */
/* Find p - choose a starting place */
if (pgpBnGenRand(&sec->s.p, rcdummy, bits, 0xC0, 3, bits-4) < 0)
goto nomem;
/* And search for a prime */
if (bnGermainPrimeGen(&sec->s.p, 1, progress, arg) < 0)
goto nomem;
/* We have chosen p so 2 is a good choice for generator */
if (bnSetQ(&sec->s.g, 2) < 0)
goto nomem;
/* Choose a random x of reasonable size as secret key */
expbits = elgExpBits(bits);
if (pgpBnGenRand(&sec->s.x, rc, expbits, 0, 0, expbits) < 0)
goto nomem;
/* And calculate g**x as public key */
if (bnTwoExpMod(&sec->s.y, &sec->s.x, &sec->s.p) < 0)
goto nomem;
#else /* !ELG_GERMAIN - the faster version */
bnBegin(&q, mgr, FALSE );
bnBegin(&h, mgr, FALSE );
bnBegin(&e, mgr, FALSE );
/*
* Choose a random starting place for q, a bit less than p.
* (See function header comment above for the theory behind this.)
*/
lengthdiff = 0;
for (bits2 = bits; bits2; bits2 >>= 1)
lengthdiff++;
lengthdiff += (lengthdiff+1)/2;
bits2 = bits - lengthdiff;
if (pgpBnGenRand(&q, rcdummy, bits2, 0x80, 1, bits2-2) < 0)
goto nomem;
/* And search for a prime */
i = bnPrimeGen(&q, NULL, progress, arg, 0);
if (i < 0)
goto nomem;
if (progress != NULL)
progress(arg, ' ');
/* ...and now a random start for p */
(void)bnSetQ(&sec->s.p, 0);
if (pgpBnGenRand(&sec->s.p, rcdummy, bits, 0xC0, 1, bits-bits2-3) < 0)
goto nomem;
/* Double q to make it a suitable stride */
if (bnLShift(&q, 1) < 0)
goto nomem;
/* Set p = p - (p mod 2q) + 1, i.e. congruent to 1 mod 2q */
if (bnMod(&h, &sec->s.p, &q) < 0)
goto nomem;
if (bnSub(&sec->s.p, &h) < 0 || bnAddQ(&sec->s.p, 1) < 0)
goto nomem;
/* This loop is very rarely executed */
retry:
/* And search for a prime, 1+2kq for some k */
i = bnPrimeGenStrong(&sec->s.p, &q, progress, arg);
if (i < 0)
goto nomem;
if (progress != NULL)
progress(arg, ' ');
/* Now check two as g: first, find (p-1)/q = 2*((p-1)/(2*q)) */
if (bnDivMod(&e, &h, &sec->s.p, &q) < 0 || bnLShift(&e, 1) < 0)
goto nomem;
/* e is now (p-1)/q, and h is the remainder (one!) */
pgpAssert (bnBits(&h) == 1);
/*
* Make sure 2**((p-1)/q) mod p is not small. This should imply
* that the period of 2 as a generator has at least q as a factor,
* meaning it is very big.
* With (p-1)/q as small as it is, the chances are *excellent*
* that it will work. If (p-1)/q were less than a few hundred
* 2**((p-1)/q) would be less than p and coundn't possibly be 1.
*/
if (bnTwoExpMod(&h, &e, &sec->s.p) < 0)
goto nomem;
if (bnBits(&h) < 2) {
if (progress != NULL)
progress(arg, ' ');
goto retry;
}
bnEnd(&e);
bnEnd(&h);
bnEnd(&q);
#endif
/* We have done things so 2 is a good choice for generator */
if (bnSetQ(&sec->s.g, 2) < 0)
goto nomem;
/* May get here directly from above if fixed primes are used */
choose_x:
/* Choose a random x of reasonable size as secret key */
bits2 = pgpDiscreteLogExponentBits(bits)*3/2;
if (pgpBnGenRand(&sec->s.x, rc, bits2, 0, 0, bits2) < 0)
goto nomem;
/* And calculate g**x as public key */
if (bnExpMod(&sec->s.y, &sec->s.g, &sec->s.x, &sec->s.p) < 0)
goto nomem;
/* And that's it... success! */
/* Fill in structs */
sec->cryptkey = NULL;
sec->ckalloc = sec->cklen = 0;
sec->locked = 0;
elgFillSecKey(seckey, sec);
/* Fill in cryptkey structure, unencrypted */
elgChangeLock (seckey, pgpEnv, NULL, NULL, 0, kPGPStringToKey_Simple);
goto done;
nomem:
#if !ELG_GERMAIN
bnEnd(&e);
bnEnd(&h);
bnEnd(&q);
#endif
bnEnd(&sec->s.p);
bnEnd(&sec->s.g);
bnEnd(&sec->s.y);
bnEnd(&sec->s.x);
/* Fall through */
memerror:
if ( IsntNull( seckey ) )
pgpContextMemFree( context, seckey);
if ( IsntNull( sec ) )
PGPFreeData( sec ); /* Wipes as it frees */
seckey = NULL;
*error = kPGPError_OutOfMemory;
done:
if (rcdummy)
{
pgpRandomDestroy (rcdummy);
}
return seckey;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -