Last active
September 19, 2025 15:05
-
-
Save pcaversaccio/710bee6cc4e760eadb76770ac17610a6 to your computer and use it in GitHub Desktop.
Ensure a hardware wallet's message signature uses an RFC-6979-compliant nonce for secure, deterministic signing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
mnemonic==0.21 | |
eth_account==0.13.7 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from mnemonic import Mnemonic | |
from eth_account import Account | |
from eth_account.messages import encode_defunct | |
############## | |
# TEST SETUP # | |
############## | |
def generate_test_seed_24_words() -> str: | |
"""Generate a 24-word BIP39 seed phrase for testing. | |
Returns | |
------- | |
seed_phrase: str | |
The randomly generated 24-word seed phrase. | |
""" | |
mnemo = Mnemonic("english") | |
seed_phrase = mnemo.generate(strength=256) | |
return seed_phrase | |
def derive_with_eth_account() -> str: | |
"""Derive an Ethereum private key from a random mnemonic phrase. | |
Returns | |
------- | |
private_key: str | |
The 32-byte private key as hex string. | |
""" | |
mnemonic_phrase = generate_test_seed_24_words() | |
Account.enable_unaudited_hdwallet_features() | |
# Use the standard Ethereum derivation path: `m/44'/60'/0'/0/0`. | |
account = Account.from_mnemonic(mnemonic_phrase, account_path="m/44'/60'/0'/0/0") | |
return account.key.hex() | |
######## | |
# MAIN # | |
######## | |
if __name__ == "__main__": | |
"""An illustrative signature verification. | |
You can import the first account from your hardware wallet's dummy seed | |
and sign a message here: https://etherscan.io/verifiedSignatures. Use the | |
message and signature below for verification. | |
""" | |
msg = "Vyper is awesome!" | |
msghash = encode_defunct(text=msg) | |
# Use here a derived private key from a dummy seed that you generated on your hardware wallet. | |
# You can use the `derive_with_eth_account` function to get the private key for the test account. | |
private_key = derive_with_eth_account() | |
# Generate an RFC-6979-compliant ECDSA signature. | |
signature = Account.sign_message(msghash, private_key) | |
r_check = signature.r | |
# Set here your original signature in bytes. You can convert from hex to bytes by using `bytes.fromhex(...)`. | |
original_sig = signature.signature | |
r_original = int(original_sig[:32].hex(), 16) | |
# If `r_original == r_check`, your signature used an RFC-6979-compliant nonce. | |
print(f"Original `r`: {r_original}") | |
print(f"Recomputed `r`: {r_check}") | |
assert ( | |
r_original == r_check | |
), "Original `r` is not using an RFC-6979-compliant nonce!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This script relies on the assumption that
eth_account
itself is compliant with RFC 6979.