Encrypted, authenticated sessions for the pamoja SDK.
pamoja-security proves a payload came from a
device and was not altered. This crate adds the other half a networked link needs:
confidentiality and a fresh, ordered, replay-protected channel, so a reading is
not just trustworthy but private, and a captured message cannot be replayed to
reopen a valve or re-trigger an alarm.
Two devices that each hold the other's authenticated public key agree a session
key with [Session::establish] and then exchange messages with [Session::seal]
and [Session::open]. The whole exchange is built from published standards and
the tests are pinned to their reference vectors:
- X25519 key agreement, RFC 7748, so neither side ever sends the key.
- HKDF-SHA256, RFC 5869, to derive a per-session key bound to both public keys.
- ChaCha20-Poly1305, RFC 8439, to encrypt and authenticate each message; chosen because the cheap hardware this SDK targets rarely has AES acceleration.
Establishing a session is deterministic given the keys and salt, and every
operation works in place on caller-owned buffers, so the crate is no_std and
allocation-free and runs unchanged on a microcontroller. It is the secured-channel
groundwork the security pillar builds on, ahead of full transport TLS/DTLS.
Authenticating the peer
Key agreement gives a private channel; it does not by itself say who is on the
other end. The peer's [AgreementPublicKey] must be authenticated out of band,
by pinning it at provisioning time or by having it signed with the peer's
pamoja-security identity. Without that, the channel is confidential but open to
a man in the middle.
Examples
use ;
// Each device holds its own seed and the other's authenticated public key.
let sensor = from_seed;
let gateway = from_seed;
// A fresh salt is exchanged in the clear to start the session.
let salt = ;
let mut a = establish;
let mut b = establish;
// Seal a reading; the device id rides along as authenticated-but-readable data.
let mut reading = *b"tank: 18%";
let sealed = a.seal;
// The gateway opens it, recovering the reading and proving it is authentic.
b.open.expect;
assert_eq!;
// A replay of the same message is refused.
assert!;