Skip to content

Commit 9890f78

Browse files
committed
Added CreateCryptoGroup(securityLevel) to MultiplicativeGroupAlgebra.
1 parent 4d97fed commit 9890f78

File tree

8 files changed

+220
-6
lines changed

8 files changed

+220
-6
lines changed

CHANGELOG.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# SPDX-License-Identifier: CC0-1.0
22
# SPDX-FileCopyrightText: Lukas Prediger
33

4+
- 2.1.0: Added more elliptic curve parameters and convenience functions
5+
to obtain groups for a specified security level.
46
- 2.0.0-alpha
57
- Fix: MultiplicativeGroupAlgebra.Negate now works correctly for all elements
68
in the prime field, not only the subgroup.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# SPDX-License-Identifier: CC0-1.0
22
# SPDX-FileCopyrightText: Lukas Prediger
33

4+
- 1.1.0: Added more elliptic curve parameters and convenience functions
5+
to obtain groups for a specified security level.
46
- 1.0.0-alpha: Initial release of CompactCryptoGroupAlgebra.LibCrypto library

CompactCryptoGroupAlgebra.Tests/BigPrimeTests.cs

Lines changed: 19 additions & 1 deletion
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: 2020-2021 Lukas Prediger <lumip@lumip.de>
3+
// SPDX-FileCopyrightText: 2022 Lukas Prediger <lumip@lumip.de>
44
// SPDX-License-Identifier: GPL-3.0-or-later
55
// SPDX-FileType: SOURCE
66

@@ -147,6 +147,24 @@ public void TestEqualsIsFalseForDifferent()
147147
Assert.AreNotEqual(prime, otherPrime);
148148
}
149149

150+
[Test]
151+
public void TestEqualsIsTrueForEqualBigInteger()
152+
{
153+
var prime = BigPrime.CreateWithoutChecks(11);
154+
var bigint = new BigInteger(11);
155+
156+
Assert.AreEqual(prime, bigint);
157+
}
158+
159+
[Test]
160+
public void TestEqualsIsFalseForUnequalBigInteger()
161+
{
162+
var prime = BigPrime.CreateWithoutChecks(11);
163+
var bigint = new BigInteger(13);
164+
165+
Assert.AreNotEqual(prime, bigint);
166+
}
167+
150168
[Test]
151169
public void TestEqualsIsFalseForNull()
152170
{

CompactCryptoGroupAlgebra.Tests/Multiplicative/MultiplicativeGroupAlgebraTests.cs

Lines changed: 67 additions & 1 deletion
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: 2020-2021 Lukas Prediger <lumip@lumip.de>
3+
// SPDX-FileCopyrightText: 2022 Lukas Prediger <lumip@lumip.de>
44
// SPDX-License-Identifier: GPL-3.0-or-later
55
// SPDX-FileType: SOURCE
66

@@ -19,8 +19,11 @@
1919

2020
using System;
2121
using System.Numerics;
22+
using System.Security.Cryptography;
23+
using System.Diagnostics;
2224

2325
using NUnit.Framework;
26+
using Moq;
2427

2528
namespace CompactCryptoGroupAlgebra.Multiplicative
2629
{
@@ -296,5 +299,68 @@ public void TestCreateCryptoGroup()
296299
Assert.AreEqual(groupAlgebra, group.Algebra);
297300
}
298301

302+
[Test]
303+
public void TestCreateCryptoGroupForLevelRngReturnsOdd()
304+
{
305+
int securityLevel = 32;
306+
var expectedPrimeLength = MultiplicativeGroupAlgebra.ComputePrimeLengthForSecurityLevel(securityLevel);
307+
var expectedOrderLength = NumberLength.FromBitLength(expectedPrimeLength.InBits - 1);
308+
309+
var primeBeforeOrder = BigInteger.Parse("332306998946228968225951765070101393");
310+
var order = BigPrime.CreateWithoutChecks(BigInteger.Parse("166153499473114484112975882535050719"));
311+
var prime = BigPrime.CreateWithoutChecks(BigInteger.Parse("332306998946228968225951765070101439"));
312+
313+
var rngResponse = (primeBeforeOrder - 2).ToByteArray();
314+
315+
Debug.Assert(expectedPrimeLength.InBits == NumberLength.GetLength(prime).InBits);
316+
Debug.Assert(expectedOrderLength.InBits == NumberLength.GetLength(order).InBits);
317+
318+
var rngMock = new Mock<RandomNumberGenerator>(MockBehavior.Strict);
319+
rngMock.Setup(rng => rng.GetBytes(It.IsAny<byte[]>()))
320+
.Callback<byte[]>(buffer => {
321+
Buffer.BlockCopy(buffer, 0, rngResponse, 0, Math.Min(rngResponse.Length, buffer.Length));
322+
});
323+
324+
var group = MultiplicativeGroupAlgebra.CreateCryptoGroup(securityLevel, rngMock.Object);
325+
Assert.IsInstanceOf<MultiplicativeGroupAlgebra>(group.Algebra);
326+
327+
Assert.That(group.SecurityLevel >= securityLevel, "Created group does not meet security level!");
328+
Assert.AreEqual(order, group.Algebra.Order);
329+
Assert.AreEqual(prime, ((MultiplicativeGroupAlgebra)group.Algebra).Prime);
330+
Assert.That(group.Algebra.IsSafeElement(group.Algebra.Generator));
331+
}
332+
333+
334+
[Test]
335+
public void TestCreateCryptoGroupForLevelRngReturnsEven()
336+
{
337+
int securityLevel = 32;
338+
var expectedPrimeLength = MultiplicativeGroupAlgebra.ComputePrimeLengthForSecurityLevel(securityLevel);
339+
var expectedOrderLength = NumberLength.FromBitLength(expectedPrimeLength.InBits - 1);
340+
341+
var primeBeforeOrder = BigInteger.Parse("332306998946228968225951765070101393");
342+
var order = BigPrime.CreateWithoutChecks(BigInteger.Parse("166153499473114484112975882535050719"));
343+
var prime = BigPrime.CreateWithoutChecks(BigInteger.Parse("332306998946228968225951765070101439"));
344+
345+
var rngResponse = (primeBeforeOrder - 3).ToByteArray();
346+
347+
Debug.Assert(expectedPrimeLength.InBits == NumberLength.GetLength(prime).InBits);
348+
Debug.Assert(expectedOrderLength.InBits == NumberLength.GetLength(order).InBits);
349+
350+
var rngMock = new Mock<RandomNumberGenerator>(MockBehavior.Strict);
351+
rngMock.Setup(rng => rng.GetBytes(It.IsAny<byte[]>()))
352+
.Callback<byte[]>(buffer => {
353+
Buffer.BlockCopy(buffer, 0, rngResponse, 0, Math.Min(rngResponse.Length, buffer.Length));
354+
});
355+
356+
var group = MultiplicativeGroupAlgebra.CreateCryptoGroup(securityLevel, rngMock.Object);
357+
Assert.IsInstanceOf<MultiplicativeGroupAlgebra>(group.Algebra);
358+
359+
Assert.That(group.SecurityLevel >= securityLevel, "Created group does not meet security level!");
360+
Assert.AreEqual(order, group.Algebra.Order);
361+
Assert.AreEqual(prime, ((MultiplicativeGroupAlgebra)group.Algebra).Prime);
362+
Assert.That(group.Algebra.IsSafeElement(group.Algebra.Generator));
363+
}
364+
299365
}
300366
}

CompactCryptoGroupAlgebra.Tests/RandomNumberGeneratorExtensionsTests.cs

Lines changed: 70 additions & 1 deletion
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: 2020-2021 Lukas Prediger <lumip@lumip.de>
3+
// SPDX-FileCopyrightText: 2022 Lukas Prediger <lumip@lumip.de>
44
// SPDX-License-Identifier: GPL-3.0-or-later
55
// SPDX-FileType: SOURCE
66

@@ -74,5 +74,74 @@ public void TestRandomBetweenDoesNotExceedUpperBound()
7474
Assert.AreEqual(expected, result);
7575
rngMock.Verify(rng => rng.GetBytes(It.IsAny<byte[]>()), Times.Exactly(2));
7676
}
77+
78+
[Test]
79+
public void TestRandomWithLengthNonByteBoundary()
80+
{
81+
var length = NumberLength.FromBitLength(17);
82+
83+
var leadingOneRngBuffer = ((BigInteger.One << (3 * 8)) - 1).ToByteArray(); // 3 bytes of ones
84+
var leadingZeroRngBuffer = ((BigInteger.One << (length.InBits - 2))).ToByteArray(); // only bit 16 set
85+
var expectedFirst = (BigInteger.One << length.InBits) - 1;
86+
var expectedSecond = ((BigInteger.One << (length.InBits - 2)) ^ (BigInteger.One << (length.InBits - 1)));
87+
88+
bool firstCall = true;
89+
var rngMock = new Mock<RandomNumberGenerator>(MockBehavior.Strict);
90+
rngMock.Setup(rng => rng.GetBytes(It.IsAny<byte[]>()))
91+
.Callback<byte[]>(buffer => {
92+
if (firstCall)
93+
Buffer.BlockCopy(leadingOneRngBuffer, 0, buffer, 0, Math.Min(leadingOneRngBuffer.Length, buffer.Length));
94+
else
95+
Buffer.BlockCopy(leadingZeroRngBuffer, 0, buffer, 0, Math.Min(leadingZeroRngBuffer.Length, buffer.Length));
96+
firstCall = false;
97+
});
98+
99+
var firstResult = rngMock.Object.GetBigIntegerWithLength(length);
100+
Assert.AreEqual(1, firstResult.Sign, "GetBigIntegerWithLength returned negative number");
101+
Assert.AreEqual(length, NumberLength.GetLength(firstResult));
102+
Assert.AreEqual(expectedFirst, firstResult);
103+
104+
var secondResult = rngMock.Object.GetBigIntegerWithLength(length);;
105+
Assert.AreEqual(1, secondResult.Sign, "GetBigIntegerWithLength returned negative number");
106+
Assert.AreEqual(length, NumberLength.GetLength(secondResult));
107+
Assert.AreEqual(expectedSecond, secondResult);
108+
109+
rngMock.Verify(rng => rng.GetBytes(It.IsAny<byte[]>()), Times.Exactly(2));
110+
}
111+
112+
113+
[Test]
114+
public void TestRandomWithLengthByteBoundary()
115+
{
116+
var length = NumberLength.FromBitLength(16);
117+
118+
var leadingOneRngBuffer = ((BigInteger.One << (3 * 8)) - 1).ToByteArray(); // 3 bytes of ones
119+
var leadingZeroRngBuffer = ((BigInteger.One << (length.InBits - 2))).ToByteArray(); // only bit 15 set
120+
var expectedFirst = (BigInteger.One << length.InBits) - 1;
121+
var expectedSecond = ((BigInteger.One << (length.InBits - 2)) ^ (BigInteger.One << (length.InBits - 1)));
122+
123+
bool firstCall = true;
124+
var rngMock = new Mock<RandomNumberGenerator>(MockBehavior.Strict);
125+
rngMock.Setup(rng => rng.GetBytes(It.IsAny<byte[]>()))
126+
.Callback<byte[]>(buffer => {
127+
if (firstCall)
128+
Buffer.BlockCopy(leadingOneRngBuffer, 0, buffer, 0, Math.Min(leadingOneRngBuffer.Length, buffer.Length));
129+
else
130+
Buffer.BlockCopy(leadingZeroRngBuffer, 0, buffer, 0, Math.Min(leadingZeroRngBuffer.Length, buffer.Length));
131+
firstCall = false;
132+
});
133+
134+
var firstResult = rngMock.Object.GetBigIntegerWithLength(length);
135+
Assert.AreEqual(1, firstResult.Sign, "GetBigIntegerWithLength returned negative number");
136+
Assert.AreEqual(length, NumberLength.GetLength(firstResult));
137+
Assert.AreEqual(expectedFirst, firstResult);
138+
139+
var secondResult = rngMock.Object.GetBigIntegerWithLength(length);;
140+
Assert.AreEqual(1, secondResult.Sign, "GetBigIntegerWithLength returned negative number");
141+
Assert.AreEqual(length, NumberLength.GetLength(secondResult));
142+
Assert.AreEqual(expectedSecond, secondResult);
143+
144+
rngMock.Verify(rng => rng.GetBytes(It.IsAny<byte[]>()), Times.Exactly(2));
145+
}
77146
}
78147
}

CompactCryptoGroupAlgebra/BigPrime.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,13 +166,15 @@ public static BigPrime Create(BigInteger primeValue, RandomNumberGenerator rando
166166
/// <inheritdoc/>
167167
public override bool Equals(object obj)
168168
{
169+
if (obj is BigInteger)
170+
return _value.Equals(obj);
169171
return obj is BigPrime prime && _value.Equals(prime._value);
170172
}
171173

172174
/// <inheritdoc/>
173175
public override int GetHashCode()
174176
{
175-
return -1937169414 + _value.GetHashCode();
177+
return _value.GetHashCode();
176178
}
177179

178180
/// <inheritdoc/>

CompactCryptoGroupAlgebra/Multiplicative/MultiplicativeGroupAlgebra.cs

Lines changed: 36 additions & 1 deletion
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: 2020-2021 Lukas Prediger <lumip@lumip.de>
3+
// SPDX-FileCopyrightText: 2022 Lukas Prediger <lumip@lumip.de>
44
// SPDX-License-Identifier: GPL-3.0-or-later
55
// SPDX-FileType: SOURCE
66

@@ -20,6 +20,8 @@
2020
using System;
2121
using System.Collections.Generic;
2222
using System.Numerics;
23+
using System.Security.Cryptography;
24+
using System.Diagnostics;
2325

2426
namespace CompactCryptoGroupAlgebra.Multiplicative
2527
{
@@ -195,6 +197,39 @@ public static CryptoGroup<BigInteger, BigInteger> CreateCryptoGroup(BigPrime pri
195197
prime, order, generator
196198
));
197199
}
200+
201+
/// <summary>
202+
/// Creates a <see cref="CryptoGroup{BigInteger, BigInteger}" /> instance that satisfies at least a given security level.
203+
///
204+
/// Finds random primes q, p such that p = 2q+1 and the bit length of p satisfies the security level requirements.
205+
/// Then finds an element of the q-order subgroup of the multiplicative group defined by p to use as the generator.
206+
///
207+
/// This process may take some time, depending on the security level chosen.
208+
/// </summary>
209+
/// <param name="securityLevel">The minimal security level for the group to be created.</param>
210+
/// <param name="randomNumberGenerator">A random number generator (used for sampling primes).</param>
211+
public static CryptoGroup<BigInteger, BigInteger> CreateCryptoGroup(int securityLevel, RandomNumberGenerator randomNumberGenerator)
212+
{
213+
var primeLength = ComputePrimeLengthForSecurityLevel(securityLevel);
214+
var sgPrimeLength = NumberLength.FromBitLength(primeLength.InBits - 1);
215+
BigInteger sgCandidate = randomNumberGenerator.GetBigIntegerWithLength(sgPrimeLength);
216+
sgCandidate |= BigInteger.One; // ensure sgCandidate is odd
217+
BigInteger primeCandidate = 2 * sgCandidate + 1;
218+
while ( !PrimalityTest.IsProbablyPrime(sgCandidate, randomNumberGenerator) ||
219+
!PrimalityTest.IsProbablyPrime(primeCandidate, randomNumberGenerator) )
220+
{
221+
sgCandidate += 2;
222+
primeCandidate += 4;
223+
}
224+
Debug.Assert(NumberLength.GetLength(sgCandidate).InBits == sgPrimeLength.InBits);
225+
Debug.Assert(NumberLength.GetLength(primeCandidate).InBits == primeLength.InBits);
226+
227+
var groupAlgebra = new MultiplicativeGroupAlgebra(
228+
BigPrime.CreateWithoutChecks(primeCandidate), BigPrime.CreateWithoutChecks(sgCandidate), new BigInteger(4)
229+
);
230+
231+
return new CryptoGroup<BigInteger, BigInteger>(groupAlgebra);
232+
}
198233

199234
}
200235
}

CompactCryptoGroupAlgebra/RandomNumberGeneratorExtensions.cs

Lines changed: 21 additions & 1 deletion
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: 2020-2021 Lukas Prediger <lumip@lumip.de>
3+
// SPDX-FileCopyrightText: 2022 Lukas Prediger <lumip@lumip.de>
44
// SPDX-License-Identifier: GPL-3.0-or-later
55
// SPDX-FileType: SOURCE
66

@@ -54,5 +54,25 @@ public static BigInteger GetBigIntegerBetween(
5454
Debug.Assert(delta >= BigInteger.Zero);
5555
return lower + delta;
5656
}
57+
58+
/// <summary>
59+
/// Returns a random positive <see cref="BigInteger"/> with the given bit length.
60+
/// </summary>
61+
/// <param name="randomNumberGenerator">Random number generator.</param>
62+
/// <param name="length">The bit length of the generator number.</param>
63+
/// <returns>The random <see cref="BigInteger"/>.</returns>
64+
public static BigInteger GetBigIntegerWithLength(
65+
this RandomNumberGenerator randomNumberGenerator, NumberLength length
66+
)
67+
{
68+
byte[] buffer = new byte[length.InBytes + 1];
69+
randomNumberGenerator.GetBytes(buffer);
70+
buffer[buffer.Length - 1] = 0; // ensure that there is an additional zero byte so that BigInteger does not treat it as negative
71+
72+
var candidate = new BigInteger(buffer);
73+
candidate &= (BigInteger.One << length.InBits) - 1; // cut off additional bits generated in the highest-order byte
74+
candidate |= BigInteger.One << (length.InBits - 1); // ensure msb is set to achieve desired length
75+
return candidate;
76+
}
5777
}
5878
}

0 commit comments

Comments
 (0)