Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ public GenericSqrtRatioCalculator(final ECCurve curve, final BigInteger z)
this.c3 = this.c2.subtract(BigInteger.ONE).divide(BigInteger.valueOf(2));
this.c4 = BigInteger.valueOf(2).pow(this.c1).subtract(BigInteger.ONE);
this.c5 = BigInteger.valueOf(2).pow(this.c1 - 1);
this.c6 = z.modPow(this.c2, this.q);
this.c7 = z.modPow(this.c2.add(BigInteger.ONE).divide(BigInteger.valueOf(2)), q);
// c2 = 2*c3 + 1: share z^c3 between the two constants (RFC 9380, F.2.1.1).
BigInteger zToC3 = z.modPow(this.c3, this.q);
this.c7 = zToC3.multiply(z).mod(this.q);
this.c6 = zToC3.multiply(this.c7).mod(this.q);
}

private int calculateC1()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.bouncycastle.crypto.hash2curve.test.impl.GenericSqrtRatioCalculatorTest;
import org.bouncycastle.crypto.hash2curve.test.impl.GenericSqrtRatioConstantsTest;
import org.bouncycastle.crypto.hash2curve.test.impl.SimplifiedShallueVanDeWoestijneMapToCurveTest;
import org.bouncycastle.test.PrintTestResult;

Expand All @@ -23,6 +24,7 @@ public static Test suite()
suite.addTestSuite(HashToFieldTest.class);
suite.addTestSuite(OPRFHashToScalarTest.class);
suite.addTestSuite(GenericSqrtRatioCalculatorTest.class);
suite.addTestSuite(GenericSqrtRatioConstantsTest.class);

suite.addTestSuite(SimplifiedShallueVanDeWoestijneMapToCurveTest.class);
suite.addTestSuite(H2cUtilsTest.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package org.bouncycastle.crypto.hash2curve.test.impl;

import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.Random;

import junit.framework.TestCase;

import org.bouncycastle.crypto.hash2curve.impl.GenericSqrtRatioCalculator;
import org.bouncycastle.crypto.hash2curve.impl.SqrtRatio;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve;
import org.bouncycastle.math.ec.custom.sec.SecP384R1Curve;
import org.bouncycastle.math.ec.custom.sec.SecP521R1Curve;

public class GenericSqrtRatioConstantsTest
extends TestCase
{
public void testInitializationUsesSingleModularExponentiation()
{
CountingBigInteger z = new CountingBigInteger(BigInteger.valueOf(-10));
new GenericSqrtRatioCalculator(new SecP256R1Curve(), z);
assertEquals("constructor modular exponentiations", 1, z.calls);
}

public void testConstantsMatchDirectPowersForSmallFields()
throws Exception
{
// Includes c3 == 0 (3, 5, 17, 257) and several two-adic valuations of q - 1.
int[] primes = { 3, 5, 7, 13, 17, 29, 97, 257 };
for (int i = 0; i < primes.length; ++i)
{
int p = primes[i];
ECCurve curve = smallCurve(BigInteger.valueOf(p));
for (int z = -2 * p; z <= 2 * p; ++z)
{
checkConstants(curve, BigInteger.valueOf(z));
}
}
}

public void testConstantsMatchDirectPowersForLargeFields()
throws Exception
{
ECCurve[] curves = {
new SecP256R1Curve(), new SecP384R1Curve(), new SecP521R1Curve(),
smallCurve(BigInteger.ONE.shiftLeft(255).subtract(BigInteger.valueOf(19))),
smallCurve(BigInteger.ONE.shiftLeft(448).subtract(BigInteger.ONE.shiftLeft(224)).subtract(BigInteger.ONE))
};
Random random = new Random(9380L);
for (int i = 0; i < curves.length; ++i)
{
BigInteger q = curves[i].getField().getCharacteristic();
BigInteger[] edges = {
BigInteger.ZERO, BigInteger.ONE, BigInteger.ONE.negate(),
BigInteger.valueOf(-10), q.subtract(BigInteger.ONE), q,
q.add(BigInteger.ONE), q.shiftLeft(1).add(BigInteger.valueOf(3))
};
for (int j = 0; j < edges.length; ++j)
{
checkConstants(curves[i], edges[j]);
}
for (int j = 0; j < 16; ++j)
{
BigInteger z = new BigInteger(2 * q.bitLength(), random);
checkConstants(curves[i], (j & 1) == 0 ? z : z.negate());
}
}
}

public void testRepeatedRatiosOverSmallFields()
{
int[] primes = { 3, 5, 7, 13, 17, 29 };
for (int i = 0; i < primes.length; ++i)
{
BigInteger q = BigInteger.valueOf(primes[i]);
BigInteger half = q.subtract(BigInteger.ONE).shiftRight(1);
BigInteger z = BigInteger.valueOf(2);
while (z.modPow(half, q).equals(BigInteger.ONE))
{
z = z.add(BigInteger.ONE);
}
GenericSqrtRatioCalculator calculator = new GenericSqrtRatioCalculator(smallCurve(q), z);
for (int u = 1; u < primes[i]; ++u)
{
for (int v = 1; v < primes[i]; ++v)
{
BigInteger numerator = BigInteger.valueOf(u);
BigInteger denominator = BigInteger.valueOf(v);
boolean square = numerator.multiply(denominator.modInverse(q)).mod(q)
.modPow(half, q).equals(BigInteger.ONE);
SqrtRatio result = calculator.sqrtRatio(numerator, denominator);
assertEquals("quadratic-residue flag", square, result.isQR());
BigInteger expected = square ? numerator : numerator.multiply(z).mod(q);
assertEquals("square-root equation", expected,
result.getRatio().multiply(result.getRatio()).multiply(denominator).mod(q));
}
}
}
}

private static ECCurve smallCurve(BigInteger q)
{
// Fixed, known primes only. No point arithmetic is needed for these constant tests.
return new ECCurve.Fp(q, BigInteger.ONE, BigInteger.ONE, null, null, true);
}

private static void checkConstants(ECCurve curve, BigInteger z)
throws Exception
{
BigInteger q = curve.getField().getCharacteristic();
BigInteger oddPart = q.subtract(BigInteger.ONE);
oddPart = oddPart.shiftRight(oddPart.getLowestSetBit());
GenericSqrtRatioCalculator calculator = new GenericSqrtRatioCalculator(curve, z);
// Compare the actual stored constants, not a separate implementation of the rewrite.
assertEquals("c6 for q=" + q + ", z=" + z,
z.modPow(oddPart, q), constant(calculator, "c6"));
assertEquals("c7 for q=" + q + ", z=" + z,
z.modPow(oddPart.add(BigInteger.ONE).shiftRight(1), q), constant(calculator, "c7"));
}

private static BigInteger constant(GenericSqrtRatioCalculator calculator, String name)
throws Exception
{
Field field = GenericSqrtRatioCalculator.class.getDeclaredField(name);
field.setAccessible(true);
return (BigInteger)field.get(calculator);
}

private static class CountingBigInteger
extends BigInteger
{
private int calls;

CountingBigInteger(BigInteger value)
{
super(value.toByteArray());
}

public BigInteger modPow(BigInteger exponent, BigInteger modulus)
{
++calls;
return super.modPow(exponent, modulus);
}
}
}