Ed25519 Quirks

As with other digital signature schemes, Ed25519 consists of three protocols: key generation, signing and verification. They are similar, but distinct, from the generic Schnorr scheme.

Key Generation

Ed25519 does not match secret keys to scalars. Instead, a secret scalar is generated from a seed, a 32-byte string, which should be filled at random from a cryptographically secure RNG.

Expanded seed
xseed = Hash(seed) = "cyHeeE3DZHhrYCCC03Qz0/hdMhiVAYSk48b5Fz5MqECHbsLWD5xheWZWNgefvushxqZLhdTYjFlj2YT6WdpgkA=="

The seed is expanded to 64 bytes with the help of a hash function. Most Ed25519 implementations use SHA-512, but any cryptographic hash function with 64-byte output can suffice.

Secret scalar
a = Sc(clamp(xseed[..32])) = Sc("vNEGBeQ2GxgS7UH2WI23f/hdMhiVAYSk48b5Fz5MqAA=")

The first 32 bytes are “clamped” by setting the lower 3 bits and the highest bit (in the LSB interpretation) to 0, and the second-highest bit to 1. The resulting byte sequence is interpreted as a scalar a.

Nonce
nonce = xseed[32..] = "h27C1g+cYXlmVjYHn77rIcamS4XU2IxZY9mE+lnaYJA="

The upper 32 bytes of the expanded seed are used as a nonce during signing.

Public key
A = [a]B = Pt("w7B3kKYKtgk4eA8fgG20E1txF2MaUKFMxFQtxjfknow=")

The public key is still (the encoding of) a point on the elliptic curve, obtained by multiplying the basepoint B by the secret scalar.

If you want to know why the secret scalar is clamped in this way, refer to this explanation.

Signing

Signing in Ed25519 is deterministic: it doesn't require an RNG during signing. A faulty RNG during signing can leak the secret key, so this is an understandable design choice.

“Random” scalar
r = Sc(Hash(nonce ‖ M)) = Sc("5pn+bamVdM77oTY0RKoNkE+JNJja86pwuawRMcB7Kw4=")

Like in RFC 6979, the “random” scalar r is chosen based on the secret key and the message M.

Signature point
R = [r]B = Pt("I5K5cKtQCZdKEkbvOjkxEV7FyJAF5K7j7yGxzx227Cw=")
Hash scalar
h = Sc(Hash(R ‖ A ‖ M)) = Sc("6h/qyIIdGPv7uuunN5FFDqXH1eHsKdCFEuLiqm9AiAo=")

Unlike vanilla Schnorr, we include the public key A to values being hashed.

Signature scalar
s = r + h*a = Sc("JwopJmpTkHkhVkRXxPXE5mYOUu1RyJBgYWoY8xYJpgM=")

Verification

Verification uses the equation following from Schnorr and the modified signing procedure:

[s]B == R + [H(R ‖ A ‖ M)]A.
Hash scalar
h = Sc(Hash(R ‖ A ‖ M)) = Sc("6h/qyIIdGPv7uuunN5FFDqXH1eHsKdCFEuLiqm9AiAo=")

Hash scalar can be readily recreated from public information.

EC point
R′ = [s]B - [h]A = Pt("I5K5cKtQCZdKEkbvOjkxEV7FyJAF5K7j7yGxzx227Cw=")

To verify whether signature is valid, it’s enough to compare R′ to first 32 bytes of the signature (i.e., R).