Module: Schnorr::MuSig

Defined in:
lib/schnorr/mu_sig.rb,
lib/schnorr/mu_sig/session.rb

Overview

Defined Under Namespace

Classes: Session

Constant Summary collapse

TAG =

SHA256(“MuSig coefficient”)

['74894a2bec01af68225002cae9b0430ab63151ede5d31f641791976c7140b57b'].pack('H*')

Class Method Summary collapse

Class Method Details

.compute_ell(public_keys) ⇒ String

Computes ell = SHA256(pk, …, pk)

Parameters:

  • public_keys (Array[String])

    The set of public keys with binary format.

Returns:

  • (String)

    ell



16
17
18
# File 'lib/schnorr/mu_sig.rb', line 16

def compute_ell(public_keys)
  Digest::SHA256.digest(public_keys.join)
end

.partial_sig_combine(combined_nonce, signatures) ⇒ Schnorr::Signature

Combine the partial signatures to obtain a complete signature.

Parameters:

  • combined_nonce (Array)
  • signatures (Array)

    co-signer’s signature.

Returns:



73
74
75
76
77
78
# File 'lib/schnorr/mu_sig.rb', line 73

def partial_sig_combine(combined_nonce, signatures)
  point_r = ECDSA::Format::PointOctetString.decode(combined_nonce, ECDSA::Group::Secp256k1)
  field = ECDSA::PrimeField.new(ECDSA::Group::Secp256k1.order)
  signature = signatures.inject{|sum, s|field.mod(sum + s)}
  Schnorr::Signature.new(point_r.x, signature)
end

.pubkey_combine(public_keys, ell: nil) ⇒ String

Combine public keys.

Parameters:

  • public_keys (Array[String])

    The set of public keys with binary format.

Returns:

  • (String)

    a combined public key point with binary format.



23
24
25
26
27
28
29
30
31
# File 'lib/schnorr/mu_sig.rb', line 23

def pubkey_combine(public_keys, ell: nil)
  ell = compute_ell(public_keys) unless ell
  points = public_keys.map.with_index do |p, idx|
    xi = ECDSA::Format::PointOctetString.decode(p, ECDSA::Group::Secp256k1)
    xi.multiply_by_scalar(coefficient(ell, idx))
  end
  sum = points.inject(:+)
  ECDSA::Format::PointOctetString.encode(sum, compression: true)
end

.session_initialize(session_id, private_key, message, combined_pubkey, ell, index, num_signers) ⇒ Schnorr::MuSig::Session

Initialize session to signer starts the session.

Parameters:

  • session_id (String)

    if session_id is nil, generate new one.

  • private_key (Integer)

    a private key.

  • message (String)

    a message for sign with binary format.

  • combined_pubkey (String)

    combined public key with binary format.

  • ell (String)

    ell with binary format.

  • index (Integer)

    public key index.

  • num_signers (Integer)

    the number of signers.

Returns:

Raises:

  • (ArgumentError)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/schnorr/mu_sig.rb', line 51

def session_initialize(session_id, private_key, message, combined_pubkey, ell, index, num_signers)
  raise ArgumentError, 'session_id must be 32 bytes.' if session_id && session_id.bytesize != 32
  raise ArgumentError, 'message must be 32 bytes.' unless message.bytesize == 32
  raise ArgumentError, 'combined_pubkey must be 33 bytes.' unless combined_pubkey.bytesize == 33
  raise ArgumentError, 'ell must be 32 bytes.' unless ell.bytesize == 32
  secret = ECDSA::Format::IntegerOctetString.encode(private_key, ECDSA::Group::Secp256k1.byte_length)

  field = ECDSA::PrimeField.new(ECDSA::Group::Secp256k1.order)
  session = Schnorr::MuSig::Session.new(session_id, num_signers)
  coefficient = coefficient(ell, index)
  session.secret_key = field.mod(private_key * coefficient)
  session.secret_nonce = Digest::SHA256.digest(session.id + message + combined_pubkey + secret).unpack('H*').first.to_i(16)
  raise ArgumentError, 'secret nonce must be an integer in the ragen 1..n-1' unless field.include?(session.secret_nonce)
  point_r = ECDSA::Group::Secp256k1.new_point(session.secret_nonce)
  session.nonce = ECDSA::Format::PointOctetString.encode(point_r, compression: true)
  session
end