Skip to content

Commit 79e3dc2

Browse files
committed
nonce rfc6979
1 parent f106efb commit 79e3dc2

File tree

5 files changed

+118
-17
lines changed

5 files changed

+118
-17
lines changed

src/bip32.zig

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ pub const WifPrivateKey = struct {
228228
const netslice: [1]u8 = switch (net) {
229229
Network.mainnet => [1]u8{0b10000000},
230230
Network.testnet => [1]u8{0b11101111},
231+
Network.regtest => [1]u8{0b11101111},
231232
else => unreachable,
232233
};
233234

src/crypto/crypto.zig

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub const Secp256k1Point = @import("secp256k1.zig").Point;
33
pub const secp256k1_number_of_points = @import("secp256k1.zig").number_of_points;
44
pub const secp256k1_base_point = @import("secp256k1.zig").base_point;
55
pub const signEcdsa = @import("ecdsa.zig").sign;
6+
pub const nonceFnRfc6979 = @import("ecdsa.zig").nonceFnRfc6979;
67
pub const EcdsaSignature = @import("ecdsa.zig").Signature;
78
pub const Bech32Encoder = @import("bech32.zig").standard.Encoder;
89
pub const Bech32Decoder = @import("bech32.zig").standard.Decoder;

src/crypto/ecdsa.zig

+88-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const modinv = @import("math.zig").modinv;
44
const math = std.math;
55
const crypto = @import("crypto.zig");
66
const is_test = @import("builtin").is_test;
7+
const HmacSha256 = std.crypto.auth.hmac.sha2.HmacSha256;
78

89
fn intToHexStr(comptime T: type, data: T, buffer: []u8) !void {
910
// Number of characters to represent data in hex
@@ -48,15 +49,10 @@ pub const Signature = struct {
4849
// pk is the private key
4950
// z is the hash of the msg we want to sign
5051
// 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 {
5653
const n = crypto.secp256k1_number_of_points;
5754
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);
6056
var p = crypto.Secp256k1Point{ .x = crypto.secp256k1_base_point.x, .y = crypto.secp256k1_base_point.y };
6157
p.multiply(k);
6258
const r = @mod(p.x, n);
@@ -78,6 +74,75 @@ pub fn sign(pk: [32]u8, z: [32]u8, comptime nonce: ?u256) Signature {
7874
unreachable;
7975
}
8076

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+
81146
test "sign" {
82147
var buffer: [32]u8 = undefined;
83148
rand.bytes(&buffer);
@@ -88,7 +153,7 @@ test "sign" {
88153
rand.bytes(&pk);
89154
var pkhex: [64]u8 = undefined;
90155
_ = 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);
92157

93158
try std.testing.expectEqual(true, signature.s >= 1);
94159
try std.testing.expectEqual(true, signature.r >= 1);
@@ -116,13 +181,12 @@ test "sign" {
116181
test "signWithDeterministicNonce" {
117182
const msghex = "d7b60220e1b9b2c1ab40845118baf515203f7b6f0ad83cbb68d3c89b5b3098a6";
118183
const privkeyhex = "7306f5092467981e66eff98b6b03bfe925922c5ecfaf14c4257ef18e81becf1f";
119-
const nonce: u256 = 123456789;
120184
var pk: [32]u8 = undefined;
121185
_ = try std.fmt.hexToBytes(&pk, privkeyhex);
122186
var msg: [32]u8 = undefined;
123187
_ = try std.fmt.hexToBytes(&msg, msghex);
124188

125-
const signature = sign(pk, msg, nonce);
189+
const signature = sign(pk, msg, nonceFn123456789);
126190
try std.testing.expectEqual(4051293998585674784991639592782214972820158391371785981004352359465450369227, signature.r);
127191
try std.testing.expectEqual(22928756034338380041288899807245402174768928418361705349511346173579327129676, signature.s);
128192
}
@@ -145,3 +209,17 @@ test "derEncode" {
145209
try std.testing.expectEqual(signature2.r, 20563619043091547917171744686276600212876833865692707756359368710575856337166);
146210
try std.testing.expectEqual(signature2.s, 53911059558236206164581555165446301142462550061465543326712028582562493425622);
147211
}
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+
}

src/tx.zig

+13-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const utils = @import("utils.zig");
44
const KeyPath = @import("bip44.zig").KeyPath;
55
const assert = @import("std").debug.assert;
66
const signEcdsa = @import("crypto").signEcdsa;
7+
const nonceFnRfc6979 = @import("crypto").nonceFnRfc6979;
78
const db = @import("db/db.zig");
89
const sqlite = @import("sqlite");
910
const bip32 = @import("bip32.zig");
@@ -318,7 +319,7 @@ test "createTx" {
318319
}
319320

320321
// [72]u8 = 64 txid + 8 vout
321-
pub fn signTx(allocator: std.mem.Allocator, tx: *Transaction, privkeys: std.AutoHashMap([72]u8, [32]u8), pubkeys: std.AutoHashMap([72]u8, bip32.PublicKey), comptime nonce: ?u256) !void {
322+
pub fn signTx(allocator: std.mem.Allocator, tx: *Transaction, privkeys: std.AutoHashMap([72]u8, [32]u8), pubkeys: std.AutoHashMap([72]u8, bip32.PublicKey), comptime nonce_fn: fn (pk: [32]u8, z: [32]u8) u256) !void {
322323
const inputs_preimage_hash = try getTxInputsPreImageHash(allocator, tx.inputs.items);
323324
const inputs_sequences_preimage_hash = try getTxInputsSequencesPreImageHash(allocator, tx.inputs.items);
324325
const outputs_preimage_hash = try getTxOutputsPreImageHash(allocator, tx.outputs.items);
@@ -334,7 +335,7 @@ pub fn signTx(allocator: std.mem.Allocator, tx: *Transaction, privkeys: std.Auto
334335
const pubkey = pubkeys.get(key).?;
335336
const privkey = privkeys.get(key).?;
336337
const preimage_hash = try getPreImageHash(tx.version, inputs_preimage_hash, inputs_sequences_preimage_hash, outputs_preimage_hash, tx.locktime, input, pubkey, sighash_type);
337-
const witness = try createWitness(allocator, preimage_hash, privkey, pubkey, sighash_type, nonce);
338+
const witness = try createWitness(allocator, preimage_hash, privkey, pubkey, sighash_type, nonce_fn);
338339
try tx.addWitness(witness);
339340
}
340341
}
@@ -367,7 +368,7 @@ test "signTx" {
367368
var tx = try createTx(allocator, &inputs, &outputs);
368369
defer tx.deinit();
369370

370-
try signTx(allocator, &tx, privkeys, pubkeys, 123456789);
371+
try signTx(allocator, &tx, privkeys, pubkeys, nonceFn123456789);
371372
var tx_raw: [194]u8 = undefined;
372373
try encodeTx(allocator, &tx_raw, tx, true);
373374
const expected_tx_raw_hex = "02000000000101ac4994014aa36b7f53375658ef595b3cb2891e1735fe5b441686f5e53338e76a0100000000ffffffff01204e0000000000001976a914ce72abfd0e6d9354a660c18f2825eb392f060fdc88ac02473044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c012103eed0d937090cae6ffde917de8a80dc6156e30b13edd5e51e2e50d52428da1c8700000000";
@@ -818,8 +819,8 @@ test "getPreImageHash" {
818819
try std.testing.expectEqualSlices(u8, &expected_bytes, &hash);
819820
}
820821

821-
pub fn createWitness(allocator: std.mem.Allocator, preimage_hash: [32]u8, privkey: [32]u8, pubkey: bip32.PublicKey, sighash_type: SighashType, comptime nonce: ?u256) !TxWitness {
822-
const signature = signEcdsa(privkey, preimage_hash, nonce);
822+
pub fn createWitness(allocator: std.mem.Allocator, preimage_hash: [32]u8, privkey: [32]u8, pubkey: bip32.PublicKey, sighash_type: SighashType, comptime nonce_fn: fn (pk: [32]u8, z: [32]u8) u256) !TxWitness {
823+
const signature = signEcdsa(privkey, preimage_hash, nonce_fn);
823824
const partial_serialized = try signature.derEncode(allocator);
824825
defer allocator.free(partial_serialized);
825826
var sighash_type_hex: [2]u8 = undefined;
@@ -838,6 +839,12 @@ pub fn createWitness(allocator: std.mem.Allocator, preimage_hash: [32]u8, privke
838839
return witness;
839840
}
840841

842+
fn nonceFn123456789(pk: [32]u8, z: [32]u8) u256 {
843+
_ = pk;
844+
_ = z;
845+
return 123456789;
846+
}
847+
841848
test "createWitness" {
842849
const allocator = std.testing.allocator;
843850
const preimage_hash_hex = "d7b60220e1b9b2c1ab40845118baf515203f7b6f0ad83cbb68d3c89b5b3098a6";
@@ -848,7 +855,7 @@ test "createWitness" {
848855
_ = try std.fmt.hexToBytes(&privkey, &privkey_hex);
849856
const pubkey = bip32.generatePublicKey(privkey);
850857

851-
const witness = try createWitness(allocator, preimage_hash, privkey, pubkey, SighashType.sighash_all, 123456789);
858+
const witness = try createWitness(allocator, preimage_hash, privkey, pubkey, SighashType.sighash_all, nonceFn123456789);
852859
defer witness.deinit();
853860

854861
const expected_signature_hex = "3044022008f4f37e2d8f74e18c1b8fde2374d5f28402fb8ab7fd1cc5b786aa40851a70cb022032b1374d1a0f125eae4f69d1bc0b7f896c964cfdba329f38a952426cf427484c01";

src/walle.zig

+15-1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,13 @@ pub fn main() !void {
248248
const extended_privkey = try bip32.ExtendedPrivateKey.fromAddress(private_descriptor.?.extended_key);
249249
const privkey = try bip44.generatePrivateFromAccountPrivateKey(extended_privkey, output.keypath.?.path[3], output.keypath.?.path[4]);
250250

251+
const wif = bip32.WifPrivateKey.fromPrivateKey(privkey, network, true);
252+
const wifbase58 = try wif.toBase58();
253+
std.debug.print("wifbase58: {s}\n", .{wifbase58});
254+
255+
utils.debugPrintBytes(64, &privkey);
256+
std.debug.print("pubkey: {s}\n", .{try pubkey.toStrCompressed()});
257+
251258
try pubkeys.put(map_key, pubkey);
252259
try privkeys.put(map_key, privkey);
253260
}
@@ -266,7 +273,13 @@ pub fn main() !void {
266273

267274
var send_transaction = try tx.createTx(allocator, tx_inputs, tx_outputs);
268275

269-
try tx.signTx(allocator, &send_transaction, privkeys, pubkeys, null);
276+
const raw_tx_cap_no_sign = tx.encodeTxCap(send_transaction, false);
277+
const raw_tx_no_sign = try allocator.alloc(u8, raw_tx_cap_no_sign);
278+
const raw_tx_hex_no_sign = try allocator.alloc(u8, raw_tx_cap_no_sign * 2);
279+
try tx.encodeTx(allocator, raw_tx_no_sign, send_transaction, false);
280+
_ = try std.fmt.bufPrint(raw_tx_hex_no_sign, "{x}", .{std.fmt.fmtSliceHexLower(raw_tx_no_sign)});
281+
282+
try tx.signTx(allocator, &send_transaction, privkeys, pubkeys, crypto.nonceFnRfc6979);
270283
std.debug.print("send transaction\n{}\n", .{send_transaction});
271284

272285
const raw_tx_cap = tx.encodeTxCap(send_transaction, true);
@@ -275,6 +288,7 @@ pub fn main() !void {
275288
try tx.encodeTx(allocator, raw_tx, send_transaction, true);
276289
_ = try std.fmt.bufPrint(raw_tx_hex, "{x}", .{std.fmt.fmtSliceHexLower(raw_tx)});
277290

291+
std.debug.print("\n{s}\n", .{raw_tx_hex_no_sign});
278292
std.debug.print("\n{s}\n", .{raw_tx_hex});
279293
},
280294
}

0 commit comments

Comments
 (0)