Module: Schnorr::MuSig2
- Extended by:
- Util
- Defined in:
- lib/schnorr/musig2.rb,
lib/schnorr/musig2/context/key_agg.rb,
lib/schnorr/musig2/context/session.rb
Defined Under Namespace
Classes: Error, KeyAggContext, SessionContext
Class Method Summary collapse
-
.aggregate(pubkeys) ⇒ Schnorr::MuSig2::KeyAggContext
Compute aggregate public key.
-
.aggregate_nonce(pub_nonces) ⇒ String
Aggregate public nonces.
-
.aggregate_with_tweaks(pubkeys, tweaks, modes) ⇒ Schnorr::MuSig2::KeyAggContext
Compute aggregate public key with tweaks.
-
.deterministic_sign(sk, agg_other_nonce, pubkeys, msg, tweaks: [], modes: [], rand: nil) ⇒ Array
Generate deterministic signature.
-
.gen_nonce(pk:, sk: nil, agg_pubkey: nil, msg: nil, extra_in: nil, rand: SecureRandom.bytes(32)) ⇒ Array(String)
Generate nonce.
-
.hash_keys(pubkeys) ⇒ Object
Compute.
- .second_key(pubkeys) ⇒ Object
-
.sort_pubkeys(pubkeys) ⇒ Array(String)
Sort the list of public keys in lexicographical order.
Methods included from Util
hex2bin, hex_string?, string2point
Class Method Details
.aggregate(pubkeys) ⇒ Schnorr::MuSig2::KeyAggContext
Compute aggregate public key.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/schnorr/musig2.rb', line 23 def aggregate(pubkeys) pubkeys = pubkeys.map do |p| pubkey = hex2bin(p) raise ArgumentError, "Public key must be 33 bytes." unless pubkey.bytesize == 33 pubkey end pk2 = second_key(pubkeys) q = ECDSA::Ext::JacobianPoint.infinity_point(GROUP) l = hash_keys(pubkeys) pubkeys.each do |p| begin point = string2point(p).to_jacobian rescue ECDSA::Format::DecodeError raise ArgumentError, 'Invalid public key.' end coeff = p == pk2 ? 1 : Schnorr.tagged_hash('KeyAgg coefficient', l + p).bti q += point * coeff end KeyAggContext.new(q.to_affine, 1, 0) end |
.aggregate_nonce(pub_nonces) ⇒ String
Aggregate public nonces.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/schnorr/musig2.rb', line 114 def aggregate_nonce(pub_nonces) 2.times.map do |i| r = GROUP.generator.to_jacobian.infinity_point pub_nonces = pub_nonces.each do |nonce| nonce = hex2bin(nonce) raise ArgumentError, "" unless nonce.bytesize == 66 begin p = string2point(nonce[(i * 33)...(i + 1)*33]).to_jacobian raise ArgumentError, 'Public nonce is infinity' if p.infinity? rescue ECDSA::Format::DecodeError raise ArgumentError, "Invalid public nonce." end r += p end r.to_affine.encode.unpack1('H*') end.join end |
.aggregate_with_tweaks(pubkeys, tweaks, modes) ⇒ Schnorr::MuSig2::KeyAggContext
Compute aggregate public key with tweaks.
49 50 51 52 53 54 55 56 57 58 |
# File 'lib/schnorr/musig2.rb', line 49 def aggregate_with_tweaks(pubkeys, tweaks, modes) raise ArgumentError, 'tweaks and modes must be same length' unless tweaks.length == modes.length agg_ctx = aggregate(pubkeys) tweaks.each.with_index do |tweak, i| tweak = hex2bin(tweak) raise ArgumentError, 'tweak value must be 32 bytes' unless tweak.bytesize == 32 agg_ctx = agg_ctx.apply_tweak(tweak, modes[i]) end agg_ctx end |
.deterministic_sign(sk, agg_other_nonce, pubkeys, msg, tweaks: [], modes: [], rand: nil) ⇒ Array
Generate deterministic signature.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/schnorr/musig2.rb', line 141 def deterministic_sign(sk, agg_other_nonce, pubkeys, msg, tweaks: [], modes: [], rand: nil) raise ArgumentError, 'The tweaks and modes arrays must have the same length.' unless tweaks.length == modes.length sk = hex2bin(sk) msg = hex2bin(msg) agg_other_nonce = hex2bin(agg_other_nonce) sk_ = rand ? gen_aux(sk, hex2bin(rand)) : sk agg_ctx = aggregate_with_tweaks(pubkeys, tweaks, modes) agg_pk = [agg_ctx.x_only_pubkey].pack("H*") k1 = deterministic_nonce_hash(sk_, agg_other_nonce, agg_pk, msg, 0).bti k2 = deterministic_nonce_hash(sk_, agg_other_nonce, agg_pk, msg, 1).bti r1 = (GROUP.generator.to_jacobian * k1).to_affine r2 = (GROUP.generator.to_jacobian * k2).to_affine raise ArgumentError, 'R1 must not be infinity.' if r1.infinity? raise ArgumentError, 'R2 must not be infinity.' if r2.infinity? pub_nonce = r1.encode + r2.encode pk = (GROUP.generator.to_jacobian * sk.bti).to_affine sec_nonce = ECDSA::Format::IntegerOctetString.encode(k1, GROUP.byte_length) + ECDSA::Format::IntegerOctetString.encode(k2, GROUP.byte_length) + pk.encode agg_nonce = aggregate_nonce([pub_nonce, agg_other_nonce]) ctx = SessionContext.new(agg_nonce, pubkeys, msg, tweaks, modes) sig = ctx.sign(sec_nonce, sk) [pub_nonce.unpack1('H*'), sig] end |
.gen_nonce(pk:, sk: nil, agg_pubkey: nil, msg: nil, extra_in: nil, rand: SecureRandom.bytes(32)) ⇒ Array(String)
Generate nonce.
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 |
# File 'lib/schnorr/musig2.rb', line 68 def gen_nonce(pk: , sk: nil, agg_pubkey: nil, msg: nil, extra_in: nil, rand: SecureRandom.bytes(32)) rand = hex2bin(rand) raise ArgumentError, 'The rand must be 32 bytes.' unless rand.bytesize == 32 pk = hex2bin(pk) raise ArgumentError, 'The pk must be 33 bytes.' unless pk.bytesize == 33 rand = if sk.nil? rand else sk = hex2bin(sk) raise ArgumentError, "The sk must be 32 bytes." unless sk.bytesize == 32 gen_aux(sk, rand) end agg_pubkey = if agg_pubkey agg_pubkey = hex2bin(agg_pubkey) raise ArgumentError, 'The agg_pubkey must be 33 bytes.' unless agg_pubkey.bytesize == 32 agg_pubkey else '' end msg_prefixed = if msg.nil? [0].pack('C') else msg = hex2bin(msg) [1, msg.bytesize].pack('CQ>') + msg end extra_in = extra_in ? hex2bin(extra_in) : '' k1 = nonce_hash(rand, pk, agg_pubkey, 0, msg_prefixed, extra_in) k1_i = k1.bti % GROUP.order k2 = nonce_hash(rand, pk, agg_pubkey, 1, msg_prefixed, extra_in) k2_i = k2.bti % GROUP.order raise ArgumentError, 'k1 must not be zero.' if k1_i.zero? raise ArgumentError, 'k2 must not be zero.' if k2_i.zero? r1 = (GROUP.generator.to_jacobian * k1_i).to_affine r2 = (GROUP.generator.to_jacobian * k2_i).to_affine pub_nonce = r1.encode + r2.encode sec_nonce = k1 + k2 + pk [sec_nonce.unpack1('H*'), pub_nonce.unpack1('H*')] end |
.hash_keys(pubkeys) ⇒ Object
Compute
173 174 175 |
# File 'lib/schnorr/musig2.rb', line 173 def hash_keys(pubkeys) Schnorr.tagged_hash('KeyAgg list', pubkeys.join) end |
.second_key(pubkeys) ⇒ Object
165 166 167 168 169 170 |
# File 'lib/schnorr/musig2.rb', line 165 def second_key(pubkeys) pubkeys[1..].each do |p| return p unless p == pubkeys[0] end ['00'].pack("H*") * 33 end |
.sort_pubkeys(pubkeys) ⇒ Array(String)
Sort the list of public keys in lexicographical order.
16 17 18 |
# File 'lib/schnorr/musig2.rb', line 16 def sort_pubkeys(pubkeys) pubkeys.map{|p| hex_string?(p) ? p : p.unpack1('H*')}.sort end |