From 762796dc577c09653cf75568a73e542131c21407 Mon Sep 17 00:00:00 2001 From: Lukas Prediger Date: Tue, 15 Oct 2024 22:00:48 +0300 Subject: [PATCH] Added RandomNumberGeneratorExtensions.GetBigPrime --- .../RandomNumberGeneratorExtensionsTests.cs | 37 ++++++++++++++++-- .../RandomNumberGeneratorExtensions.cs | 39 ++++++++++++++++++- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/CompactCryptoGroupAlgebra.Tests/RandomNumberGeneratorExtensionsTests.cs b/CompactCryptoGroupAlgebra.Tests/RandomNumberGeneratorExtensionsTests.cs index 85792f0..0ed9025 100644 --- a/CompactCryptoGroupAlgebra.Tests/RandomNumberGeneratorExtensionsTests.cs +++ b/CompactCryptoGroupAlgebra.Tests/RandomNumberGeneratorExtensionsTests.cs @@ -1,6 +1,6 @@ // CompactCryptoGroupAlgebra - C# implementation of abelian group algebra for experimental cryptography -// SPDX-FileCopyrightText: 2022 Lukas Prediger +// SPDX-FileCopyrightText: 2022-2024 Lukas Prediger // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileType: SOURCE @@ -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(MockBehavior.Strict); @@ -151,5 +151,36 @@ public void TestRandomWithLengthByteBoundary() rngMock.Verify(rng => rng.GetBytes(It.IsAny()), 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(MockBehavior.Strict); + rngMock.Setup(rng => rng.GetBytes(It.IsAny())) + .Callback(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); + } + } } diff --git a/CompactCryptoGroupAlgebra/RandomNumberGeneratorExtensions.cs b/CompactCryptoGroupAlgebra/RandomNumberGeneratorExtensions.cs index 5b1851d..ea7d13b 100644 --- a/CompactCryptoGroupAlgebra/RandomNumberGeneratorExtensions.cs +++ b/CompactCryptoGroupAlgebra/RandomNumberGeneratorExtensions.cs @@ -1,6 +1,6 @@ // CompactCryptoGroupAlgebra - C# implementation of abelian group algebra for experimental cryptography -// SPDX-FileCopyrightText: 2022 Lukas Prediger +// SPDX-FileCopyrightText: 2022-2024 Lukas Prediger // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileType: SOURCE @@ -59,7 +59,7 @@ public static BigInteger GetBigIntegerBetween( /// Returns a random positive with the given bit length. /// /// Random number generator. - /// The bit length of the generator number. + /// The bit length of the generated number. /// The random . public static BigInteger GetBigIntegerWithLength( this RandomNumberGenerator randomNumberGenerator, NumberLength length @@ -74,5 +74,40 @@ public static BigInteger GetBigIntegerWithLength( candidate |= BigInteger.One << (length.InBits - 1); // ensure msb is set to achieve desired length return candidate; } + + /// + /// Returns a random with the given bit length. + /// + /// Random number generator. + /// The bit length of the generated prime number. + /// The random . + 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); + } } }