#include "precomp.h"
#include "smallPrimes32.h"
#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_ARM64
#define SYMCRYPT_TRIALDIVISION_DIGIT_REDUCTION_CYCLES (16)
#define SYMCRYPT_TRIALDIVISION_DIVIDE_TEST_CYCLES (2)
#define SYMCRYPT_RABINMILLER_DIGIT_CYCLES (43000)
#elif SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM
#define SYMCRYPT_TRIALDIVISION_DIGIT_REDUCTION_CYCLES (18)
#define SYMCRYPT_TRIALDIVISION_DIVIDE_TEST_CYCLES (16)
#define SYMCRYPT_RABINMILLER_DIGIT_CYCLES (25300)
#else
#define SYMCRYPT_TRIALDIVISION_DIGIT_REDUCTION_CYCLES (18)
#define SYMCRYPT_TRIALDIVISION_DIVIDE_TEST_CYCLES (16)
#define SYMCRYPT_RABINMILLER_DIGIT_CYCLES (25300)
#endif
#define SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME (1<<22)
C_ASSERT( SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME <= UINT32_MAX );
C_ASSERT( SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME == ((UINT32) SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME) );
VOID
SYMCRYPT_CALL
SymCryptFdefMaskedCopyC(
_In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PCBYTE pbSrc,
_Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PBYTE pbDst,
UINT32 nDigits,
UINT32 mask )
{
UINT64 m64 = (UINT64)0 - (mask & 1);
PUINT64 pSrc = (PUINT64) pbSrc;
PUINT64 pDst = (PUINT64) pbDst;
SIZE_T i;
SYMCRYPT_ASSERT( (mask + 1) < 2 );
for( i=0; i< nDigits * SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT64 ); i += 2 )
{
pDst[i ] = (pSrc[i ] & m64) | (pDst[i ] & ~m64 );
pDst[i+1] = (pSrc[i+1] & m64) | (pDst[i+1] & ~m64 );
}
}
VOID
SYMCRYPT_CALL
SymCryptFdefMaskedCopy(
_In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PCBYTE pbSrc,
_Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PBYTE pbDst,
UINT32 nDigits,
UINT32 mask )
{
#if SYMCRYPT_CPU_AMD64 | SYMCRYPT_CPU_X86 | SYMCRYPT_CPU_ARM64 | SYMCRYPT_CPU_ARM
SYMCRYPT_ASSERT_ASYM_ALIGNED( pbSrc );
SYMCRYPT_ASSERT_ASYM_ALIGNED( pbDst );
SymCryptFdefMaskedCopyAsm( pbSrc, pbDst, nDigits, mask );
#else
SymCryptFdefMaskedCopyC( pbSrc, pbDst, nDigits, mask );
#endif
}
VOID
SYMCRYPT_CALL
SymCryptFdefConditionalSwapC(
_Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PBYTE pbSrc1,
_Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PBYTE pbSrc2,
UINT32 nDigits,
UINT32 cond )
{
UINT64 m64 = (UINT64)0 - (cond & 1);
PUINT64 pSrc1 = (PUINT64) pbSrc1;
PUINT64 pSrc2 = (PUINT64) pbSrc2;
UINT64 tmp1 = 0;
UINT64 tmp2 = 0;
SIZE_T i;
SYMCRYPT_ASSERT( cond < 2 );
for( i=0; i< nDigits * SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT64 ); i += 2 )
{
tmp1 = (pSrc1[i ] ^ pSrc2[i ]) & m64;
tmp2 = (pSrc1[i+1] ^ pSrc2[i+1]) & m64;
pSrc1[i ] ^= tmp1; pSrc2[i ] ^= tmp1;
pSrc1[i+1] ^= tmp2; pSrc2[i+1] ^= tmp2;
}
}
VOID
SYMCRYPT_CALL
SymCryptFdefConditionalSwap(
_Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PBYTE pbSrc1,
_Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PBYTE pbSrc2,
UINT32 nDigits,
UINT32 cond )
{
SymCryptFdefConditionalSwapC( pbSrc1, pbSrc2, nDigits, cond );
}
UINT32
SymCryptFdefDigitsFromBits( UINT32 nBits )
{
UINT32 res;
if( nBits == 0 )
{
res = 1;
}
else
{
SYMCRYPT_ASSERT( nBits <= SYMCRYPT_INT_MAX_BITS );
if( nBits > SYMCRYPT_INT_MAX_BITS )
{
res = 0;
} else {
res = SYMCRYPT_FDEF_DIGITS_FROM_BITS( nBits );
}
}
return res;
}
C_ASSERT( SYMCRYPT_INT_MAX_BITS < (1 << 30) );
PSYMCRYPT_INT
SYMCRYPT_CALL
SymCryptFdefIntAllocate( UINT32 nDigits )
{
PVOID p = NULL;
UINT32 cb;
PSYMCRYPT_INT res = NULL;
cb = SymCryptFdefSizeofIntFromDigits( nDigits );
if( cb != 0 )
{
p = SymCryptCallbackAlloc( cb );
}
if( p == NULL )
{
goto cleanup;
}
res = SymCryptIntCreate( p, cb, nDigits );
cleanup:
return res;
}
UINT32
SYMCRYPT_CALL
SymCryptFdefSizeofIntFromDigits( UINT32 nDigits )
{
SYMCRYPT_ASSERT( nDigits != 0 );
SYMCRYPT_ASSERT( nDigits <= SYMCRYPT_FDEF_UPB_DIGITS );
if( nDigits == 0 || nDigits > SYMCRYPT_FDEF_UPB_DIGITS )
{
return 0;
}
return SYMCRYPT_FIELD_OFFSET( SYMCRYPT_INT, ti ) + nDigits * SYMCRYPT_FDEF_DIGIT_SIZE;
}
PSYMCRYPT_INT
SYMCRYPT_CALL
SymCryptFdefIntCreate(
_Out_writes_bytes_( cbBuffer ) PBYTE pbBuffer,
SIZE_T cbBuffer,
UINT32 nDigits )
{
PSYMCRYPT_INT pInt = NULL;
UINT32 cb = SymCryptFdefSizeofIntFromDigits( nDigits );
SYMCRYPT_ASSERT( cb >= sizeof(SYMCRYPT_INT) );
SYMCRYPT_ASSERT( cbBuffer >= cb );
if( (cb == 0) || (cbBuffer < cb) )
{
goto cleanup;
}
SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
pInt = (PSYMCRYPT_INT) pbBuffer;
pInt->type = 'gI' << 16;
pInt->nDigits = nDigits;
pInt->cbSize = cb;
SYMCRYPT_SET_MAGIC( pInt );
cleanup:
return pInt;
}
VOID
SymCryptFdefIntCopyFixup(
_In_ PCSYMCRYPT_INT pSrc,
_Out_ PSYMCRYPT_INT pDst )
{
UNREFERENCED_PARAMETER( pSrc );
UNREFERENCED_PARAMETER( pDst );
SYMCRYPT_SET_MAGIC( pDst );
}
VOID
SymCryptFdefIntCopy(
_In_ PCSYMCRYPT_INT piSrc,
_Out_ PSYMCRYPT_INT piDst )
{
SYMCRYPT_CHECK_MAGIC( piSrc );
SYMCRYPT_CHECK_MAGIC( piDst );
SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
if( piSrc != piDst )
{
memcpy( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_FDEF_INT_PUINT32( piSrc ), SYMCRYPT_OBJ_NBYTES( piDst ));
}
}
VOID
SymCryptFdefIntMaskedCopy(
_In_ PCSYMCRYPT_INT piSrc,
_Inout_ PSYMCRYPT_INT piDst,
UINT32 mask )
{
SYMCRYPT_CHECK_MAGIC( piSrc );
SYMCRYPT_CHECK_MAGIC( piDst );
SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
SymCryptFdefMaskedCopy( (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piSrc ), (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piDst ), piSrc->nDigits, mask );
}
VOID
SYMCRYPT_CALL
SymCryptFdefIntConditionalCopy(
_In_ PCSYMCRYPT_INT piSrc,
_Inout_ PSYMCRYPT_INT piDst,
UINT32 cond )
{
SYMCRYPT_CHECK_MAGIC( piSrc );
SYMCRYPT_CHECK_MAGIC( piDst );
SYMCRYPT_ASSERT( piSrc->nDigits == piDst->nDigits );
SymCryptFdefMaskedCopy( (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piSrc ), (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piDst ), piSrc->nDigits, SYMCRYPT_MASK32_NONZERO( cond ) );
}
VOID
SYMCRYPT_CALL
SymCryptFdefIntConditionalSwap(
_Inout_ PSYMCRYPT_INT piSrc1,
_Inout_ PSYMCRYPT_INT piSrc2,
UINT32 cond )
{
SYMCRYPT_CHECK_MAGIC( piSrc1 );
SYMCRYPT_CHECK_MAGIC( piSrc2 );
SYMCRYPT_ASSERT( piSrc1->nDigits == piSrc2->nDigits );
SymCryptFdefConditionalSwap( (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piSrc1 ), (PBYTE) SYMCRYPT_FDEF_INT_PUINT32( piSrc2 ), piSrc1->nDigits, cond );
}
UINT32
SYMCRYPT_CALL
SymCryptFdefIntBitsizeOfObject( _In_ PCSYMCRYPT_INT piSrc )
{
return SYMCRYPT_FDEF_DIGIT_BITS * piSrc->nDigits;
}
UINT32
SYMCRYPT_CALL
SymCryptFdefNumberofDigitsFromInt( _In_ PCSYMCRYPT_INT piSrc )
{
return piSrc->nDigits;
}
SYMCRYPT_ERROR
SymCryptFdefIntCopyMixedSize(
_In_ PCSYMCRYPT_INT piSrc,
_Out_ PSYMCRYPT_INT piDst )
{
UINT32 n;
SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
SYMCRYPT_CHECK_MAGIC( piSrc );
SYMCRYPT_CHECK_MAGIC( piDst );
if( piSrc == piDst )
{
goto cleanup;
}
n = SYMCRYPT_MIN( piSrc->nDigits, piDst->nDigits );
memcpy( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_FDEF_INT_PUINT32( piSrc ), n * SYMCRYPT_FDEF_DIGIT_SIZE );
if( piDst->nDigits > n )
{
SymCryptWipe( &SYMCRYPT_FDEF_INT_PUINT32( piDst )[n * SYMCRYPT_FDEF_DIGIT_NUINT32], (piDst->nDigits - n) * SYMCRYPT_FDEF_DIGIT_SIZE );
}
if( piSrc->nDigits > n )
{
PUINT64 p = (PUINT64) &SYMCRYPT_FDEF_INT_PUINT32( piSrc )[n * SYMCRYPT_FDEF_DIGIT_NUINT32];
UINT64 v = 0;
UINT32 i = (piSrc->nDigits - n) * SYMCRYPT_FDEF_DIGIT_SIZE / sizeof( UINT64 );
while( i > 0 )
{
v |= *p++;
i--;
}
if( v != 0 )
{
scError = SYMCRYPT_BUFFER_TOO_SMALL;
goto cleanup;
}
}
cleanup:
return scError;
}
UINT32
SYMCRYPT_CALL
SymCryptFdefBitsizeOfUint32( UINT32 v )
{
UINT32 res;
UINT32 mask;
UINT32 vUpper;
UINT32 vBit1;
vUpper = v & 0xffff0000;
mask = (UINT32) ( (0 -(UINT64)(vUpper)) >> 32 );
res = mask & 16;
v = ((v & 0xffff) & ~mask) | ((vUpper >> 16) & mask);
vUpper = v & 0xff00;
mask = (0 - vUpper) >> 16;
res |= mask & 8;
v = ((v & 0xff) & ~mask) | ((v >> 8) & mask);
vUpper = v & 0xf0;
mask = (0 - vUpper) >> 16;
res |= mask & 4;
v = ((v & 0xf) & ~mask) | ((v >> 4) & mask );
vUpper = v & 0xc;
mask = (0 - vUpper) >> 16;
res |= mask & 2;
v = ((v & 0x3) & ~mask) | ((v >> 2) & mask);
vBit1 = (v >> 1) & 1;
res |= vBit1;
res += (v | vBit1) & 1;
return res;
}
UINT32
SYMCRYPT_CALL
SymCryptFdefIntBitsizeOfValue( _In_ PCSYMCRYPT_INT piSrc )
{
UINT32 nUint32 = SYMCRYPT_OBJ_NUINT32( piSrc );
UINT32 res = 0;
UINT32 msNonzeroWord = 0;
UINT32 searchingMask = SYMCRYPT_MASK32_SET;
UINT32 d;
UINT32 dIsNonzeroMask;
UINT32 foundMask;
SYMCRYPT_CHECK_MAGIC( piSrc );
while( nUint32 > 0 )
{
nUint32--;
d = SYMCRYPT_FDEF_INT_PUINT32( piSrc )[nUint32];
dIsNonzeroMask = SYMCRYPT_MASK32_NONZERO( d );
foundMask = dIsNonzeroMask & searchingMask;
res |= nUint32 & foundMask;
msNonzeroWord |= d & foundMask;
searchingMask &= ~foundMask;
}
res = res * 8 * sizeof( UINT32 ) + SymCryptFdefBitsizeOfUint32( msNonzeroWord );
return res;
}
VOID
SYMCRYPT_CALL
SymCryptFdefIntSetValueUint32(
UINT32 u32Src,
_Out_ PSYMCRYPT_INT piDst )
{
SYMCRYPT_CHECK_MAGIC( piDst );
SymCryptWipe( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_OBJ_NBYTES( piDst ) );
SYMCRYPT_FDEF_INT_PUINT32( piDst )[0] = u32Src;
}
C_ASSERT( SYMCRYPT_FDEF_DIGIT_SIZE >= 8 );
VOID
SYMCRYPT_CALL
SymCryptFdefIntSetValueUint64(
UINT64 u64Src,
_Out_ PSYMCRYPT_INT piDst )
{
SYMCRYPT_CHECK_MAGIC( piDst );
SymCryptWipe( SYMCRYPT_FDEF_INT_PUINT32( piDst ), SYMCRYPT_OBJ_NBYTES( piDst ) );
SYMCRYPT_FDEF_INT_PUINT32( piDst )[0] = (UINT32) u64Src;
SYMCRYPT_FDEF_INT_PUINT32( piDst )[1] = (UINT32)(u64Src >> 32);
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptFdefRawSetValue(
_In_reads_bytes_(cbSrc) PCBYTE pbSrc,
SIZE_T cbSrc,
SYMCRYPT_NUMBER_FORMAT format,
_Out_writes_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32) PUINT32 pDst,
UINT32 nDigits )
{
SYMCRYPT_ERROR scError;
UINT32 b;
INT32 step;
UINT32 w;
UINT32 windex;
UINT32 i;
UINT32 nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
switch( format )
{
case SYMCRYPT_NUMBER_FORMAT_LSB_FIRST:
step = 1;
break;
case SYMCRYPT_NUMBER_FORMAT_MSB_FIRST:
step = -1;
pbSrc += cbSrc;
pbSrc--;
break;
default:
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
for( windex = 0; windex < nWords; windex++ )
{
w = 0;
for( i=0; i<4; i++ )
{
if( cbSrc > 0 )
{
b = *pbSrc;
cbSrc -= 1;
pbSrc += step;
w |= b << 8*i;
}
}
pDst[windex] = w;
}
b = 0;
while( cbSrc > 0 )
{
b |= *pbSrc;
pbSrc += step;
cbSrc -= 1;
}
if( b > 0 )
{
scError = SYMCRYPT_BUFFER_TOO_SMALL;
goto cleanup;
}
scError = SYMCRYPT_NO_ERROR;
cleanup:
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptFdefIntSetValue(
_In_reads_bytes_(cbSrc) PCBYTE pbSrc,
SIZE_T cbSrc,
SYMCRYPT_NUMBER_FORMAT format,
_Out_ PSYMCRYPT_INT piDst )
{
SYMCRYPT_ERROR scError;
SYMCRYPT_CHECK_MAGIC( piDst );
scError = SymCryptFdefRawSetValue( pbSrc, cbSrc, format, SYMCRYPT_FDEF_INT_PUINT32( piDst ), piDst->nDigits );
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptFdefRawGetValue(
_In_reads_(nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32) PCUINT32 pSrc,
UINT32 nDigits,
_Out_writes_bytes_(cbDst) PBYTE pbDst,
SIZE_T cbDst,
SYMCRYPT_NUMBER_FORMAT format )
{
SYMCRYPT_ERROR scError;
UINT32 b;
INT32 step;
UINT32 w;
UINT32 windex;
UINT32 i;
UINT32 nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
switch( format )
{
case SYMCRYPT_NUMBER_FORMAT_LSB_FIRST:
step = 1;
break;
case SYMCRYPT_NUMBER_FORMAT_MSB_FIRST:
step = -1;
pbDst += cbDst;
pbDst--;
break;
default:
scError = SYMCRYPT_INVALID_ARGUMENT;
goto cleanup;
}
for( windex = 0; windex < nWords; windex++ )
{
w = pSrc[windex];
for( i=0; i<4; i++ )
{
b = w & 0xff;
w >>= 8;
if( cbDst > 0 )
{
*pbDst = (BYTE)b;
cbDst -= 1;
pbDst += step;
} else {
if( b != 0 )
{
scError = SYMCRYPT_BUFFER_TOO_SMALL;
goto cleanup;
}
}
}
}
while( cbDst > 0 )
{
*pbDst = 0;
pbDst += step;
cbDst -= 1;
}
scError = SYMCRYPT_NO_ERROR;
cleanup:
return scError;
}
SYMCRYPT_ERROR
SYMCRYPT_CALL
SymCryptFdefIntGetValue(
_In_ PCSYMCRYPT_INT piSrc,
_Out_writes_bytes_(cbDst) PBYTE pbDst,
SIZE_T cbDst,
SYMCRYPT_NUMBER_FORMAT format )
{
SYMCRYPT_ERROR scError;
SYMCRYPT_CHECK_MAGIC( piSrc );
scError = SymCryptFdefRawGetValue( &SYMCRYPT_FDEF_INT_PUINT32( piSrc )[0], piSrc->nDigits, pbDst, cbDst, format );
return scError;
}
UINT32
SYMCRYPT_CALL
SymCryptFdefIntGetValueLsbits32( _In_ PCSYMCRYPT_INT piSrc )
{
return SYMCRYPT_FDEF_INT_PUINT32( piSrc )[0];
}
UINT64
SYMCRYPT_CALL
SymCryptFdefIntGetValueLsbits64( _In_ PCSYMCRYPT_INT piSrc )
{
PCUINT32 p = SYMCRYPT_FDEF_INT_PUINT32( piSrc );
return ((UINT64)(p[1]) << 32) | p[0];
}
UINT32
SYMCRYPT_CALL
SymCryptFdefRawIsEqualUint32(
_In_reads_(nDigits*SYMCRYPT_FDEF_DIGIT_NUINT32) PCUINT32 pSrc1,
UINT32 nDigits,
_In_ UINT32 u32Src2 )
{
UINT32 d;
UINT32 nWords = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
d = pSrc1[0] ^ u32Src2;
for( UINT32 i=1; i<nWords; i++)
{
d |= pSrc1[i];
}
return SYMCRYPT_MASK32_ZERO( d );
}
UINT32
SYMCRYPT_CALL
SymCryptFdefIntIsEqualUint32(
_In_ PCSYMCRYPT_INT piSrc1,
_In_ UINT32 u32Src2 )
{
return SymCryptFdefRawIsEqualUint32( &SYMCRYPT_FDEF_INT_PUINT32( piSrc1 )[0], piSrc1->nDigits, u32Src2 );
}
UINT32
SYMCRYPT_CALL
SymCryptFdefIntIsEqual(
_In_ PCSYMCRYPT_INT piSrc1,
_In_ PCSYMCRYPT_INT piSrc2 )
{
UINT32 d;
UINT32 n1 = SYMCRYPT_OBJ_NUINT32( piSrc1 );
UINT32 n2 = SYMCRYPT_OBJ_NUINT32( piSrc2 );
UINT32 i;
UINT32 n;
PCUINT32 pSrc1 = SYMCRYPT_FDEF_INT_PUINT32( piSrc1 );
PCUINT32 pSrc2 = SYMCRYPT_FDEF_INT_PUINT32( piSrc2 );
n = SYMCRYPT_MIN( n1, n2 );
d = 0;
for( i=0; i < n ; i++ )
{
d |= pSrc1[i] ^ pSrc2[i];
}
while( i < n1 )
{
d |= pSrc1[i];
i++;
}
while( i < n2 )
{
d |= pSrc2[i];
i++;
}
return SYMCRYPT_MASK32_ZERO( d );
}
PSYMCRYPT_DIVISOR
SYMCRYPT_CALL
SymCryptFdefDivisorAllocate( UINT32 nDigits )
{
PVOID p = NULL;
UINT32 cb;
PSYMCRYPT_DIVISOR res = NULL;
cb = SymCryptFdefSizeofDivisorFromDigits( nDigits );
if( cb != 0 )
{
p = SymCryptCallbackAlloc( cb );
}
if( p == NULL )
{
goto cleanup;
}
res = SymCryptFdefDivisorCreate( p, cb, nDigits );
cleanup:
return res;
}
UINT32
SYMCRYPT_CALL
SymCryptFdefSizeofDivisorFromDigits( UINT32 nDigits )
{
SYMCRYPT_ASSERT( nDigits != 0 );
SYMCRYPT_ASSERT( nDigits <= SYMCRYPT_FDEF_UPB_DIGITS );
if( nDigits == 0 || nDigits > SYMCRYPT_FDEF_UPB_DIGITS )
{
return 0;
}
return SYMCRYPT_FIELD_OFFSET( SYMCRYPT_DIVISOR, Int ) + SymCryptFdefSizeofIntFromDigits( nDigits );
}
PSYMCRYPT_DIVISOR
SYMCRYPT_CALL
SymCryptFdefDivisorCreate(
_Out_writes_bytes_( cbBuffer ) PBYTE pbBuffer,
SIZE_T cbBuffer,
UINT32 nDigits )
{
PSYMCRYPT_DIVISOR pdDiv = NULL;
UINT32 cb = SymCryptSizeofDivisorFromDigits( nDigits );
SYMCRYPT_ASSERT( cb >= sizeof(SYMCRYPT_DIVISOR) );
SYMCRYPT_ASSERT( cbBuffer >= cb );
if( (cb == 0) || (cbBuffer < cb) )
{
goto cleanup;
}
SYMCRYPT_ASSERT_ASYM_ALIGNED( pbBuffer );
pdDiv = (PSYMCRYPT_DIVISOR) pbBuffer;
pdDiv->type = 'gD' << 16;
pdDiv->nDigits = nDigits;
pdDiv->cbSize = cb;
SYMCRYPT_SET_MAGIC( pdDiv );
SymCryptIntCreate( (PBYTE)&pdDiv->Int, cbBuffer - SYMCRYPT_FIELD_OFFSET( SYMCRYPT_DIVISOR, Int ), nDigits );
cleanup:
return pdDiv;
}
VOID
SymCryptFdefDivisorCopyFixup(
_In_ PCSYMCRYPT_DIVISOR pdSrc,
_Out_ PSYMCRYPT_DIVISOR pdDst )
{
UNREFERENCED_PARAMETER( pdSrc );
UNREFERENCED_PARAMETER( pdDst );
SymCryptFdefIntCopyFixup( &pdSrc->Int, &pdDst->Int );
SYMCRYPT_SET_MAGIC( pdDst );
}
VOID
SymCryptFdefDivisorCopy(
_In_ PCSYMCRYPT_DIVISOR pdSrc,
_Out_ PSYMCRYPT_DIVISOR pdDst )
{
SYMCRYPT_CHECK_MAGIC( pdSrc );
SYMCRYPT_CHECK_MAGIC( pdDst );
SYMCRYPT_ASSERT( pdSrc->nDigits == pdDst->nDigits );
if( pdSrc != pdDst )
{
memcpy( pdDst, pdSrc, pdDst->cbSize );
SymCryptFdefDivisorCopyFixup( pdSrc, pdDst );
}
}
VOID
SYMCRYPT_CALL
SymCryptFdefClaimScratch( PBYTE pbScratch, SIZE_T cbScratch, SIZE_T cbMin )
{
#if SYMCRYPT_DEBUG
SYMCRYPT_ASSERT( cbScratch >= cbMin );
SymCryptWipe( pbScratch, cbMin );
#else
UNREFERENCED_PARAMETER( pbScratch );
UNREFERENCED_PARAMETER( cbScratch );
UNREFERENCED_PARAMETER( cbMin );
#endif
}
UINT32
SymCryptTestTrialdivisionMaxSmallPrime(
_In_ PCSYMCRYPT_TRIALDIVISION_CONTEXT pContext )
{
return pContext->maxTrialPrime;
}
UINT64
SymCryptInverseMod2e64( UINT64 m )
{
UINT32 inv32;
UINT64 inv64;
UINT32 m32;
m32 = (UINT32)m;
inv32 = m32 ^ (((m32 - 1) * 0x6) & 0x8);
SYMCRYPT_ASSERT( ((m&1) == 0) || (((inv32 * m32) & 0xf) == 1) );
inv32 = inv32 * (2 - inv32 * m32 );
SYMCRYPT_ASSERT( ((m&1) == 0) || (((inv32 * m32) & 0xff) == 1) );
inv32 = inv32 * (2 - inv32 * m32 );
SYMCRYPT_ASSERT( ((m&1) == 0) || (((inv32 * m32) & 0xffff) == 1) );
inv32 = inv32 * (2 - inv32 * m32 );
SYMCRYPT_ASSERT( ((m&1) == 0) || ((inv32 * m32) == 1) );
inv64 = inv32;
inv64 = inv64 * (2 - inv64 * m );
SYMCRYPT_ASSERT( ((m&1) == 0) || ((inv64 * m) == 1) );
return inv64;
}
VOID
SYMCRYPT_CALL
SymCryptFdefInitTrialdivisionPrime(
UINT32 prime,
_Out_ PSYMCRYPT_TRIALDIVISION_PRIME pPrime )
{
pPrime->invMod2e64 = SymCryptInverseMod2e64( prime );
pPrime->compareLimit = ((UINT64) -1) / prime;
}
FORCEINLINE
UINT32
SymCryptIsMultipleOfSmallPrime( UINT64 value, PCSYMCRYPT_TRIALDIVISION_PRIME pPrime )
{
return (value * pPrime->invMod2e64) <= pPrime->compareLimit;
}
VOID
SYMCRYPT_CALL
SymCryptFdefInitTrialDivisionGroup( PSYMCRYPT_TRIALDIVISION_GROUP pGroup, UINT32 nPrimes, UINT32 primeProd )
{
UINT32 f;
UINT32 r;
UINT32 i;
pGroup->nPrimes = nPrimes;
f = (UINT32) (((UINT64)1 << 32) % primeProd);
pGroup->factor[0] = f;
r = f;
for( i=1; i<9; i++ )
{
r = (UINT32) (SYMCRYPT_MUL32x32TO64( r, f ) % primeProd);
pGroup->factor[i] = r;
}
}
UINT32
SYMCRYPT_CALL
SymCryptGenerateSmallPrimes( UINT32 maxPrime, PUINT32 * ppList )
{
UINT32 nPrimes = 0;
PUINT32 pList = NULL;
UINT32 nSieve;
PBYTE pSieve;
UINT32 pi;
UINT32 p;
UINT32 si;
UINT32 i;
maxPrime = SYMCRYPT_MAX( maxPrime, 32 );
maxPrime = SYMCRYPT_MIN( maxPrime, 1 << 24 );
nSieve = (maxPrime - 1) / 2 + 1;
pSieve = SymCryptCallbackAlloc( nSieve );
if( pSieve == NULL )
{
goto cleanup;
}
SymCryptWipe( pSieve, nSieve );
pi = 1;
p = 2*pi + 1;
for(;;)
{
si = 2*(pi*pi + pi);
if( si > nSieve )
{
break;
}
while( si < nSieve )
{
pSieve[si] = 1;
si += p;
}
do {
pi += 1;
} while( pSieve[pi] != 0 );
p = 2*pi + 1;
}
pSieve[1] = 1;
pSieve[2] = 1;
pSieve[8] = 1;
for( i=1; i<nSieve; i++ )
{
nPrimes += 1 - pSieve[i];
}
pList = SymCryptCallbackAlloc( nPrimes * sizeof( UINT32 ) );
if( pList == NULL )
{
goto cleanup;
}
pi = 0;
for( i=1; i<nSieve; i++ )
{
if( pSieve[i] == 0 )
{
pList[pi++] = 2*i+1;
}
}
SYMCRYPT_ASSERT( pi == nPrimes );
cleanup:
if( pSieve != NULL )
{
SymCryptWipe( pSieve, nSieve );
SymCryptCallbackFree( pSieve );
}
*ppList = pList;
return nPrimes;
}
PCSYMCRYPT_TRIALDIVISION_CONTEXT
SYMCRYPT_CALL
SymCryptFdefCreateTrialDivisionContext( UINT32 nDigits )
{
PSYMCRYPT_TRIALDIVISION_CONTEXT pRes = NULL;
PBYTE pAlloc;
UINT32 nBytes;
UINT32 iPrime;
UINT32 iGroup;
UINT32 nPrimes;
UINT32 nGroups;
UINT32 M;
UINT32 iGroupSpec;
UINT32 i;
UINT32 j;
UINT64 cRabinMillerCost;
UINT64 cPerPrimeCost;
UINT64 tmp64;
UINT32 maxPrime;
UINT32 minPrime;
UINT32 nSmallPrimes = 0;
UINT32 n;
UINT32 nP;
UINT32 nG;
PUINT32 pSmallPrimeList = NULL;
if( nDigits <= 1000 )
{
if( nDigits == 0 )
{
goto cleanup;
}
cRabinMillerCost = (UINT64) nDigits * nDigits * nDigits * (SYMCRYPT_RABINMILLER_DIGIT_CYCLES * 8 / 10);
i = 0;
minPrime = 0;
for(;;)
{
nPrimes = g_SymCryptSmallPrimeGroupsSpec[i].nPrimes;
maxPrime = g_SymCryptSmallPrimeGroupsSpec[i].maxPrime;
nGroups = g_SymCryptSmallPrimeGroupsSpec[i].nGroups;
cPerPrimeCost = (UINT64) nDigits * SYMCRYPT_TRIALDIVISION_DIGIT_REDUCTION_CYCLES / nPrimes + SYMCRYPT_TRIALDIVISION_DIVIDE_TEST_CYCLES;
if( nGroups == 0 || maxPrime * cPerPrimeCost >= cRabinMillerCost)
{
break;
}
i++;
minPrime = maxPrime;
}
tmp64 = cRabinMillerCost / cPerPrimeCost;
tmp64 = SYMCRYPT_MIN( tmp64, SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME );
maxPrime = (UINT32) tmp64;
maxPrime = SYMCRYPT_MAX( maxPrime, minPrime );
}
else
{
maxPrime = SYMCRYPT_TRIALDIVISION_MAX_SMALL_PRIME;
}
nSmallPrimes = SymCryptGenerateSmallPrimes( maxPrime, &pSmallPrimeList );
n = nSmallPrimes;
nG = 0;
nP = 0;
i = 0;
for(;;)
{
nPrimes = g_SymCryptSmallPrimeGroupsSpec[i].nPrimes;
nGroups = g_SymCryptSmallPrimeGroupsSpec[i].nGroups;
if( n < nPrimes * nGroups || nGroups == 0 )
{
n = n / nPrimes;
nG += n;
nP += n * nPrimes;
n = 0;
break;
}
nG += nGroups;
nP += nPrimes * nGroups;
n -= nPrimes * nGroups;
i++;
}
nBytes = sizeof( SYMCRYPT_TRIALDIVISION_CONTEXT )
+ (nG + 1) * sizeof( SYMCRYPT_TRIALDIVISION_GROUP )
+ (nP + 1) * sizeof( SYMCRYPT_TRIALDIVISION_PRIME )
+ (nP + 1) * sizeof( UINT32 );
pAlloc = SymCryptCallbackAlloc( nBytes );
if( pAlloc == NULL )
{
goto cleanup;
}
pRes = (PSYMCRYPT_TRIALDIVISION_CONTEXT) pAlloc;
pAlloc += sizeof( *pRes );
pRes->nBytesAlloc = nBytes;
pRes->pGroupList = (PSYMCRYPT_TRIALDIVISION_GROUP)pAlloc;
pAlloc += (nG + 1) * sizeof( SYMCRYPT_TRIALDIVISION_GROUP );
pRes->pPrimeList = (PSYMCRYPT_TRIALDIVISION_PRIME) pAlloc;
pAlloc += (nP + 1) * sizeof( SYMCRYPT_TRIALDIVISION_PRIME );
pRes->pPrimes = (PUINT32) pAlloc;
pAlloc += (nP + 1) * sizeof( UINT32 );
SYMCRYPT_ASSERT( nBytes == (SIZE_T)(pAlloc - (PBYTE)pRes) );
SymCryptFdefInitTrialdivisionPrime( 3, &pRes->Primes3_5_17[0] );
SymCryptFdefInitTrialdivisionPrime( 5, &pRes->Primes3_5_17[1] );
SymCryptFdefInitTrialdivisionPrime( 17, &pRes->Primes3_5_17[2] );
memcpy( pRes->pPrimes, pSmallPrimeList, nP * sizeof( UINT32 ) );
pRes->pPrimes[nP] = 0;
pRes->maxTrialPrime = pRes->pPrimes[nP-1];
for( iPrime = 0; iPrime < nP; iPrime++ )
{
SymCryptFdefInitTrialdivisionPrime( pRes->pPrimes[iPrime], &pRes->pPrimeList[iPrime] );
}
pRes->pPrimeList[nP].invMod2e64 = 0;
pRes->pPrimeList[nP].compareLimit = 0;
#pragma warning( suppress: 4127 )
SYMCRYPT_ASSERT( SYMCRYPT_MAX_SMALL_PRIME_GROUP_PRODUCT <= (UINT32)-1 );
iGroup = 0;
iPrime = 0;
iGroupSpec = 0;
nPrimes = g_SymCryptSmallPrimeGroupsSpec[iGroupSpec].nPrimes;
nGroups = g_SymCryptSmallPrimeGroupsSpec[iGroupSpec].nGroups;
while( iPrime < nP )
{
if( nGroups == 0 )
{
iGroupSpec +=1 ;
nPrimes = g_SymCryptSmallPrimeGroupsSpec[iGroupSpec].nPrimes;
nGroups = g_SymCryptSmallPrimeGroupsSpec[iGroupSpec].nGroups;
if( nGroups == 0 )
{
nGroups = nG - iGroup;
}
}
SYMCRYPT_ASSERT( iPrime + nPrimes <= nP );
M = pRes->pPrimes[iPrime++];
for( j=1; j<nPrimes; j++ )
{
SYMCRYPT_ASSERT( M <= SYMCRYPT_MAX_SMALL_PRIME_GROUP_PRODUCT / pRes->pPrimes[iPrime] );
M *= pRes->pPrimes[iPrime++];
}
SymCryptFdefInitTrialDivisionGroup( &pRes->pGroupList[iGroup], nPrimes, M );
iGroup++;
nGroups--;
}
SYMCRYPT_ASSERT( iPrime == nP && iGroup == nG );
pRes->pGroupList[iGroup].nPrimes = 0;
cleanup:
if( pSmallPrimeList != NULL )
{
SymCryptWipe( pSmallPrimeList, nSmallPrimes * sizeof( UINT32 ) );
SymCryptCallbackFree( pSmallPrimeList );
pSmallPrimeList = NULL;
}
return pRes;
}
VOID
SYMCRYPT_CALL
SymCryptFdefFreeTrialDivisionContext( PCSYMCRYPT_TRIALDIVISION_CONTEXT pContext )
{
SymCryptWipe( (PBYTE) pContext, pContext->nBytesAlloc );
SymCryptCallbackFree( (PSYMCRYPT_TRIALDIVISION_CONTEXT) pContext );
}
UINT32
SYMCRYPT_CALL
SymCryptFdefIntFindSmallDivisor(
_In_ PCSYMCRYPT_TRIALDIVISION_CONTEXT pContext,
_In_ PCSYMCRYPT_INT piSrc,
_Out_writes_bytes_( cbScratch ) PBYTE pbScratch,
SIZE_T cbScratch )
{
PCUINT32 pSrc = SYMCRYPT_FDEF_INT_PUINT32( piSrc );
PCUINT32 p;
UINT32 nDigits = piSrc->nDigits;
UINT32 nUint32 = nDigits * SYMCRYPT_FDEF_DIGIT_NUINT32;
UINT64 Acc;
PCSYMCRYPT_TRIALDIVISION_GROUP pGroup;
PCSYMCRYPT_TRIALDIVISION_PRIME pPrime;
UINT32 nPrimes;
UINT32 res;
if( (*pSrc & 1) == 0 )
{
res = 2;
goto cleanup;
}
Acc = 0;
p = pSrc;
do {
#if SYMCRYPT_FDEF_DIGIT_SIZE == 16
Acc = Acc + p[0] + p[1] + p[2] + p[3];
p += 4;
#elif (SYMCRYPT_FDEF_DIGIT_SIZE % 32) == 0
Acc = Acc + p[0] + p[1] + p[2] + p[3] + p[4] + p[5] + p[6] + p[7];
p += 8;
#else
#error ??
#endif
} while( p < pSrc + nUint32 );
if( SymCryptIsMultipleOfSmallPrime( Acc, &pContext->Primes3_5_17[0] ) )
{
res = 3;
goto cleanup;
}
if( SymCryptIsMultipleOfSmallPrime( Acc, &pContext->Primes3_5_17[1] ) )
{
res = 5;
goto cleanup;
}
if( SymCryptIsMultipleOfSmallPrime( Acc, &pContext->Primes3_5_17[2] ) )
{
res = 17;
goto cleanup;
}
pGroup = pContext->pGroupList;
pPrime = pContext->pPrimeList;
while( (nPrimes = pGroup->nPrimes) != 0 )
{
Acc = 0;
p = pSrc + nUint32;
#if SYMCRYPT_FDEF_DIGIT_SIZE == 16
if( (nUint32 & 4) != 0 )
{
p -= 4;
Acc =
p[0] +
SYMCRYPT_MUL32x32TO64( p[1], pGroup->factor[0] ) +
SYMCRYPT_MUL32x32TO64( p[2], pGroup->factor[1] ) +
SYMCRYPT_MUL32x32TO64( p[3], pGroup->factor[2] );
} else {
p -= 8;
Acc =
p[0] +
SYMCRYPT_MUL32x32TO64( p[1], pGroup->factor[0] ) +
SYMCRYPT_MUL32x32TO64( p[2], pGroup->factor[1] ) +
SYMCRYPT_MUL32x32TO64( p[3], pGroup->factor[2] ) +
SYMCRYPT_MUL32x32TO64( p[4], pGroup->factor[3] ) +
SYMCRYPT_MUL32x32TO64( p[5], pGroup->factor[4] ) +
SYMCRYPT_MUL32x32TO64( p[6], pGroup->factor[5] ) +
SYMCRYPT_MUL32x32TO64( p[7], pGroup->factor[6] );
}
#elif (SYMCRYPT_FDEF_DIGIT_SIZE % 32) == 0
p -= 8;
Acc =
p[0] +
SYMCRYPT_MUL32x32TO64( p[1], pGroup->factor[0] ) +
SYMCRYPT_MUL32x32TO64( p[2], pGroup->factor[1] ) +
SYMCRYPT_MUL32x32TO64( p[3], pGroup->factor[2] ) +
SYMCRYPT_MUL32x32TO64( p[4], pGroup->factor[3] ) +
SYMCRYPT_MUL32x32TO64( p[5], pGroup->factor[4] ) +
SYMCRYPT_MUL32x32TO64( p[6], pGroup->factor[5] ) +
SYMCRYPT_MUL32x32TO64( p[7], pGroup->factor[6] );
#else
#error ??
#endif
while( p > pSrc )
{
p -= 8;
Acc =
p[0] +
SYMCRYPT_MUL32x32TO64( p[1], pGroup->factor[0] ) +
SYMCRYPT_MUL32x32TO64( p[2], pGroup->factor[1] ) +
SYMCRYPT_MUL32x32TO64( p[3], pGroup->factor[2] ) +
SYMCRYPT_MUL32x32TO64( p[4], pGroup->factor[3] ) +
SYMCRYPT_MUL32x32TO64( p[5], pGroup->factor[4] ) +
SYMCRYPT_MUL32x32TO64( p[6], pGroup->factor[5] ) +
SYMCRYPT_MUL32x32TO64( p[7], pGroup->factor[6] ) +
SYMCRYPT_MUL32x32TO64( (UINT32) Acc , pGroup->factor[7] ) +
SYMCRYPT_MUL32x32TO64( (UINT32)(Acc >> 32), pGroup->factor[8] );
}
while( nPrimes > 0 )
{
if( SymCryptIsMultipleOfSmallPrime( Acc, pPrime ) )
{
res = pContext->pPrimes[ (pPrime - pContext->pPrimeList) ];
goto cleanup;
}
pPrime++;
nPrimes--;
}
pGroup++;
}
UNREFERENCED_PARAMETER( pbScratch );
UNREFERENCED_PARAMETER( cbScratch );
res = 0;
cleanup:
return res;
}
VOID
SYMCRYPT_CALL
SymCryptFdefMaskedCopyAsm(
_In_reads_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PCBYTE pbSrc,
_Inout_updates_bytes_( nDigits*SYMCRYPT_FDEF_DIGIT_SIZE ) PBYTE pbDst,
UINT32 nDigits,
UINT32 mask )
{
SymCryptFdefMaskedCopyC( pbSrc, pbDst, nDigits, mask );
}