Skip to content

Security

sputlab edited this page Nov 13, 2026 · 1 revision

Security

Status: v0.12.3, not independently audited. Only standard JCA/JCE primitives are used (no custom cryptography, no third-party crypto libraries). Nobody outside the project has reviewed the design. Do not rely on it where lives or liberty depend on it.

The authoritative, always-current document is docs/SECURITY.md in the repository; this page is a summary.

What is protected

Property How
Confidentiality between two nodes, even across relays AES-256-GCM with a key only the endpoints can derive
Forward secrecy ephemeral ECDH; keys live in memory only and are wiped when the session ends
Key rotation new handshake after 1 hour or 500 messages, without interrupting traffic
Integrity and authenticity GCM tag; sender, recipient, type, message ID and session ID are in the AAD
Delivery receipts cannot be forged ACKs are sealed with the same session
HELLO cannot be forged or replayed sealed, with an increasing sequence number
Relays cannot read or alter traffic they hold no key for what they carry
A verified contact cannot be impersonated its full key is pinned from the QR scan
Group messages one pairwise-sealed copy per member, authenticated per author
At rest text encrypted with an Android Keystore AES-256-GCM key; identity private key wrapped by it; allowBackup=false

Handshake

  1. Each device has a P-256 key pair used for signing only; the node ID is the first 8 bytes of SHA-256(public key).
  2. Two KEY packets, a signed ephemeral Diffie-Hellman:
    • A -> B: IA, EA, Sign_IA("req" | idA | idB | EA)
    • B -> A: IB, EB, Sign_IB("resp" | idB | idA | EB | EA)
  3. Each side checks that the point is on the curve, that SHA-256 of the identity key matches the node ID, and the signature.
  4. Session keys: HKDF-SHA256(ECDH(ea, EB)) bound to both IDs and both ephemeral keys, expanded into one key per direction plus a 32-bit session id. Ephemeral private keys are dropped immediately.
  5. The initiator adopts the session at once; the responder only after it decrypts a message under it, so a replayed request cannot hijack outgoing traffic. Retired sessions keep decrypting for two minutes; every session ends after two hours at most. Requests from one peer are accepted at most every 2 s, with at most 6 sessions per peer.
  6. Stopping the radio wipes every session key.

Verified contacts

Scanning someone's QR code stores their key as verified and pins it in SessionManager. Verification is one-way. A code with a key not on the curve, or whose node ID collides with a different stored key, is refused.

Groups

Random 64-bit group ID, name (max 24 bytes), up to 8 members, one admin. No group key. The receiver enforces: invitations are pending until accepted; a message counts only if the group is joined and its authenticated sender is a member; only the admin can change name or members; a node left out of the admin's new list deletes the group.

Known limitations (read these)

  • Forward secrecy is best effort on the device: the JVM may leave key copies in memory. It protects against later theft of stored keys, not a memory dump of a running app.
  • Chat history is not forward secret: it is readable by anything that can use the Keystore key (an unlocked, compromised phone). Use CLEAR.
  • Metadata is visible to anyone in Bluetooth range: node IDs and names (advertised), packet type, sender/recipient IDs, sizes, timing.
  • Node IDs are 64 bits: impersonating a specific node needs about 2^64 work, out of reach for casual attackers but not a well-funded one. Verify in person for high-stakes use.
  • First contact is trusted until you scan the contact's QR code.
  • A stolen long-term key allows impersonation from then on (not decrypting the past). There is no revocation.
  • Denial of service (jamming, flooding) is not prevented. DATA_REFUSE is unauthenticated and can be forged to stop a transfer.
  • Groups trust their admin and members: a removed member keeps what it received; a malicious member can send different texts to different members; no common ordering.
  • Positions received are stored in clear in the app database; anyone who receives a position can keep and pass it on; the timestamp is the sender's clock.
  • Wi-Fi Direct: the group name DIRECT-NW-xxxx is visible nearby and shows NodeWave is in use; an allowed peer can make your phone join and leave Wi-Fi networks. Photos have EXIF removed but pixels are sent.
  • A compromised phone exposes everything the app can read.
  • Release builds are debug-signed for now; verify what you install.

Reporting a problem

Open an issue, or contact the maintainers privately for anything that looks exploitable.

Clone this wiki locally