I'm currently generating a private key in the browser and deriving its public key using the noble-secp256k1 javascript library:
const privKey = secp.utils.randomPrivateKey()
const pubKey = Buffer.from(secp.schnorr.getPublicKey(privKey)).toString('hex')
I then send the public key to my server, which uses the secp256k1 library to verify a payload signature I pass along as well. This fails when I try to instantiate the public key:
pub_key = secp256k1.PublicKey(binascii.unhexlify(hex_pub_key), raw=True)
This works if I build a key pair using the python library (python -m secp256k1 privkey -p
), but if I send the key generated on the the client the server raises an error:
Exception: unknown public key size (expected 33 or 65)
The python library generates a 66-character hex-encoded public key. The client generates a 64-character hex-encoded public key using the secp.schnorr.getPublicKey
method, and a 130-character hex-encoded public key using the secp.getPublicKey
method. Is there a way to get my python library to accept the schnorr pubkey generated on the frontend? Is there anywhere I can read about what this semi-overlap between secp256k1 and schnorr is all about?
The 32 bytes public key of the NodeJS library (noble-secp256k1) has to be extended to 33 bytes with a leading 0x02 and can then be imported by the Python library (secp256k1):
from the documentation of BIP0340, sec. Design, Implicit Y coordinates.
Test:
The following NodeJS code uses the NodeJS library and generates a key pair and a Schnorr signature:
with the following possible output:
The following Python code uses the Python library and verifies the Schnorr signature with the key exported by the NodeJS code, which is extended by a leading 0x02:
With the output:
i.e. a successful verification.
Note: Even if a 0x03 (instead of a 0x02) is prepended, verification is successful. Probably the library only checks if the key is compressed (0x02 or 0x03), and if so, the leading byte is simply truncated.