Skip to content

Commit

Permalink
Added RandomNumberGeneratorExtensions.GetBigPrime
Browse files Browse the repository at this point in the history
  • Loading branch information
lumip committed Oct 21, 2024
1 parent 4b5cf4b commit 762796d
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 5 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 @@ -85,9 +85,9 @@ public void TestRandomWithLengthNonByteBoundary()
var length = NumberLength.FromBitLength(17);

var leadingOneRngBuffer = ((BigInteger.One << (3 * 8)) - 1).ToByteArray(); // 3 bytes of ones
var leadingZeroRngBuffer = ((BigInteger.One << (length.InBits - 2))).ToByteArray(); // only bit 16 set
var leadingZeroRngBuffer = (BigInteger.One << (length.InBits - 2)).ToByteArray(); // only bit 16 set
var expectedFirst = (BigInteger.One << length.InBits) - 1;
var expectedSecond = ((BigInteger.One << (length.InBits - 2)) ^ (BigInteger.One << (length.InBits - 1)));
var expectedSecond = (BigInteger.One << (length.InBits - 2)) ^ (BigInteger.One << (length.InBits - 1));

bool firstCall = true;
var rngMock = new Mock<RandomNumberGenerator>(MockBehavior.Strict);
Expand Down Expand Up @@ -151,5 +151,36 @@ public void TestRandomWithLengthByteBoundary()

rngMock.Verify(rng => rng.GetBytes(It.IsAny<byte[]>()), Times.Exactly(2));
}

[Test]
[TestCase(3)] // ((prime - 3) | 1) mod 6 == ((prime - 2) | 1) == 5
[TestCase(4)] // ((prime - 4) | 1) mod 6 == 3
[TestCase(6)] // ((prime - 6) | 1) mod 6 == 1
public void TestRandomBigPrime(int subtrahend)
{
var length = NumberLength.FromBitLength(128);
var prime = BigPrime.CreateWithoutChecks(BigInteger.Parse("340282366920938463463374607431768211297"));

var rngResponse = (prime - 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 =>
{
if (firstCall)
Buffer.BlockCopy(rngResponse, 0, buffer, 0, Math.Min(rngResponse.Length, buffer.Length));
else
random.NextBytes(buffer);
firstCall = false;
});

var result = rngMock.Object.GetBigPrime(length);

Assert.AreEqual(prime, result);
}

}
}
39 changes: 37 additions & 2 deletions CompactCryptoGroupAlgebra/RandomNumberGeneratorExtensions.cs
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 @@ -59,7 +59,7 @@ public static BigInteger GetBigIntegerBetween(
/// Returns a random positive <see cref="BigInteger"/> with the given bit length.
/// </summary>
/// <param name="randomNumberGenerator">Random number generator.</param>
/// <param name="length">The bit length of the generator number.</param>
/// <param name="length">The bit length of the generated number.</param>
/// <returns>The random <see cref="BigInteger"/>.</returns>
public static BigInteger GetBigIntegerWithLength(
this RandomNumberGenerator randomNumberGenerator, NumberLength length
Expand All @@ -74,5 +74,40 @@ public static BigInteger GetBigIntegerWithLength(
candidate |= BigInteger.One << (length.InBits - 1); // ensure msb is set to achieve desired length
return candidate;
}

/// <summary>
/// Returns a random <see cref="BigPrime"/> with the given bit length.
/// </summary>
/// <param name="randomNumberGenerator">Random number generator.</param>
/// <param name="length">The bit length of the generated prime number.</param>
/// <returns>The random <see cref="BigPrime"/>.</returns>
public static BigPrime GetBigPrime(
this RandomNumberGenerator randomNumberGenerator, NumberLength length
)
{
BigInteger primeCandidate = randomNumberGenerator.GetBigIntegerWithLength(length);

// Ensure primeCandidate is odd.
primeCandidate |= 1;

// Ensure primeCandidate mod 6 == 1 or 5. Any prime p can only have p mod 6 == 1 or p mod 6 == 5.
// Relying on bit operations to avoid any branching statements.
var residue = primeCandidate % 6;
primeCandidate += residue & 2; // <-> primeCandidate += (residue == 3) ? 2 : 0;
residue += residue & 2; // <-> residue += (residue == 3) ? 2 : 0;
int step = 2 << (int)((residue ^ (residue >> 2)) & 1); // <-> step = (residue == 1) ? 4 : 2;
int shiftDir = -1 + (step & 2); // <-> shiftDir = (step == 2) ? 1 : -1;

while (!PrimalityTest.IsProbablyPrime(primeCandidate, randomNumberGenerator))
{
primeCandidate += step;

// Below is equivalent to step = (step == 2) ? 4 : 2;
step = (step << shiftDir) | (step >> -shiftDir);
shiftDir = -shiftDir;
}

return BigPrime.CreateWithoutChecks(primeCandidate);
}
}
}

0 comments on commit 762796d

Please sign in to comment.