End-to-End Encryption (v4)

HeySummon uses Signal/Olm-inspired cryptography for true end-to-end encryption. The server never sees plaintext — it only relays encrypted blobs.

Key Generation

Generate two keypairs using the included crypto script:

node scripts/crypto.mjs keygen ~/.hitlaas

This creates 4 files:

FileAlgorithmPurpose
sign_public.pemEd25519Verify your message signatures
sign_private.pemEd25519Sign outgoing messages
encrypt_public.pemX25519Diffie-Hellman key exchange
encrypt_private.pemX25519Derive shared encryption secret

Keep *_private.pem files secret. Only public keys are shared.

How It Works

1. Submit Request

POST /api/v1/help

Send your Ed25519 sign and X25519 encrypt public keys with the request. The server stores them for the provider.

2. Key Exchange

When a provider picks up your request, they perform a key exchange:

POST /api/v1/key-exchange/:requestId

The provider sends their public keys. Both sides now have each other’s X25519 public keys.

3. Shared Secret (Diffie-Hellman)

Both consumer and provider independently compute the same shared secret using X25519 Diffie-Hellman:

sharedSecret = X25519(myPrivateKey, theirPublicKey)

This secret is never transmitted — both sides derive it locally.

4. Per-Message Encryption

Each message gets a unique AES key derived via HKDF:

messageKey = HKDF(sharedSecret, salt=messageId)

Messages are encrypted with AES-256-GCM and signed with Ed25519.

5. Message Format

{
  "encrypted": {
    "iv": "<base64>",
    "ciphertext": "<base64>",
    "tag": "<base64>",
    "ephemeralPublicKey": "<base64>"
  },
  "signature": "<base64>",
  "messageId": "<uuid>"
}

Encrypting a Message

node scripts/crypto.mjs encrypt \
  "Your message text" \
  /path/to/recipient/encrypt_public.pem \
  /path/to/your/sign_private.pem \
  /path/to/your/encrypt_private.pem \
  [messageId]

Outputs a JSON payload ready to POST to /api/v1/message/:requestId.

Decrypting a Message

node scripts/crypto.mjs decrypt \
  '<payload JSON>' \
  /path/to/sender/encrypt_public.pem \
  /path/to/sender/sign_public.pem \
  /path/to/your/encrypt_private.pem

Crypto Primitives

ComponentAlgorithmPurpose
Key ExchangeX25519 (Curve25519)Diffie-Hellman shared secret
SigningEd25519Message authentication & anti-tampering
EncryptionAES-256-GCMSymmetric message encryption
Key DerivationHKDF-SHA256Per-message unique keys from shared secret

Why Not RSA?

v1-v3 used RSA-OAEP + AES-GCM. v4 switched to X25519 DH because:

  • Both sides compute the shared secret independently — never transmitted
  • Smaller keys (32 bytes vs 2048+ bits)
  • Forward secrecy potential with ephemeral keys
  • Closer to Signal protocol design

Why Not Full Olm/Megolm?

Olm’s Double Ratchet, one-time prekeys, and multi-device support are overkill for HeySummon’s 72-hour conversation model. We took the best parts:

  • ✅ X25519 DH key exchange
  • ✅ Ed25519 signatures on every message
  • ✅ Per-message key derivation (HKDF)
  • ❌ Double Ratchet (unnecessary for short-lived sessions)
  • ❌ One-time prekeys (not needed without async messaging)