Module: Schnorr

Defined in:
lib/schnorr.rb,
lib/schnorr/signature.rb,
lib/schnorr/sign_to_contract.rb

Defined Under Namespace

Modules: SignToContract Classes: InvalidSignatureError, Signature

Constant Summary collapse

GROUP =
ECDSA::Group::Secp256k1
ALGO16 =
"SCHNORR + SHA256"

Class Method Summary collapse

Class Method Details

.check_sig!(message, signature, public_key) ⇒ Boolean

Verifies the given Signature and raises an InvalidSignatureError if it is invalid.

Parameters:

  • message (String)

    A message to be signed with binary format.

  • public_key (String)

    The public key with binary format.

  • signature (String)

    The signature with binary format.

Returns:

  • (Boolean)

Raises:



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/schnorr.rb', line 56

def check_sig!(message, signature, public_key)
  sig = Schnorr::Signature.decode(signature)
  pubkey = ECDSA::Format::PointOctetString.decode(public_key, GROUP)
  field = GROUP.field

  raise Schnorr::InvalidSignatureError, "Invalid signature: r is not in the field." unless field.include?(sig.r)
  raise Schnorr::InvalidSignatureError, "Invalid signature: s is not in the field." unless field.include?(sig.s)
  raise Schnorr::InvalidSignatureError, "Invalid signature: r is zero." if sig.r.zero?
  raise Schnorr::InvalidSignatureError, "Invalid signature: s is zero." if sig.s.zero?
  raise Schnorr::InvalidSignatureError, "Invalid signature: r is larger than field size." if sig.r >= field.prime
  raise Schnorr::InvalidSignatureError, "Invalid signature: s is larger than group order." if sig.s >= GROUP.order

  e = create_challenge(sig.r, pubkey, message)

  r = GROUP.new_point(sig.s) + pubkey.multiply_by_scalar(e).negate

  if r.infinity? || r.x != sig.r || ECDSA::PrimeField.jacobi(r.y, GROUP.field.prime) != 1
    raise Schnorr::InvalidSignatureError, "signature verification failed."
  end

  true
end

.create_challenge(x, p, message) ⇒ Integer

create signature digest.

Parameters:

Returns:



83
84
85
86
87
# File 'lib/schnorr.rb', line 83

def create_challenge(x, p, message)
  r_x = ECDSA::Format::IntegerOctetString.encode(x, GROUP.byte_length)
  p_str = p.to_hex.htb
  (ECDSA.normalize_digest(Digest::SHA256.digest(r_x + p_str + message), GROUP.bit_length)) % GROUP.order
end

.deterministic_nonce(message, private_key) ⇒ Object



30
31
32
33
34
35
36
37
38
# File 'lib/schnorr.rb', line 30

def deterministic_nonce(message, private_key)
  secret = ECDSA::Format::IntegerOctetString.encode(private_key, GROUP.byte_length)
  secret = secret + message + ALGO16
  nonce = Tapyrus::Secp256k1::RFC6979.generate_rfc6979_nonce(secret, "")

  k0 = nonce % GROUP.order
  raise "Creation of signature failed. k is zero" if k0.zero?
  k0
end

.sign(message, private_key) ⇒ Schnorr::Signature

Generate schnorr signature. (The number of times to add the generator point to itself to get the public key.)

Parameters:

  • message (String)

    A message to be signed with binary format.

  • private_key (Integer)

    The private key.

Returns:



15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/schnorr.rb', line 15

def sign(message, private_key)
  raise "The message must be a 32-byte array." unless message.bytesize == 32
  raise "private_key is zero or over the curve order." if private_key == 0 || private_key >= GROUP.order

  p = GROUP.new_point(private_key)
  k0 = deterministic_nonce(message, private_key)

  r = GROUP.new_point(k0)
  k = ECDSA::PrimeField.jacobi(r.y, GROUP.field.prime) == 1 ? k0 : GROUP.order - k0

  e = create_challenge(r.x, p, message)

  Schnorr::Signature.new(r.x, (k + e * private_key) % GROUP.order)
end

.valid_sig?(message, signature, public_key) ⇒ Boolean

Verifies the given Signature and returns true if it is valid.

Parameters:

  • message (String)

    A message to be signed with binary format.

  • public_key (String)

    The public key with binary format.

  • signature (String)

    The signature with binary format.

Returns:

  • (Boolean)

    whether signature is valid.



45
46
47
48
49
# File 'lib/schnorr.rb', line 45

def valid_sig?(message, signature, public_key)
  check_sig!(message, signature, public_key)
rescue InvalidSignatureError, ECDSA::Format::DecodeError
  false
end