bip-schnorrrb Build Status Gem Version MIT License

This is a Ruby implementation of the Schnorr signature scheme over the elliptic curve. This implementation relies on the ecdsa gem for operate elliptic curves.

The code is based upon the BIP340.

Installation

Add this line to your application’s Gemfile:

“by gem ‘bip-schnorr’, require: ‘schnorr’

And then execute:

$ bundle

Or install it yourself as:

$ gem install bip-schnorr

Usage

Signing

“by require ‘schnorr’

private_key = [B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF].pack(“H“)

message = [5E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C].pack(‘H‘)

create signature

signature = Schnorr.sign(message, private_key)

if use auxiliary random data, specify it to the 3rd arguments.

aux_rand = SecureRandom.bytes(32) # aux_rand must be a 32-byte binary. signature = Schnorr.sign(message, private_key, aux_rand)

signature r value

signature.r

signature s value

signature.s

convert signature to binary

signature.encode

Verification

“by require ‘schnorr’

public key does not start with 02 or 03.

public_key = [DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659].pack(‘H‘)

signature = [6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A].pack(‘H‘)

message = [243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89].pack(‘H‘)

verify signature.(result is true or false)

result = Schnorr.valid_sig?(message, public_key, signature)

signature convert to Signature object

sig = Schnorr::Signature.decode(signature)

MuSig2*

This library support MuSig2* as defined BIP-327.

“by require ‘schnorr’

sk1 = 1 + SecureRandom.random_number(Schnorr::GROUP.order - 1) pk1 = (Schnorr::GROUP.generator.to_jacobian * sk1).to_affine.encode

sk2 = 1 + SecureRandom.random_number(Schnorr::GROUP.order - 1) pk2 = (Schnorr::GROUP.generator.to_jacobian * sk2).to_affine.encode

pubkeys = [pk1, pk2]

Key aggregation.

agg_ctx = Schnorr::MuSig2.aggregate(pubkeys)

if you have tweak value.

agg_ctx = Schnorr::MuSig2.aggregate_with_tweaks(pubkeys, tweaks, modes)

Aggregated pubkey is

Return point:

agg_ctx.q

Return x-only pubkey string

agg_ctx.x_only_pubkey

msg = SecureRandom.bytes(32)

Generate secret nonce and public nonce.

sec_nonce1, pub_nonce1 = Schnorr::MuSig2.gen_nonce( pk: pk1, sk: sk1, # optional agg_pubkey: agg_ctx.x_only_pubkey, # optional msg: msg, # optional extra_in: SecureRandom.bytes(4), # optional rand: SecureRandom.bytes(32) # optional )

for stateless signer.

agg_other_nonce = described_class.aggregate_nonce([pub_nonce1]) pub_nonce2, sig2 = described_class.deterministic_sign( sk2, agg_other_nonce, pubkeys, msg, tweaks: tweaks, # optional modes: modes, # optional rand: SecureRandom.bytes(32) # optional )

Nonce aggregation

agg_nonce = Schnorr::MuSig2.aggregate_nonce([pub_nonce1, pub_nonce2])

Generate partial signature.

session_ctx = Schnorr::MuSig2::SessionContext.new( agg_nonce, pubkeys, msg, tweaks, # optional modes # optional ) sig1 = session_ctx.sign(sec_nonce1, sk1)

Verify partial signature.

signer_index = 0 session_ctx.valid_partial_sig?(sig1, pub_nonce1, signer_index)

Signature aggregation.

sig = session_ctx.aggregate_partial_sigs([sig1, sig2])

Verify signature.

Schnorr.valid_sig?(msg, agg_ctx.x_only_pubkey, sig.encode)

Note

This library changes the following functions of ecdsa gem in lib/schnorr/ec_point_ext.rb.

  • ECDSA::Point class has following two instance methods.
    • #has_even_y? check the y-coordinate of this point is an even.
    • #encode(only_x = false) encode this point into a binary string.
  • ECDSA::Format::PointOctetString#decode:
    • supports decoding only from x coordinate.
    • decode 33 bytes of zeros as infinity points.