Skip to content

Commit

Permalink
MultiplicativeGroupAlgebra.CreateCryptoGroup skips more known non-pri…
Browse files Browse the repository at this point in the history
…me candidates.

Sophie Germain primes can only have a value of 5 modulo 6, which is used to improve the prime finding algorithm in MultiplicativeGroupAlgebra.CreateCryptoGroup, which previously was checking every odd number.
This reduces the number of primality tests that are performed by up to 2/3.

Also joins the previously separate test cases into a single implementation and fixes a bug in which the rng mock was
not returning the expected bytes.
  • Loading branch information
lumip committed Oct 21, 2024
1 parent 92ad034 commit 4b5cf4b
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// CompactCryptoGroupAlgebra - C# implementation of abelian group algebra for experimental cryptography

// SPDX-FileCopyrightText: 2022 Lukas Prediger <lumip@lumip.de>
// SPDX-FileCopyrightText: 2022-2024 Lukas Prediger <lumip@lumip.de>
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileType: SOURCE

Expand Down Expand Up @@ -299,60 +299,36 @@ public void TestCreateCryptoGroup()
}

[Test]
public void TestCreateCryptoGroupForLevelRngReturnsOdd()
[TestCase(1)] // rng returns even, +1 mod 6 == 1
[TestCase(4)] // rng returns odd, mod 6 == 1
[TestCase(6)] // rng returns odd, mod 6 == 5
[TestCase(8)] // rng returns odd, mod 6 == 3
public void TestCreateCryptoGroupForLevel(int subtrahend)
{
int securityLevel = 32;
var expectedPrimeLength = MultiplicativeGroupAlgebra.ComputePrimeLengthForSecurityLevel(securityLevel);
var expectedOrderLength = NumberLength.FromBitLength(expectedPrimeLength.InBits - 1);

var primeBeforeOrder = BigInteger.Parse("332306998946228968225951765070101393");
var primeBeforeOrder = BigInteger.Parse("166153499473114484112975882535050653"); // not Sophie Germain prime
var order = BigPrime.CreateWithoutChecks(BigInteger.Parse("166153499473114484112975882535050719"));
var prime = BigPrime.CreateWithoutChecks(BigInteger.Parse("332306998946228968225951765070101439"));

var rngResponse = (primeBeforeOrder - 2).ToByteArray();

Debug.Assert(expectedPrimeLength.InBits == NumberLength.GetLength(prime).InBits);
Debug.Assert(expectedOrderLength.InBits == NumberLength.GetLength(order).InBits);

var rngMock = new Mock<RandomNumberGenerator>(MockBehavior.Strict);
rngMock.Setup(rng => rng.GetBytes(It.IsAny<byte[]>()))
.Callback<byte[]>(buffer =>
{
Buffer.BlockCopy(buffer, 0, rngResponse, 0, Math.Min(rngResponse.Length, buffer.Length));
}
);

var group = MultiplicativeGroupAlgebra.CreateCryptoGroup(securityLevel, rngMock.Object);
Assert.IsInstanceOf<MultiplicativeGroupAlgebra>(group.Algebra);

Assert.That(group.SecurityLevel >= securityLevel, "Created group does not meet security level!");
Assert.AreEqual(order, group.Algebra.Order);
Assert.AreEqual(prime, ((MultiplicativeGroupAlgebra)group.Algebra).Prime);
Assert.That(group.Algebra.IsSafeElement(group.Algebra.Generator));
}


[Test]
public void TestCreateCryptoGroupForLevelRngReturnsEven()
{
int securityLevel = 32;
var expectedPrimeLength = MultiplicativeGroupAlgebra.ComputePrimeLengthForSecurityLevel(securityLevel);
var expectedOrderLength = NumberLength.FromBitLength(expectedPrimeLength.InBits - 1);

var primeBeforeOrder = BigInteger.Parse("332306998946228968225951765070101393");
var order = BigPrime.CreateWithoutChecks(BigInteger.Parse("166153499473114484112975882535050719"));
var prime = BigPrime.CreateWithoutChecks(BigInteger.Parse("332306998946228968225951765070101439"));

var rngResponse = (primeBeforeOrder - 3).ToByteArray();

Debug.Assert(expectedPrimeLength.InBits == NumberLength.GetLength(prime).InBits);
Debug.Assert(expectedOrderLength.InBits == NumberLength.GetLength(order).InBits);
var rngResponse = (primeBeforeOrder - subtrahend).ToByteArray();
Random random = new Random(0);
bool firstCall = true;

var rngMock = new Mock<RandomNumberGenerator>(MockBehavior.Strict);
rngMock.Setup(rng => rng.GetBytes(It.IsAny<byte[]>()))
.Callback<byte[]>(buffer =>
{
Buffer.BlockCopy(buffer, 0, rngResponse, 0, Math.Min(rngResponse.Length, buffer.Length));
if (firstCall)
Buffer.BlockCopy(rngResponse, 0, buffer, 0, Math.Min(rngResponse.Length, buffer.Length));
else
random.NextBytes(buffer);
firstCall = false;
}
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// CompactCryptoGroupAlgebra - C# implementation of abelian group algebra for experimental cryptography

// SPDX-FileCopyrightText: 2022 Lukas Prediger <lumip@lumip.de>
// SPDX-FileCopyrightText: 2022-2024 Lukas Prediger <lumip@lumip.de>
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileType: SOURCE

Expand Down Expand Up @@ -213,18 +213,26 @@ public static CryptoGroup<BigInteger, BigInteger> CreateCryptoGroup(int security
var primeLength = ComputePrimeLengthForSecurityLevel(securityLevel);
var sgPrimeLength = NumberLength.FromBitLength(primeLength.InBits - 1);
BigInteger sgCandidate = randomNumberGenerator.GetBigIntegerWithLength(sgPrimeLength);
sgCandidate |= BigInteger.One; // ensure sgCandidate is odd

// Ensure sgCandidate mod 6 == 5. Any prime p can only have p mod 6 == 1 or p mod 6 == 5.
// If sgCandidate mod 6 == 1, then below primeCandidate mod 6 == 3, which makes it divisable by 3.
// If sgCandidate mod 6 == 5, then below primeCandidate mod 6 == 5 as well, meaning it could be prime.
var factorOfSix = sgCandidate / 6;
sgCandidate = factorOfSix * 6 + 5;

BigInteger primeCandidate = 2 * sgCandidate + 1;
while (
!PrimalityTest.IsProbablyPrime(sgCandidate, randomNumberGenerator) ||
!PrimalityTest.IsProbablyPrime(primeCandidate, randomNumberGenerator)
)
{
sgCandidate += 2;
primeCandidate += 4;
sgCandidate += 6;
primeCandidate += 12;
}

Debug.Assert(NumberLength.GetLength(sgCandidate).InBits == sgPrimeLength.InBits);
Debug.Assert(NumberLength.GetLength(primeCandidate).InBits == primeLength.InBits);
Debug.Assert(2 * sgCandidate + 1 == primeCandidate);

var groupAlgebra = new MultiplicativeGroupAlgebra(
BigPrime.CreateWithoutChecks(primeCandidate), BigPrime.CreateWithoutChecks(sgCandidate), new BigInteger(4)
Expand Down

0 comments on commit 4b5cf4b

Please sign in to comment.