[curves] Ed25519 sigs with Curve25519/X25519 keypairs
Trevor Perrin
trevp at trevp.net
Mon Oct 13 00:06:05 PDT 2014
We've discussed using Curve25519 keypairs for Ed25519 signatures: in
particular, converting the Curve25519/X25519 format public key to
Ed25519 format, and deriving the deterministic Schnorr nonce directly
from the private scalar:
https://moderncrypto.org/mail-archive/curves/2014/000205.html
https://moderncrypto.org/mail-archive/curves/2014/000212.html
If anyone's interested, I wrote up a spec for this:
https://github.com/trevp/curve25519sigs/blob/master/curve25519sigs.md
********************
Introduction
=
This document describes the use of [Curve25519][] keypairs for
creating and verifying [Ed25519][] signatures. This enables using a
single public key format for ECDH and signatures. In some situations,
a single keypair can be used for both ECDH and signatures.
Algorithms
=
Variables
-
Name Explanation Size (bytes)
----- -------------- ------------
a Private scalar 32
A Curve25519 public key 32
A_ed Ed25519 conversion of Curve25519 public key 32
B Ed25519 base point -
random Random value from secure RNG 64
label [0xFE] || [0xFF]*31 (for different oracles) 32
msg Message to be signed any
L Order of base point -
Key generation
-
The private scalar is a typical Curve25519 or Ed25519 private key,
which can be generated by following the advice in
[Curve25519](#Curve25519): "generate 32 uniform random bytes, clear
bits 0, 1, 2 of the first byte, clear bit 7 of the last byte, and set
bit 6 of the last byte."
Signing
-
Sign(a, msg, random):
# Derive Schnorr nonce
r = SHA512(label || a || msg || random) (mod L)
# Calculate Ed25519 public key
A_ed = a * B
# Create standard Ed25519 signature
R = r * B
S = r + SHA512(R || A_ed || msg) * a (mod L)
signature = R || S
# Copy sign bit of public key into unused bit of signature:
signature[63] |= (A_ed[31] & 0x80)
return signature
Verifying
-
Verify(A, msg, signature):
# Convert Curve25519 public key to Ed25519 form
A_ed = (A-1) * ((A+1)^-1) (mod 2^255-19)
# Move sign bit from signature to public key
A_ed[31] |= (signature[63] & 0x80)
signature[63] &= 0x7F
# Verify standard Ed25519 signature
return Ed25519_Verify(A_ed, signature, msg)
Implementation considerations
=
Key conversion
-
Converting the Curve25519 public key to Ed25519 is straightforward
using the equivalence `y = (A-1)/(A+1)` ([Ed25519](#Ed25519),
[TwistedEdwards][]). To prevent `A=-1` causing a division-by-zero
error, this value should be mapped to an Ed25519 value of zero. This
happens naturally if inversion is done by exponentiation to -1 (mod
2^255-19).
Performance
-
**Signing:** Recalculating the Ed25519 public key on every signature
is simple but slightly inefficient. The public key could be stored for
higher performance.
**Verifying:** Converting the public key and performing a standard
Ed25519 verification is simple but slightly inefficient. Conversion
could be combined with decompression for higher performance.
Security considerations
=
Sign bit
-
The Ed25519 public key's sign bit is conveyed in the signature. This
allows an attacker to try to forge a signature for Alice based on
either her actual Ed25519 public key or its negative. Since both are
legitimate public keys, an attacker who can't break Ed25519 can't
forge signatures for either.
Nonce
-
The [Ed25519][] paper recommends deriving the Schnorr nonce as:
r = SHA512(nonce_key || msg) (mod L)
Where both `nonce_key` and the private scalar are derived from a
master key. To sign with existing scalars we instead do:
r = SHA512(label || a || msg || random) (mod L)
The `label` is to aid security proofs in the Random Oracle Model (such
as [Pointcheval-Stern][]) by separating uses of the hash function.
Note that the `label` is not a possible value for `R`. If using the
same keypair for signatures and ECDH, the ECDH output should be hashed
via a different "random oracle", e.g. SHA512 with a label prefix of
`[0xFF]*32`.
The random value is not essential but helps ensures the nonce and
scalar are independent, and reduces the risk of nonce collisions or
biases.
Acknowledgements
=
Thanks to Robert Ransom for suggesting storing the sign bit in the
signature. Thanks to Robert Ransom, Mike Hamburg, Samuel Neves, and
Christian Winnerlein for advice and feedback.
References
=
[Curve25519]: #Curve25519
<a name="Curve25519">**Curve25519:**</a>
<http://cr.yp.to/ecdh/curve25519-20060209.pdf>
[Ed25519]: #Ed25519
<a name="Ed25519">**Ed25519:**</a>
<http://ed25519.cr.yp.to/ed25519-20110926.pdf>
[Pointcheval-Stern]: #Pointcheval-Stern
<a name="Pointcheval-Stern">**Pointcheval-Stern:**</a>
<http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.11.8213>
[TwistedEdwards]: #TwistedEdwards
<a name="TwistedEdwards">**TwistedEdwards:**</a>
<http://eprint.iacr.org/2008/013.pdf>
Trevor
More information about the Curves
mailing list