forked from zone117x/Secp256k1.Net
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Tests.cs
262 lines (218 loc) · 10.9 KB
/
Tests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
using System;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
using Xunit;
namespace Secp256k1Net.Test
{
public class Tests
{
ref struct KeyPair
{
public Span<byte> PrivateKey;
public Span<byte> PublicKey;
}
Span<byte> GeneratePrivateKey(Secp256k1 secp256k1)
{
var rnd = RandomNumberGenerator.Create();
Span<byte> privateKey = new byte[32];
do
{
rnd.GetBytes(privateKey);
}
while (!secp256k1.SecretKeyVerify(privateKey));
return privateKey;
}
KeyPair GenerateKeyPair(Secp256k1 secp256k1)
{
var privateKey = GeneratePrivateKey(secp256k1);
Span<byte> publicKey = new byte[64];
if (!secp256k1.PublicKeyCreate(publicKey, privateKey))
{
throw new Exception("Public key creation failed");
}
return new KeyPair { PrivateKey = privateKey, PublicKey = publicKey };
}
[Fact]
public void EcdhTest()
{
using (var secp256k1 = new Secp256k1())
{
var kp1 = GenerateKeyPair(secp256k1);
var kp2 = GenerateKeyPair(secp256k1);
Span<byte> sec1 = new byte[32];
Assert.True(secp256k1.Ecdh(sec1, kp1.PublicKey, kp2.PrivateKey));
Span<byte> sec2 = new byte[32];
Assert.True(secp256k1.Ecdh(sec2, kp2.PublicKey, kp1.PrivateKey));
Span<byte> sec3 = new byte[32];
Assert.True(secp256k1.Ecdh(sec3, kp1.PublicKey, kp1.PrivateKey));
Assert.Equal(sec1.ToHexString(), sec2.ToHexString());
Assert.NotEqual(sec3.ToHexString(), sec2.ToHexString());
}
}
[Fact]
public void EcdhTestCustomHash()
{
using (var secp256k1 = new Secp256k1())
{
var kp1 = GenerateKeyPair(secp256k1);
var kp2 = GenerateKeyPair(secp256k1);
EcdhHashFunction hashFunc = (Span<byte> output, Span<byte> x, Span<byte> y, IntPtr data) =>
{
// XOR points together (dumb)
for (var i = 0; i < 32; i++)
{
output[i] = (byte)(x[i] ^ y[i]);
}
return 1;
};
Span<byte> sec1 = new byte[32];
Assert.True(secp256k1.Ecdh(sec1, kp1.PublicKey, kp2.PrivateKey, hashFunc, IntPtr.Zero));
Span<byte> sec2 = new byte[32];
Assert.True(secp256k1.Ecdh(sec2, kp2.PublicKey, kp1.PrivateKey, hashFunc, IntPtr.Zero));
Span<byte> sec3 = new byte[32];
Assert.True(secp256k1.Ecdh(sec3, kp1.PublicKey, kp1.PrivateKey, hashFunc, IntPtr.Zero));
Assert.Equal(sec1.ToHexString(), sec2.ToHexString());
Assert.NotEqual(sec3.ToHexString(), sec2.ToHexString());
}
}
[Fact]
public void KeyPairGeneration()
{
using (var secp256k1 = new Secp256k1())
{
var kp = GenerateKeyPair(secp256k1);
}
}
[Fact]
public void SignAndVerify()
{
using (var secp256k1 = new Secp256k1())
{
var kp = GenerateKeyPair(secp256k1);
Span<byte> msg = new byte[32];
RandomNumberGenerator.Create().GetBytes(msg);
Span<byte> signature = new byte[64];
Assert.True(secp256k1.Sign(signature, msg, kp.PrivateKey));
Assert.True(secp256k1.Verify(signature, msg, kp.PublicKey));
}
}
[Fact]
public void ParseDerSignatureTest()
{
using (var secp256k1 = new Secp256k1())
{
Span<byte> signatureOutput = new byte[Secp256k1.SIGNATURE_LENGTH];
Span<byte> validDerSignature = "30440220484ECE2B365D2B2C2EAD34B518328BBFEF0F4409349EEEC9CB19837B5795A5F5022040C4F6901FE489F923C49D4104554FD08595EAF864137F87DADDD0E3619B0605".HexToBytes();
Assert.True(secp256k1.SignatureParseDer(signatureOutput, validDerSignature));
Span<byte> invalidDerSignature = "00".HexToBytes();
Assert.False(secp256k1.SignatureParseDer(signatureOutput, invalidDerSignature));
}
}
/*
[Fact]
public void SignatureNormalize()
{
using (var secp256k1 = new Secp256k1())
{
Assert.True(secp256k1.SignatureNormalize()
}
}
*/
[Fact]
public void SigningTest()
{
using (var secp256k1 = new Secp256k1())
{
Span<byte> signature = new byte[Secp256k1.UNSERIALIZED_SIGNATURE_SIZE];
Span<byte> messageHash = new byte[] { 0xc9, 0xf1, 0xc7, 0x66, 0x85, 0x84, 0x5e, 0xa8, 0x1c, 0xac, 0x99, 0x25, 0xa7, 0x56, 0x58, 0x87, 0xb7, 0x77, 0x1b, 0x34, 0xb3, 0x5e, 0x64, 0x1c, 0xca, 0x85, 0xdb, 0x9f, 0xef, 0xd0, 0xe7, 0x1f };
Span<byte> secretKey = "e815acba8fcf085a0b4141060c13b8017a08da37f2eb1d6a5416adbb621560ef".HexToBytes();
bool result = secp256k1.SignRecoverable(signature, messageHash, secretKey);
Assert.True(result);
// Recover the public key
Span<byte> publicKeyOutput = new byte[Secp256k1.PUBKEY_LENGTH];
result = secp256k1.Recover(publicKeyOutput, signature, messageHash);
Assert.True(result);
// Serialize the public key
Span<byte> serializedKey = new byte[Secp256k1.SERIALIZED_UNCOMPRESSED_PUBKEY_LENGTH];
result = secp256k1.PublicKeySerialize(serializedKey, publicKeyOutput);
Assert.True(result);
// Slice off any prefix.
serializedKey = serializedKey.Slice(serializedKey.Length - Secp256k1.PUBKEY_LENGTH);
Assert.Equal("0x3a2361270fb1bdd220a2fa0f187cc6f85079043a56fb6a968dfad7d7032b07b01213e80ecd4fb41f1500f94698b1117bc9f3335bde5efbb1330271afc6e85e92", serializedKey.ToHexString(), true);
// Verify it works with variables generated from our managed code.
BigInteger ecdsa_r = BigInteger.Parse("68932463183462156574914988273446447389145511361487771160486080715355143414637");
BigInteger ecdsa_s = BigInteger.Parse("47416572686988136438359045243120473513988610648720291068939984598262749281683");
byte recoveryId = 1;
byte[] ecdsa_r_bytes = BigIntegerConverter.GetBytes(ecdsa_r);
byte[] ecdsa_s_bytes = BigIntegerConverter.GetBytes(ecdsa_s);
signature = ecdsa_r_bytes.Concat(ecdsa_s_bytes).ToArray();
// Allocate memory for the signature and create a serialized-format signature to deserialize into our native format (platform dependent, hence why we do this).
Span<byte> serializedSignature = ecdsa_r_bytes.Concat(ecdsa_s_bytes).ToArray();
signature = new byte[Secp256k1.UNSERIALIZED_SIGNATURE_SIZE];
result = secp256k1.RecoverableSignatureParseCompact(signature, serializedSignature, recoveryId);
if (!result)
throw new Exception("Unmanaged EC library failed to parse serialized signature.");
// Recover the public key
publicKeyOutput = new byte[Secp256k1.PUBKEY_LENGTH];
result = secp256k1.Recover(publicKeyOutput, signature, messageHash);
Assert.True(result);
// Serialize the public key
serializedKey = new byte[Secp256k1.SERIALIZED_UNCOMPRESSED_PUBKEY_LENGTH];
result = secp256k1.PublicKeySerialize(serializedKey, publicKeyOutput);
Assert.True(result);
// Slice off any prefix.
serializedKey = serializedKey.Slice(serializedKey.Length - Secp256k1.PUBKEY_LENGTH);
// Assert our key
Assert.Equal("0x3a2361270fb1bdd220a2fa0f187cc6f85079043a56fb6a968dfad7d7032b07b01213e80ecd4fb41f1500f94698b1117bc9f3335bde5efbb1330271afc6e85e92", serializedKey.ToHexString(), true);
}
}
}
public static class Extensions
{
public static string ToHexString(this Span<byte> span)
{
return "0x" + BitConverter.ToString(span.ToArray()).Replace("-", "").ToLowerInvariant();
}
public static byte[] HexToBytes(this string hexString)
{
int chars = hexString.Length;
byte[] bytes = new byte[chars / 2];
for (int i = 0; i < chars; i += 2)
{
bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
}
return bytes;
}
}
public abstract class BigIntegerConverter
{
/// <summary>
/// Obtains the bytes that represent the BigInteger as if it was a big endian 256-bit integer.
/// </summary>
/// <param name="bigInteger">The BigInteger to obtain the byte representation of.</param>
/// <returns>Returns the bytes that represent BigInteger as if it was a 256-bit integer.</returns>
public static byte[] GetBytes(BigInteger bigInteger, int byteCount = 32)
{
// Obtain the bytes which represent this BigInteger.
byte[] result = bigInteger.ToByteArray();
// We'll operate on the data in little endian (since we'll extend the array anyways and we'd have to copy the data over anyways).
if (!BitConverter.IsLittleEndian)
Array.Reverse(result);
// Store the original size of the data, then resize it to the size of a word.
int originalSize = result.Length;
Array.Resize(ref result, byteCount);
// BigInteger uses the most significant bit as sign and optimizes to return values like -1 as 0xFF instead of as 0xFFFF or larger (since there is no bound size, and negative values have all leading bits set)
// Instead if we wanted to represent 256 (0xFF), we would add a leading zero byte so the sign bit comes from it, and will be zero (positive) (0x00FF), this way, BigInteger knows to represent this as a positive value.
// Because we resized the array already, it would have added leading zero bytes which works for positive numbers, but if it's negative, all extended bits should be set, so we check for that case.
// If the integer is negative, any extended bits should all be set.
if (bigInteger.Sign < 0)
for (int i = originalSize; i < result.Length; i++)
result[i] = 0xFF;
// Flip the array so it is in big endian form.
Array.Reverse(result);
return result;
}
}
}