BLAKE3ZMQ

[!WARNING] This is experimental and the gem is not maintained by cryptographers. It has not been independently audited. For production, use CurveZMQ instead.

BLAKE3ZMQ is a security mechanism for OMQ that replaces CurveZMQ with modern primitives:

  • X25519 key exchange (perfect forward secrecy)
  • ChaCha20-BLAKE3 AEAD (32-byte authentication tags)
  • BLAKE3 transcript hash and key derivation

It implements the Protocol::ZMTP::Mechanism::Blake3 class for use with protocol-zmtp.

See RFC.md for the full protocol specification.

Features

  • 4-message handshake (HELLO, WELCOME, INITIATE, READY)
  • Transcript hash binding across all handshake messages; all post-handshake frames (including commands) AEAD-encrypted, unlike CurveZMQ
  • Deterministic nonces (no per-message randomness needed)
  • 32 bytes overhead per message (no wire nonce, no command wrapper)
  • Stateless server until client authentication (cookie mechanism)
  • Mutual authentication or server-only (anonymous client) modes
  • Works out of the box (native C X25519 + Rust AEAD included)
  • Crypto-backend-agnostic (can substitute your own primitives)

Installation

gem "omq-blake3zmq"

Batteries included: ships with x25519 (native C) and chacha20blake3 (Rust native).

Usage

require "omq"
require "omq/blake3zmq"

Crypto = OMQ::Blake3ZMQ::Crypto

# Generate or load keys
server_sk = Crypto::PrivateKey.generate
server_pk = server_sk.public_key.to_s

client_sk = Crypto::PrivateKey.generate
client_pk = client_sk.public_key.to_s

# Server socket
server = OMQ::REP.new
server.mechanism = Protocol::ZMTP::Mechanism::Blake3.server(
  public_key: server_pk,
  secret_key: server_sk.to_s,
  authenticator: ->(peer) { peer.public_key.to_s == client_pk },
)
server.bind("tcp://127.0.0.1:9999")

# Client socket (keys optional — omit for anonymous/ephemeral identity)
client = OMQ::REQ.new
client.mechanism = Protocol::ZMTP::Mechanism::Blake3.client(
  server_key: server_pk,
  public_key: client_pk,
  secret_key: client_sk.to_s,
)
client.connect("tcp://127.0.0.1:9999")

Benchmarks

CurveZMQ (RbNaCl/libsodium) vs BLAKE3ZMQ (Rust native ChaCha20-BLAKE3 + C native X25519).

Ruby 4.0.2, x86_64 Linux.

Handshake latency (100 rounds)

Time Per handshake
CurveZMQ/RbNaCl 226 ms 2.26 ms
BLAKE3ZMQ 120 ms 1.20 ms
Speedup 1.9x

Message encrypt + decrypt throughput (20,000 messages)

Size CurveZMQ/RbNaCl BLAKE3ZMQ Speedup
64 B 8.4 MB/s 16.1 MB/s 1.9x
256 B 38.0 MB/s 51.4 MB/s 1.4x
1 KB 88.1 MB/s 137.5 MB/s 1.6x
4 KB 196.3 MB/s 265.5 MB/s 1.4x
16 KB 289.1 MB/s 387.1 MB/s 1.3x
64 KB 413.3 MB/s 452.4 MB/s 1.1x
128 KB 426.4 MB/s 527.0 MB/s 1.2x
256 KB 428.7 MB/s 538.0 MB/s 1.3x

Full round-trip (handshake + 1,000 messages over UNIXSocket)

Size CurveZMQ/RbNaCl BLAKE3ZMQ Speedup
64 B 69.4 ms (0.9 MB/s) 50.8 ms (1.3 MB/s) 1.4x
1 KB 54.6 ms (18.7 MB/s) 62.6 ms (16.3 MB/s) 0.9x
64 KB 281.7 ms (232.6 MB/s) 242.2 ms (270.6 MB/s) 1.2x

Run benchmarks yourself:

OMQ_DEV=1 bundle exec ruby bench/throughput.rb

Per-message overhead

BLAKE3ZMQ CurveZMQ
Tag size 32 bytes 16 bytes
Counter on wire 0 bytes 8 bytes
Command wrapper none 17 bytes
Total 32 bytes 41 bytes

Development

bundle install
bundle exec rake test

License

ISC