pythonpython-3.xed25519nacl-cryptographypynacl

Sign a text with pynacl (Ed25519) importing a private key


In the code below I try to sign a nonce using pynacl.

from nacl.encoding import Base64Encoder
from nacl.signing import SigningKey
import base58
import base64

secret = '5N3SxG4UzVDpNe4LyDoZyb6bSgE9tk3pE2XP5znXo5bF'
nonce = '3eaf8814caa842d94fdb96fc26d02f7c339e65ff'

h=hashlib.new('sha256')
h.update(str.encode(nonce))
hashednonce = h.hexdigest()

key = base58.b58decode(secret)
signingkey = SigningKey(key)

signednonce = signingkey.sign(hashednonce.encode())

That secret key is in base58 (Don't worry, it's not mine and is publicly posted here). In that page is also the signature it should output, but it's not the same.

I'm afraid that the key is being imported in the wrong way


Solution

  • The sign() method of the used NodeJS library expects key and message hex encoded, see here. The hex format is rather a special characteristic of this particular library.

    In contrast, PyNaCl expects both as bytes like objects, see here. For the key this is already satisfied, because b58decode() returns the data as bytes like object. For hashing the nonce it is easiest to use digest() instead of hexdigest(), then the hashed nonce is also returned as a bytes like object.

    The result returned in the Python code from sign() consists of the concatenation of the 64 bytes signature and the hashed nonce, i.e. the first 64 bytes correspond to the signature from the NodeJS example.

    Full code:

    from nacl.signing import SigningKey
    import base58
    import hashlib
    
    secret = '5N3SxG4UzVDpNe4LyDoZyb6bSgE9tk3pE2XP5znXo5bF'
    nonce = '3eaf8814caa842d94fdb96fc26d02f7c339e65ff'
    
    h = hashlib.new('sha256')
    h.update(nonce.encode('utf-8'))
    hashednonce = h.digest()
    
    key = base58.b58decode(secret)
    signingkey = SigningKey(key)
    signednonce = signingkey.sign(hashednonce)
    print("Hashed nonce, hex:             " + hashednonce.hex())
    print("Signature | hashed nonce, hex: " + signednonce.hex())
    print("Signature, hex:                " + signednonce[:64].hex())
    

    Output:

    Hashed nonce, hex:             6d748f209e5af1f5b8825f7822d6659c45c874076cd2b3337c7861fd94cd3ba5
    Signature | hashed nonce, hex: 270c2e502c5c753e39159683981e452444f81a10d798f56406a9c471d672a5ede1792cb7f97d4f9c9efeec7bf35577dd1f8482afca7e3710291868a65bf91e076d748f209e5af1f5b8825f7822d6659c45c874076cd2b3337c7861fd94cd3ba5
    Signature, hex:                270c2e502c5c753e39159683981e452444f81a10d798f56406a9c471d672a5ede1792cb7f97d4f9c9efeec7bf35577dd1f8482afca7e3710291868a65bf91e07
    

    As can be seen, the hashed nonce and the signature correspond to the values from the NodeJS example.