Source code for dposlib.ark.secp256k1.schnorr
# -*- encoding:utf-8 -*-
from . import *
# https://github.com/bcoin-org/bcrypto/blob/v4.1.0/lib/js/schnorr.js
[docs]def bcrypto410_sign(msg, seckey0):
"""
Generate message signature according to `Bcrypto 4.10 schnorr <https://git\
hub.com/bcoin-org/bcrypto/blob/v4.1.0/lib/js/schnorr.js>`_ spec.
Args:
msg (:class:`bytes`): sha256 message-hash
secret0 (:class:`bytes`): private key
Returns:
:class:`bytes`: RAW signature
"""
if len(msg) != 32:
raise ValueError('The message must be a 32-byte array.')
seckey = int_from_bytes(seckey0)
if not (1 <= seckey <= n - 1):
raise ValueError(
'The secret key must be an integer in the range 1..n-1.'
)
k0 = int_from_bytes(hash_sha256(seckey0 + msg)) % n
if k0 == 0:
raise RuntimeError(
'Failure. This happens only with negligible probability.'
)
R = G * k0
Rraw = bytes_from_int(R.x)
e = int_from_bytes(
hash_sha256(Rraw + encoded_from_point(G*seckey) + msg)
) % n
seckey %= n
k0 %= n
k = n - k0 if not is_quad(R.y) else k0
s = (k + e * seckey) % n
s %= n
return Rraw + bytes_from_int(s)
[docs]def bcrypto410_verify(msg, pubkey, sig):
"""
Check if public key match message signature according to `Bcrypto 4.10 sch\
norr <https://github.com/bcoin-org/bcrypto/blob/v4.1.0/lib/js/schnorr.js>`_
spec.
Args:
msg (:class:`bytes`): sha256 message-hash
pubkey (:class:`bytes`): encoded public key
sig (:class:`bytes`): signature
Returns:
:class:`bool`: True if match
"""
if len(msg) != 32:
raise ValueError('The message must be a 32-byte array.')
if len(sig) != 64:
raise ValueError('The signature must be a 64-byte array.')
P = PublicKey.decode(pubkey)
r, s = int_from_bytes(sig[:32]), int_from_bytes(sig[32:])
if r >= p or s >= n:
return False
e = int_from_bytes(hash_sha256(sig[0:32] + pubkey + msg)) % n
R = Point(*(G*s + point_mul(P, n-e))) # P*(n-e) does not work...
if R is None or not is_quad(R.y) or R.x != r:
return False
return True
# Note that bip schnorr uses a very different public key format (32 bytes) than
# the ones used by existing systems (which typically use elliptic curve points
# as public keys, 33-byte or 65-byte encodings of them). A side effect is that
# ``PubKey(sk) = PubKey(bytes(n-int(sk))``, so every public key has two
# corresponding private keys.
[docs]def bytes_from_point(P):
"""
Encode a public key as defined in `BIP schnorr <https://github.com/sipa/bi\
ps/blob/bip-schnorr/bip-schnorr.mediawiki>`_ spec.
Args:
P (:class:`Point`): secp256k1 curve point
Returns:
:class:`bytes`: encoded public key
"""
return bytes_from_int(x(P))
[docs]def point_from_bytes(pubkeyB):
"""
Decode a public key as defined in `BIP schnorr <https://github.com/sipa/bi\
ps/blob/bip-schnorr/bip-schnorr.mediawiki>`_ spec.
Args:
pubkeyB (:class:`bytes`): encoded public key
Returns:
:class:`Point`: secp256k1 curve point
"""
x = int_from_bytes(pubkeyB)
y = y_from_x(x)
if not y:
return None
return [x, y]
[docs]def sign(msg, seckey0):
"""
Generate message signature according to `BIP schnorr <https://github.com/s\
ipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki>`_ spec.
Args:
msg (:class:`bytes`): sha256 message-hash
seckey0 (:class:`bytes`): private key
Returns:
:class:`bytes`: RAW signature
"""
if len(msg) != 32:
raise ValueError('The message must be a 32-byte array.')
seckey0 = int_from_bytes(seckey0)
if not (1 <= seckey0 <= n - 1):
raise ValueError(
'The secret key must be an integer in the range 1..n-1.'
)
P = G*seckey0
seckey = seckey0 if is_quad(P.y) else n - seckey0
k0 = int_from_bytes(
tagged_hash("BIPSchnorrDerive", bytes_from_int(seckey) + msg)
) % n
if k0 == 0:
raise RuntimeError(
'Failure. This happens only with negligible probability.'
)
R = G*k0
k = n - k0 if not is_quad(R.y) else k0
r = bytes_from_point(R)
e = int_from_bytes(
tagged_hash("BIPSchnorr", r + bytes_from_point(P) + msg)
) % n
return r + bytes_from_int((k + e * seckey) % n)
[docs]def verify(msg, pubkey, sig):
"""
Check if public key match message signature according to `BIP schnorr <htt\
ps://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki>`_ spec.
Args:
msg (:class:`bytes`): sha256 message-hash
pubkey (:class:`bytes`): encoded public key
sig (:class:`bytes`): signature
Returns:
:class:`bool`: True if match
"""
if len(msg) != 32:
raise ValueError('The message must be a 32-byte array.')
if len(pubkey) != 32:
raise ValueError('The public key must be a 32-byte array.')
if len(sig) != 64:
raise ValueError('The signature must be a 64-byte array.')
P = point_from_bytes(pubkey)
if (P is None):
return False
r, s = int_from_bytes(sig[:32]), int_from_bytes(sig[32:])
if (r >= p or s >= n):
return False
e = int_from_bytes(tagged_hash("BIPSchnorr", sig[0:32] + pubkey + msg)) % n
R = Point(*(G*s + point_mul(P, n-e))) # P*(n-e) does not work...
if R is None or not is_quad(R.y) or R.x != r:
return False
return True