📄 pgpcfb.c
字号:
const void * salt)
{
PGPError err = kPGPError_NoErr;
PGPValidateCFB( ref );
PGPValidatePtr( salt );
PGPValidateParam( ref->interleave == 1 );
pgpCFBRandCycle( ref, salt);
return( err );
}
/*____________________________________________________________________________
____________________________________________________________________________*/
PGPError
PGPCFBRandomWash(
PGPCFBContextRef ref,
const void * in,
PGPSize bytesIn )
{
PGPError err = kPGPError_NoErr;
PGPValidateCFB( ref );
PGPValidatePtr( in );
PGPValidateParam( ref->interleave == 1 );
pgpCFBRandWash( ref, in, bytesIn );
return( err );
}
#if PRAGMA_MARK_SUPPORTED
#pragma mark --- Internal Routines ---
#endif
PGPCFBContextRef
pgpCFBCreate(
PGPMemoryMgrRef memoryMgr,
PGPCipherVTBL const * vtbl)
{
PGPError err = kPGPError_NoErr;
PGPCFBContextRef newRef = NULL;
PGPSymmetricCipherContextRef symmetricRef = NULL;
pgpAssert( vtbl->blocksize <= PGP_CFB_MAXBLOCKSIZE );
pgpAssert( PGPMemoryMgrIsValid( memoryMgr ) );
err = pgpNewSymmetricCipherContextInternal( memoryMgr,
vtbl->algorithm, vtbl->keysize, &symmetricRef );
if ( IsntPGPError( err ) )
{
err = PGPNewCFBContext( symmetricRef, 1, &newRef );
if ( IsPGPError( err ) )
{
PGPFreeSymmetricCipherContext( symmetricRef );
symmetricRef = NULL;
pgpAssert( IsNull( newRef ) );
}
}
return newRef;
}
/*____________________________________________________________________________
Initialize contexts.
If key is NULL, the current key is not changed.
if iv is NULL, the IV is set to all zero.
____________________________________________________________________________*/
static void
pgpCFBInit(
PGPCFBContext * ref,
void const * key,
void const * iv)
{
PGPSize blockSize;
PGPUInt32 cfbIndex;
PGPByte const * curIV = (const PGPByte *) iv;
PGPGetSymmetricCipherSizes( IndCFB( ref, 0 ).symmetricRef,
NULL, &blockSize );
for ( cfbIndex = 0; cfbIndex < ref->interleave; ++cfbIndex )
{
CFBInterleaveStruct * cfb;
cfb = &IndCFB( ref, cfbIndex);
if ( IsntNull( key ) )
{
PGPInitSymmetricCipher( cfb->symmetricRef, key );
}
pgpClearMemory( cfb->prev, sizeof( cfb->prev ) );
pgpClearMemory( cfb->iv, sizeof( cfb->iv ) );
if ( IsntNull( iv ) )
{
pgpCopyMemory( curIV, &cfb->iv, blockSize );
curIV += blockSize;
}
cfb->bufLeft = 0;
}
ref->curCFBIndex = 0;
ref->bytesInCur = 0;
/* rely on the symmetric cipher to know whether it has been inited */
/* also, iv of NULL is OK, since semantics say that means zeroes */
ref->cfbInited = TRUE;
}
PGPSize
pgpCFBGetKeySize( PGPCFBContextRef ref )
{
PGPSize keySize;
pgpAssert( pgpCFBIsValid( ref ) );
PGPGetSymmetricCipherSizes( IndCFB( ref, 0).symmetricRef, &keySize, NULL );
return( keySize );
}
PGPSize
pgpCFBGetBlockSize( PGPCFBContextRef ref )
{
PGPSize blockSize;
pgpAssert( pgpCFBIsValid( ref ) );
PGPGetSymmetricCipherSizes( IndCFB( ref, 0).symmetricRef,
NULL, &blockSize );
return( blockSize );
}
void
pgpCFBWipe( PGPCFBContextRef ref)
{
PGPUInt32 cfbIndex;
ref->cfbInited = FALSE;
pgpAssert( pgpCFBIsValid( ref ) );
for ( cfbIndex = 0; cfbIndex < ref->interleave; ++cfbIndex )
{
CFBInterleaveStruct * cfb;
cfb = &IndCFB( ref, cfbIndex);
PGPWipeSymmetricCipher( cfb->symmetricRef );
pgpClearMemory( cfb->prev, sizeof( cfb->prev ) );
pgpClearMemory( cfb->iv, sizeof( cfb->iv ) );
cfb->bufLeft = 0;
}
ref->curCFBIndex = 0;
}
/*____________________________________________________________________________
Encrypt a buffer of data, using a block cipher in CFB mode.
here are more compact ways of writing this, but this is
written for speed.
____________________________________________________________________________*/
static void
pgpCFBEncrypt(
CFBInterleaveStruct * cfb,
void const * srcParam,
PGPSize len,
void * destParam )
{
PGPSize blockSize;
PGPSize bufLeft = cfb->bufLeft;
PGPByte * bufptr;
const PGPByte * src;
PGPByte * dest;
PGPGetSymmetricCipherSizes( cfb->symmetricRef, NULL, &blockSize );
bufptr = cfb->iv + blockSize - bufLeft;
src = (const PGPByte *) srcParam;
dest = (PGPByte *) destParam;
/*
* If there are no more bytes to encrypt that there are bytes
* in the buffer, XOR them in and return.
*/
if (len <= bufLeft)
{
cfb->bufLeft = bufLeft - len;
while (len--)
{
*dest++ = *bufptr++ ^= *src++;
}
return;
}
len -= bufLeft;
/* Encrypt the first bufLeft (0 to 7) bytes of the input by XOR
* with the last bufLeft bytes in the iv buffer.
*/
while (bufLeft--)
{
*dest++ = (*bufptr++ ^= *src++);
}
/* Encrypt middle blocks of the input by cranking the cipher,
* XORing blockSize-byte blocks, and repeating until the len
* is blockSize or less.
*/
while (len > blockSize)
{
bufptr = cfb->iv;
pgpCopyMemory( bufptr, cfb->prev, blockSize);
pgpSymmetricCipherEncryptInternal(cfb->symmetricRef, bufptr, bufptr);
bufLeft = blockSize;
len -= blockSize;
do
{
*dest++ = (*bufptr++ ^= *src++);
} while (--bufLeft);
}
/* Do the last 1 to blockSize bytes */
bufptr = cfb->iv;
pgpCopyMemory( bufptr, cfb->prev, blockSize);
pgpSymmetricCipherEncryptInternal(cfb->symmetricRef, bufptr, bufptr);
cfb->bufLeft = blockSize-len;
do
{
*dest++ = (*bufptr++ ^= *src++);
} while (--len);
}
/*____________________________________________________________________________
Decrypt a buffer of data, using a cipher in CFB mode.
There are more compact ways of writing this, but this is
written for speed.
____________________________________________________________________________*/
static void
pgpCFBDecrypt(
CFBInterleaveStruct * cfb,
void const * srcParam,
PGPSize len,
void * destParam )
{
PGPSize blockSize;
PGPSize bufLeft = cfb->bufLeft;
PGPByte * bufptr = NULL;
PGPByte t;
const PGPByte * src = (const PGPByte *) srcParam;
PGPByte * dest = (PGPByte *) destParam;
PGPGetSymmetricCipherSizes( cfb->symmetricRef, NULL, &blockSize );
bufptr = cfb->iv + (blockSize-bufLeft);
if (len <= bufLeft)
{
cfb->bufLeft = bufLeft - len;
while (len--)
{
t = *bufptr;
*dest++ = t ^ (*bufptr++ = *src++);
}
return;
}
len -= bufLeft;
while (bufLeft--)
{
t = *bufptr;
*dest++ = t ^ (*bufptr++ = *src++);
}
while (len > blockSize)
{
bufptr = cfb->iv;
pgpCopyMemory( bufptr, cfb->prev, 8);
pgpSymmetricCipherEncryptInternal(cfb->symmetricRef, bufptr, bufptr);
bufLeft = blockSize;
len -= blockSize;
do
{
t = *bufptr;
*dest++ = t ^ (*bufptr++ = *src++);
} while (--bufLeft);
}
bufptr = cfb->iv;
pgpCopyMemory( bufptr, cfb->prev, blockSize);
pgpSymmetricCipherEncryptInternal(cfb->symmetricRef, bufptr, bufptr);
cfb->bufLeft = blockSize-len;
do
{
t = *bufptr;
*dest++ = t ^ (*bufptr++ = *src++);
} while (--len);
}
/*____________________________________________________________________________
Okay, explanation time:
Phil invented a unique way of doing CFB that's sensitive to semantic
boundaries within the data being encrypted. One way to phrase
CFB en/decryption on an 8-byte block cipher is to say that you XOR
the current 8 bytes with CRYPT(previous 8 bytes of ciphertext).
Normally, you repeat this at 8-byte intervals, but Phil decided to
resync things on the boundaries between elements in the stream being
encrypted.
That is, the last 4 bytes of a 12-byte field are en/decrypted using
the first 4 bytes of CRYPT(previous 8 bytes of ciphertext), but then
the last 4 bytes of that CRYPT computation are thrown away, and the
first 8 bytes of the next field are en/decrypted using
CRYPT(last 8 bytes of ciphertext). This is equivalent to using a
shorter feedback length (if you're familiar with the general CFB
technique) briefly, and doesn't weaken the cipher any (using shorter
CFB lengths makes it stronger, actually), it just makes it a bit unusual.
Anyway, to accomodate this behaviour, every time we do an
encryption of 8 bytes of ciphertext to get 8 bytes of XOR mask,
we remember the ciphertext. Then if we have to resync things
after having processed, say, 2 bytes, we refill the iv buffer
with the last 6 bytes of the old ciphertext followed by the
2 bytes of new ciphertext stored in the front of the iv buffer.
____________________________________________________________________________*/
static void
pgpCFBSync(CFBInterleaveStruct *cfb)
{
PGPSize blockSize;
PGPSize bufLeft = cfb->bufLeft;
(void)PGPGetSymmetricCipherSizes( cfb->symmetricRef, NULL, &blockSize );
if (bufLeft)
{
pgpCopyMemory( cfb->iv, cfb->iv + bufLeft, blockSize - bufLeft);
pgpCopyMemory( cfb->prev + blockSize - bufLeft, cfb->iv, bufLeft);
cfb->bufLeft = 0;
}
}
/*____________________________________________________________________________
Cryptographically strong pseudo-random-number generator.
The design is from Appendix C of ANSI X9.17, "Financial
Institution Key Management (Wholesale)", with IDEA
substituted for triple-DES.
This generates one block (64 bits) of random number R, based on a
key K, an initial vector V, and a time-dependent salt I. I need not
be truly random, but should differ for each block of output R, and
contain as much entropy as possible. In X9.17, I is encrypt(K,time()),
where time() is the most accurate time available. This has I supplied
(from the true random number pool, which is based on similar information)
to the ideaRandCycle function.
The operation of ideaRandCycle is straight from X9.17::
R = encrypt(K, V^I)
V = encrypt(K, R^I)
One thing worth noting is that, given fixed values of K and V,
there is an isomorphism between values of I and values of R.
Thus, R always contains at least as much entropy as is in I;
if I is truly completely random, it does not matter if K and/or V
are known. Thus, if the supplied I (from the true random number pool)
are good, the output of this is good.
Fills in the supplied buffer with up to "count" pseudo-random bytes.
Returns the number of bytes filled in. If less than "count",
pgpCFBRandCycle will need to be called with a new random salt value
before more bytes are available. pgpCFBRandBytes will return 0 until
that is done.
____________________________________________________________________________*/
static PGPSize
pgpCFBRandBytes(
PGPCFBContextRef ref,
PGPSize count,
void * dest )
{
PGPSize bufLeft;
CFBInterleaveStruct * cfb;
pgpAssert( ref->interleave == 1 );
cfb = &IndCFB( ref, 0 );
bufLeft = cfb->bufLeft;
if (count > bufLeft)
count = bufLeft;
cfb->bufLeft = bufLeft - count;
pgpCopyMemory( cfb->prev + pgpCFBGetBlockSize( ref ) - bufLeft,
dest, count);
return count;
}
/*____________________________________________________________________________
Make more random bytes available from ideaRandBytes using the X9.17
algorithm and the supplied random salt. Expects "ref->cipher->blockSize"
bytes of salt.
____________________________________________________________________________*/
static void
pgpCFBRandCycle(
PGPCFBContext * ref,
void const * saltParam)
{
PGPSize i;
PGPSize blockSize = pgpCFBGetBlockSize( ref );
CFBInterleaveStruct * cfb;
const PGPByte * salt = (const PGPByte *) saltParam;
pgpAssert( ref->interleave == 1 );
cfb = &IndCFB( ref, 0 );
pgpAssert( ref->interleave == 1 );
for (i = 0; i < blockSize; i++)
{
cfb->iv[i] ^= salt[i];
}
pgpSymmetricCipherEncryptInternal( cfb->symmetricRef, cfb->iv, cfb->prev);
for (i = 0; i < blockSize; i++)
{
cfb->iv[i] = cfb->prev[i] ^ salt[i];
}
pgpSymmetricCipherEncryptInternal( cfb->symmetricRef, cfb->iv, cfb->iv);
cfb->bufLeft = blockSize;
}
/*____________________________________________________________________________
"Wash" the random number state using an irreversible transformation
based on the input buffer and the previous state.
____________________________________________________________________________*/
static void
pgpCFBRandWash(
PGPCFBContextRef ref,
void const * bufParam,
PGPSize len)
{
PGPSize blockSize = pgpCFBGetBlockSize( ref );
PGPSize i, j;
CFBInterleaveStruct * cfb = NULL;
PGPSymmetricCipherContextRef symmetricRef;
const PGPByte * buf = (const PGPByte *) bufParam;
pgpAssert( ref->interleave == 1 );
cfb = &IndCFB( ref, 0 );
symmetricRef = cfb->symmetricRef;
/* Wash the key (if supported) */
PGPWashSymmetricCipher( symmetricRef, buf, len);
/*
* Wash the IV - a bit ad-hoc, but it works. It's
* basically a CBC-MAC.
*/
for (i = j = 0; i < len; i++)
{
cfb->iv[j] ^= buf[i];
if (++j == blockSize)
{
pgpSymmetricCipherEncryptInternal(symmetricRef, cfb->iv, cfb->iv);
j = 0;
}
}
/* Pad with typical CBC padding indicating the length */
while (j < blockSize)
{
cfb->iv[j++] ^= (PGPByte)len;
}
pgpSymmetricCipherEncryptInternal( symmetricRef, cfb->iv, cfb->iv);
/* Set to empty */
cfb->bufLeft = 0;
}
/*__Editor_settings____
Local Variables:
tab-width: 4
End:
vi: ts=4 sw=4
vim: si
_____________________*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -