std/signature/ed25519
std/signature/src/ed25519.trb
Ed25519 (RFC 8032 section 5.1): Ed25519PrivateKey signs, Ed25519PublicKey verifies, and Ed25519Signature is
the 64 bytes between them. Three capsules, each made by a factory that checks what it is given, so a public key is
always a point of the curve and a signature always has an S below the group order.
A private key is its 32-byte seed; everything else - the secret scalar, the prefix the nonce is hashed from, and the public key - is derived from the seed once, when the key is made. Signing is deterministic: the same key and message give the same signature, and no randomness is needed after the seed.
type Ed25519PrivateKey
type Ed25519PrivateKey with Show, Encode
An Ed25519 private key: the 32-byte seed RFC 8032 calls the private key, with the secret scalar, the prefix and the
public key derived from it once. It signs with Ed25519PrivateKey.signature.
Its Show and its Encode write the public key and never the seed, so a key that ends up in a log line or in an
encoded configuration does not leak; Ed25519PrivateKey.seed is the one way to the secret.
Examples
const seed: List<UInt8> = List.filled 32, 7
const key = Ed25519PrivateKey.fromSeed(seed).expect("32 bytes")
const message = "a release of acme/http".bytes().toList()
const signature = key.signature message
print key.publicKey().verifies(message, signature)
Pitfalls
- Where the seed comes from is the caller's:
std/signaturemakes no randomness (docs/design/RANDOM.md). A seed has to be 32 bytes of the system's source of randomness, never a password or a hash of one. ==compares the seeds byte by byte and stops at the first difference, which is not constant time; compare public keys instead.
fn fromSeed
static fn fromSeed(seed: Bytes): Result<Self, SignatureError>
The key of a 32-byte seed (RFC 8032 section 5.1.5). One multiplication of the base point, the cost of a signature.
Errors
SignatureError.WrongLengthfor a seed that is not 32 bytes.
fn fromHex
static fn fromHex(text: String): Result<Self, SignatureError>
The key of a seed written as 64 hexadecimal digits, either case: how a key comes out of an environment variable.
Errors
SignatureError.NotHexadecimalorSignatureError.WrongLengthfor a text that is not 64 hexadecimal digits.
fn seed
fn seed(): List<UInt8>
The 32 bytes of the seed: the secret itself, for the one place that keeps it.
fn seedHex
fn seedHex(): String
The seed as 64 lowercase hexadecimal digits, which Ed25519PrivateKey.fromHex reads back.
fn publicKey
fn publicKey(): Ed25519PublicKey
The public key that verifies what this key signs.
fn signature
fn signature(message: Bytes): Ed25519Signature
The signature of message (RFC 8032 section 5.1.6): R = r B for the nonce r hashed from the prefix and the message,
then S = r + k s modulo L for the challenge k hashed from R, the public key and the message. The multiplication of
the base point and the arithmetic on the secret scalar take the same steps for every key and message.
fn show
fn show(): String
Ed25519PrivateKey(public key <hex>): the seed is never shown.
fn encode
fn encode<Target: Encoder>(var target: Target)
What Ed25519PrivateKey.show writes, as a string: an encoded configuration never carries the seed.
type Ed25519PublicKey
type Ed25519PublicKey with Show, Equals, Hash, TryFrom<String, SignatureError>
An Ed25519 public key: 32 bytes that encode a point of the curve, checked when the key is made - a y below p, an x
that exists, and the canonical sign (RFC 8032 section 5.1.3). It verifies with Ed25519PublicKey.verifies.
Written as text it is 64 lowercase hexadecimal digits: Ed25519PublicKey.hex, its show() and String.from(key)
write them, and Ed25519PublicKey.tryFrom(text) reads them back, which is also how the key is encoded and decoded.
Examples
const key = Ed25519PublicKey.tryFrom "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"
print key.isOk()
fn fromBytes
static fn fromBytes(bytes: Bytes): Result<Self, SignatureError>
The key 32 bytes encode.
Errors
SignatureError.WrongLengthfor other than 32 bytes, andSignatureError.NotAPointfor bytes that encode no point of the curve or encode one in a form that is not canonical.
fn tryFrom
static fn tryFrom(value: String): Result<Self, SignatureError>
The key 64 hexadecimal digits of either case write.
Errors
SignatureError.NotHexadecimalorSignatureError.WrongLengthfor a text that is not 64 hexadecimal digits, andSignatureError.NotAPointas forEd25519PublicKey.fromBytes.
fn bytes
fn bytes(): List<UInt8>
The 32 bytes of the encoding.
fn hex
fn hex(): String
The 32 bytes as 64 lowercase hexadecimal digits.
fn show
fn show(): String
The same as Ed25519PublicKey.hex.
fn equals
fn equals(other: Self): Bool
Whether the two keys are the same 32 bytes: the point is derived from them, in whichever limbs it came out.
fn hash
fn hash(): Int
The hash of the 32 bytes, so that a key is a key of a Map or a member of a Set by its bytes alone.
fn verifies
fn verifies(message: Bytes, signature: Ed25519Signature): Bool
Whether signature is this key's signature of message (RFC 8032 section 5.1.7): with k the challenge hashed from
R, the key and the message, whether S B - k A encodes as R. That is the equation without the cofactor, as ref10,
libsodium and OpenSSL check it; the S of the signature was checked to be below L when the signature was made.
It takes variable time - everything it reads is public - and about as long as three signatures.
extend String with From<Ed25519PublicKey>
extend String with From<Ed25519PublicKey>
A public key as its 64 lowercase hexadecimal digits: the way back of Ed25519PublicKey.tryFrom.
fn from
static fn from(value: Ed25519PublicKey): Self
type Ed25519Signature
type Ed25519Signature with Show, TryFrom<String, SignatureError>
An Ed25519 signature: 64 bytes, the encoded point R and then the scalar S, checked to be below the group order L
when the signature is made (RFC 8032 section 5.1.7). As text it is 128 lowercase hexadecimal digits, read back by
Ed25519Signature.tryFrom.
fn fromBytes
static fn fromBytes(bytes: Bytes): Result<Self, SignatureError>
The signature 64 bytes are.
Errors
SignatureError.WrongLengthfor other than 64 bytes, andSignatureError.ScalarOutOfRangefor an S that is not below L.
fn tryFrom
static fn tryFrom(value: String): Result<Self, SignatureError>
The signature 128 hexadecimal digits of either case write.
Errors
SignatureError.NotHexadecimalorSignatureError.WrongLengthfor a text that is not 128 hexadecimal digits, andSignatureError.ScalarOutOfRangeas forEd25519Signature.fromBytes.
fn bytes
fn bytes(): List<UInt8>
The 64 bytes: R, then S.
fn hex
fn hex(): String
The 64 bytes as 128 lowercase hexadecimal digits.
fn show
fn show(): String
The same as Ed25519Signature.hex.
extend String with From<Ed25519Signature>
extend String with From<Ed25519Signature>
A signature as its 128 lowercase hexadecimal digits: the way back of Ed25519Signature.tryFrom.
fn from
static fn from(value: Ed25519Signature): Self