-
Notifications
You must be signed in to change notification settings - Fork 0
e2ee
Last updated: 2026-04-20 (S09 β Kyber signature hardening)
nChat E2EE uses the Signal Protocol via @signalapp/libsignal-client (currently v0.88.x).
It provides forward secrecy and break-in recovery through the Double Ratchet algorithm,
with post-quantum key encapsulation via Kyber-768 (ML-KEM).
As of S09 (2026-04-20), the Kyber prekey signature in signal-client.ts:processPreKeyBundle
is a real Ed25519 signature produced by the local identity private key over the Kyber KEM
public key bytes. This replaces the previous 64-byte zero-buffer dummy.
// Kyber KEM keypair generated fresh per session
const kyberKeyPair = SignalClient.KEMKeyPair.generate()
// Identity private key signs the Kyber public key bytes
const identityPrivKey = SignalClient.PrivateKey.deserialize(
Buffer.from(localIdentityKeyPair.privateKey)
)
const kyberSig = identityPrivKey.sign(kyberKeyPair.getPublicKey().serialize())
// PreKeyBundle.new() validates the signature; it throws on invalid input
const prekeyBundle = SignalClient.PreKeyBundle.new(
..., kyberKeyPair.getPublicKey(), kyberSig
)- Signature algorithm: Ed25519 (via libsignal native Rust binding)
- Signature length: 64 bytes
- Signing key: local identity private key (same key used for SignedPreKey sigs)
- Signed message:
kyberKeyPair.getPublicKey().serialize()bytes
tests/e2ee/kyber-signature.test.ts β four test cases:
- Signature is non-zero (regression against dummy
new Uint8Array(64)) - Signature length is 64 bytes (Ed25519 spec)
- Signature verifies under the identity public key (round-trip)
-
processPreKeyBundleconstructs a bundle without throwing (integration proof)
InMemoryKyberPreKeyStore (in signal-client.ts) implements the full Signal Protocol
KyberPreKeyStore interface:
| Method | Behaviour |
|---|---|
saveKyberPreKey(id, record) |
Stores record in memory |
getKyberPreKey(id) |
Returns record or throws if not found |
markKyberPreKeyUsed(id, signedPreKeyId, baseKey) |
Removes key (one-time semantics) |
markKyberPreKeyUsed accepts three arguments per the libsignal abstract class definition
(kyberPreKeyId, signedPreKeyId, baseKey). The prior stub only accepted one argument
and would have caused a runtime mismatch; S09 fixed the signature.
Backwards-compat: decryptMessage falls back to a fresh InMemoryKyberPreKeyStore when
the caller does not supply one. Existing sessions using non-Kyber prekeys continue to work.
src/lib/graphql/admin-client.ts implements a three-layer production guard:
-
Build-time (
next.config.js): Throws at build start ifNODE_ENV=productionandHASURA_ADMIN_SECRETis unset. Output contains[BUILD GATE FIRED]. -
Runtime β production:
validateEnvironment()throwsFATAL: HASURA_ADMIN_SECRET environment variable must be setbefore any Apollo client is constructed. -
Dev-mode guard:
SKIP_ENV_VALIDATION=trueis only accepted whenNODE_ENV !== 'production'andNEXT_PUBLIC_ALLOW_DUMMY_ADMIN_SECRET=true. Any attempt to useSKIP_ENV_VALIDATION=truein a production build is rejected with aCRITICALerror.
The CI workflow (ci.yml) includes a lint step that fails if any file under
src/lib/e2ee/ or src/lib/graphql/ (excluding test files) contains a bare
new Uint8Array(N) // pattern. This catches future accidental zero-buffer sentinels.
| Primitive | Library | Purpose |
|---|---|---|
| X3DH | libsignal-client | Key agreement (session initiation) |
| Double Ratchet | libsignal-client | Forward-secret message encryption |
| Kyber-768 (ML-KEM) | libsignal-client | Post-quantum KEM in prekey bundle |
| Ed25519 | libsignal-client | Identity key signatures, prekey signatures |
| AES-256-GCM | Web Crypto API | Master key encryption, attachment encryption |
| HKDF-SHA256 | libsignal-client | Key derivation |
- E2EE-Implementation-Summary β original implementation notes (v0.4.0)
- E2EE-Security-Audit β security audit findings
- E2EE-THREAT-MODEL β threat model
-
β vulnerability reporting policy
- Home
nself-chat v0.3.0 | GitHub | Issues | Discussions | Demo
Edit this page | MIT License | Β© 2026
(See π Security section below for 2FA, PIN Lock, and security audits.)
(Search lives in π Reference below.)
- π¬ Advanced Messaging
- π E2EE Setup
- π Search Setup
- π Call Management
- πΊ Live Streaming
- π₯οΈ Screen Sharing
- πΉ Video Calling
- ποΈ Voice Calling
- π± Mobile Optimization
- π§ͺ Testing
- π i18n
- π API Overview
- π Complete Reference
- π» API Examples
- π€ Bot API
- π Auth API
- π GraphQL Schema
- π Deployment Overview
- π³ Docker
- βΈοΈ Kubernetes
- β Helm Charts
- β Production Checklist
- π Production Validation
- π’ Multi-Tenant
- ποΈ Architecture
- π Diagrams
- ποΈ Database Schema
- π Project Structure
- π TypeScript Types
- π SPORT Reference
- π 2FA
- π¬ Messaging
- π Call Management
- π Call State Machine
- π E2EE
- πΊ Live Streaming
- π± Mobile Calls
- π PIN Lock
- π Polls
- π₯οΈ Screen Sharing
- π Search
- π Social Media
- ποΈ Voice Calling
- π Security Overview
- π‘οΈ Security Audit
- β‘ Performance
- π Best Practices
- π 2FA
- π PIN Lock
- π E2EE
- π‘οΈ E2EE Audit
v1.0.0 β’ 2026