Module: Schnorr
- Defined in:
- lib/schnorr.rb,
lib/schnorr/mu_sig.rb,
lib/schnorr/version.rb,
lib/schnorr/signature.rb,
lib/schnorr/mu_sig/session.rb
Defined Under Namespace
Modules: MuSig Classes: InvalidSignatureError, Signature
Constant Summary collapse
- VERSION =
"0.4.0"
Class Method Summary collapse
-
.check_sig!(message, public_key, signature, group) ⇒ Object
Verifies the given Signature and raises an InvalidSignatureError if it is invalid.
- .create_challenge(x, pubkey, message, field, group) ⇒ Object
-
.sign(message, private_key, group: ECDSA::Group::Secp256k1) ⇒ Schnorr::Signature
Generate schnorr signature.
-
.valid_sig?(message, public_key, signature, group: ECDSA::Group::Secp256k1) ⇒ Boolean
Verifies the given Signature and returns true if it is valid.
-
.valid_sigs?(messages, public_keys, signatures, group: ECDSA::Group::Secp256k1) ⇒ Boolean
Batch verification.
Class Method Details
.check_sig!(message, public_key, signature, group) ⇒ Object
Verifies the given Signature and raises an InvalidSignatureError if it is invalid.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/schnorr.rb', line 92 def check_sig!(, public_key, signature, group) 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? e = create_challenge(sig.r, pubkey, , field, group) r = group.new_point(sig.s) + pubkey.multiply_by_scalar(e).negate if r.infinity? || ECDSA::PrimeField.jacobi(r.y, group.field.prime) != 1 || r.x != sig.r raise Schnorr::InvalidSignatureError, 'signature verification failed.' end true end |
.create_challenge(x, pubkey, message, field, group) ⇒ Object
113 114 115 116 117 |
# File 'lib/schnorr.rb', line 113 def create_challenge(x, pubkey, , field, group) r = ECDSA::Format::IntegerOctetString.encode(x, group.byte_length) public_key = ECDSA::Format::PointOctetString.encode(pubkey, compression: true) field.mod(ECDSA.normalize_digest(Digest::SHA256.digest(r + public_key + ), group.bit_length)) end |
.sign(message, private_key, group: ECDSA::Group::Secp256k1) ⇒ Schnorr::Signature
Generate schnorr signature. (The number of times to add the generator point to itself to get the public key.)
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/schnorr.rb', line 16 def sign(, private_key, group: ECDSA::Group::Secp256k1) secret = ECDSA::Format::IntegerOctetString.encode(private_key, group.byte_length) field = ECDSA::PrimeField.new(group.order) k = field.mod(ECDSA::Format::IntegerOctetString.decode(Digest::SHA256.digest(secret + ))) raise 'Creation of signature failed. k is zero' if k.zero? r_point = group.new_point(k) unless ECDSA::PrimeField.jacobi(r_point.y, group.field.prime) == 1 k = group.order - k end e = create_challenge(r_point.x, group.new_point(private_key), , field, group) Schnorr::Signature.new(r_point.x, field.mod(k + e * private_key)) end |
.valid_sig?(message, public_key, signature, group: ECDSA::Group::Secp256k1) ⇒ Boolean
Verifies the given Signature and returns true if it is valid.
38 39 40 41 42 |
# File 'lib/schnorr.rb', line 38 def valid_sig?(, public_key, signature, group: ECDSA::Group::Secp256k1) check_sig!(, public_key, signature, group) rescue InvalidSignatureError, ECDSA::Format::DecodeError false end |
.valid_sigs?(messages, public_keys, signatures, group: ECDSA::Group::Secp256k1) ⇒ Boolean
Batch verification
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 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/schnorr.rb', line 50 def valid_sigs?(, public_keys, signatures, group: ECDSA::Group::Secp256k1) raise ArgumentError, 'all parameters must be an array with the same length.' if .size != public_keys.size || public_keys.size != signatures.size field = group.field pubkeys = public_keys.map{|p| ECDSA::Format::PointOctetString.decode(p, group)} sigs = signatures.map do|signature| sig = Schnorr::Signature.decode(signature) 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? sig end left = 0 right = nil pubkeys.each_with_index do |pubkey, i| r = sigs[i].r s = sigs[i].s e = create_challenge(r, pubkey, [i], field, group) c = field.mod(r.pow(3) + 7) y = c.pow((field.prime + 1)/4, field.prime) raise Schnorr::InvalidSignatureError, 'c is not equal to y^2.' unless c == y.pow(2, field.prime) r_point = ECDSA::Point.new(group, r, y) if i == 0 left = s right = r_point + pubkey.multiply_by_scalar(e) else a = 1 + SecureRandom.random_number(group.order - 1) left += (a * s) right += (r_point.multiply_by_scalar(a) + pubkey.multiply_by_scalar(a * e)) end end group.new_point(left) == right rescue InvalidSignatureError, ECDSA::Format::DecodeError false end |