Skip to content

Commit c5b13e1

Browse files
Add a random class for ECC testing
1 parent b935ff4 commit c5b13e1

File tree

1 file changed

+169
-0
lines changed
  • Src/Tests/Bitcoin/Cryptography/EllipticCurve

1 file changed

+169
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Autarkysoft Tests
2+
// Copyright (c) 2020 Autarkysoft
3+
// Distributed under the MIT software license, see the accompanying
4+
// file LICENCE or http://www.opensource.org/licenses/mit-license.php.
5+
6+
using Autarkysoft.Bitcoin;
7+
using Autarkysoft.Bitcoin.Cryptography.Hashing;
8+
using System;
9+
using System.Security.Cryptography;
10+
using System.Text;
11+
12+
namespace Tests.Bitcoin.Cryptography.EllipticCurve
13+
{
14+
/// <summary>
15+
/// xoshiro PRNG https://prng.di.unimi.it/
16+
/// https://github.com/bitcoin-core/secp256k1/blob/77af1da9f631fa622fb5b5895fd27be431432368/src/testrand.h
17+
/// https://github.com/bitcoin-core/secp256k1/blob/77af1da9f631fa622fb5b5895fd27be431432368/src/testrand_impl.h
18+
/// </summary>
19+
internal class Rand
20+
{
21+
private readonly ulong[] state = new ulong[4];
22+
23+
private void secp256k1_testrand_seed(byte[] seed16)
24+
{
25+
Assert.Equal(16, seed16.Length);
26+
byte[] PREFIX = Encoding.UTF8.GetBytes("secp256k1 test init");
27+
Assert.Equal(19, PREFIX.Length);
28+
29+
// Use SHA256(PREFIX || seed16) as initial state.
30+
using Sha256 sha = new();
31+
byte[] out32 = sha.ComputeHash(PREFIX.ConcatFast(seed16));
32+
33+
for (int i = 0; i < 4; i++)
34+
{
35+
ulong s = 0;
36+
for (int j = 0; j < 8; ++j)
37+
{
38+
s = (s << 8) | out32[8 * i + j];
39+
}
40+
state[i] = s;
41+
}
42+
}
43+
44+
private static ulong rotl(ulong x, int k) => (x << k) | (x >> (64 - k));
45+
46+
private ulong secp256k1_testrand64()
47+
{
48+
/* Test-only Xoshiro256++ RNG. See https://prng.di.unimi.it/ */
49+
ulong result = rotl(state[0] + state[3], 23) + state[0];
50+
ulong t = state[1] << 17;
51+
state[2] ^= state[0];
52+
state[3] ^= state[1];
53+
state[1] ^= state[2];
54+
state[0] ^= state[3];
55+
state[2] ^= t;
56+
state[3] = rotl(state[3], 45);
57+
return result;
58+
}
59+
60+
private ulong secp256k1_testrand_bits(int bits)
61+
{
62+
if (bits == 0) return 0;
63+
return secp256k1_testrand64() >> (64 - bits);
64+
}
65+
66+
uint secp256k1_testrand32()
67+
{
68+
return (uint)(secp256k1_testrand64() >> 32);
69+
}
70+
71+
private uint secp256k1_testrand_int(uint range)
72+
{
73+
uint mask = 0;
74+
uint range_copy;
75+
/* Reduce range by 1, changing its meaning to "maximum value". */
76+
Assert.True(range != 0);
77+
range -= 1;
78+
/* Count the number of bits in range. */
79+
range_copy = range;
80+
while (range_copy != 0)
81+
{
82+
mask = (mask << 1) | 1U;
83+
range_copy >>= 1;
84+
}
85+
/* Generation loop. */
86+
while (true)
87+
{
88+
uint val = (uint)(secp256k1_testrand64() & mask);
89+
if (val <= range) return val;
90+
}
91+
}
92+
93+
private void secp256k1_testrand256(byte[] b32)
94+
{
95+
for (int i = 0; i < b32.Length; i += 8)
96+
{
97+
ulong val = secp256k1_testrand64();
98+
b32[i + 0] = (byte)val;
99+
b32[i + 1] = (byte)(val >> 8);
100+
b32[i + 2] = (byte)(val >> 16);
101+
b32[i + 3] = (byte)(val >> 24);
102+
b32[i + 4] = (byte)(val >> 32);
103+
b32[i + 5] = (byte)(val >> 40);
104+
b32[i + 6] = (byte)(val >> 48);
105+
b32[i + 7] = (byte)(val >> 56);
106+
}
107+
}
108+
109+
private void secp256k1_testrand_bytes_test(byte[] bytes, int len)
110+
{
111+
for (int i = 0; i < len; i++)
112+
{
113+
bytes[i] = 0;
114+
}
115+
116+
int bits = 0;
117+
while (bits < len * 8)
118+
{
119+
int now = (int)(1 + (secp256k1_testrand_bits(6) * secp256k1_testrand_bits(5) + 16) / 31);
120+
uint val = (uint)secp256k1_testrand_bits(1);
121+
while (now > 0 && bits < len * 8)
122+
{
123+
bytes[bits / 8] |= (byte)(val << (bits % 8));
124+
now--;
125+
bits++;
126+
}
127+
}
128+
}
129+
130+
private void secp256k1_testrand256_test(byte[] b32)
131+
{
132+
secp256k1_testrand_bytes_test(b32, 32);
133+
}
134+
135+
private void secp256k1_testrand_flip(byte[] b, int len)
136+
{
137+
b[secp256k1_testrand_int((uint)len)] ^= (byte)(1 << (int)secp256k1_testrand_bits(3));
138+
}
139+
140+
private void secp256k1_testrand_init(string hexseed)
141+
{
142+
byte[] seed16 = new byte[16];
143+
if (!string.IsNullOrEmpty(hexseed))
144+
{
145+
Span<byte> temp = Helper.HexToBytes(hexseed);
146+
if (temp.Length <= seed16.Length)
147+
{
148+
temp.CopyTo(seed16);
149+
}
150+
else
151+
{
152+
temp.Slice(0, seed16.Length).CopyTo(seed16);
153+
}
154+
}
155+
else
156+
{
157+
RandomNumberGenerator.Fill(seed16);
158+
}
159+
160+
secp256k1_testrand_seed(seed16);
161+
}
162+
163+
private void secp256k1_testrand_finish()
164+
{
165+
byte[] run32 = new byte[32];
166+
secp256k1_testrand256(run32);
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)