Skip to content

Commit 762796d

Browse files
committed
Added RandomNumberGeneratorExtensions.GetBigPrime
1 parent 4b5cf4b commit 762796d

File tree

2 files changed

+71
-5
lines changed

2 files changed

+71
-5
lines changed

CompactCryptoGroupAlgebra.Tests/RandomNumberGeneratorExtensionsTests.cs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// CompactCryptoGroupAlgebra - C# implementation of abelian group algebra for experimental cryptography
22

3-
// SPDX-FileCopyrightText: 2022 Lukas Prediger <[email protected]>
3+
// SPDX-FileCopyrightText: 2022-2024 Lukas Prediger <[email protected]>
44
// SPDX-License-Identifier: GPL-3.0-or-later
55
// SPDX-FileType: SOURCE
66

@@ -85,9 +85,9 @@ public void TestRandomWithLengthNonByteBoundary()
8585
var length = NumberLength.FromBitLength(17);
8686

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

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

152152
rngMock.Verify(rng => rng.GetBytes(It.IsAny<byte[]>()), Times.Exactly(2));
153153
}
154+
155+
[Test]
156+
[TestCase(3)] // ((prime - 3) | 1) mod 6 == ((prime - 2) | 1) == 5
157+
[TestCase(4)] // ((prime - 4) | 1) mod 6 == 3
158+
[TestCase(6)] // ((prime - 6) | 1) mod 6 == 1
159+
public void TestRandomBigPrime(int subtrahend)
160+
{
161+
var length = NumberLength.FromBitLength(128);
162+
var prime = BigPrime.CreateWithoutChecks(BigInteger.Parse("340282366920938463463374607431768211297"));
163+
164+
var rngResponse = (prime - subtrahend).ToByteArray();
165+
166+
Random random = new Random(0);
167+
bool firstCall = true;
168+
169+
var rngMock = new Mock<RandomNumberGenerator>(MockBehavior.Strict);
170+
rngMock.Setup(rng => rng.GetBytes(It.IsAny<byte[]>()))
171+
.Callback<byte[]>(buffer =>
172+
{
173+
if (firstCall)
174+
Buffer.BlockCopy(rngResponse, 0, buffer, 0, Math.Min(rngResponse.Length, buffer.Length));
175+
else
176+
random.NextBytes(buffer);
177+
firstCall = false;
178+
});
179+
180+
var result = rngMock.Object.GetBigPrime(length);
181+
182+
Assert.AreEqual(prime, result);
183+
}
184+
154185
}
155186
}

CompactCryptoGroupAlgebra/RandomNumberGeneratorExtensions.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// CompactCryptoGroupAlgebra - C# implementation of abelian group algebra for experimental cryptography
22

3-
// SPDX-FileCopyrightText: 2022 Lukas Prediger <[email protected]>
3+
// SPDX-FileCopyrightText: 2022-2024 Lukas Prediger <[email protected]>
44
// SPDX-License-Identifier: GPL-3.0-or-later
55
// SPDX-FileType: SOURCE
66

@@ -59,7 +59,7 @@ public static BigInteger GetBigIntegerBetween(
5959
/// Returns a random positive <see cref="BigInteger"/> with the given bit length.
6060
/// </summary>
6161
/// <param name="randomNumberGenerator">Random number generator.</param>
62-
/// <param name="length">The bit length of the generator number.</param>
62+
/// <param name="length">The bit length of the generated number.</param>
6363
/// <returns>The random <see cref="BigInteger"/>.</returns>
6464
public static BigInteger GetBigIntegerWithLength(
6565
this RandomNumberGenerator randomNumberGenerator, NumberLength length
@@ -74,5 +74,40 @@ public static BigInteger GetBigIntegerWithLength(
7474
candidate |= BigInteger.One << (length.InBits - 1); // ensure msb is set to achieve desired length
7575
return candidate;
7676
}
77+
78+
/// <summary>
79+
/// Returns a random <see cref="BigPrime"/> with the given bit length.
80+
/// </summary>
81+
/// <param name="randomNumberGenerator">Random number generator.</param>
82+
/// <param name="length">The bit length of the generated prime number.</param>
83+
/// <returns>The random <see cref="BigPrime"/>.</returns>
84+
public static BigPrime GetBigPrime(
85+
this RandomNumberGenerator randomNumberGenerator, NumberLength length
86+
)
87+
{
88+
BigInteger primeCandidate = randomNumberGenerator.GetBigIntegerWithLength(length);
89+
90+
// Ensure primeCandidate is odd.
91+
primeCandidate |= 1;
92+
93+
// Ensure primeCandidate mod 6 == 1 or 5. Any prime p can only have p mod 6 == 1 or p mod 6 == 5.
94+
// Relying on bit operations to avoid any branching statements.
95+
var residue = primeCandidate % 6;
96+
primeCandidate += residue & 2; // <-> primeCandidate += (residue == 3) ? 2 : 0;
97+
residue += residue & 2; // <-> residue += (residue == 3) ? 2 : 0;
98+
int step = 2 << (int)((residue ^ (residue >> 2)) & 1); // <-> step = (residue == 1) ? 4 : 2;
99+
int shiftDir = -1 + (step & 2); // <-> shiftDir = (step == 2) ? 1 : -1;
100+
101+
while (!PrimalityTest.IsProbablyPrime(primeCandidate, randomNumberGenerator))
102+
{
103+
primeCandidate += step;
104+
105+
// Below is equivalent to step = (step == 2) ? 4 : 2;
106+
step = (step << shiftDir) | (step >> -shiftDir);
107+
shiftDir = -shiftDir;
108+
}
109+
110+
return BigPrime.CreateWithoutChecks(primeCandidate);
111+
}
77112
}
78113
}

0 commit comments

Comments
 (0)