RFC 6979. Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)

https://www.rfc-editor.org/rfc/rfc6979

1. Introduction

  • k - a fresh random value for signature generation.

2.1. Key Parameters

  • E - an elliptic curve.
  • q - a sufficiently large prime number that is divisor of the curve.
  • G - a point of E, of order q. G is such that qG = 0.

Compute jG, where j ranges from 0 to q-1.

2.2. Key Pairs

  • x is a private key taken module q. x shall not be 0; hence x is an integer in the range [1, q-1].
  • U is a public key. U = xG.

2.3. Integer Conversion

  • qlen is the binary length of q.
  • rlen is equal to qlen, rounded up to the next multiple of 8. See roundUp8 function. Unrelated to r.
  • blen is the length (in bits) of an input sequence.
2.3.1. Bits and Octets

MSB first.

2.3.2. Bit String to Integer

The bits2int transform takes as input a sequence of blen bits and outputs a non-negative integer that is less than 2^qlen. An alternative name is vec2Uint.

const bits2int: (qlen: bigint) => (b: Vec) => bigint
2.3.3. Integer to Octet String
const int2octets: (qlen: bigint) => (x: bigint) => Vec
2.3.4. Bit String to Octet String
const bits2octets: (q: bigint) => (b: Vec) => Vec

2.4. Signature Generation

  • H is a cryptographic hash function, which returns a bit vector of length hlen.
  • m is an input message. H(m).
  1. h = bits2int(H(m)) mod q.
  2. k is a random value module q. It shall not be 0.
  3. r = (kG).x mod q: the X coordinate of kG (a member of the field over which E is defined), reduced modulo q. If r is 0, select new k.
  4. s = (h+x*r)/k mod q

The pair (r, s) is the signature.

3. Deterministic ECDSA

3.1. Building Blocks

3.1.1. HMAC

HMAC_K(V) is HMAC with the key K over data V, which returns a sequence of bits of length hlen.

3.2. Generation of k

  • a. h1 = H(m)
  • b. V = 0x01 0x01 ... 0x01 such that the length of V is equal to 8*ceil(hlen/8).
  • c. K = 0x00 0x00 ... 0x00 such that the length of K, in bits, is equal to 8*ceil(hlen/8).
  • d. K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1)).
  • e. V = HMAC_K(V).
  • h. Apply the following algorithm until a proper value is found for k:
1. Set T to the empty sequence, so tlen = 0.
2. while tlen < qlen do:
   - V = HMAC_K(V)
   - T = T || V
3. Compute k = bits2int(T). If k is not in [1, q-1] or kG = 0 then
   - K = HMAC_K(V || 0x00)
   - V = HMAC_K(V)
   and loop (try to generate a new T, and so on). Return to step 1.

Digital Signature

We use elliptic curves for digital signatures.

z is a hash of a message.

k is a unique for each z and secret.

R = G * k.

r = R.x mod q.

s = ((z + r * d) / k) mod q.

If r or s is 0, the signature is invalid and k must be selected again; sign does not select again yet, and refuses instead.

The signature is (r, s).

Verifying a signature

verify checks (r, s) against the public key Q = G * d. All arithmetic on scalars is modulo q.

  1. If Q is not a valid public key, or r or s is not in [1, q-1], the signature is invalid. A valid public key is not the point at infinity, has coordinates in [0, p-1], lies on the curve, and satisfies Q * q = 0 (SEC 1 §3.2.2.1, isPublicKey in ../secp, which also requires q^2 > p + 1 + 2√p, so that, for the prime q ECDSA requires, Q * q = 0 puts Q in the subgroup of G).
  2. w = 1/s
  3. u1 = z * w and u2 = r * w
  4. X = G * u1 + Q * u2
  5. If X is the point at infinity, the signature is invalid: it has no x.
  6. v = X.x mod q

The signature is valid if v = r.

The message exchange demo

demo.f.mjs starts from RFC 6979 A.2.5 and shows Alice signing, Eve intercepting and Bob verifying. The diagram separates the packet route through Eve from Bob’s trust in Alice’s public key. One explanation beneath Bob shows why a substituted key cannot authenticate Alice. It reflows vertically on narrow screens. Typing updates Alice's local signing output; Send message transmits a public snapshot to Eve. Forward to Bob commits Eve's outgoing packet and verifies it on delivery. Neither action transmits a private key or the deterministic nonce.

Alice's draft, Eve's original packet and edits, and Bob's last delivery are independent immutable values. Switching attacks starts from the intercepted packet; Reset Eve restores it in Modify message mode, ready to forward unchanged. Another Send replaces Eve's packet without changing Bob's last delivery. Bob retains Alice's trusted public key from that exchange, separately from the received packet's key.

Eve can modify the message, modify the signature, or replace the public key and sign with her own demo-only key. Bob always verifies with Alice's trusted key. In the replacement scenario he also verifies with the received key: a signature valid under Eve's key proves that key signed the message, not that Alice sent it. The demo models trust as Alice's public key established at Send; it does not implement a real identity or key-distribution protocol.

Malformed hexadecimal and oversized messages are refused before forwarding. Parsed signature scalars, including zero and out-of-range values, reach the actual verifier so Bob can show rejection. A changed signature is not assumed invalid: ECDSA also admits (r, q-s) for the same key and message.

After Forward to Bob, a copyable Bash/OpenSSL command checks Bob's last received message and DER signature with Alice's saved trusted public key, encoded as a PEM SubjectPublicKeyInfo. The selected curve is carried by the key's named-curve OID. Later Alice or Eve edits do not change this command; forwarding another packet does. Eve's replacement key never replaces the trusted key in the command. Shell arguments are quoted literally; a message containing NUL cannot be represented as a shell argument, so its command is refused.

A separate Why nonce reuse reveals the private key section lets readers edit two messages, a demo private key, and one shared nonce. P-256/SHA-256 stays fixed. It starts with sample, test, and k = 7; editing any input recalculates both hashes, signatures, and the recovered demo private key:

x = (s1·h2 − s2·h1) / (r·(s2 − s1)) mod q.

This needs distinct hashes modulo q and a nonzero denominator. Repeating the same deterministic signature for the same message alone supplies no independent second equation. Equal hashes leave both signatures visible with an explanation of the zero recovery denominator; malformed hex, oversized messages, invalid scalar ranges, and zero signature scalars are refused. The section reports whether the recovered key matches the entered demo key. Its state is independent of Alice, Eve, and Bob. The deliberately broken signer is separate from Alice's RFC 6979 exchange; its demo inputs are never used by that exchange.

Proof

Scalar equalities below are modulo q, the order of G.

  1. X = G * (z * 1/s) + Q * (r * 1/s)
  2. X = G * z * 1/s + G * d * r * 1/s
  3. G * (z * 1/s + d * r * 1/s) = G * k
  4. z * 1/s + d * r * 1/s = k
  5. z + d * r = k * s
  6. z + d * r = k * (z + r * d) / k
  7. z + d * r = z + r * d

Demo

ECDSA signs a message with a private key and verifies it with a public key. Follow Alice → Eve → Bob to see why signature validity and sender identity are different. The initial signing values reproduce RFC 6979 A.2.5.

Alice (sign) → Eve (intercept or tamper) → Bob (verify with Alice’s trusted public key)

Alice

Sign messagePrivate key stays with Alice
Message + signature + public key

Eve

Intercept or tamperEdit message / signatureSwap public key
Outgoing packet

Bob

Verify signatureUse Alice’s trusted public key

Bob checks with Alice’s trusted key. A signature that passes only with a substituted key does not prove Alice sent it.

Alice’s public key
Already trusted by Bob
Bob’s trusted Alice key
The separate key path represents trust in Alice’s public key, not a packet sent through Eve. This demo establishes that trusted key when Alice clicks Send.

Alice — Sign

Signer

Demo-only keys. Do not enter real private keys into this public website.

Public key U = xG, hex:

Ux = 60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6
Uy = 7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299

Signature r, hex:

efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716

Signature s, hex:

f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8
Signing details — RFC 6979

Message → Hash → ECDSA Sign → (r, s). The page opens on RFC 6979 A.2.5: P-256, SHA-256 and the message sample. Compare these hexadecimal values with the published example.

Deterministic nonce k, hex (never transmitted):

a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60

Keep k secret: knowing it reveals the private key. RFC 6979 derives k from the private key and message.

r is the x coordinate of kG modulo q; s = (h + r·x) / k mod q. Bob checks this relation with the public key alone.

Eve — Intercept & Attack

Interceptor

Waiting for Alice to send a message…

Bob — Receive & Verify

Verifier

Waiting for a message…

Why nonce reuse reveals the private key

Experiment with two messages signed using one shared nonce. P-256 / SHA-256 stays fixed in this section. Change any input to recalculate both signatures and recover the private key from their public scalars.

The same k gives the same r. From s₁k = h₁ + rx and s₂k = h₂ + rx, eliminate k to recover x using only the two signatures and message hashes. All scalar arithmetic is modulo q.

x = (s1·h2 − s2·h1) / (r·(s2 − s1)) mod q

Curve order q, hex:

ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551

Message 1: h = bits2int(SHA-256(message)) mod q. Values in hex:

h = af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf
r = 8e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3
s = c29b78215338f383a45130842d12b0af7277334cfbd408e70212a2676638bd65

Message 2: h = bits2int(SHA-256(message)) mod q. Values in hex:

h = 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
r = 8e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3
s = 9bcd08ca9776d10899f60d33588f1303d8cc845c48d5838e384c1e5f71eae4ad

Recovered private key x, hex:

0000000000000000000000000000000000000000000000000000000012345678

Matches original demo private key: true.

Recovery needs distinct hashes modulo q and a nonzero denominator. Try making both messages identical to see why repeating one signature does not provide two independent equations.

Keep k secret and never reuse it across different message hashes. Alice’s signer above uses RFC 6979 to derive k from the private key and message; signing the same message again deterministically is safe.

Emergent Testing

Idle. Press Run to start the suite.

  • ./nonce_reuse/proof.f.mjs
  • ./proof.f.mjs