A signature has to prove two things at once: that the signer holds the private key, and that the message is the one they meant. ECDSA does it with two numbers, r and s, and one throwaway secret.
First the message is hashed. ECDSA never sees your bytes; it sees an integer z derived from the digest — the leftmost bits of it, as many as the group order has, interpreted as a number. That is why the choice of hash is part of the signature scheme and not an implementation detail, and why ES256 means “P-256 with SHA-256” specifically.
choose k a nonce: random, secret, used exactly once
r = (k*G).x mod n
s = inv(k) * (z + r*d) mod n
signature = (r, s)
if r == 0 or s == 0, throw k away and choose anotherLook at what each half carries. r is a fingerprint of a point nobody else can produce without knowing k. s mixes three things — the message hash z, the private key d, and the nonce k — in a way that can be checked against the public key but not unpicked. The private key appears exactly once, inside a product, wrapped in a modular inverse. That is the whole design.
The nonce is not a formality
k must be secret, and it must be different for every signature. If two signatures over different messages share a nonce, anyone holding both can recover the private key with school algebra:
s1 = inv(k)*(z1 + r*d) same k, so the same r appears twice
s2 = inv(k)*(z2 + r*d)
s1 - s2 = inv(k)*(z1 - z2)
k = (z1 - z2) / (s1 - s2) <- the nonce falls out
d = (s1*k - z1) / r <- and then the private keyThis is not a theoretical concern. It is how the PlayStation 3 code-signing key was recovered in 2010, how thousands of Bitcoin wallets have been emptied, and how a long list of embedded devices with weak entropy at boot have leaked their keys. The failure is invisible from the outside — the signatures are perfectly valid — until someone notices two of them sharing an r.