distribute/crypto/otp_crypto_adapter

OTP Crypto adapter for production use.

This adapter provides real cryptographic security using Erlang’s built-in :crypto module (OpenSSL/LibreSSL backend):

Requires no external dependencies beyond standard OTP 22+.

Features

Nonce Policy

This implementation uses 12-byte nonces as specified in RFC 8439. XChaCha20-Poly1305 (24-byte nonces) is NOT supported. Random nonces are generated per encryption, which is safe for typical message volumes (birthday bound ~2^48 messages per key).

Usage

import distribute/crypto/adapter
import distribute/crypto/otp_crypto_adapter

let opts = adapter.default_options("my_crypto")
let provider = otp_crypto_adapter.new()
let assert Ok(handle) = provider.init(opts)

// Start handshake with remote node
let assert Ok(result) = provider.handshake_start(handle, local, remote, None)

Security Notes

Types

Commands handled by the sodium adapter actor.

pub opaque type Command

Values

pub fn child_spec(
  options: types.ProviderOptions,
) -> supervision.ChildSpecification(types.ProviderHandle)

Create a child specification for OTP supervision.

Returns a supervision child spec for integrating the OTP crypto adapter with OTP supervision trees. The provider will be automatically restarted on crashes.

Arguments

  • options - Provider configuration options

Example

let opts = adapter.default_options("cluster_crypto")
let crypto_spec = otp_crypto_adapter.child_spec(opts)

supervision.start(
  children: [crypto_spec, transport_spec, discovery_spec],
)

Lifecycle

The supervised process will:

  1. Initialize crypto state and actor
  2. Register in the global registry
  3. Handle handshakes, encryption, and rekey commands
  4. Clean up contexts and registry on shutdown
pub fn get_handle(
  name: String,
) -> Result(types.ProviderHandle, Nil)

Get a handle to a running OTP crypto provider by name.

Retrieves a handle to a previously started OTP crypto adapter from the registry. Use this to obtain a handle when you didn’t directly start the provider (e.g., it was started by a supervisor).

Arguments

  • name - The registered name of the provider

Returns

  • Ok(handle) - Valid handle to the running provider
  • Error(Nil) - No provider found with that name

Example

// Provider started elsewhere (e.g., by supervisor)
case otp_crypto_adapter.get_handle("cluster_crypto") {
  Ok(handle) -> {
    let ctx = adapter.secure_context(handle, remote_node)
    adapter.encrypt(handle, ctx, message)
  }
  Error(Nil) -> Error(CryptoNotInitialized)
}
pub fn new() -> adapter.CryptoAdapter

Create a new OTP crypto adapter.

Returns a CryptoAdapter that provides real cryptographic security using Erlang’s built-in :crypto module (backed by OpenSSL/LibreSSL).

Cryptographic Algorithms

  • Key Exchange: X25519 (Curve25519 ECDH)
  • Encryption: ChaCha20-Poly1305 AEAD (RFC 8439, 12-byte nonce)
  • Key Derivation: HKDF-SHA256

Features

  • Ephemeral keys per handshake (forward secrecy)
  • Authenticated encryption with associated data
  • Key rotation via HKDF-based rekeying
  • Full metrics and health monitoring
  • OTP supervisor integration

Requirements

  • OTP 22+ (for X25519 and ChaCha20-Poly1305 support)
  • No external dependencies

Example

let adapter = otp_crypto_adapter.new()
let opts = adapter.default_options("production_crypto")
let assert Ok(handle) = adapter.init(opts)

// Complete handshake with remote node
let assert Ok(result) = adapter.handshake_start(handle, local, remote, None)

Security Notes

  • Keys are never logged
  • Nonces are randomly generated for each encryption
  • Old keys are discarded on rekey
  • Does NOT provide secure memory zeroing (use sodium_adapter for that)
Search Document