class Zilliqa::Crypto::Schnorr

Constants

G
N

Public Class Methods

hash(q_point, pubkey_point, message) click to toggle source

Hash (r | M).

# File lib/zilliqa/crypto/schnorr.rb, line 125
def self.hash(q_point, pubkey_point, message)
  sha256 = Digest::SHA256.new
  sha256 << q_point.to_octet_string(:compressed)
  sha256 << pubkey_point.to_octet_string(:compressed)
  sha256 << Util.decode_hex(message)

  OpenSSL::BN.new(sha256.hexdigest, 16)
end
new() click to toggle source
# File lib/zilliqa/crypto/schnorr.rb, line 13
def initialize
end
sign(message, private_key, public_key) click to toggle source

sign

@param {String} msg @param {String} key

# File lib/zilliqa/crypto/schnorr.rb, line 20
def self.sign(message, private_key, public_key)
  sig = nil
  until sig
    k = Util.encode_hex SecureRandom.random_bytes(32)
    k_bn = OpenSSL::BN.new(k, 16)

    sig = try_sign(message, private_key, k_bn, public_key)
    sig = Zilliqa::Util::Validator.signature?(sig.to_s) ? sig : nil
  end

  sig
end
try_sign(message, private_key, k_bn, public_key) click to toggle source

trySign

@param {String} message - the message to sign over @param {String} privateKey - the private key @param {BN} k_bn - output of the HMAC-DRBG

@returns {Signature | null =>}

# File lib/zilliqa/crypto/schnorr.rb, line 40
def self.try_sign(message, private_key, k_bn, public_key)
  group = OpenSSL::PKey::EC::Group.new('secp256k1')

  prikey_bn = OpenSSL::BN.new(private_key, 16)

  pubkey_bn = OpenSSL::BN.new(public_key, 16)
  pubkey_point = OpenSSL::PKey::EC::Point.new(group, pubkey_bn)

  throw 'Bad private key.' if prikey_bn.zero? || prikey_bn >= N

  # 1a. check that k is not 0
  return nil if k_bn.zero?

  # 1b. check that k is < the order of the group
  return nil if k_bn >= N

  # 2. Compute commitment Q = kG, where g is the base point
  q_point = pubkey_point.mul(0, k_bn)

  # 3. Compute the challenge r = H(Q || pubKey || msg)
  # mod reduce the r value by the order of secp256k1, n
  r_bn = hash(q_point, pubkey_point, message) % N

  return nil if r_bn.zero?

  # 4. Compute s = k - r * prv
  # 4a. Compute r * prv
  s_bn = r_bn * prikey_bn % N
  # 4b. Compute s = k - r * prv mod n
  s_bn = k_bn.mod_sub(s_bn, N)

  return nil if s_bn.zero?

  Signature.new(r_bn.to_s(16), s_bn.to_s(16))
end
verify(message, sig, public_key) click to toggle source

Verify signature.

@param {Buffer} message @param {Buffer} sig @param {Buffer} public_key

@returns {boolean}

  1. Check if r,s is in [1, …, order-1]

  2. Compute Q = sG + r*kpub

  3. If Q = O (the neutral point), return 0;

  4. r' = H(Q, kpub, m)

  5. return r' == r

# File lib/zilliqa/crypto/schnorr.rb, line 90
def self.verify(message, sig, public_key)
  pubkey = PublicKey.new
  pubkey.deserialize Util.decode_hex(public_key)

  r = sig.r
  r_bn = OpenSSL::BN.new(r, 16)

  s = sig.s
  s_bn = OpenSSL::BN.new(s, 16)

  throw 'Invalid signature' if (s_bn.zero? || r_bn.zero?)

  throw 'Invalid signature' if (s_bn.negative? || r_bn.negative?)

  throw 'Invalid signature' if (s_bn >= N || r_bn >= N)

  group = OpenSSL::PKey::EC::Group.new('secp256k1')
  pubkey_bn = OpenSSL::BN.new(public_key, 16)
  pubkey_point = OpenSSL::PKey::EC::Point.new(group, pubkey_bn)

  throw 'Invalid public key' unless pubkey_point.on_curve?

  q_point = pubkey_point.mul(r_bn, s_bn)

  throw 'Invalid intermediate point.' if q_point.infinity?

  h_bn = self.hash(q_point, pubkey_point, message) % N

  throw 'Invalid hash.' if (h_bn.zero?)

  h_bn.eql?(r_bn)
end