VOID testCompositeModInv()

in unittest/lib/testArithmetic.cpp [3672:3757]


VOID testCompositeModInv()
{
    // We just check that ModInv works properly with weird inputs
    PSYMCRYPT_MODULUS pMod = SymCryptModulusAllocate( 1 );
    PSYMCRYPT_MODELEMENT pEl = SymCryptModElementAllocate( pMod );
    PSYMCRYPT_MODELEMENT pInv = SymCryptModElementAllocate( pMod );
    SIZE_T cbScratch = 1 << 20;
    PBYTE pbScratch = (PBYTE) SymCryptCallbackAlloc( cbScratch );   // this allocator provides the necessary alignment
    SYMCRYPT_ERROR scError;

    CHECK( pMod != NULL && pEl != NULL && pbScratch != NULL, "Out of memory" );

    for( int cnt = 0; cnt < 1000; cnt++ )
    {
        UINT32 mod = (UINT32) g_rng.sizet( 2, (1<<16) );

        // Must be 2 or odd to even pass the sanity checks for prime moduli
        if( mod != 2 )
        {
            mod |= 1;
        }

        UINT32 x = (UINT32) g_rng.sizet( mod );
        CHECK( x < mod, "?" );
        SymCryptIntSetValueUint32( mod, SymCryptIntFromModulus( pMod ) );

        // Our current code requires the PRIME and DATA_PUBLIC flags.

        UINT32 modFlags = 0;
        BYTE b = g_rng.byte();

        /* Code to generat random flags (for when we support them)
        if( b & 1 )
        {
            if( b & 2 )
            {
                modFlags |= SYMCRYPT_FLAG_DATA_PUBLIC;
            } else {
                modFlags |= SYMCRYPT_FLAG_MODULUS_PARITY_PUBLIC;
            }
        }
        if( (b & 4) != 0 && (mod == 2 || (mod &  1) != 0 )  )
        {
            // We deliberately limit ourselves to only checking for oddness in primes
            // as our RSA/DSA code doesn't check for primality when receiving parameters from
            // outside parties.
            modFlags |= SYMCRYPT_FLAG_MODULUS_PRIME;
        }
        */
        modFlags = SYMCRYPT_FLAG_DATA_PUBLIC | SYMCRYPT_FLAG_MODULUS_PRIME;

        SymCryptIntToModulus( SymCryptIntFromModulus( pMod ), pMod, g_rng.byte(), modFlags, pbScratch, cbScratch );

        SymCryptModElementSetValueUint32( x, pMod, pEl, pbScratch, cbScratch );

        // We must use DATA_PUBLIC, otherwise the modinv routine blinds the input which
        // can introduce errors when the modulus isn't prime, and that makes our test
        // less sensitive.
        UINT32 opFlags = SYMCRYPT_FLAG_DATA_PUBLIC;

        scError = SymCryptModInv( pMod, pEl, pInv, opFlags, pbScratch, cbScratch );

        // Check that the result is correct when we get no error
        if( scError == SYMCRYPT_NO_ERROR )
        {
            SymCryptModMul( pMod, pEl, pInv, pInv, pbScratch, cbScratch );
            SYMCRYPT_ERROR scError2 = SymCryptModElementGetValue( pMod, pInv, &b, 1, SYMCRYPT_NUMBER_FORMAT_LSB_FIRST, pbScratch, cbScratch );
            CHECK( scError2 == SYMCRYPT_NO_ERROR && b == 1, "ModInv * input is not 1" );
        }

        BOOL coPrime = GcdUint32( x, mod ) == 1;

        CHECK( coPrime || scError != SYMCRYPT_NO_ERROR, "No error for modinv that does not exist" );

        CHECK( (modFlags & SYMCRYPT_FLAG_DATA_PUBLIC) == 0 ||
                (modFlags & SYMCRYPT_FLAG_MODULUS_PRIME) == 0 ||
                !coPrime ||
                scError == SYMCRYPT_NO_ERROR, "Unexpected error for modinverse" );
    }

    SymCryptWipe(pbScratch,cbScratch);
    SymCryptCallbackFree(pbScratch);
    SymCryptModElementFree( pMod, pEl );
    SymCryptModElementFree( pMod, pInv );
    SymCryptModulusFree( pMod );
}