{"id":15640634,"url":"https://github.com/pcaversaccio/ecdsa-nonce-reuse-attack","last_synced_at":"2025-04-07T14:13:40.472Z","repository":{"id":217505539,"uuid":"743504300","full_name":"pcaversaccio/ecdsa-nonce-reuse-attack","owner":"pcaversaccio","description":"This repository implements a Python function that recovers the private key from two different signatures that use the same random nonce during signature generation.","archived":false,"fork":false,"pushed_at":"2025-01-22T15:10:31.000Z","size":38,"stargazers_count":89,"open_issues_count":0,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-31T12:07:35.799Z","etag":null,"topics":["ecdsa","nonce-reuse","private-key-recovery","secp256k1"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pcaversaccio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-15T11:33:09.000Z","updated_at":"2025-01-22T15:10:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"8d3f732b-a4b8-4a92-8ae2-6de8ae2c22b3","html_url":"https://github.com/pcaversaccio/ecdsa-nonce-reuse-attack","commit_stats":null,"previous_names":["pcaversaccio/ecdsa-nonce-reuse-attack"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pcaversaccio%2Fecdsa-nonce-reuse-attack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pcaversaccio%2Fecdsa-nonce-reuse-attack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pcaversaccio%2Fecdsa-nonce-reuse-attack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pcaversaccio%2Fecdsa-nonce-reuse-attack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pcaversaccio","download_url":"https://codeload.github.com/pcaversaccio/ecdsa-nonce-reuse-attack/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247666015,"owners_count":20975788,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecdsa","nonce-reuse","private-key-recovery","secp256k1"],"created_at":"2024-10-03T11:38:54.465Z","updated_at":"2025-04-07T14:13:40.448Z","avatar_url":"https://github.com/pcaversaccio.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🛡️ ECDSA Nonce Reuse Attack\n\n[![👮‍♂️ Sanity checks](https://github.com/pcaversaccio/ecdsa-nonce-reuse-attack/actions/workflows/checks.yml/badge.svg)](https://github.com/pcaversaccio/ecdsa-nonce-reuse-attack/actions/workflows/checks.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/license/mit)\n\nThis repository implements a Python function [`recover_private_key`](https://github.com/pcaversaccio/ecdsa-nonce-reuse-attack/blob/main/scripts/recover_private_key.py) that recovers the private key from two different signatures that use the same random nonce $k$ during signature generation. Note that if the same $k$ is used in two signatures, this implies that the secp256k1 32-byte signature parameter $r$ is identical. This property is asserted in this function.\n\n## 🧠 Mathematical Derivation\n\nFirst, note that the integer order $n$ of $G$ (a base point of prime order on the curve) for the [secp256k1 elliptic curve](https://en.bitcoin.it/wiki/Secp256k1) is:\n\n```console\n# Represented as hex value.\nn = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036414\n# Represented as integer value.\nn = 115792089237316195423570985008687907852837564279074904382605163141518161494337\n```\n\n#### 1. Public-Private-Key-Relationship\n\n\u003c!-- prettier-ignore-start --\u003e\n$$ Q_{A} = d_{A} \\cdot G $$\n\u003c!-- prettier-ignore-end --\u003e\n\n$Q_{A}$ is the public key, $d_{A}$ is the private key, and $G$ is the elliptic curve base point.\n\n#### 2. The secp256k1 32-Byte Signature Parameter $r$\n\n\u003c!-- prettier-ignore-start --\u003e\n$$ r = G \\cdot k \\quad \\left(\\textnormal{mod} \\enspace n\\right) $$\n\u003c!-- prettier-ignore-end --\u003e\n\n$r$ is the first secp256k1 32-byte signature parameter, $n$ is the integer order of $G$, and $k$ is the random nonce value.\n\n#### 3. The secp256k1 32-Byte Signature Parameter $s$\n\n\u003c!-- prettier-ignore-start --\u003e\n$$ s = \\frac{h + d_{A} \\cdot r}{k} \\quad \\left(\\textnormal{mod} \\enspace n\\right) $$\n\u003c!-- prettier-ignore-end --\u003e\n\n$s$ is the second secp256k1 32-byte signature parameter and $h$ is the 32-byte message digest of a message.\n\n#### 4. Recover the Private Key\n\nLet's assume that $d_{A}$ has used the same random value $k$ for two different signatures. This implies from the above definition of $r$ that $r$ is the same for both signatures, since $G$ and $n$ are constants. Thus, we have:\n\n\u003c!-- prettier-ignore-start --\u003e\n$$ s_{1} = \\frac{h_{1} + d_{A} \\cdot r}{k} \\quad \\left(\\textnormal{mod} \\enspace n\\right) $$\n\u003c!-- prettier-ignore-end --\u003e\n\nand\n\n\u003c!-- prettier-ignore-start --\u003e\n$$ s_{2} = \\frac{h_{2} + d_{A} \\cdot r}{k} \\quad \\left(\\textnormal{mod} \\enspace n\\right). $$\n\u003c!-- prettier-ignore-end --\u003e\n\nWe can solve for $k$ with the above system of equations:\n\n\u003c!-- prettier-ignore-start --\u003e\n$$ s_{1} - s_{2} =  \\frac{h_{1} + d_{A} \\cdot r}{k} - \\frac{h_{2} + d_{A} \\cdot r}{k} \\quad \\left(\\textnormal{mod} \\enspace n\\right), $$\n\n$$ s_{1} - s_{2} =  \\frac{h_{1} + d_{A} \\cdot r - h_{2} - d_{A} \\cdot r}{k}\\quad \\left(\\textnormal{mod} \\enspace n\\right), $$\n\n$$ s_{1} - s_{2} =  \\frac{h_{1} - h_{2}}{k}\\quad \\left(\\textnormal{mod} \\enspace n\\right), $$\n\n$$ k =  \\frac{h_{1} - h_{2}}{s_{1} - s_{2}}\\quad \\left(\\textnormal{mod} \\enspace n\\right). $$\n\u003c!-- prettier-ignore-end --\u003e\n\nEventually, we can now plug $k$ into the equation $s_{1}$ and recover the private key $d_{A}$:\n\n\u003c!-- prettier-ignore-start --\u003e\n$$ s_{1} = \\frac{h_{1} + d_{A} \\cdot r}{\\frac{h_{1} - h_{2}}{s_{1} - s_{2}}} \\quad \\left(\\textnormal{mod} \\enspace n\\right), $$\n\n$$ s_{1} = \\frac{\\left(s_{1} - s_{2}\\right)\\cdot\\left(h_{1} + d_{A} \\cdot r\\right)}{h_{1} - h_{2}} \\quad \\left(\\textnormal{mod} \\enspace n\\right), $$\n\n$$ d_{A} = \\frac{(s_{2} \\cdot h_{1} - s_{1} \\cdot h_{2})}{r \\cdot (s_{1} - s_{2})} \\quad \\left(\\textnormal{mod} \\enspace n\\right). $$\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003e The function [`recover_private_key`](./scripts/recover_private_key.py) uses the last equation in conjunction with modular arithmetic properties to recover the private key.\n\n## 📚 Further References\n\n- [Elliptic Curve Digital Signature Algorithm](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)\n- [RFC 6979](https://datatracker.ietf.org/doc/html/rfc6979)\n- [A Glimpse of the Deep: Finding a Creature in Ethereum's Dark Forest](https://bertcmiller.com/glimpse.html)\n- [How Hackers Can Exploit Weak ECDSA Signatures](https://www.halborn.com/blog/post/how-hackers-can-exploit-weak-ecdsa-signatures)\n- [ECDSA Nonce Reuse Exploit Example](https://github.com/Marsh61/ECDSA-Nonce-Reuse-Exploit-Example)\n- [Identifying Key Leakage of Bitcoin Users](https://link.springer.com/content/pdf/10.1007/978-3-030-00470-5_29.pdf)\n- [How Do You Derive the Private Key From Two Signatures That Share the Same `k` Value?](https://bitcoin.stackexchange.com/a/73624)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpcaversaccio%2Fecdsa-nonce-reuse-attack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpcaversaccio%2Fecdsa-nonce-reuse-attack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpcaversaccio%2Fecdsa-nonce-reuse-attack/lists"}