@@ -4,6 +4,7 @@ const modinv = @import("math.zig").modinv;
4
4
const math = std .math ;
5
5
const crypto = @import ("crypto.zig" );
6
6
const is_test = @import ("builtin" ).is_test ;
7
+ const HmacSha256 = std .crypto .auth .hmac .sha2 .HmacSha256 ;
7
8
8
9
fn intToHexStr (comptime T : type , data : T , buffer : []u8 ) ! void {
9
10
// Number of characters to represent data in hex
@@ -48,15 +49,10 @@ pub const Signature = struct {
48
49
// pk is the private key
49
50
// z is the hash of the msg we want to sign
50
51
// nonce is used in tests to recreate deterministic signature.
51
- // I don't like this parameter, using the same nonce can expose the private key, but I havent found any better solution
52
- pub fn sign (pk : [32 ]u8 , z : [32 ]u8 , comptime nonce : ? u256 ) Signature {
53
- comptime if (nonce != null and is_test == false ) {
54
- unreachable ;
55
- };
52
+ pub fn sign (pk : [32 ]u8 , z : [32 ]u8 , comptime nonce_fn : fn (pk : [32 ]u8 , z : [32 ]u8 ) u256 ) Signature {
56
53
const n = crypto .secp256k1_number_of_points ;
57
54
while (true ) {
58
- const k : u256 = if (comptime nonce != null ) nonce .? else rand .intRangeAtMost (u256 , 0 , n - 1 );
59
- // const k = rand.intRangeAtMost(u256, 0, n - 1);
55
+ const k : u256 = nonce_fn (pk , z );
60
56
var p = crypto.Secp256k1Point { .x = crypto .secp256k1_base_point .x , .y = crypto .secp256k1_base_point .y };
61
57
p .multiply (k );
62
58
const r = @mod (p .x , n );
@@ -78,6 +74,75 @@ pub fn sign(pk: [32]u8, z: [32]u8, comptime nonce: ?u256) Signature {
78
74
unreachable ;
79
75
}
80
76
77
+ // The original implementation can be found here: https://github.com/Raiden1411/zabi/blob/main/src/crypto/Signer.zig#L192
78
+ // Thanks Raiden1411
79
+ pub fn nonceFnRfc6979 (pk : [32 ]u8 , z : [32 ]u8 ) u256 {
80
+ // We already ask for the hashed message.
81
+ // message_hash == h1 and x == private_key.
82
+ // Section 3.2.a
83
+ var v : [33 ]u8 = undefined ;
84
+ var k : [32 ]u8 = undefined ;
85
+ var buffer : [97 ]u8 = undefined ;
86
+
87
+ // Section 3.2.b
88
+ @memset (v [0.. 32], 0x01 );
89
+ v [32 ] = 0x00 ;
90
+
91
+ // Section 3.2.c
92
+ @memset (& k , 0x00 );
93
+
94
+ // Section 3.2.d
95
+ @memcpy (buffer [0.. 32], v [0.. 32]);
96
+ buffer [32 ] = 0x00 ;
97
+
98
+ @memcpy (buffer [33.. 65], & pk );
99
+ @memcpy (buffer [65.. 97], & z );
100
+
101
+ HmacSha256 .create (& k , & buffer , & k );
102
+
103
+ // Section 3.2.e
104
+ HmacSha256 .create (v [0.. 32], v [0.. 32], & k );
105
+
106
+ // Section 3.2.f
107
+ @memcpy (buffer [0.. 32], v [0.. 32]);
108
+ buffer [32 ] = 0x01 ;
109
+
110
+ @memcpy (buffer [33.. 65], & pk );
111
+ @memcpy (buffer [65.. 97], & z );
112
+ HmacSha256 .create (& k , & buffer , & k );
113
+
114
+ // Section 3.2.g
115
+ HmacSha256 .create (v [0.. 32], v [0.. 32], & k );
116
+
117
+ // Section 3.2.h
118
+ HmacSha256 .create (v [0.. 32], v [0.. 32], & k );
119
+
120
+ while (true ) {
121
+ const k_int = std .mem .readInt (u256 , v [0.. 32], .big );
122
+
123
+ // K is within [1,q-1] and is in R value.
124
+ // that is not 0 so we break here.
125
+ if (k_int > 0 and k_int < crypto .secp256k1_number_of_points ) {
126
+ break ;
127
+ }
128
+
129
+ // Keep generating until we found a valid K.
130
+ HmacSha256 .create (& k , v [0.. ], & k );
131
+ HmacSha256 .create (v [0.. 32], v [0.. 32], & k );
132
+ }
133
+
134
+ const n = std .mem .readInt (u256 , v [0.. 32], .big );
135
+
136
+ std .debug .print ("calculated nonce = {d}\n " , .{n });
137
+ return n ;
138
+ }
139
+
140
+ fn nonceFn123456789 (pk : [32 ]u8 , z : [32 ]u8 ) u256 {
141
+ _ = pk ;
142
+ _ = z ;
143
+ return 123456789 ;
144
+ }
145
+
81
146
test "sign" {
82
147
var buffer : [32 ]u8 = undefined ;
83
148
rand .bytes (& buffer );
@@ -88,7 +153,7 @@ test "sign" {
88
153
rand .bytes (& pk );
89
154
var pkhex : [64 ]u8 = undefined ;
90
155
_ = std .fmt .bufPrint (& pkhex , "{x}" , .{std .fmt .fmtSliceHexLower (& bytes )}) catch unreachable ;
91
- const signature = sign (pk , bytes , null );
156
+ const signature = sign (pk , bytes , nonceFnRfc6979 );
92
157
93
158
try std .testing .expectEqual (true , signature .s >= 1 );
94
159
try std .testing .expectEqual (true , signature .r >= 1 );
@@ -116,13 +181,12 @@ test "sign" {
116
181
test "signWithDeterministicNonce" {
117
182
const msghex = "d7b60220e1b9b2c1ab40845118baf515203f7b6f0ad83cbb68d3c89b5b3098a6" ;
118
183
const privkeyhex = "7306f5092467981e66eff98b6b03bfe925922c5ecfaf14c4257ef18e81becf1f" ;
119
- const nonce : u256 = 123456789 ;
120
184
var pk : [32 ]u8 = undefined ;
121
185
_ = try std .fmt .hexToBytes (& pk , privkeyhex );
122
186
var msg : [32 ]u8 = undefined ;
123
187
_ = try std .fmt .hexToBytes (& msg , msghex );
124
188
125
- const signature = sign (pk , msg , nonce );
189
+ const signature = sign (pk , msg , nonceFn123456789 );
126
190
try std .testing .expectEqual (4051293998585674784991639592782214972820158391371785981004352359465450369227 , signature .r );
127
191
try std .testing .expectEqual (22928756034338380041288899807245402174768928418361705349511346173579327129676 , signature .s );
128
192
}
@@ -145,3 +209,17 @@ test "derEncode" {
145
209
try std .testing .expectEqual (signature2 .r , 20563619043091547917171744686276600212876833865692707756359368710575856337166 );
146
210
try std .testing .expectEqual (signature2 .s , 53911059558236206164581555165446301142462550061465543326712028582562493425622 );
147
211
}
212
+
213
+ test "generateNonceRfc6979" {
214
+ const msghex = "d7b60220e1b9b2c1ab40845118baf515203f7b6f0ad83cbb68d3c89b5b3098a6" ;
215
+ const privkeyhex = "7306f5092467981e66eff98b6b03bfe925922c5ecfaf14c4257ef18e81becf1f" ;
216
+ var pk : [32 ]u8 = undefined ;
217
+ _ = try std .fmt .hexToBytes (& pk , privkeyhex );
218
+ var msg : [32 ]u8 = undefined ;
219
+ _ = try std .fmt .hexToBytes (& msg , msghex );
220
+
221
+ const s1 = sign (pk , msg , nonceFnRfc6979 );
222
+ const s2 = sign (pk , msg , nonceFnRfc6979 );
223
+ try std .testing .expectEqual (s1 .r , s2 .r );
224
+ try std .testing .expectEqual (s1 .s , s2 .s );
225
+ }
0 commit comments