Skip to content

Commit

Permalink
Add a random class for ECC testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Coding-Enthusiast committed Dec 15, 2023
1 parent b935ff4 commit c5b13e1
Showing 1 changed file with 169 additions and 0 deletions.
169 changes: 169 additions & 0 deletions Src/Tests/Bitcoin/Cryptography/EllipticCurve/Rand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Autarkysoft Tests
// Copyright (c) 2020 Autarkysoft
// Distributed under the MIT software license, see the accompanying
// file LICENCE or http://www.opensource.org/licenses/mit-license.php.

using Autarkysoft.Bitcoin;
using Autarkysoft.Bitcoin.Cryptography.Hashing;
using System;
using System.Security.Cryptography;
using System.Text;

namespace Tests.Bitcoin.Cryptography.EllipticCurve
{
/// <summary>
/// xoshiro PRNG https://prng.di.unimi.it/
/// https://github.com/bitcoin-core/secp256k1/blob/77af1da9f631fa622fb5b5895fd27be431432368/src/testrand.h
/// https://github.com/bitcoin-core/secp256k1/blob/77af1da9f631fa622fb5b5895fd27be431432368/src/testrand_impl.h
/// </summary>
internal class Rand
{
private readonly ulong[] state = new ulong[4];

private void secp256k1_testrand_seed(byte[] seed16)
{
Assert.Equal(16, seed16.Length);
byte[] PREFIX = Encoding.UTF8.GetBytes("secp256k1 test init");
Assert.Equal(19, PREFIX.Length);

// Use SHA256(PREFIX || seed16) as initial state.
using Sha256 sha = new();
byte[] out32 = sha.ComputeHash(PREFIX.ConcatFast(seed16));

for (int i = 0; i < 4; i++)
{
ulong s = 0;
for (int j = 0; j < 8; ++j)
{
s = (s << 8) | out32[8 * i + j];
}
state[i] = s;
}
}

private static ulong rotl(ulong x, int k) => (x << k) | (x >> (64 - k));

private ulong secp256k1_testrand64()
{
/* Test-only Xoshiro256++ RNG. See https://prng.di.unimi.it/ */
ulong result = rotl(state[0] + state[3], 23) + state[0];
ulong t = state[1] << 17;
state[2] ^= state[0];
state[3] ^= state[1];
state[1] ^= state[2];
state[0] ^= state[3];
state[2] ^= t;
state[3] = rotl(state[3], 45);
return result;
}

private ulong secp256k1_testrand_bits(int bits)
{
if (bits == 0) return 0;
return secp256k1_testrand64() >> (64 - bits);
}

uint secp256k1_testrand32()
{
return (uint)(secp256k1_testrand64() >> 32);
}

private uint secp256k1_testrand_int(uint range)
{
uint mask = 0;
uint range_copy;
/* Reduce range by 1, changing its meaning to "maximum value". */
Assert.True(range != 0);
range -= 1;
/* Count the number of bits in range. */
range_copy = range;
while (range_copy != 0)
{
mask = (mask << 1) | 1U;
range_copy >>= 1;
}
/* Generation loop. */
while (true)
{
uint val = (uint)(secp256k1_testrand64() & mask);
if (val <= range) return val;
}
}

private void secp256k1_testrand256(byte[] b32)
{
for (int i = 0; i < b32.Length; i += 8)
{
ulong val = secp256k1_testrand64();
b32[i + 0] = (byte)val;
b32[i + 1] = (byte)(val >> 8);
b32[i + 2] = (byte)(val >> 16);
b32[i + 3] = (byte)(val >> 24);
b32[i + 4] = (byte)(val >> 32);
b32[i + 5] = (byte)(val >> 40);
b32[i + 6] = (byte)(val >> 48);
b32[i + 7] = (byte)(val >> 56);
}
}

private void secp256k1_testrand_bytes_test(byte[] bytes, int len)
{
for (int i = 0; i < len; i++)
{
bytes[i] = 0;
}

int bits = 0;
while (bits < len * 8)
{
int now = (int)(1 + (secp256k1_testrand_bits(6) * secp256k1_testrand_bits(5) + 16) / 31);
uint val = (uint)secp256k1_testrand_bits(1);
while (now > 0 && bits < len * 8)
{
bytes[bits / 8] |= (byte)(val << (bits % 8));
now--;
bits++;
}
}
}

private void secp256k1_testrand256_test(byte[] b32)
{
secp256k1_testrand_bytes_test(b32, 32);
}

private void secp256k1_testrand_flip(byte[] b, int len)
{
b[secp256k1_testrand_int((uint)len)] ^= (byte)(1 << (int)secp256k1_testrand_bits(3));
}

private void secp256k1_testrand_init(string hexseed)
{
byte[] seed16 = new byte[16];
if (!string.IsNullOrEmpty(hexseed))
{
Span<byte> temp = Helper.HexToBytes(hexseed);
if (temp.Length <= seed16.Length)
{
temp.CopyTo(seed16);
}
else
{
temp.Slice(0, seed16.Length).CopyTo(seed16);
}
}
else
{
RandomNumberGenerator.Fill(seed16);
}

secp256k1_testrand_seed(seed16);
}

private void secp256k1_testrand_finish()
{
byte[] run32 = new byte[32];
secp256k1_testrand256(run32);
}
}
}

0 comments on commit c5b13e1

Please sign in to comment.