📄 ctx_elg.c
字号:
BN_mod_mul( s, s, kInv, phi_p, /* s = ( s * k^-1 ) mod phi( p ) */
pkcInfo->bnCTX );
/* Encode the result as a DL data block */
length = encodeDLValues( buffer, r, s );
return( ( status == -1 ) ? CRYPT_ERROR_FAILED : length );
}
/* Signature check a single block of data */
static int sigCheck( CONTEXT_INFO *contextInfoPtr, BYTE *buffer, int noBytes )
{
PKC_INFO *pkcInfo = &contextInfoPtr->ctxPKC;
BIGNUM *p = &pkcInfo->dlpParam_p, *g = &pkcInfo->dlpParam_g;
BIGNUM *y = &pkcInfo->dlpParam_y;
BIGNUM *r = &pkcInfo->tmp1, *s = &pkcInfo->tmp1;
int status;
/* Decode the values from a DL data block and make sure that r and s are
valid */
status = decodeDLValues( buffer + ELGAMAL_SIGPART_SIZE, noBytes, &r, &s );
if( cryptStatusError( status ) )
return( status );
/* Verify that 0 < r < p. If this check isn't done, an adversary can
forge signatures given one existing valid signature for a key */
if( BN_is_zero( r ) || BN_cmp( r, p ) >= 0 )
status = CRYPT_ERROR_SIGNATURE;
else
{
BIGNUM *hash, *u1, *u2;
hash = BN_new();
u1 = BN_new();
u2 = BN_new();
BN_bin2bn( buffer, ELGAMAL_SIGPART_SIZE, hash );
/* u1 = ( y^r * r^s ) mod p */
BN_mod_exp_mont( u1, y, r, p, /* y' = ( y^r ) mod p */
pkcInfo->bnCTX, &pkcInfo->dlpParam_mont_p );
BN_mod_exp_mont( r, r, s, p, /* r' = ( r^s ) mod p */
pkcInfo->bnCTX, &pkcInfo->dlpParam_mont_p );
BN_mod_mul_mont( u1, u1, r, p, /* u1 = ( y' * r' ) mod p */
pkcInfo->bnCTX, &pkcInfo->dlpParam_mont_p );
/* u2 = g^hash mod p */
BN_mod_exp_mont( u2, g, hash, p, pkcInfo->bnCTX,
&pkcInfo->dlpParam_mont_p );
/* if u1 == u2, signature is good */
if( BN_cmp( u1, u2 ) && cryptStatusOK( status ) )
status = CRYPT_ERROR_SIGNATURE;
BN_clear_free( hash );
BN_clear_free( u2 );
BN_clear_free( u1 );
}
return( status );
}
#endif /* 0 */
/****************************************************************************
* *
* Encrypt/Decrypt a Data Block *
* *
****************************************************************************/
/* Encrypt a single block of data. We have to append the distinguisher 'Fn'
to the name since some systems already have 'encrypt' and 'decrypt' in
their standard headers */
static int encryptFn( CONTEXT_INFO *contextInfoPtr, BYTE *buffer, int noBytes )
{
PKC_INFO *pkcInfo = contextInfoPtr->ctxPKC;
DLP_PARAMS *dlpParams = ( DLP_PARAMS * ) buffer;
BIGNUM *p = &pkcInfo->dlpParam_p, *g = &pkcInfo->dlpParam_g;
BIGNUM *y = &pkcInfo->dlpParam_y;
BIGNUM *tmp = &pkcInfo->tmp1, *k = &pkcInfo->tmp2;
BIGNUM *r = &pkcInfo->tmp3, *s = &pkcInfo->dlpTmp1;
BIGNUM *phi_p = &pkcInfo->dlpTmp2;
const int length = bitsToBytes( pkcInfo->keySizeBits );
int i, bnStatus = BN_STATUS, status;
assert( noBytes == sizeof( DLP_PARAMS ) );
assert( dlpParams->inParam1 != NULL && dlpParams->inLen1 == length );
assert( dlpParams->inParam2 == NULL && \
( dlpParams->inLen2 == 0 || dlpParams->inLen2 == -999 ) );
assert( dlpParams->outParam != NULL && \
dlpParams->outLen >= ( 2 + length ) * 2 );
/* Make sure that we're not being fed suspiciously short data
quantities */
for( i = 0; i < length; i++ )
if( buffer[ i ] )
break;
if( length - i < 56 )
return( CRYPT_ERROR_BADDATA );
/* Generate the secret random value k. During the initial self-test
the random data pool may not exist yet, and may in fact never exist in
a satisfactory condition if there isn't enough randomness present in
the system to generate cryptographically strong random numbers. To
bypass this problem, if the caller passes in a second length parameter
of -999, we know that it's an internal self-test call and use a fixed
bit pattern for k that avoids having to call generateBignum(). This
is a somewhat ugly use of 'magic numbers', but it's safe because this
function can only be called internally, so all we need to trap is
accidental use of the parameter which is normally unused */
if( dlpParams->inLen2 == -999 )
BN_bin2bn( ( BYTE * ) kRandomVal, length, k );
else
{
/* Generate the random value k, with the same 32-bit adjustment used
in the DSA code to avoid bias in the output (the only real
difference is that we eventually reduce it mode phi(p) rather than
mod q) */
status = generateBignum( k, bytesToBits( length ) + 32, 0x80, 0 );
if( cryptStatusError( status ) )
return( status );
}
/* Generate phi( p ) and use it to get k, k < p-1 and k relatively prime
to p-1. Since (p-1)/2 is prime, the initial choice for k will be
divisible by (p-1)/2 with probability 2/(p-1), so we'll do at most two
gcd operations with very high probability. A k of (p-3)/2 will be
chosen with probability 3/(p-1), and all other numbers from 1 to p-1
will be chosen with probability 2/(p-1), giving a nearly uniform
distribution of exponents */
CKPTR( BN_copy( phi_p, p ) );
CK( BN_sub_word( phi_p, 1 ) ); /* phi( p ) = p - 1 */
CK( BN_mod( k, k, phi_p, /* Reduce k to the correct range */
pkcInfo->bnCTX ) );
CK( BN_gcd( s, k, phi_p, pkcInfo->bnCTX ) );
while( bnStatusOK( bnStatus ) && !BN_is_one( s ) )
{
CK( BN_sub_word( k, 1 ) );
CK( BN_gcd( s, k, phi_p, pkcInfo->bnCTX ) );
}
if( bnStatusError( bnStatus ) )
return( getBnStatus( bnStatus ) );
/* Move the input data into a bignum */
BN_bin2bn( ( BYTE * ) dlpParams->inParam1, length, tmp );
/* s = ( y^k * M ) mod p */
CK( BN_mod_exp_mont( r, y, k, p, /* y' = y^k mod p */
pkcInfo->bnCTX, &pkcInfo->dlpParam_mont_p ) );
CK( BN_mod_mul( s, r, tmp, p, /* s = y'M mod p */
pkcInfo->bnCTX ) );
/* r = g^k mod p */
CK( BN_mod_exp_mont( r, g, k, p, pkcInfo->bnCTX,
&pkcInfo->dlpParam_mont_p ) );
if( bnStatusError( bnStatus ) )
return( getBnStatus( bnStatus ) );
/* Encode the result as a DL data block */
status = encodeDLValues( dlpParams->outParam, dlpParams->outLen, r, s,
dlpParams->formatType );
if( !cryptStatusError( status ) )
{
dlpParams->outLen = status;
status = CRYPT_OK; /* encodeDLValues() returns a byte count */
}
return( status );
}
/* Decrypt a single block of data */
static int decryptFn( CONTEXT_INFO *contextInfoPtr, BYTE *buffer, int noBytes )
{
PKC_INFO *pkcInfo = contextInfoPtr->ctxPKC;
DLP_PARAMS *dlpParams = ( DLP_PARAMS * ) buffer;
BIGNUM *p = &pkcInfo->dlpParam_p, *x = &pkcInfo->dlpParam_x;
BIGNUM *r = &pkcInfo->tmp1, *s = &pkcInfo->tmp2, *tmp = &pkcInfo->tmp3;
const int length = bitsToBytes( pkcInfo->keySizeBits );
int bnStatus = BN_STATUS, status;
assert( noBytes == sizeof( DLP_PARAMS ) );
assert( dlpParams->inParam1 != NULL && \
dlpParams->inLen1 >= ( 2 + ( length - 1 ) ) * 2 );
assert( dlpParams->inParam2 == NULL && dlpParams->inLen2 == 0 );
assert( dlpParams->outParam != NULL && dlpParams->outLen >= length );
/* Decode the values from a DL data block and make sure that r and s are
valid */
status = decodeDLValues( dlpParams->inParam1, dlpParams->inLen1, &r, &s,
dlpParams->formatType );
if( cryptStatusError( status ) )
return( status );
if( BN_cmp( r, p ) >= 0 || BN_cmp( s, p ) >= 0 )
return( CRYPT_ERROR_BADDATA );
/* M = ( s / ( r^x ) ) mod p */
CK( BN_mod_exp_mont( r, r, x, p, /* r' = r^x */
pkcInfo->bnCTX, &pkcInfo->dlpParam_mont_p ) );
CKPTR( BN_mod_inverse( tmp, r, p, /* r'' = r'^-1 */
pkcInfo->bnCTX ) );
CK( BN_mod_mul( s, s, tmp, p, /* s = s * r'^-1 mod p */
pkcInfo->bnCTX ) );
if( bnStatusError( bnStatus ) )
return( getBnStatus( bnStatus ) );
/* Copy the result to the output. Since the bignum code performs
leading-zero truncation, we have to adjust where we copy the
result to in the buffer to take into account extra zero bytes
that aren't extracted from the bignum */
memset( dlpParams->outParam, 0, 16 );
BN_bn2bin( s, dlpParams->outParam + ( length - BN_num_bytes( s ) ) );
dlpParams->outLen = length;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Key Management *
* *
****************************************************************************/
/* Load key components into an encryption context */
static int initKey( CONTEXT_INFO *contextInfoPtr, const void *key,
const int keyLength )
{
PKC_INFO *pkcInfo = contextInfoPtr->ctxPKC;
int status;
#ifndef USE_FIPS140
/* Load the key component from the external representation into the
internal bignums unless we're doing an internal load */
if( key != NULL )
{
const CRYPT_PKCINFO_DLP *egKey = ( CRYPT_PKCINFO_DLP * ) key;
/* Load the key components into the bignums */
contextInfoPtr->flags |= ( egKey->isPublicKey ) ? \
CONTEXT_ISPUBLICKEY : CONTEXT_ISPRIVATEKEY;
BN_bin2bn( egKey->p, bitsToBytes( egKey->pLen ),
&pkcInfo->dlpParam_p );
BN_bin2bn( egKey->g, bitsToBytes( egKey->gLen ),
&pkcInfo->dlpParam_g );
BN_bin2bn( egKey->q, bitsToBytes( egKey->qLen ),
&pkcInfo->dlpParam_q );
BN_bin2bn( egKey->y, bitsToBytes( egKey->yLen ),
&pkcInfo->dlpParam_y );
if( !egKey->isPublicKey )
BN_bin2bn( egKey->x, bitsToBytes( egKey->xLen ),
&pkcInfo->dlpParam_x );
contextInfoPtr->flags |= CONTEXT_PBO;
}
#endif /* USE_FIPS140 */
/* Complete the key checking and setup */
status = initDLPkey( contextInfoPtr, FALSE );
if( cryptStatusOK( status ) )
/* PGP Elgamal keys don't follow X9.42 and are effectively PKCS #3
keys, so if the key is being instantiated from PGP key data and
doesn't have a q parameter, we mark it as a PKCS #3 key to
ensure that it doesn't fail the validity check for q != 0 */
status = checkDLPkey( contextInfoPtr,
( key == NULL && pkcInfo->openPgpKeyIDSet && \
BN_is_zero( &pkcInfo->dlpParam_q ) ) ? \
TRUE : FALSE );
if( cryptStatusOK( status ) )
status = calculateKeyID( contextInfoPtr );
return( status );
}
/* Generate a key into an encryption context */
static int generateKey( CONTEXT_INFO *contextInfoPtr, const int keySizeBits )
{
int status;
status = generateDLPkey( contextInfoPtr, keySizeBits, CRYPT_USE_DEFAULT,
TRUE );
if( cryptStatusOK( status ) &&
#ifndef USE_FIPS140
( contextInfoPtr->flags & CONTEXT_SIDECHANNELPROTECTION ) &&
#endif /* USE_FIPS140 */
!pairwiseConsistencyTest( contextInfoPtr, TRUE ) )
{
assert( NOTREACHED );
status = CRYPT_ERROR_FAILED;
}
if( cryptStatusOK( status ) )
status = calculateKeyID( contextInfoPtr );
return( status );
}
/****************************************************************************
* *
* Capability Access Routines *
* *
****************************************************************************/
static const CAPABILITY_INFO FAR_BSS capabilityInfo = {
CRYPT_ALGO_ELGAMAL, bitsToBytes( 0 ), "Elgamal",
bitsToBytes( MIN_PKCSIZE_BITS ), bitsToBytes( 1024 ), CRYPT_MAX_PKCSIZE,
selfTest, getDefaultInfo, NULL, NULL, initKey, generateKey, encryptFn, decryptFn
};
const CAPABILITY_INFO *getElgamalCapability( void )
{
return( &capabilityInfo );
}
#endif /* USE_ELGAMAL */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -