Skip to content

Commit

Permalink
Merge pull request #8 from aprismatic/fixes
Browse files Browse the repository at this point in the history
fixes to GenRandomBits() to ensure it generates exact number of bits …
  • Loading branch information
bazzilic authored Dec 6, 2021
2 parents 3c3292c + da0dc74 commit 6f56284
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 63 deletions.
55 changes: 29 additions & 26 deletions src/Aprismatic.BigIntegerExt/BigIntegerExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static class BigIntegerExt
/// <returns>Modulo inverse of this; or 1 if mod.inv. does not exist.</returns>
public static BigInteger ModInverse(this BigInteger T, BigInteger mod)
{
BigInteger i = mod, v = 0, d = 1, t, x;
BigInteger i = mod, v = BigInteger.Zero, d = BigInteger.One, t, x;

while (T.Sign > 0)
{
Expand All @@ -54,16 +54,16 @@ public static BigInteger ModInverse(this BigInteger T, BigInteger mod)
v = x;
}

v %= mod;
v %= mod; // TODO: this could be faster (?)
if (v.Sign < 0)
v = (v + mod) % mod;
v = (v + mod) % mod; // TODO: this too (?)

return v;
}


/// <summary>
/// Returns the position of the most significant bit in the BigInteger
/// Returns the position of the most significant bit of the BigInteger's absolute value.
/// </summary>
/// <example>
/// 1) The result is 1, if the value of BigInteger is 0...0000 0000
Expand All @@ -75,7 +75,7 @@ public static BigInteger ModInverse(this BigInteger T, BigInteger mod)
/// <returns></returns>
public static int BitCount(this BigInteger T)
{
var data = T.ToByteArray();
var data = T.Sign >= 0 ? T.ToByteArray() : (-T).ToByteArray();
byte value = data[data.Length - 1];
byte mask = 0x80;
var bits = 8;
Expand All @@ -102,8 +102,10 @@ public static BigInteger GenRandomBits(this BigInteger T, int bits, RandomNumber
if (bits <= 0)
throw new ArithmeticException("Number of required bits is not valid.");

bits++; // add one for the sign

var bytes = bits >> 3;
var remBits = bits % 8;
var remBits = bits % 8; // TODO: this can be made faster

if (remBits != 0)
bytes++;
Expand All @@ -112,23 +114,24 @@ public static BigInteger GenRandomBits(this BigInteger T, int bits, RandomNumber

rng.GetBytes(data);

if (remBits != 0)
if (remBits == 1) // bytes must be != 0 here due to bits++
{
byte mask;

if (bits != 1)
{
mask = (byte) (0x01 << (remBits - 1));
data[bytes - 1] |= mask;
}

mask = (byte) (0xFF >> (8 - remBits));
data[bytes - 1] &= mask;
data[bytes - 1] = 0; // added byte set 0 for positive sign
data[bytes - 2] |= 0x80; // MSB set to 1
}
else
data[bytes - 1] |= 0x80;
else if (remBits > 1)
{
var mask = (byte)(0x01 << (remBits - 2));
data[bytes - 1] |= mask;

data[bytes - 1] &= 0x7F;
mask = (byte)(0xFF >> (8 - remBits + 1));
data[bytes - 1] &= mask; // set the sign bit to 0
}
else // remBits == 0
{
data[bytes - 1] |= 0x40; // MSB to 1
data[bytes - 1] &= 0x7F; // Sign to 0
}

return new BigInteger(data);
}
Expand All @@ -152,7 +155,7 @@ public static BigInteger GenPseudoPrime(this BigInteger T, int bits, int confide
while (!done)
{
result = result.GenRandomBits(bits, rand);
result |= 1; // make it odd
result |= BigInteger.One; // make it odd

// prime test
done = result.IsProbablePrime(confidence);
Expand All @@ -177,7 +180,7 @@ public static bool IsProbablePrime(this BigInteger T, int confidence)

if (thisVal <= UInt64.MaxValue)
{
var uival = (UInt64) thisVal;
var uival = (UInt64)thisVal;


for (var i = 0; i < PrimesBelow2000.Length; i++) // test for divisibility by primes < 2000
Expand Down Expand Up @@ -243,22 +246,22 @@ public static bool RabinMillerTest(this BigInteger w, int confidence)
do
{
b = BigInteger.Zero.GenRandomBits(wlen, rng);
} while (b >= w - 1 || b < 2);
} while (b >= w - BigInteger.One || b < 2);

var z = BigInteger.ModPow(b, m, w);
if (z.IsOne || z == w - 1)
if (z.IsOne || z == w - BigInteger.One)
continue;

for (var j = 1; j < a; j++)
{
z = BigInteger.ModPow(z, 2, w);
if (z.IsOne)
return false;
if (z == w - 1)
if (z == w - BigInteger.One)
break;
}

if (z != w - 1)
if (z != w - BigInteger.One)
return false;
}

Expand Down
4 changes: 2 additions & 2 deletions test/BigIntegerExtTest/BigIntegerExtTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
112 changes: 77 additions & 35 deletions test/BigIntegerExtTest/BigIntegerExtTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,43 @@ namespace BigIntegerExtTests
{
public class BigIntegerExtTests
{
[Fact(DisplayName = "BitCount")]
public void TestBitCount()
{
Assert.Equal(1, BigInteger.Zero.BitCount());
Assert.Equal(1, BigInteger.One.BitCount());
Assert.Equal(1, BigInteger.MinusOne.BitCount());

var b1 = new BigInteger(Int32.MaxValue);
var b2 = new BigInteger(Int32.MinValue);
Assert.Equal(31, b1.BitCount());
Assert.Equal(32, b2.BitCount());
Assert.Equal(32, (b1 + 1).BitCount());

var b3 = new BigInteger(UInt32.MaxValue);
Assert.Equal(32, b3.BitCount());

var b4 = new BigInteger(7);
var b5 = new BigInteger(8);
Assert.Equal(3, b4.BitCount());
Assert.Equal(4, b5.BitCount());
}

[Fact(DisplayName = "ModInverse")]
public void TestModInverse()
{
{
var a = new BigInteger[] { 0, 0, 1, 3, 7, 25, 2, 13, 19, 31, 3 };
var m = new BigInteger[] { 1000000007, 1999, 2, 6, 87, 87, 91, 91, 1212393831, 73714876143, 73714876143 };
var m = new BigInteger[]
{ 1000000007, 1999, 2, 6, 87, 87, 91, 91, 1212393831, 73714876143, 73714876143 };
var r = new BigInteger[] { 736445995, 1814, 1, 1, 25, 7, 46, 1, 701912218, 45180085378, 1 };

var t = new BigInteger();
BigInteger.TryParse("470782681346529800216759025446747092045188631141622615445464429840250748896490263346676188477401449398784352124574498378830506322639352584202116605974693692194824763263949618703029846313252400361025245824301828641617858127932941468016666971398736792667282916657805322080902778987073711188483372360907612588995664533157503380846449774089269965646418521613225981431666593065726252482995754339317299670566915780168", out t);
a[0] = t; a[1] = t;
BigInteger.TryParse(
"470782681346529800216759025446747092045188631141622615445464429840250748896490263346676188477401449398784352124574498378830506322639352584202116605974693692194824763263949618703029846313252400361025245824301828641617858127932941468016666971398736792667282916657805322080902778987073711188483372360907612588995664533157503380846449774089269965646418521613225981431666593065726252482995754339317299670566915780168",
out t);
a[0] = t;
a[1] = t;

Assert.Equal(a.Length, m.Length);
Assert.Equal(m.Length, r.Length);
Expand All @@ -45,6 +71,7 @@ public void TestModInverse()
bi = bi.GenRandomBits(rnd.Next(1, 1024), rng);
j = 0;
}

mod = mod.GenRandomBits(rnd.Next(1, 128), rng);
}

Expand All @@ -64,76 +91,91 @@ public void TestIsProbablePrime()
for (var i = 2UL; i < 2000; i++) // since we have an array of primes below 2000 that we can check against
{
var res = (new BigInteger(i)).IsProbablePrime(10);
Assert.True(BigIntegerExt.PrimesBelow2000.Contains(i) == res, $"{i} is prime is {BigIntegerExt.PrimesBelow2000.Contains(i)} but was evaluated as {res}");
Assert.True(BigIntegerExt.PrimesBelow2000.Contains(i) == res,
$"{i} is prime is {BigIntegerExt.PrimesBelow2000.Contains(i)} but was evaluated as {res}");
}

foreach (var p in new[] { 633910111, 838041647, 15485863, 452930477, 28122569887267, 29996224275833, 571245373823500631 })
foreach (var p in new[]
{ 633910111, 838041647, 15485863, 452930477, 28122569887267, 29996224275833, 571245373823500631 })
{
Assert.True((new BigInteger(p)).IsProbablePrime(10));
Assert.True(new BigInteger(p).IsProbablePrime(10));
}

foreach (var p in new[] { 398012025725459, 60030484763, 571245373823500630 })
{
Assert.False((new BigInteger(p)).IsProbablePrime(50));
Assert.False(new BigInteger(p).IsProbablePrime(50));
}
}

[Fact(DisplayName = "SecuredGenRandomBits")]
public void TestSecuredGenRandomBits()
[Fact(DisplayName = "GenPseudoPrime")]
public void TestGenPseudoPrime()
{
var bi = new BigInteger();
var rng = RandomNumberGenerator.Create();
var rand = new Random();

for (var i = 0; i < 9999; i++)
{ // Test < 32 bits
// Test arbitrary values
for (var i = 0; i < 200; i++)
{
var prime = bi.GenPseudoPrime(rand.Next(2, 768), 2, rng);

foreach (var pr in BigIntegerExt.PrimesBelow2000)
{
Assert.True(prime == pr || prime % pr != 0,
$"prime: {prime}{Environment.NewLine}" +
$"pr: {pr}");
}
}
}

[Fact(DisplayName = "GenRandomBits")]
public void TestGenRandomBits()
{
var rng = RandomNumberGenerator.Create();
var rand = new Random();

for (var i = 0; i < 9999; i++) // Test < 32 bits
{
var bi = new BigInteger();

bi = bi.GenRandomBits(rand.Next(1, 33), rng);

var bytes = bi.ToByteArray();
var new_bytes = new byte[4];
var new_bytes = new byte[5];
Array.Copy(bytes, new_bytes, bytes.Length);

Assert.True(BitConverter.ToUInt32(new_bytes, 0) < (Math.Pow(2, 32) - 1));
Assert.True(BitConverter.ToUInt32(new_bytes, 0) < Math.Pow(2, 32));
}

// Test on random number of bits
for (var i = 0; i < 9999; i++)
for (var i = 0; i < 9999; i++) // Test on random number of bits
{
var bi = new BigInteger();
var bits = rand.Next(1, 70 * 32 + 1);
bi = bi.GenRandomBits(bits, rng);
Assert.True(bits >= bi.BitCount());
Assert.True(bits == bi.BitCount());
Assert.True(bi >= 0);
}

for (var i = 0; i < 9999; i++)
{ // Test lower boudary value
for (var i = 0; i < 9999; i++) // Test lower boundary value
{
var bi = new BigInteger();

bi = bi.GenRandomBits(1, rng);
Assert.True(bi.ToByteArray()[0] == 1 || bi.ToByteArray()[0] == 0);
}
}

[Fact(DisplayName = "GenPseudoPrime")]
public void TestGenPseudoPrime()
{
var bi = new BigInteger();
var rng = RandomNumberGenerator.Create();
var rand = new Random();

// Test arbitrary values
for (var i = 0; i < 200; i++)
for (var i = 0; i < 9999; i++) // some edge cases
{
var prime = bi.GenPseudoPrime(rand.Next(2, 768), 2, rng);
var bi = new BigInteger();

foreach (var pr in BigIntegerExt.PrimesBelow2000)
{
Assert.True(prime == pr || prime % pr != 0,
$"prime: {prime}{Environment.NewLine}" +
$"pr: {pr}");
}
var bi256 = bi.GenRandomBits(256, rng);
Assert.Equal(256, bi256.BitCount());

var bi257 = bi.GenRandomBits(257, rng);
Assert.Equal(257, bi257.BitCount());

var bi255 = bi.GenRandomBits(255, rng);
Assert.Equal(255, bi255.BitCount());
}
}
}
Expand Down

0 comments on commit 6f56284

Please sign in to comment.