Module: Secp256k1::SchnorrSig

Included in:
Secp256k1
Defined in:
lib/secp256k1/schnorrsig.rb

Overview

SchnorrSig module

Examples:

include Secp256k1

sk, pk = generate_key_pair

# sign and verify (Schnorr)
signature = sign_schnorr(msg, sk)
verify_schnorr(msg, signature, pk[2..-1]) # public key must be 32 bytes

Constant Summary collapse

SCHNORRSIG_EXTRAPARAMS_MAGIC =

Magic bytes for secp256k1_schnorrsig_extraparams.

[0xda, 0x6f, 0xb3, 0x8c].pack('C*').freeze

Instance Method Summary collapse

Instance Method Details

#sign_schnorr(data, private_key, aux_rand = nil) ⇒ String

Sign to data using schnorr.

Parameters:

  • data (String)

    The 32-byte message hash being signed with binary format.

  • private_key (String)

    a private key with hex format using sign.

  • aux_rand (String) (defaults to: nil)

    The 32-byte extra entropy.

Returns:

  • (String)

    signature data with binary format. If unsupported algorithm specified, return nil.

Raises:

  • (ArgumentError)

    If invalid arguments specified.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/secp256k1/schnorrsig.rb', line 20

def sign_schnorr(data, private_key, aux_rand = nil)
  validate_string!("data", data, 32)
  validate_string!("private_key", private_key, 32)
  validate_string!("aux_rand", aux_rand, 32) if aux_rand
  raise ArgumentError, "aux_rand must be String." if !aux_rand.nil? && !aux_rand.is_a?(String)
  private_key = hex2bin(private_key)
  data = hex2bin(data)

  with_context do |context|
    keypair = [create_keypair(private_key)].pack('H*')
    keypair = FFI::MemoryPointer.new(:uchar, 96).put_bytes(0, keypair)
    signature = FFI::MemoryPointer.new(:uchar, 64)
    msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
    aux_rand = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, aux_rand) if aux_rand
    raise Error, 'Failed to generate schnorr signature.' unless secp256k1_schnorrsig_sign32(context, signature, msg32, keypair, aux_rand) == 1
    signature.read_string(64)
  end
end

#sign_schnorr_custom(data, private_key, aux_rand = nil) ⇒ String

Sign to a variable-length message using schnorr (BIP340 sign with custom parameters).

Parameters:

  • data (String)

    The message being signed with binary format. Unlike #sign_schnorr, the length is arbitrary.

  • private_key (String)

    a private key with hex format using sign.

  • aux_rand (String) (defaults to: nil)

    (Optional)The 32-byte extra entropy.

Returns:

  • (String)

    signature data with binary format(64 bytes).

Raises:

  • (Secp256k1::Error)

    If signing failed.

  • (ArgumentError)

    If invalid arguments specified.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/secp256k1/schnorrsig.rb', line 49

def sign_schnorr_custom(data, private_key, aux_rand = nil)
  raise ArgumentError, "data must be String." unless data.is_a?(String)
  validate_string!("private_key", private_key, 32)
  validate_string!("aux_rand", aux_rand, 32) if aux_rand
  private_key = hex2bin(private_key)
  data = hex2bin(data)
  aux_rand = hex2bin(aux_rand) if aux_rand

  with_context do |context|
    keypair = [create_keypair(private_key)].pack('H*')
    keypair = FFI::MemoryPointer.new(:uchar, 96).put_bytes(0, keypair)
    signature = FFI::MemoryPointer.new(:uchar, 64)
    msg = FFI::MemoryPointer.new(:uchar, [data.bytesize, 1].max).put_bytes(0, data)

    # Build secp256k1_schnorrsig_extraparams: magic[4], noncefp(NULL=default BIP340), ndata(aux_rand or NULL).
    ptr_size = FFI::Pointer.size
    extraparams = FFI::MemoryPointer.new(:uchar, ptr_size * 3)
    extraparams.put_bytes(0, SCHNORRSIG_EXTRAPARAMS_MAGIC)
    extraparams.put_pointer(ptr_size, FFI::Pointer::NULL)
    ndata = aux_rand ? FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, aux_rand) : FFI::Pointer::NULL
    extraparams.put_pointer(ptr_size * 2, ndata)

    raise Error, 'Failed to generate schnorr signature.' unless secp256k1_schnorrsig_sign_custom(context, signature, msg, data.bytesize, keypair, extraparams) == 1
    signature.read_string(64)
  end
end

#verify_schnorr(data, signature, pubkey) ⇒ Boolean

Verify schnorr signature.

Parameters:

  • data (String)

    The 32-byte message hash assumed to be signed.

  • signature (String)

    signature data with binary format

  • pubkey (String)

    a public key with hex format using verify.

Returns:

  • (Boolean)

    verification result.

Raises:

  • (ArgumentError)

    If invalid arguments specified.



82
83
84
85
# File 'lib/secp256k1/schnorrsig.rb', line 82

def verify_schnorr(data, signature, pubkey)
  validate_string!("data", data, 32)
  verify_schnorr_internal(data, signature, pubkey)
end

#verify_schnorr_custom(data, signature, pubkey) ⇒ Boolean

Verify a schnorr signature over a variable-length message (counterpart of #sign_schnorr_custom).

Parameters:

  • data (String)

    The message assumed to be signed. The length is arbitrary.

  • signature (String)

    signature data with binary format(64 bytes).

  • pubkey (String)

    an x-only public key with hex format(32 bytes) using verify.

Returns:

  • (Boolean)

    verification result.

Raises:

  • (ArgumentError)

    If invalid arguments specified.



93
94
95
96
# File 'lib/secp256k1/schnorrsig.rb', line 93

def verify_schnorr_custom(data, signature, pubkey)
  raise ArgumentError, "data must be String." unless data.is_a?(String)
  verify_schnorr_internal(data, signature, pubkey)
end