X25519 key agreement, HKDF, and ChaCha20-Poly1305 with an anti-replay window, with no TLS stack. One capability of pamoja, one memory-safe Rust core with bindings for TypeScript, Python, and C#.
npm install @pamoja/session
This pulls in @pamoja/native, the compiled engine. npm install pamoja is the whole framework in one package.
The test that runs in CI, spliced here as it ran.
From bindings/node/guides/session.ts:
import { AgreementKey, Role, Session } from '@pamoja/session'
// Each device is provisioned with a 32-byte seed and publishes the key it derives. A real
// seed comes from the factory or a secure element; any 32 bytes stand in here.
const node = new AgreementKey(Buffer.alloc(32, 7))
const gateway = new AgreementKey(Buffer.alloc(32, 9))
// Neither side sends the session key. Both derive it from the shared secret, a salt that
// travels in the clear, and both public keys, with opposite roles.
//
// The salt must be fresh for every session: reusing one derives the same key from the same
// pair of devices twice. The initiator draws it and sends it in the clear, so the responder
// uses the salt it received rather than one of its own.
const salt = randomBytes(16)
const uplink = new Session(node, gateway.publicKey(), salt, Role.Initiator)
const downlink = new Session(gateway, node.publicKey(), salt, Role.Responder)
console.log('both sides derived a key without sending one')
// The pump id is authenticated but not encrypted, so a router still reads it while any
// change to it fails the tag.
const reading = Buffer.from('flow=41.2')
const sealed = uplink.seal(reading, Buffer.from('pump-3'))
console.log(`sealed the reading is no longer readable: ${!sealed.ciphertext.equals(reading)}`)
console.log(`opened ${downlink.open(sealed, Buffer.from('pump-3')).toString()}`)
// The anti-replay window refuses a counter it has already accepted, so a frame captured
// off the air and sent again is not delivered a second time.
try {
downlink.open(sealed, Buffer.from('pump-3'))
console.log('a replayed frame was accepted, which should never happen')
} catch (error) {
console.log(`replay refused: ${(error as Error).message}`)
}
| Language | Package | Reference |
|---|---|---|
| Rust | pamoja-session |
reference, docs.rs, install |
| TypeScript | @pamoja/session |
reference, install |
| Python | pamoja-session |
reference, install |
| C# | Pamoja.Session |
reference, install |
@pamoja/session reference, every class, function, and type this package exports.MIT
Ergonomic facade over the generated session binding.
Two devices that already know each other's public keys can agree on a session key without ever sending it, and then exchange messages that are confidential, cannot be altered undetected, and cannot be replayed. That is the whole of what a small device usually needs from transport security, at a fraction of what a TLS stack costs it.
The role is re-exported as a runtime Role object, because the generated enum is types-only.