Browse the docs

silk.crypto.x25519.

Agree on a shared secret with X25519 (RFC 7748) Diffie-Hellman.

When to use#

Use X25519 when two parties must get the same secret over a channel that others can read, for example in a TLS 1.3 or Noise handshake. Each side makes a key with X25519.generate, sends X25519.publicKey, and calls X25519.agree with the public key of the other side. Then derive the real keys from the shared secret with silk.crypto.hkdf.

Details#

Secrets, public keys, and shared secrets are 32 bytes, little-endian. A secret is clamped as RFC 7748 says, so all 32-byte values are valid. X25519.agree consumes the key, because an ephemeral key is for one exchange. Nothing allocates.

Errors#

  • X25519Error for an input with the wrong length, or for a peer key that gives an all-zero secret.

Gotchas#

The exchange does not tell you who the other side is. An attacker in the middle can run one exchange with each side. Authenticate the exchange, for example with a certificate signature. Do not use the shared secret directly as a key. The key is not erased when it is dropped.

Examples#

Agree on a secret (RFC 7748 test vector)#

silk
import silk.core.result { Result }
import silk.crypto.x25519 { X25519, X25519Error }

pub fn main() -> i32 {
  // Real programs use `X25519.generate()` with a secure `Random` provider.
  let alice = match X25519.fromSecret(
    b"\x77\x07\x6d\x0a\x73\x18\xa5\x7d\x3c\x16\xc1\x72\x51\xb2\x66\x45\xdf\x4c\x2f\x87\xeb\xc0\x99\x2a\xb1\x77\xfb\xa5\x1d\xb9\x2c\x2a"
  ) {
    Result<X25519, X25519Error>.Success { value } => move value
    Result<X25519, X25519Error>.Failure { error: _ } => {
      return 1
    }
  }
  let bob = match X25519.fromSecret(
    b"\x5d\xab\x08\x7e\x62\x4a\x8a\x4b\x79\xe1\x7f\x8b\x83\x80\x0e\xe6\x6f\x3b\xb1\x29\x26\x18\xb6\xfd\x1c\x2f\x8b\x27\xff\x88\xe0\xeb"
  ) {
    Result<X25519, X25519Error>.Success { value } => move value
    Result<X25519, X25519Error>.Failure { error: _ } => {
      return 1
    }
  }
  let alicePublic = X25519.publicKey(&alice)
  let bobPublic = X25519.publicKey(&bob)
  let fromAlice = match X25519.agree(move alice, &bobPublic) {
    Result<[u8; 32], X25519Error>.Success { value } => value
    Result<[u8; 32], X25519Error>.Failure { error: _ } => {
      return 1
    }
  }
  let fromBob = match X25519.agree(move bob, &alicePublic) {
    Result<[u8; 32], X25519Error>.Success { value } => value
    Result<[u8; 32], X25519Error>.Failure { error: _ } => {
      return 1
    }
  }

  // Both sides get 4a5d9d5b...1742.
  if alicePublic[0] != 0x85 || fromAlice[0] != 0x4a || fromBob[0] != 0x4a || fromBob[31] != 0x42 {
    return 1
  }
  return 0
}

Import as X25519 with import silk.crypto.x25519 { X25519 }.

Public declarations: 2.

X25519Error#

silk
pub union X25519Error

The reason that an X25519 call failed. It holds no secret bytes.

InvalidScalarLength#

silk
InvalidScalarLength

The secret is not 32 bytes.

InvalidPeerLength#

silk
InvalidPeerLength

The public key of the peer is not 32 bytes.

AllZeroSharedSecret#

silk
AllZeroSharedSecret

The public key of the peer gives an all-zero shared secret. This occurs for a few low-order points that a hostile peer can send.

Implementation Debug for X25519Error#

silk
impl Debug for X25519Error

Operation debug#

silk
effect fn debug(self: &Self, formatter: &mut Formatter) -> () ! WriterError ? &mut Writer

Writes the name of the variant and its fields, such as InvalidScalarLength.

X25519#

silk
pub struct X25519

An X25519 key pair for one exchange: a secret and its public key.

When to use#

Use it when two parties must get the same secret over a channel that others can read, for example in a TLS 1.3 or Noise handshake. Each side makes a key with X25519.generate, sends X25519.publicKey, and calls X25519.agree with the public key of the other side.

Details#

X25519.agree consumes it. Make a new one for each exchange. Secrets, public keys, and shared secrets are 32 bytes, little-endian. Nothing allocates.

Gotchas#

The exchange does not tell you who the other side is. An attacker in the middle can run one exchange with each side. Authenticate the exchange, for example with a certificate signature. Derive the real keys from the shared secret with silk.crypto.hkdf. The key is not erased when it is dropped.

Implementation X25519#

silk
impl X25519

Associated function fromSecret#

silk
pub fn fromSecret(bytes: &[u8]) -> Result<X25519, X25519Error>

Returns the key pair for the 32-byte secret bytes, after RFC 7748 clamping.

Use it for test vectors or a secret that you stored. The bytes are copied.

Errors#

  • InvalidScalarLength if bytes is not 32 bytes.

Gotchas#

For a new exchange, use X25519.generate. A secret that is used for two exchanges gives the same public key to both, so the exchanges can be linked.

Associated function generate#

silk
pub effect fn generate() -> X25519 ? &mut Random

Returns a new key pair with a secret from 32 bytes of the ambient Random.

The Random provider must be cryptographically secure. If it fails, the program stops. There is no fallback to a weaker source.

Method publicKey#

silk
pub fn publicKey(self: &X25519) -> [u8; 32]

Returns the 32-byte public key to send to the peer. It does not consume the key pair.

Method agree#

silk
pub fn agree(self: X25519, peer: &[u8]) -> Result<[u8; 32], X25519Error>

Returns the 32-byte shared secret for the public key peer of the other side, and consumes the key pair.

As RFC 7748 says, the top bit of peer is ignored and any 32-byte value is accepted, also a point that is not on the curve.

Errors#

  • InvalidPeerLength if peer is not 32 bytes.

  • AllZeroSharedSecret if the result is all zeros. Stop the exchange.

The key pair is consumed also when it fails.

Gotchas#

Pass the result through a key derivation function such as silk.crypto.hkdf before you use it as a key.

Category: Cryptography.

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.

View the source of this page