
Disclaimer: This article is not intended to identify or attribute cause to any specific exchange or incident. It provides a technical explanation of a nonce-reuse vulnerability class that can arise in publicly available cryptographic signature structures, and summarizes implications from a defensive and verification perspective. This article does not include details that could excessively increase reproducibility (such as identifiable accounts, transactions, raw data, or immediately exploitable scripts), and all experiments were conducted solely in a controlled test environment.
Acknowledgment: Special thanks to Dr. Suhyeon Lee for the analytical direction that inspired this article and for the guidance provided throughout the writing process.
In 2025, cryptocurrency assets worth several hundred million USD were drained from the Solana hot wallets of Exchange A. Multiple Solana-based tokens (estimated at over tens of millions USD) were affected; the service has since been restored. In relation to this incident, the possibility of private key exposure was noted.
According to recent research (Jacquot and Donnet, "Short Paper: Oops...I Did It Again. I Reused my Nonce," to appear in Financial Cryptography and Data Security 2026 [link]), nonce reuse alone across six chains including Bitcoin and Ethereum has exposed 3,620 private keys, placing assets equivalent to €101M at risk. This article explains the technical mechanism of a nonce-reuse vulnerability, corresponding to Section 5.2 of that research where the same nonce is reused across different keys, in the context of Solana, and presents the results of reproducing this on the Solana devnet.
Digital signatures are used in blockchains to prove that the sender of a transaction is the legitimate owner of the corresponding wallet. Most major blockchains rely on digital signatures based on Elliptic Curve Cryptography (ECC).
The core principle of elliptic curve cryptography is straightforward. Given a fixed base point G on an elliptic curve, multiplying G by a secret scalar a yields the public key A:
Computing A from a is efficient, but recovering a from A alone is computationally infeasible with current hardware. This one-way property is the foundation of digital signature security.
The objective of an elliptic curve digital signature is to prove knowledge of the secret key a without revealing it. To achieve this, a fresh ephemeral nonce r is generated for each signature and incorporated into the signing equation alongside a.
Because the nonce r introduces a second unknown, a single signature equation with two unknowns (a and r) does not permit recovery of a.
The general structure of an elliptic curve digital signature scheme is as follows.
Signing
Generate an ephemeral nonce .
Compute (the elliptic curve point corresponding to the nonce).
Compute using the hash of message , the secret key , and the nonce .
e.g.,
Publish the pair as the signature.
Verification
Since during signing, multiplying both sides by G yields:
The verifier checks whether equals .
The right-hand side of this equation can be computed using only the signature component , the public key , and the message . Consequently, anyone can determine whether a signature was produced by someone who knows , without learning itself.
While all elliptic curve signature schemes share this sign/verify structure, they differ in how the nonce is generated.
r ← external random number generator (e.g., OS /dev/urandom)
R = r * G
S = (H(M) + R.x * a) / r (mod n)
The nonce r is drawn from an external source for each signature. The advantage is that a different nonce is produced each time. The disadvantage is that if the random number generator fails or degrades such that the same r is used twice, the difference between the two S values allows recovery of the secret key a. In other words, the security of the signature depends on the quality of the random number generator.
Ed25519 (Solana, SSH, TLS, etc.)
#a fixed value derived from the secret key
nonce_prefix ← lower 32 bytes of SHA-512(seed)
#nonce generated by hashing the fixed value with the message
r = SHA-512(nonce_prefix || M)
R = r * G
S = r + H(R, A, M) * a (mod L)
Rather than drawing r from an external source, Ed25519 concatenates a secret-key-derived component called the nonce_prefix with the message M and hashes the result to produce the nonce.
The nonce_prefix is a fixed value derived once from the secret key; because a different message yields a different hash output, a distinct nonce is produced each time. This eliminates dependence on an external random number generator, but it requires that the nonce_prefix be unique to each signer. If two signers share the same nonce_prefix, signing the same message will produce the same nonce r.
In Ed25519, a single secret key (seed, 32 bytes) is hashed with SHA-512 to produce 64 bytes, which are then split into two halves:
SHA-512(seed) → 64 bytes
First 32 bytes → scalar (a) : the actual secret scalar used in signature computation.
Last 32 bytes → nonce prefix : the seed value used to derive the per-signature nonce.
The scalar a is the secret number used directly in the mathematical operations of the signature, while the nonce prefix fulfills the role that the operating system's random number generator plays in ECDSA. In other words, deriving the nonce from the secret key itself is the central design principle of Ed25519.
When Ed25519 signs a message M (e.g., transaction data), it produces a pair (R, S):
r = SHA-512(nonce_prefix || M) ... nonce generation
R = r * G ... elliptic curve point corresponding to the nonce
S = r + H(R, A, M) * a ... computation involving the secret key a
The meaning of each symbol is summarized below:
| Symbol | Description | Public? |
|---|---|---|
r | Nonce. The hash of nonce_prefix concatenated with message M | No (intermediate value) |
R | Elliptic curve point corresponding to r | Yes (included in signature) |
a | Secret key scalar | No (protected) |
S | Computation result involving the secret key a | Yes (included in signature) |
H(R, A, M) | Hash of R, public key A, and message M combined | Computable by anyone |
G | Fixed base point known to all participants | Yes (constant) |
The critical property here is that if nonce_prefix differs across signers, then r differs even for the same message, and consequently also differs. The uniqueness of nonce_prefix per signer is the property that upholds Ed25519 security.
In Solana, the signature is published as a 64-byte value consisting of R (32 bytes) concatenated with S (32 bytes).
What happens if two signers possess the same nonce_prefix?
Consider a scenario in which two signers sign the same transaction (the same message ):
If the nonce_prefix is different for each signer, then and consequently .
However, if , then for the same message we get , and therefore .
This phenomenon, in which two distinct signers produce the same , is called Shared-R. Since both and are publicly recorded on the blockchain, the occurrence of Shared-R gives rise to a system of equations that enables recovery of the secret keys from the difference of the two values (the concrete derivation is presented in Section 4).
Solana wallets typically derive multiple child keys from a single master seed. This approach is known as HD (Hierarchical Deterministic) key derivation, and it allows all child keys to be restored from a single seed backup. Solana uses the SLIP-0010 standard for this process.
The SLIP-0010 specification defines Ed25519 child key derivation as follows:
"the returned child key is "
: SLIP-0010, Private parent key → private child key
Here is the derived child key and is the left 32 bytes of the 64-byte SHA-512 output. That is, the specification only defines how to produce the 32-byte child key.
However, as discussed in Section 2.2, Ed25519 signing requires both a scalar (32 bytes) and a nonce_prefix (32 bytes), totaling 64 bytes. The 32-byte child key must therefore be passed through SHA-512 again to produce 64 bytes. This step is referred to as re-expansion.
In a correct implementation, re-expansion is performed so that each child obtains a unique nonce_prefix:
Correct: child_key → SHA-512(child_key) → [new scalar | new prefix] (unique per child)
Incorrect: master_seed → SHA-512 → [master_scalar | master_prefix]
child_0: [child_scalar_0 | master_prefix] ← same prefix
child_1: [child_scalar_1 | master_prefix] ← same prefix
All children share the same prefix, and signing the same message produces identical R values.
Because re-expansion is not an explicitly mandated step in the specification, custom implementations that optimize or simplify the key derivation process may omit it, thereby introducing the Shared-R vulnerability.
Vulnerability patterns similar to the one described here have been reported and registered in several prior disclosures:
| Source | Description |
|---|---|
| CVE-2022-50237 / RUSTSEC-2022-0093 | A flaw in the ed25519-dalek library (Rust) that allows separate manipulation of the scalar and nonce_prefix, enabling private key extraction from two signatures |
| orlp/ed25519 Issue #3 | The ed25519_add_scalar function fails to update the nonce prefix, causing R reuse |
| MystenLabs/ed25519-unsafe-libs | A catalog of over 40 Ed25519 libraries affected by the same vulnerability class |
Given two transactions exhibiting Shared-R, the private keys can be extracted through the following procedure.
Applying the signature formula from Section 2 to two signers (signer 0, signer 1) across two transactions (TX1, TX2):
Subtracting (1)-(2) and (3)-(4) eliminates the nonce :
With two unknowns (, ) and two equations, the system is solvable.
Expressed in matrix form:
All input values (, , ) can be computed from publicly available on-chain data.
The secret key scalars and are therefore fully determined.
The extracted scalars are verified by recomputing the public keys and comparing them against the originals:
A match confirms successful private key extraction.
To confirm that the vulnerability is exploitable in practice, the same conditions were reproduced on the Solana devnet.
========================================================================
Shared-R PoC — Ed25519 nonce reuse vulnerability reproduction (DEVNET)
========================================================================
[Phase 1] Vulnerable keypair generation (SLIP-0010 + master prefix reuse)
Signer 0: GxKGiE5sGmgAs3QrLFEkrmwYtMV8crxqq5Zo2ZaqAe4J
Signer 1: EXrP8x4UWSvWZJ9GjhumL21bodu2qz69D6ZC6nr34bQ1
Shared prefix (hex): 51338c9a28c92c134555dbfc913a634a...
Key generation verification: OK
[Phase 3] Transaction 1 construction and submission
Sig 0 R: affb089cfd802e72b1d4d04373a8cefd...
Sig 1 R: affb089cfd802e72b1d4d04373a8cefd...
Shared-R: YES
TX1 submitted
[Phase 4] Transaction 2 construction and submission
Sig 0 R: bee13c44cbb4b9f044cc990080ce375b...
Sig 1 R: bee13c44cbb4b9f044cc990080ce375b...
Shared-R: YES
TX2 submitted
[Phase 5] On-chain data retrieval
TX1 Shared-R on-chain confirmation: YES
TX2 Shared-R on-chain confirmation: YES
[Phase 6] Private key extraction (Cramer's rule — using on-chain data only)
Input: Signatures (R, S), messages, and public keys from TX1/TX2 (all publicly on-chain)
Extracted Scalar 0 (hex LE): a0cf256a02afea8cce5ad165f20502ab...
Extracted Scalar 1 (hex LE): c0003bf2194765209bf14844564c25b1...
| Verification Item | Result |
|---|---|
| Does Signer 0's R match Signer 1's R within TX1 (Shared-R)? | YES |
| Does Signer 0's R match Signer 1's R within TX2 (Shared-R)? | YES |
| Does Signer 0's R in TX1 match Signer 0's R in TX2? | NO (different messages) |
| Does the extracted scalar * G equal Signer 0's public key? | YES |
| Does the extracted scalar * G equal Signer 1's public key? | YES |
| When recovering the nonce for TX1, is the same r computed for both signers? | YES |
| When recovering the nonce for TX2, is the same r computed for both signers? | YES |
| Does the extracted scalar match the original scalar in a direct comparison? | YES |
The Shared-R phenomenon can be verified directly in the following Solana devnet transactions.
Signer Addresses:
| Role | Address |
|---|---|
| Signer 0 | GxKGiE5sGmgAs3QrLFEkrmwYtMV8crxqq5Zo2ZaqAe4J |
| Signer 1 | EXrP8x4UWSvWZJ9GjhumL21bodu2qz69D6ZC6nr34bQ1 |
Transactions:
Decoding the raw bytes of the signatures reveals the R value in the first 32 bytes. Inspecting the raw signature bytes of the first transaction on Solscan confirms that the leading portions of both signatures (the base58-encoded R values) are identical.

Both private keys were extracted using only the publicly available data from these two transactions.
| Resource | Link |
|---|---|
| RFC 8032 (Ed25519 specification) | https://datatracker.ietf.org/doc/html/rfc8032#section-5.1 |
| SLIP-0010 (HD key derivation standard) | https://github.com/satoshilabs/slips/blob/master/slip-0010.md |
| CVE-2022-50237 (NVD) | https://nvd.nist.gov/vuln/detail/CVE-2022-50237 |
| RUSTSEC-2022-0093 (Rust security advisory) | https://rustsec.org/advisories/RUSTSEC-2022-0093 |
| ed25519-unsafe-libs (vulnerable library catalog) | https://github.com/MystenLabs/ed25519-unsafe-libs |
| orlp/ed25519 Issue #3 | https://github.com/orlp/ed25519/issues/3 |
| Double Public Key Signing Attack paper | https://arxiv.org/abs/2308.1500 |
| Jacquot and Donnet, "Short Paper: Oops...I Did It Again. I Reused my Nonce." (FC 2026) | https://fc26.ifca.ai/preproceedings/55.pdf |