#include "precomp.h"
#define DLGROUP_MR_ITERATIONS (64)
static const struct _DSA_NBITSOFQ_CUTOFFS {
UINT32 nBitsOfP;
UINT32 nBitsOfQ;
} g_nBitsOfQ_Cutoffs[] = {
{ 1024, 160 },
{ 2048, 256 },
{ UINT32_MAX, 256 },
};
static const BYTE ggen[] = { 'g', 'g', 'e', 'n' };
UINT32
SYMCRYPT_CALL
SymCryptDlgroupCalculateBitsizeOfQ( UINT32 nBitsOfP )
{
UINT32 i = 0;
while ( (i<SYMCRYPT_ARRAY_SIZE(g_nBitsOfQ_Cutoffs) - 1) &&
(g_nBitsOfQ_Cutoffs[i].nBitsOfP < nBitsOfP) )
{
i++;
};
return g_nBitsOfQ_Cutoffs[i].nBitsOfQ;
}
PSYMCRYPT_DLGROUP
SYMCRYPT_CALL
SymCryptDlgroupAllocate( UINT32 nBitsOfP, UINT32 nBitsOfQ )
{
PVOID p;
SIZE_T cb;
PSYMCRYPT_DLGROUP res = NULL;
if ( (nBitsOfP < SYMCRYPT_DLGROUP_MIN_BITSIZE_P) ||
((nBitsOfQ > 0) && (nBitsOfQ < SYMCRYPT_DLGROUP_MIN_BITSIZE_Q)) ||
(nBitsOfP < nBitsOfQ) )
{
goto cleanup;
}
cb = SymCryptSizeofDlgroupFromBitsizes( nBitsOfP, nBitsOfQ );
p = SymCryptCallbackAlloc( cb );
if ( p==NULL )
{
goto cleanup;
}
res = SymCryptDlgroupCreate( p, cb, nBitsOfP, nBitsOfQ );
cleanup:
return res;
}
VOID
SYMCRYPT_CALL
SymCryptDlgroupFree( _Out_ PSYMCRYPT_DLGROUP pgObj )
{
SYMCRYPT_CHECK_MAGIC( pgObj );
SymCryptDlgroupWipe( pgObj );
SymCryptCallbackFree( pgObj );
}
UINT32
SYMCRYPT_CALL
SymCryptSizeofDlgroupFromBitsizes( UINT32 nBitsOfP, UINT32 nBitsOfQ )
{
UINT32 cbSeed = 0;
if (nBitsOfQ == 0)
{
nBitsOfQ = nBitsOfP-1;
}
if ( (nBitsOfP < SYMCRYPT_DLGROUP_MIN_BITSIZE_P) ||
(nBitsOfQ < SYMCRYPT_DLGROUP_MIN_BITSIZE_Q) ||
(nBitsOfP < nBitsOfQ) )
{
return 0;
}
if ( nBitsOfP == nBitsOfQ )
{
nBitsOfQ--;
}
cbSeed = (nBitsOfQ+7)/8;
return sizeof(SYMCRYPT_DLGROUP) +
SYMCRYPT_SIZEOF_MODULUS_FROM_BITS( nBitsOfP ) +
SYMCRYPT_SIZEOF_MODULUS_FROM_BITS( nBitsOfQ ) +
SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( nBitsOfP ) +
((cbSeed + SYMCRYPT_ASYM_ALIGN_VALUE - 1)/SYMCRYPT_ASYM_ALIGN_VALUE)*SYMCRYPT_ASYM_ALIGN_VALUE;
}
PSYMCRYPT_DLGROUP
SYMCRYPT_CALL
SymCryptDlgroupCreate(
_Out_writes_bytes_( cbBuffer ) PBYTE pbBuffer,
SIZE_T cbBuffer,
UINT32 nBitsOfP,
UINT32 nBitsOfQ )
{
PSYMCRYPT_DLGROUP pDlgroup = NULL;
UINT32 cbModP;
UINT32 cbModQ;
UINT32 cbModElement;
SYMCRYPT_ASSERT( cbBuffer >= SymCryptSizeofDlgroupFromBitsizes( nBitsOfP, nBitsOfQ ) );
UNREFERENCED_PARAMETER( cbBuffer );
SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
if ( (nBitsOfP < SYMCRYPT_DLGROUP_MIN_BITSIZE_P) ||
((nBitsOfQ > 0) && (nBitsOfQ < SYMCRYPT_DLGROUP_MIN_BITSIZE_Q)) ||
(nBitsOfP < nBitsOfQ) )
{
goto cleanup;
}
if ( nBitsOfP == nBitsOfQ )
{
nBitsOfQ--;
}
pDlgroup = (PSYMCRYPT_DLGROUP) pbBuffer;
SYMCRYPT_ASSERT( cbBuffer > sizeof(SYMCRYPT_DLGROUP) );
pDlgroup->cbTotalSize = SymCryptSizeofDlgroupFromBitsizes( nBitsOfP, nBitsOfQ );
pDlgroup->fHasPrimeQ = FALSE;
pDlgroup->nBitsOfP = nBitsOfP;
pDlgroup->cbPrimeP = (nBitsOfP+7)/8;
pDlgroup->nDigitsOfP = SymCryptDigitsFromBits( nBitsOfP );
pDlgroup->nMaxBitsOfP = nBitsOfP;
pDlgroup->nBitsOfQ = nBitsOfQ;
pDlgroup->cbPrimeQ = (nBitsOfQ+7)/8;
pDlgroup->nDigitsOfQ = (nBitsOfQ>0)?SymCryptDigitsFromBits( nBitsOfQ ):0;
pDlgroup->nMaxBitsOfQ = (nBitsOfQ==0)?(nBitsOfP-1):nBitsOfQ;
pDlgroup->isSafePrimeGroup = FALSE;
pDlgroup->nMinBitsPriv = 0;
pDlgroup->nDefaultBitsPriv = nBitsOfQ;
pDlgroup->nBitsOfSeed = nBitsOfQ;
pDlgroup->cbSeed = (pDlgroup->nBitsOfSeed+7)/8;
pDlgroup->eFipsStandard = SYMCRYPT_DLGROUP_FIPS_NONE;
pDlgroup->pHashAlgorithm = NULL;
pDlgroup->dwGenCounter = 0;
pDlgroup->bIndexGenG = 1;
pbBuffer += sizeof(SYMCRYPT_DLGROUP);
cbModP = SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfP );
SYMCRYPT_ASSERT( cbBuffer > sizeof(SYMCRYPT_DLGROUP) + cbModP );
pDlgroup->pmP = SymCryptModulusCreate( pbBuffer, cbModP, pDlgroup->nDigitsOfP );
pbBuffer += cbModP;
if (nBitsOfQ>0)
{
cbModQ = SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfQ );
}
else
{
cbModQ = cbModP;
}
SYMCRYPT_ASSERT( cbBuffer > sizeof(SYMCRYPT_DLGROUP) + cbModP + cbModQ );
pDlgroup->pbQ = pbBuffer;
pDlgroup->pmQ = NULL;
pbBuffer += cbModQ;
cbModElement = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
SYMCRYPT_ASSERT( cbBuffer > sizeof(SYMCRYPT_DLGROUP) + cbModP + cbModQ + cbModElement );
pDlgroup->peG = SymCryptModElementCreate( pbBuffer, cbModElement, pDlgroup->pmP );
pbBuffer += cbModElement;
pDlgroup->pbSeed = pbBuffer;
SYMCRYPT_SET_MAGIC( pDlgroup );
cleanup:
return pDlgroup;
}
VOID
SYMCRYPT_CALL
SymCryptDlgroupWipe( _Out_ PSYMCRYPT_DLGROUP pgDst )
{
SymCryptWipe( (PBYTE) pgDst, pgDst->cbTotalSize );
}
VOID
SYMCRYPT_CALL
SymCryptDlgroupCopy(
_In_ PCSYMCRYPT_DLGROUP pgSrc,
_Out_ PSYMCRYPT_DLGROUP pgDst )
{
if( pgSrc != pgDst )
{
pgDst->cbTotalSize = pgSrc->cbTotalSize;
pgDst->fHasPrimeQ = pgSrc->fHasPrimeQ;
pgDst->nBitsOfP = pgSrc->nBitsOfP;
pgDst->cbPrimeP = pgSrc->cbPrimeP;
pgDst->nDigitsOfP = pgSrc->nDigitsOfP;
pgDst->nMaxBitsOfP = pgSrc->nMaxBitsOfP;
pgDst->nBitsOfQ = pgSrc->nBitsOfQ;
pgDst->cbPrimeQ = pgSrc->cbPrimeQ;
pgDst->nDigitsOfQ = pgSrc->nDigitsOfQ;
pgDst->nMaxBitsOfQ = pgSrc->nMaxBitsOfQ;
pgDst->isSafePrimeGroup = pgSrc->isSafePrimeGroup;
pgDst->nMinBitsPriv = pgSrc->nMinBitsPriv;
pgDst->nDefaultBitsPriv = pgSrc->nDefaultBitsPriv;
pgDst->nBitsOfSeed = pgSrc->nBitsOfSeed;
pgDst->cbSeed = pgSrc->cbSeed;
pgDst->eFipsStandard = pgSrc->eFipsStandard;
pgDst->pHashAlgorithm = pgSrc->pHashAlgorithm;
pgDst->dwGenCounter = pgSrc->dwGenCounter;
pgDst->bIndexGenG = pgSrc->bIndexGenG;
pgDst->pbQ = pgSrc->pbQ;
memcpy( (PBYTE)pgDst + sizeof(SYMCRYPT_DLGROUP), (PCBYTE)pgSrc + sizeof(SYMCRYPT_DLGROUP), pgSrc->cbTotalSize - sizeof(SYMCRYPT_DLGROUP) );
}
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptDlgroupGeneratePrimeQ_FIPS(
_In_ PSYMCRYPT_DLGROUP pDlgroup,
_In_ PCSYMCRYPT_TRIALDIVISION_CONTEXT
pTrialDivisionContext,
_Out_ PUINT32 pfPrimeQFound,
_Out_ PSYMCRYPT_INT piQ,
_Out_ PSYMCRYPT_DIVISOR pdDivTwoQ,
_Out_writes_bytes_( cbScratch )
PBYTE pbScratch,
SIZE_T cbScratch )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PCSYMCRYPT_HASH hashAlgorithm = pDlgroup->pHashAlgorithm;
UINT32 nBitsOfQ = pDlgroup->nBitsOfQ;
UINT32 cbPrimeQ = pDlgroup->cbPrimeQ;
PBYTE pbSeed = pDlgroup->pbSeed;
UINT32 cbSeed = pDlgroup->cbSeed;
PSYMCRYPT_INT piDivTwoQ = SymCryptIntFromDivisor(pdDivTwoQ);
SIZE_T cbHash = SymCryptHashResultSize( hashAlgorithm );
PBYTE pbTrHash = NULL;
PBYTE pbHashExtra = NULL;
UINT32 dwShiftBits = (8-nBitsOfQ%8)%8;
UINT32 carry = 0;
UNREFERENCED_PARAMETER( cbScratch );
SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR(SymCryptDigitsFromBits(nBitsOfQ+1)),
SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME(pDlgroup->nDigitsOfQ),
2 * cbHash )) );
SYMCRYPT_ASSERT( cbHash >= cbPrimeQ );
if (pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2)
{
SYMCRYPT_ASSERT( hashAlgorithm == SymCryptSha1Algorithm );
SYMCRYPT_ASSERT( cbScratch >= SYMCRYPT_MAX(2*cbHash, cbSeed) );
pbTrHash = pbScratch;
pbHashExtra = pbTrHash + cbHash;
scError = SymCryptIntSetValue( pbSeed, cbSeed, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piDivTwoQ );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
carry = SymCryptIntAddUint32( piDivTwoQ, 1, piDivTwoQ );
if (carry > 0)
{
scError = SYMCRYPT_FIPS_FAILURE;
goto cleanup;
}
SymCryptIntModPow2( piDivTwoQ, nBitsOfQ, piDivTwoQ );
scError = SymCryptIntGetValue( piDivTwoQ, pbTrHash, cbSeed, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
SymCryptHash( hashAlgorithm, pbTrHash, cbPrimeQ, pbHashExtra, cbHash );
SymCryptHash( hashAlgorithm, pbSeed, cbSeed, pbTrHash, cbHash );
SymCryptXorBytes( pbTrHash, pbHashExtra, pbTrHash, cbHash );
}
else if (pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_3)
{
SYMCRYPT_ASSERT( cbScratch >= cbHash );
pbTrHash = pbScratch;
SymCryptHash( hashAlgorithm, pbSeed, cbSeed, pbTrHash, cbHash );
}
else
{
scError = SYMCRYPT_FIPS_FAILURE;
goto cleanup;
}
pbTrHash += (cbHash-cbPrimeQ);
pbTrHash[0] &= ((BYTE)0xff >> (dwShiftBits));
pbTrHash[0] |= ((BYTE)0x01 << (7 - dwShiftBits));
pbTrHash[cbPrimeQ-1] |= ((BYTE)0x01);
scError = SymCryptIntSetValue( pbTrHash, cbPrimeQ, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piQ );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
*pfPrimeQFound = 0;
if (SymCryptIntFindSmallDivisor( pTrialDivisionContext, piQ, NULL, 0 ))
{
goto cleanup;
}
*pfPrimeQFound = SymCryptIntMillerRabinPrimalityTest(
piQ,
nBitsOfQ,
DLGROUP_MR_ITERATIONS,
SYMCRYPT_FLAG_DATA_PUBLIC,
pbScratch,
cbScratch );
if (*pfPrimeQFound)
{
scError = SymCryptIntCopyMixedSize( piQ, piDivTwoQ );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
SymCryptIntMulPow2( piDivTwoQ, 1, piDivTwoQ );
SymCryptIntToDivisor(
piDivTwoQ,
pdDivTwoQ,
4*pDlgroup->nBitsOfP,
SYMCRYPT_FLAG_DATA_PUBLIC,
pbScratch,
cbScratch );
}
cleanup:
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptDlgroupGeneratePrimeP_FIPS(
_In_ PSYMCRYPT_DLGROUP pDlgroup,
_In_ PSYMCRYPT_DIVISOR pdDivTwoQ,
_In_ UINT32 dwMaxCounter,
_In_ PCSYMCRYPT_TRIALDIVISION_CONTEXT
pTrialDivisionContext,
_Out_ PUINT32 pfPrimePFound,
_Out_ PSYMCRYPT_INT piP,
_Out_ PUINT32 pdwCounter,
_Out_writes_bytes_( cbScratch )
PBYTE pbScratch,
SIZE_T cbScratch )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PCSYMCRYPT_HASH hashAlgorithm = pDlgroup->pHashAlgorithm;
UINT32 nBitsOfP = pDlgroup->nBitsOfP;
PBYTE pbSeed = pDlgroup->pbSeed;
UINT32 cbSeed = pDlgroup->cbSeed;
UINT32 nBitsOfSeed = pDlgroup->nBitsOfSeed;
SIZE_T cbHash = SymCryptHashResultSize( hashAlgorithm );
UINT32 counter = 0;
UINT32 ndDivTwoQ = SymCryptDivisorDigitsizeOfObject( pdDivTwoQ );
UINT32 cbIntTwoQ = SymCryptSizeofIntFromDigits( ndDivTwoQ );
PSYMCRYPT_INT piPersistent = NULL;
PSYMCRYPT_INT piRemainder = NULL;
PBYTE pbHashOutput = NULL;
PBYTE pbTempSeed = NULL;
PBYTE pbW = NULL;
UINT32 cbW = pDlgroup->cbPrimeP;
PBYTE pbWCurr = NULL;
SIZE_T cbWBytesLeft = 0;
UINT32 carry = 0;
PBYTE pbScratchInternal = 0;
SIZE_T cbScratchInternal = 0;
UNREFERENCED_PARAMETER( cbScratch );
SYMCRYPT_ASSERT( cbScratch >= 2*cbIntTwoQ + cbHash + cbSeed + cbW +
SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( pDlgroup->nDigitsOfP, ndDivTwoQ ),
SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( pDlgroup->nDigitsOfP )) );
pbScratchInternal = pbScratch;
cbScratchInternal = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( pDlgroup->nDigitsOfP, ndDivTwoQ ),
SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( pDlgroup->nDigitsOfP ) );
pbScratch += cbScratchInternal;
piPersistent = SymCryptIntCreate( pbScratch, cbIntTwoQ, ndDivTwoQ );
pbScratch += cbIntTwoQ;
piRemainder = SymCryptIntCreate( pbScratch, cbIntTwoQ, ndDivTwoQ );
pbScratch += cbIntTwoQ;
pbHashOutput = pbScratch;
pbScratch += cbHash;
pbTempSeed = pbScratch;
pbScratch += cbSeed;
pbW = pbScratch;
scError = SymCryptIntSetValue( pbSeed, cbSeed, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piPersistent );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
if (pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2)
{
carry = SymCryptIntAddUint32( piPersistent, 1, piPersistent );
if (carry!=0)
{
scError = SYMCRYPT_FIPS_FAILURE;
goto cleanup;
}
SymCryptIntModPow2( piPersistent, nBitsOfSeed, piPersistent );
}
*pfPrimePFound = 0;
for (counter = 0; counter < dwMaxCounter+1; counter++)
{
cbWBytesLeft = cbW;
pbWCurr = pbW + cbW - SYMCRYPT_MIN(cbW,cbHash);
while (cbWBytesLeft > 0)
{
carry = SymCryptIntAddUint32( piPersistent, 1, piPersistent );
if (carry!=0)
{
scError = SYMCRYPT_FIPS_FAILURE;
goto cleanup;
}
SymCryptIntModPow2( piPersistent, nBitsOfSeed, piPersistent );
scError = SymCryptIntGetValue( piPersistent, pbTempSeed, cbSeed, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
SymCryptHash( hashAlgorithm, pbTempSeed, cbSeed, pbHashOutput, cbHash );
if (cbWBytesLeft >= cbHash)
{
memcpy(pbWCurr, pbHashOutput, cbHash );
}
else
{
memcpy(pbWCurr, pbHashOutput + cbHash - cbWBytesLeft, cbWBytesLeft );
}
cbWBytesLeft -= SYMCRYPT_MIN(cbHash,cbWBytesLeft);
pbWCurr -= SYMCRYPT_MIN(cbHash,cbWBytesLeft);
}
scError = SymCryptIntSetValue( pbW, cbW, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piP );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
SymCryptIntModPow2( piP, nBitsOfP, piP );
SymCryptIntSetBits( piP, 1, nBitsOfP-1, 1);
SymCryptIntDivMod( piP, pdDivTwoQ, NULL, piRemainder, pbScratchInternal, cbScratchInternal );
if (SymCryptIntIsEqualUint32(piRemainder, 0))
{
carry = SymCryptIntAddUint32( piP, 1, piP );
SYMCRYPT_ASSERT( carry==0 );
}
else
{
carry = SymCryptIntSubUint32( piRemainder, 1, piRemainder );
SYMCRYPT_ASSERT( carry==0 );
carry = SymCryptIntSubMixedSize( piP, piRemainder, piP );
SYMCRYPT_ASSERT( carry==0 );
}
if (SymCryptIntGetBit( piP, nBitsOfP-1 ) == 0)
{
continue;
}
if (SymCryptIntFindSmallDivisor( pTrialDivisionContext, piP, NULL, 0 ))
{
continue;
}
*pfPrimePFound = SymCryptIntMillerRabinPrimalityTest(
piP,
nBitsOfP,
DLGROUP_MR_ITERATIONS,
SYMCRYPT_FLAG_DATA_PUBLIC,
pbScratchInternal,
cbScratchInternal );
if (*pfPrimePFound)
{
*pdwCounter = counter;
break;
}
}
cleanup:
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptDlgroupGenerateGenG_FIPS(
_In_ PSYMCRYPT_DLGROUP pDlgroup,
_Out_ PSYMCRYPT_MODELEMENT peG,
_Out_writes_bytes_( cbScratch )
PBYTE pbScratch,
SIZE_T cbScratch )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PCSYMCRYPT_HASH hashAlgorithm = pDlgroup->pHashAlgorithm;
PCSYMCRYPT_MODULUS pmP = pDlgroup->pmP;
UINT32 nDigitsOfP = pDlgroup->nDigitsOfP;
UINT32 nBitsOfP = pDlgroup->nBitsOfP;
PCSYMCRYPT_MODULUS pmQ = pDlgroup->pmQ;
UINT32 nDigitsOfQ = pDlgroup->nDigitsOfQ;
PBYTE pbSeed = pDlgroup->pbSeed;
UINT32 cbSeed = pDlgroup->cbSeed;
BYTE bIndexGenG = pDlgroup->bIndexGenG;
SIZE_T cbHash = SymCryptHashResultSize( hashAlgorithm );
SYMCRYPT_ASSERT( cbHash == hashAlgorithm->resultSize );
SIZE_T cbState = SymCryptHashStateSize( hashAlgorithm );
SYMCRYPT_ASSERT( cbState == hashAlgorithm->stateSize );
UINT16 count = 0;
BYTE bTmp = 0;
PSYMCRYPT_INT piExp = NULL;
PSYMCRYPT_INT piRem = NULL;
PSYMCRYPT_MODELEMENT peOne = NULL;
PBYTE pbState = NULL;
PBYTE pbW = NULL;
UINT32 cbExp = SymCryptSizeofIntFromDigits( nDigitsOfP );
UINT32 cbRem = SymCryptSizeofIntFromDigits( nDigitsOfQ );
UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pmP );
UINT32 borrow = 0;
PBYTE pbScratchInternal = 0;
SIZE_T cbScratchInternal = 0;
UNREFERENCED_PARAMETER( cbScratch );
UNREFERENCED_PARAMETER( nDigitsOfQ );
pbScratchInternal = pbScratch;
cbScratchInternal = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( nDigitsOfP ),
SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( nDigitsOfP, nDigitsOfQ ),
SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsOfP ) ));
SYMCRYPT_ASSERT( cbScratch >= cbScratchInternal + cbExp + cbRem );
SYMCRYPT_ASSERT( cbScratch >= cbScratchInternal + cbExp + cbModElement + cbHash + cbState );
pbScratch += cbScratchInternal;
piExp = SymCryptIntCreate( pbScratch, cbExp, nDigitsOfP );
pbScratch += cbExp;
piRem = SymCryptIntCreate( pbScratch, cbRem, nDigitsOfQ );
borrow = SymCryptIntSubUint32( SymCryptIntFromModulus((PSYMCRYPT_MODULUS)pmP), 1, piExp );
if (borrow!=0)
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
SymCryptIntDivMod(
piExp,
SymCryptDivisorFromModulus( (PSYMCRYPT_MODULUS)pmQ ),
piExp,
piRem,
pbScratchInternal,
cbScratchInternal );
if ( !SymCryptIntIsEqualUint32(piRem, 0) )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
peOne = SymCryptModElementCreate( pbScratch, cbModElement, pmP);
pbScratch += cbModElement;
pbState = pbScratch;
pbScratch += cbState;
pbW = pbScratch;
SymCryptHashInit( hashAlgorithm, pbState );
SymCryptModElementSetValueUint32( 1, pmP, peOne, pbScratchInternal, cbScratchInternal );
do
{
count += 1;
if (count == 0)
{
scError = SYMCRYPT_FIPS_FAILURE;
goto cleanup;
}
SymCryptHashAppend( hashAlgorithm, pbState, pbSeed, cbSeed );
SymCryptHashAppend( hashAlgorithm, pbState, ggen, sizeof(ggen) );
SymCryptHashAppend( hashAlgorithm, pbState, &bIndexGenG, sizeof(bIndexGenG) );
bTmp = (BYTE)(count >> 8);
SymCryptHashAppend( hashAlgorithm, pbState, &bTmp, sizeof(bTmp) );
bTmp = (BYTE)count;
SymCryptHashAppend( hashAlgorithm, pbState, &bTmp, sizeof(bTmp) );
SymCryptHashResult( hashAlgorithm, pbState, pbW, cbHash );
scError = SymCryptModElementSetValue(
pbW,
cbHash,
SYMCRYPT_NUMBER_FORMAT_MSB_FIRST,
pmP,
peG,
pbScratchInternal,
cbScratchInternal );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
SymCryptModExp(
pmP,
peG,
piExp,
nBitsOfP,
SYMCRYPT_FLAG_DATA_PUBLIC,
peG,
pbScratchInternal,
cbScratchInternal );
} while (SymCryptModElementIsZero( pmP, peG ) || SymCryptModElementIsEqual( pmP, peG, peOne ));
cleanup:
return scError;
}
UINT32
SYMCRYPT_CALL
SymCryptDlgroupScratchSpace_FIPS( UINT32 nBitsOfP, UINT32 nBitsOfQ, PCSYMCRYPT_HASH pHashAlgorithm )
{
UINT32 nDigitsOfP = SymCryptDigitsFromBits( nBitsOfP );
UINT32 nDigitsOfQ = SymCryptDigitsFromBits( nBitsOfQ );
UINT32 ndDivTwoQ = SymCryptDigitsFromBits(nBitsOfQ + 1);
UINT32 cbPrimeP = (nBitsOfP+7)/8;
UINT32 cbDivTwoQ = SymCryptSizeofDivisorFromDigits(ndDivTwoQ);
UINT32 cbIntTwoQ = SymCryptSizeofIntFromDigits( ndDivTwoQ );
UINT32 cbSeed = (nBitsOfQ+7)/8;
UINT32 cbExp = SymCryptSizeofIntFromDigits( nDigitsOfP );
UINT32 cbRem = SymCryptSizeofIntFromDigits( nDigitsOfQ );
UINT32 cbModElement = SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS( nBitsOfP );
UINT32 cbHash = (UINT32)SymCryptHashResultSize( pHashAlgorithm );
UINT32 cbState = (UINT32) SymCryptHashStateSize( pHashAlgorithm );
return SYMCRYPT_MAX( cbDivTwoQ + SYMCRYPT_MAX(
SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_DIVISOR( ndDivTwoQ ),
SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( nDigitsOfQ ),
2 * cbHash)),
2*cbIntTwoQ + cbHash + cbSeed + cbPrimeP +
SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( nDigitsOfP, ndDivTwoQ ),
SYMCRYPT_SCRATCH_BYTES_FOR_INT_IS_PRIME( nDigitsOfP )) ),
SYMCRYPT_MAX(
SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS( nDigitsOfP ),
cbExp + SYMCRYPT_MAX(cbRem, cbModElement + cbState + cbHash) +
SYMCRYPT_MAX(SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP( nDigitsOfP ),
SYMCRYPT_MAX(SYMCRYPT_SCRATCH_BYTES_FOR_INT_DIVMOD( nDigitsOfP, nDigitsOfQ ),
SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( nDigitsOfP ) )) ));
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptDlgroupGenerate(
_In_ PCSYMCRYPT_HASH hashAlgorithm,
_In_ SYMCRYPT_DLGROUP_FIPS fipsStandard,
_Inout_ PSYMCRYPT_DLGROUP pDlgroup )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratch = NULL;
SIZE_T cbScratch = 0;
PBYTE pbScratchInternal = NULL;
SIZE_T cbScratchInternal = 0;
UINT32 fPrimeQFound = 0;
UINT32 fPrimePFound = 0;
PSYMCRYPT_DIVISOR pdDivTwoQ = NULL;
UINT32 cbDivTwoQ = 0;
UINT32 ndDivTwoQ = 0;
UINT32 nBitsOfP = 0;
UINT32 nDigitsOfP = 0;
UINT32 nBitsOfQ = 0;
UINT32 nDigitsOfQ = 0;
PCSYMCRYPT_TRIALDIVISION_CONTEXT pTrialDivisionContext = NULL;
if (fipsStandard == SYMCRYPT_DLGROUP_FIPS_NONE)
{
fipsStandard = SYMCRYPT_DLGROUP_FIPS_LATEST;
}
if (fipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2)
{
if (hashAlgorithm != NULL)
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
pDlgroup->eFipsStandard = fipsStandard;
hashAlgorithm = SymCryptSha1Algorithm;
}
else
{
if (hashAlgorithm == NULL)
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
pDlgroup->eFipsStandard = fipsStandard;
}
if (pDlgroup->nBitsOfQ == 0)
{
pDlgroup->nBitsOfQ = SymCryptDlgroupCalculateBitsizeOfQ(pDlgroup->nBitsOfP);
if (pDlgroup->nBitsOfQ > pDlgroup->nMaxBitsOfQ)
{
scError = SYMCRYPT_FIPS_FAILURE;
goto cleanup;
}
pDlgroup->cbPrimeQ = (pDlgroup->nBitsOfQ + 7)/8;
pDlgroup->nDigitsOfQ = SymCryptDigitsFromBits( pDlgroup->nBitsOfQ );
pDlgroup->nDefaultBitsPriv = pDlgroup->nBitsOfQ;
pDlgroup->nBitsOfSeed = pDlgroup->nBitsOfQ;
pDlgroup->cbSeed = (pDlgroup->nBitsOfSeed+7)/8;
}
nBitsOfP = pDlgroup->nBitsOfP;
nDigitsOfP = pDlgroup->nDigitsOfP;
nBitsOfQ = pDlgroup->nBitsOfQ;
nDigitsOfQ = pDlgroup->nDigitsOfQ;
pDlgroup->pmQ = SymCryptModulusCreate( pDlgroup->pbQ, SymCryptSizeofModulusFromDigits( nDigitsOfQ ), nDigitsOfQ );
if ( (8*((UINT32)SymCryptHashResultSize( hashAlgorithm )) < nBitsOfQ) ||
(8*((UINT32)SymCryptHashResultSize( hashAlgorithm )) > nBitsOfP) )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
pDlgroup->pHashAlgorithm = hashAlgorithm;
ndDivTwoQ = SymCryptDigitsFromBits(nBitsOfQ + 1);
cbDivTwoQ = SymCryptSizeofDivisorFromDigits(ndDivTwoQ);
cbScratch = SymCryptDlgroupScratchSpace_FIPS( nBitsOfP, nBitsOfQ, hashAlgorithm );
pbScratch = SymCryptCallbackAlloc(cbScratch);
if (pbScratch==NULL)
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
pdDivTwoQ = SymCryptDivisorCreate( pbScratch, cbDivTwoQ, ndDivTwoQ );
pbScratchInternal = pbScratch + cbDivTwoQ;
cbScratchInternal = cbScratch - cbDivTwoQ;
pTrialDivisionContext = SymCryptCreateTrialDivisionContext( pDlgroup->nDigitsOfP );
if (pTrialDivisionContext == NULL)
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
do
{
do
{
scError = SymCryptCallbackRandom( pDlgroup->pbSeed, pDlgroup->cbSeed );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
if ((pDlgroup->nBitsOfSeed)%8 != 0)
{
pDlgroup->pbSeed[0] &= ((BYTE)0xff >> (8 - (pDlgroup->nBitsOfSeed)%8));
}
scError = SymCryptDlgroupGeneratePrimeQ_FIPS(
pDlgroup,
pTrialDivisionContext,
&fPrimeQFound,
SymCryptIntFromModulus(pDlgroup->pmQ),
pdDivTwoQ,
pbScratchInternal,
cbScratchInternal );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
}
while (fPrimeQFound == 0);
scError = SymCryptDlgroupGeneratePrimeP_FIPS(
pDlgroup,
pdDivTwoQ,
4*nBitsOfP - 1,
pTrialDivisionContext,
&fPrimePFound,
SymCryptIntFromModulus(pDlgroup->pmP),
&(pDlgroup->dwGenCounter),
pbScratchInternal,
cbScratchInternal );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
}
while (fPrimePFound == 0);
pDlgroup->fHasPrimeQ = TRUE;
SymCryptIntToModulus(
SymCryptIntFromModulus( pDlgroup->pmP ),
pDlgroup->pmP,
1000*nBitsOfP,
SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
pbScratch,
cbScratch );
SymCryptIntToModulus(
SymCryptIntFromModulus( pDlgroup->pmQ ),
pDlgroup->pmQ,
1000*nBitsOfP,
SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
pbScratch,
cbScratch );
scError = SymCryptDlgroupGenerateGenG_FIPS( pDlgroup, pDlgroup->peG, pbScratch, cbScratch );
if (scError != SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
cleanup:
if (pTrialDivisionContext!=NULL)
{
SymCryptFreeTrialDivisionContext( pTrialDivisionContext );
}
if (pbScratch!=NULL)
{
SymCryptWipe( pbScratch, cbScratch );
SymCryptCallbackFree( pbScratch );
}
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptDlgroupSetValueSafePrime(
SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE dhSafePrimeType,
_Inout_ PSYMCRYPT_DLGROUP pDlgroup )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratch = NULL;
SIZE_T cbScratch = 0;
PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS safePrimeParams = NULL;
UINT32 i;
UINT32 nBitsOfQ;
UINT32 nMaxBitsOfP = SYMCRYPT_MIN(pDlgroup->nMaxBitsOfP, pDlgroup->nMaxBitsOfQ+1);
UINT32 nMaxDigitsOfP;
if ( dhSafePrimeType == SYMCRYPT_DLGROUP_DH_SAFEPRIMETYPE_NONE )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
for ( i=0; i<SYMCRYPT_DH_SAFEPRIME_GROUP_COUNT; i++ )
{
if ( SymCryptNamedSafePrimeGroups[i]->eDhSafePrimeType == dhSafePrimeType &&
SymCryptNamedSafePrimeGroups[i]->nBitsOfP <= nMaxBitsOfP )
{
safePrimeParams = SymCryptNamedSafePrimeGroups[i];
break;
}
}
if (safePrimeParams == NULL)
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
nMaxDigitsOfP = SymCryptDigitsFromBits(safePrimeParams->nBitsOfP);
cbScratch = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS(nMaxDigitsOfP),
SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(nMaxDigitsOfP) );
pbScratch = SymCryptCallbackAlloc( cbScratch );
if (pbScratch==NULL)
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
pDlgroup->isSafePrimeGroup = TRUE;
pDlgroup->eFipsStandard = SYMCRYPT_DLGROUP_FIPS_NONE;
pDlgroup->nMinBitsPriv = safePrimeParams->nMinBitsPriv;
pDlgroup->nDefaultBitsPriv = safePrimeParams->nDefaultBitsPriv;
pDlgroup->pHashAlgorithm = NULL;
pDlgroup->dwGenCounter = 0;
pDlgroup->nBitsOfSeed = 0;
pDlgroup->pbSeed = NULL;
pDlgroup->cbSeed = 0;
pDlgroup->nBitsOfP = safePrimeParams->nBitsOfP;
pDlgroup->cbPrimeP = (safePrimeParams->nBitsOfP + 7)/ 8;
pDlgroup->nDigitsOfP = SymCryptDigitsFromBits(safePrimeParams->nBitsOfP);
nBitsOfQ = pDlgroup->nBitsOfP - 1;
pDlgroup->nBitsOfQ = nBitsOfQ;
pDlgroup->cbPrimeQ = (nBitsOfQ + 7)/8;
pDlgroup->nDigitsOfQ = SymCryptDigitsFromBits(nBitsOfQ);
pDlgroup->fHasPrimeQ = TRUE;
pDlgroup->pmP = SymCryptModulusCreate( (PBYTE) pDlgroup->pmP, SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfP ), pDlgroup->nDigitsOfP );
scError = SymCryptIntSetValue( safePrimeParams->pcbPrimeP, pDlgroup->cbPrimeP, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, SymCryptIntFromModulus(pDlgroup->pmP) );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
SymCryptIntToModulus(
SymCryptIntFromModulus( pDlgroup->pmP ),
pDlgroup->pmP,
1000*pDlgroup->nBitsOfP,
SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
pbScratch,
cbScratch );
pDlgroup->pmQ = SymCryptModulusCreate( pDlgroup->pbQ, SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfQ ), pDlgroup->nDigitsOfQ );
SymCryptIntShr1( 0, SymCryptIntFromModulus(pDlgroup->pmP), SymCryptIntFromModulus(pDlgroup->pmQ) );
SymCryptIntToModulus(
SymCryptIntFromModulus( pDlgroup->pmQ ),
pDlgroup->pmQ,
1000*nBitsOfQ,
SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
pbScratch,
cbScratch );
SymCryptModElementSetValueUint32( 2, pDlgroup->pmP, pDlgroup->peG, pbScratch, cbScratch );
cleanup:
if (pbScratch!=NULL)
{
SymCryptWipe( pbScratch, cbScratch );
SymCryptCallbackFree( pbScratch );
}
return scError;
}
BOOLEAN
SYMCRYPT_CALL
SymCryptDlgroupIsSame(
_In_ PCSYMCRYPT_DLGROUP pDlgroup1,
_In_ PCSYMCRYPT_DLGROUP pDlgroup2 )
{
BOOLEAN fIsSameGroup = FALSE;
if ( pDlgroup1 == pDlgroup2 )
{
fIsSameGroup = TRUE;
goto cleanup;
}
if ( (pDlgroup1->nBitsOfP != pDlgroup2->nBitsOfP) ||
(pDlgroup1->nDigitsOfP != pDlgroup2->nDigitsOfP) ||
!SymCryptIntIsEqual ( SymCryptIntFromModulus(pDlgroup1->pmP), SymCryptIntFromModulus(pDlgroup2->pmP) ) ||
!SymCryptModElementIsEqual ( pDlgroup1->pmP, pDlgroup1->peG, pDlgroup2->peG ))
{
goto cleanup;
}
fIsSameGroup = TRUE;
cleanup:
return fIsSameGroup;
}
VOID
SYMCRYPT_CALL
SymCryptDlgroupGetSizes(
_In_ PCSYMCRYPT_DLGROUP pDlgroup,
_Out_ SIZE_T* pcbPrimeP,
_Out_ SIZE_T* pcbPrimeQ,
_Out_ SIZE_T* pcbGenG,
_Out_ SIZE_T* pcbSeed )
{
if (pcbPrimeP!=NULL)
{
*pcbPrimeP = pDlgroup->cbPrimeP;
}
if (pcbPrimeQ!=NULL)
{
*pcbPrimeQ = pDlgroup->cbPrimeQ;
}
if (pcbGenG!=NULL)
{
*pcbGenG = pDlgroup->cbPrimeP;
}
if (pcbSeed!=NULL)
{
*pcbSeed = pDlgroup->cbSeed;
}
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptDlgroupAutoCompleteNamedSafePrimeGroup(
_Inout_ PSYMCRYPT_DLGROUP pDlgroup,
_Out_writes_bytes_( cbScratch ) PBYTE pbScratch,
SIZE_T cbScratch )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratchInternal;
SIZE_T cbScratchInternal;
PSYMCRYPT_INT piTemp = NULL;
UINT32 cbTemp;
UINT32 i;
UINT32 nBitsOfQ;
PCSYMCRYPT_DLGROUP_DH_SAFEPRIME_PARAMS safePrimeParams = NULL;
if ( SymCryptIntGetValueLsbits64( SymCryptIntFromModulus(pDlgroup->pmP) ) != ((UINT64) -1) )
{
goto cleanup;
}
cbTemp = SymCryptSizeofIntFromDigits( pDlgroup->nDigitsOfP );
SYMCRYPT_ASSERT( cbScratch >= cbTemp );
piTemp = SymCryptIntCreate( pbScratch, cbTemp, pDlgroup->nDigitsOfP );
pbScratchInternal = pbScratch + cbTemp;
cbScratchInternal = cbScratch - cbTemp;
SymCryptModElementToInt( pDlgroup->pmP, pDlgroup->peG, piTemp, pbScratchInternal, cbScratchInternal );
if ( !SymCryptIntIsEqualUint32( piTemp, 2 ) )
{
goto cleanup;
}
for ( i=0; i<SYMCRYPT_DH_SAFEPRIME_GROUP_COUNT; i++ )
{
if ( SymCryptNamedSafePrimeGroups[i]->nBitsOfP == pDlgroup->nBitsOfP )
{
SymCryptIntSetValue( SymCryptNamedSafePrimeGroups[i]->pcbPrimeP, pDlgroup->cbPrimeP, SYMCRYPT_NUMBER_FORMAT_MSB_FIRST, piTemp );
if ( SymCryptIntIsEqual( piTemp, SymCryptIntFromModulus(pDlgroup->pmP) ) )
{
safePrimeParams = SymCryptNamedSafePrimeGroups[i];
break;
}
}
}
if (safePrimeParams != NULL)
{
if ( pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2 ||
pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_3 )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
pDlgroup->isSafePrimeGroup = TRUE;
pDlgroup->eFipsStandard = SYMCRYPT_DLGROUP_FIPS_NONE;
pDlgroup->nMinBitsPriv = safePrimeParams->nMinBitsPriv;
pDlgroup->nDefaultBitsPriv = safePrimeParams->nDefaultBitsPriv;
pDlgroup->pHashAlgorithm = NULL;
pDlgroup->dwGenCounter = 0;
pDlgroup->nBitsOfSeed = 0;
pDlgroup->pbSeed = NULL;
pDlgroup->cbSeed = 0;
nBitsOfQ = pDlgroup->nBitsOfP - 1;
pDlgroup->nBitsOfQ = nBitsOfQ;
pDlgroup->cbPrimeQ = (nBitsOfQ + 7)/8;
pDlgroup->nDigitsOfQ = SymCryptDigitsFromBits(nBitsOfQ);
pDlgroup->pmQ = SymCryptModulusCreate( pDlgroup->pbQ, SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfQ ), pDlgroup->nDigitsOfQ );
SymCryptIntShr1( 0, piTemp, piTemp );
scError = SymCryptIntCopyMixedSize( piTemp, SymCryptIntFromModulus(pDlgroup->pmQ) );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
SymCryptIntToModulus(
SymCryptIntFromModulus( pDlgroup->pmQ ),
pDlgroup->pmQ,
1000*nBitsOfQ,
SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
pbScratch,
cbScratch );
pDlgroup->fHasPrimeQ = TRUE;
}
cleanup:
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptDlgroupSetValue(
_In_reads_bytes_( cbPrimeP ) PCBYTE pbPrimeP,
SIZE_T cbPrimeP,
_In_reads_bytes_( cbPrimeQ ) PCBYTE pbPrimeQ,
SIZE_T cbPrimeQ,
_In_reads_bytes_( cbGenG ) PCBYTE pbGenG,
SIZE_T cbGenG,
SYMCRYPT_NUMBER_FORMAT numFormat,
_In_opt_ PCSYMCRYPT_HASH pHashAlgorithm,
_In_reads_bytes_( cbSeed ) PCBYTE pbSeed,
SIZE_T cbSeed,
UINT32 genCounter,
SYMCRYPT_DLGROUP_FIPS fipsStandard,
_Inout_ PSYMCRYPT_DLGROUP pDlgroup )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratch = NULL;
SIZE_T cbScratch = 0;
SIZE_T cbScratchVerify = 0;
PSYMCRYPT_INT piTemp = NULL;
UINT32 nBitsOfP = 0;
UINT32 nBitsOfQ = 0;
UINT32 nMaxDigitsOfP = SymCryptDigitsFromBits(pDlgroup->nMaxBitsOfP);
UINT32 nMaxDigitsOfQ = SymCryptDigitsFromBits(pDlgroup->nMaxBitsOfQ);
PCSYMCRYPT_TRIALDIVISION_CONTEXT pTrialDivisionContext = NULL;
if ( (pbPrimeP==NULL) || (cbPrimeP==0) ||
((pbGenG==NULL)&&(cbGenG>0)) ||
((pbPrimeQ==NULL)&&(cbPrimeQ>0)) ||
((pbGenG==NULL)&&(pbPrimeQ==NULL)) ||
((pbSeed==NULL)&&(cbSeed>0)) )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
if (fipsStandard != SYMCRYPT_DLGROUP_FIPS_NONE)
{
if ((pbPrimeQ == NULL)||
(cbPrimeQ == 0) ||
(pbSeed == NULL) ||
(cbSeed == 0) ||
((fipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2) && (pHashAlgorithm != NULL)) ||
((fipsStandard != SYMCRYPT_DLGROUP_FIPS_186_2) && (pHashAlgorithm == NULL)) )
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
}
if ( (fipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2) ||
((pHashAlgorithm==NULL) && (pbGenG == NULL)) )
{
pDlgroup->pHashAlgorithm = SymCryptSha1Algorithm;
}
else
{
pDlgroup->pHashAlgorithm = pHashAlgorithm;
}
if ( (fipsStandard != SYMCRYPT_DLGROUP_FIPS_NONE) || (pbGenG == NULL))
{
cbScratchVerify = SymCryptDlgroupScratchSpace_FIPS( pDlgroup->nMaxBitsOfP, pDlgroup->nMaxBitsOfQ, pDlgroup->pHashAlgorithm ) +
SYMCRYPT_MAX( SymCryptSizeofIntFromDigits(nMaxDigitsOfP),
SYMCRYPT_MAX( SymCryptSizeofIntFromDigits(nMaxDigitsOfQ),
2*SYMCRYPT_SIZEOF_MODELEMENT_FROM_BITS(nMaxDigitsOfP)));
}
cbScratch = SYMCRYPT_MAX( SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS(nMaxDigitsOfP) +
SYMCRYPT_MAX( SymCryptSizeofIntFromDigits(nMaxDigitsOfQ),
SYMCRYPT_SCRATCH_BYTES_FOR_INT_TO_MODULUS(nMaxDigitsOfP) ),
cbScratchVerify );
pbScratch = SymCryptCallbackAlloc( cbScratch );
if (pbScratch==NULL)
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
scError = SymCryptIntSetValue( pbPrimeP, cbPrimeP, numFormat, SymCryptIntFromModulus(pDlgroup->pmP) );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
nBitsOfP = SymCryptIntBitsizeOfValue(SymCryptIntFromModulus(pDlgroup->pmP));
if ( nBitsOfP > pDlgroup->nMaxBitsOfP)
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
if (nBitsOfP < SYMCRYPT_DLGROUP_MIN_BITSIZE_P)
{
scError = SYMCRYPT_WRONG_KEY_SIZE;
goto cleanup;
}
if (fipsStandard != SYMCRYPT_DLGROUP_FIPS_NONE &&
genCounter > 4*nBitsOfP-1 )
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
if( (SymCryptIntGetValueLsbits32( SymCryptIntFromModulus( pDlgroup->pmP ) ) & 1) == 0 )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
pDlgroup->nBitsOfP = nBitsOfP;
pDlgroup->cbPrimeP = (nBitsOfP + 7)/8;
SymCryptIntToModulus(
SymCryptIntFromModulus( pDlgroup->pmP ),
pDlgroup->pmP,
1000*nBitsOfP,
SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
pbScratch,
cbScratch );
if (pDlgroup->pmQ != NULL)
{
SymCryptModulusWipe( pDlgroup->pmQ );
}
if (pDlgroup->cbSeed != 0)
{
SymCryptWipe( pDlgroup->pbSeed, pDlgroup->cbSeed);
}
if (pbPrimeQ != NULL)
{
piTemp = SymCryptIntCreate( pbScratch, cbScratch, nMaxDigitsOfQ );
scError = SymCryptIntSetValue( pbPrimeQ, cbPrimeQ, numFormat, piTemp );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
nBitsOfQ = SymCryptIntBitsizeOfValue(piTemp);
if ( nBitsOfQ > pDlgroup->nMaxBitsOfQ)
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
if (nBitsOfQ < SYMCRYPT_DLGROUP_MIN_BITSIZE_Q)
{
scError = SYMCRYPT_WRONG_KEY_SIZE;
goto cleanup;
}
if( (SymCryptIntGetValueLsbits32( piTemp ) & 1) == 0 )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
pDlgroup->nBitsOfQ = nBitsOfQ;
pDlgroup->cbPrimeQ = (nBitsOfQ + 7)/8;
pDlgroup->nDigitsOfQ = SymCryptDigitsFromBits(nBitsOfQ);
pDlgroup->nDefaultBitsPriv = nBitsOfQ;
pDlgroup->nBitsOfSeed = nBitsOfQ;
pDlgroup->cbSeed = (nBitsOfQ+7)/8;
pDlgroup->pmQ = SymCryptModulusCreate( pDlgroup->pbQ, SymCryptSizeofModulusFromDigits( pDlgroup->nDigitsOfQ ), pDlgroup->nDigitsOfQ );
scError = SymCryptIntCopyMixedSize( piTemp, SymCryptIntFromModulus(pDlgroup->pmQ) );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
SymCryptIntToModulus(
SymCryptIntFromModulus( pDlgroup->pmQ ),
pDlgroup->pmQ,
1000*nBitsOfP,
SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME,
pbScratch,
cbScratch );
pDlgroup->fHasPrimeQ = TRUE;
}
else
{
pDlgroup->cbPrimeQ = 0;
pDlgroup->nBitsOfQ = 0;
pDlgroup->nDigitsOfQ = 0;
pDlgroup->nDefaultBitsPriv = 0;
pDlgroup->nBitsOfSeed = 0;
pDlgroup->cbSeed = 0;
pDlgroup->pmQ = NULL;
pDlgroup->fHasPrimeQ = FALSE;
}
pDlgroup->isSafePrimeGroup = FALSE;
pDlgroup->nMinBitsPriv = 0;
if (pbGenG != NULL)
{
scError = SymCryptModElementSetValue( pbGenG, cbGenG, numFormat, pDlgroup->pmP, pDlgroup->peG, pbScratch, cbScratch );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
scError = SymCryptDlgroupAutoCompleteNamedSafePrimeGroup( pDlgroup, pbScratch, cbScratch );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
if (pDlgroup->isSafePrimeGroup)
{
goto cleanup;
}
}
pDlgroup->eFipsStandard = fipsStandard;
if (pbSeed != NULL)
{
if (cbSeed != pDlgroup->cbSeed)
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
memcpy( pDlgroup->pbSeed, pbSeed, cbSeed );
}
pDlgroup->dwGenCounter = genCounter;
if (pbGenG == NULL)
{
if (pbSeed==NULL)
{
SymCryptCallbackRandom(pDlgroup->pbSeed, pDlgroup->cbSeed);
}
scError = SymCryptDlgroupGenerateGenG_FIPS(
pDlgroup,
pDlgroup->peG,
pbScratch,
cbScratch );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
}
if (fipsStandard != SYMCRYPT_DLGROUP_FIPS_NONE)
{
PBYTE pbScratchInternal = pbScratch;
SIZE_T cbScratchInternal = cbScratch;
UINT32 ndDivTwoQ = 0;
UINT32 cbDivTwoQ = 0;
PSYMCRYPT_DIVISOR pdDivTwoQ = NULL;
UINT32 cbComputed = 0;
PSYMCRYPT_INT piComputed = NULL;
UINT32 fPrimeComputed = 0;
UINT32 dwComputedCounter = 0;
PSYMCRYPT_MODELEMENT peComputed = NULL;
PSYMCRYPT_MODELEMENT peOne = NULL;
ndDivTwoQ = SymCryptDigitsFromBits(pDlgroup->nBitsOfQ + 1);
cbDivTwoQ = SymCryptSizeofDivisorFromDigits( ndDivTwoQ );
pdDivTwoQ = SymCryptDivisorCreate( pbScratchInternal, cbDivTwoQ, ndDivTwoQ );
pbScratchInternal += cbDivTwoQ;
cbScratchInternal -= cbDivTwoQ;
cbComputed = SymCryptSizeofIntFromDigits( pDlgroup->nDigitsOfQ );
piComputed = SymCryptIntCreate( pbScratchInternal, cbComputed, pDlgroup->nDigitsOfQ );
pbScratchInternal += cbComputed;
cbScratchInternal -= cbComputed;
pTrialDivisionContext = SymCryptCreateTrialDivisionContext( pDlgroup->nDigitsOfP );
if (pTrialDivisionContext == NULL)
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
scError = SymCryptDlgroupGeneratePrimeQ_FIPS(
pDlgroup,
pTrialDivisionContext,
&fPrimeComputed,
piComputed,
pdDivTwoQ,
pbScratchInternal,
cbScratchInternal );
if (scError != SYMCRYPT_NO_ERROR)
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
if ((!fPrimeComputed)||(!SymCryptIntIsEqual( piComputed, SymCryptIntFromModulus(pDlgroup->pmQ))))
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
pbScratchInternal -= cbComputed;
cbScratchInternal += cbComputed;
cbComputed = SymCryptSizeofIntFromDigits( pDlgroup->nDigitsOfP );
piComputed = SymCryptIntCreate( pbScratchInternal, cbComputed, pDlgroup->nDigitsOfP );
pbScratchInternal += cbComputed;
cbScratchInternal -= cbComputed;
scError = SymCryptDlgroupGeneratePrimeP_FIPS(
pDlgroup,
pdDivTwoQ,
pDlgroup->dwGenCounter,
pTrialDivisionContext,
&fPrimeComputed,
piComputed,
&dwComputedCounter,
pbScratchInternal,
cbScratchInternal );
if (scError != SYMCRYPT_NO_ERROR)
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
if ((!fPrimeComputed)||(dwComputedCounter!=pDlgroup->dwGenCounter)||(!SymCryptIntIsEqual( piComputed, SymCryptIntFromModulus(pDlgroup->pmP))))
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
pbScratchInternal -= cbComputed;
cbScratchInternal += cbComputed;
cbComputed = SymCryptSizeofModElementFromModulus( pDlgroup->pmP );
peOne = SymCryptModElementCreate( pbScratchInternal, cbComputed, pDlgroup->pmP );
pbScratchInternal += cbComputed;
cbScratchInternal -= cbComputed;
peComputed = SymCryptModElementCreate( pbScratchInternal, cbComputed, pDlgroup->pmP );
pbScratchInternal += cbComputed;
cbScratchInternal -= cbComputed;
SymCryptModElementSetValueUint32( 1, pDlgroup->pmP, peOne, pbScratchInternal, cbScratchInternal );
if ((SymCryptModElementIsZero(pDlgroup->pmP, pDlgroup->peG)) || (SymCryptModElementIsEqual(pDlgroup->pmP, pDlgroup->peG, peOne)))
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
SymCryptModExp(
pDlgroup->pmP,
pDlgroup->peG,
SymCryptIntFromModulus(pDlgroup->pmQ),
nBitsOfQ,
SYMCRYPT_FLAG_DATA_PUBLIC,
peComputed,
pbScratchInternal,
cbScratchInternal );
if (!SymCryptModElementIsEqual(pDlgroup->pmP, peComputed, peOne))
{
scError = SYMCRYPT_AUTHENTICATION_FAILURE;
goto cleanup;
}
}
cleanup:
if (pTrialDivisionContext!=NULL)
{
SymCryptFreeTrialDivisionContext( pTrialDivisionContext );
}
if (pbScratch!=NULL)
{
SymCryptWipe( pbScratch, cbScratch );
SymCryptCallbackFree( pbScratch );
}
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptDlgroupGetValue(
_In_ PCSYMCRYPT_DLGROUP pDlgroup,
_Out_writes_bytes_( cbPrimeP ) PBYTE pbPrimeP,
SIZE_T cbPrimeP,
_Out_writes_bytes_( cbPrimeQ ) PBYTE pbPrimeQ,
SIZE_T cbPrimeQ,
_Out_writes_bytes_( cbGenG ) PBYTE pbGenG,
SIZE_T cbGenG,
SYMCRYPT_NUMBER_FORMAT numFormat,
_Out_ PCSYMCRYPT_HASH * ppHashAlgorithm,
_Out_writes_bytes_( cbSeed ) PBYTE pbSeed,
SIZE_T cbSeed,
_Out_ PUINT32 pGenCounter )
{
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
PBYTE pbScratch = NULL;
SIZE_T cbScratch = 0;
if ( ((pbPrimeP==NULL)&&(cbPrimeP>0)) ||
((pbPrimeQ==NULL)&&(cbPrimeQ>0)) ||
((pbGenG==NULL)&&(cbGenG>0)) ||
((pbSeed==NULL)&&(cbSeed>0)) ||
((pbSeed!=NULL)&&(cbSeed!=pDlgroup->cbSeed)) )
{
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
if ((pbPrimeQ!=NULL) && (!pDlgroup->fHasPrimeQ))
{
scError = SYMCRYPT_INVALID_BLOB;
goto cleanup;
}
if (pbPrimeP!=NULL)
{
scError = SymCryptIntGetValue(
SymCryptIntFromModulus(pDlgroup->pmP),
pbPrimeP,
cbPrimeP,
numFormat );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
}
if (pbPrimeQ!=NULL)
{
scError = SymCryptIntGetValue(
SymCryptIntFromModulus(pDlgroup->pmQ),
pbPrimeQ,
cbPrimeQ,
numFormat );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
}
if (pbGenG!=NULL)
{
cbScratch = SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pDlgroup->nDigitsOfP);
pbScratch = SymCryptCallbackAlloc( cbScratch );
if (pbScratch==NULL)
{
scError = SYMCRYPT_MEMORY_ALLOCATION_FAILURE;
goto cleanup;
}
scError = SymCryptModElementGetValue(
pDlgroup->pmP,
pDlgroup->peG,
pbGenG,
cbGenG,
numFormat,
pbScratch,
cbScratch );
if (scError!=SYMCRYPT_NO_ERROR)
{
goto cleanup;
}
}
if (ppHashAlgorithm!=NULL)
{
if (pDlgroup->eFipsStandard == SYMCRYPT_DLGROUP_FIPS_186_2)
{
*ppHashAlgorithm = NULL;
}
else
{
*ppHashAlgorithm = pDlgroup->pHashAlgorithm;
}
}
if (pbSeed!=NULL && pDlgroup->pbSeed!=NULL)
{
memcpy( pbSeed, pDlgroup->pbSeed, pDlgroup->cbSeed);
}
if (pGenCounter!=NULL)
{
*pGenCounter = pDlgroup->dwGenCounter;
}
cleanup:
if (pbScratch!=NULL)
{
SymCryptWipe( pbScratch, cbScratch );
SymCryptCallbackFree( pbScratch );
}
return scError;
}