Skip to content

fix: a fresh AES-GCM nonce for every message - #4

Open
NiKrause wants to merge 4 commits into
orbitdb:mainfrom
NiKrause:fix/nonce-per-message
Open

fix: a fresh AES-GCM nonce for every message#4
NiKrause wants to merge 4 commits into
orbitdb:mainfrom
NiKrause:fix/nonce-per-message

Conversation

@NiKrause

Copy link
Copy Markdown

Fixes #3.

encrypt() derived the nonce inside deriveEncryptionKey, so one nonce covered every message until the ivInterval rotation — up to 32 000 under the same key. Measured on 0.0.2, four consecutive calls returned the identical IV.

A repeated (key, nonce) in AES-GCM discloses the XOR of the plaintexts and allows the GHASH authentication subkey to be recovered, so entries become forgeable as well as readable. The rotation does not bound this: one repeat is enough.

This change

  • A fresh nonce per encrypt(). The PBKDF2 key and salt stay cached — the derivation is the expensive part, getRandomValues is not. Salt rotation at ivInterval is unchanged.
  • decrypt() now caches the derived key against the salt rather than the nonce. Keying it on the nonce was harmless only while the nonce never changed; with a nonce per message it would re-run PBKDF2 (32 767 iterations) for every entry.

Wire format is unchanged: salt ‖ nonce ‖ ciphertext. Data written by 0.0.2 still decrypts.

Tests

test/iv.test.js asserted rotation at the interval, which can no longer be what is interesting. It now asserts that 2000 consecutive messages carry 2000 distinct nonces, that every message round-trips, and that the salt still rotates at ivInterval.

npx mocha test/index.test.js test/iv.test.js → 24 passing. test/orbitdb.test.js → 10 passing.

🤖 Generated with Claude Code

NiKrause and others added 4 commits November 29, 2025 14:39
…tabases

Problem:
- When opening an encrypted OrbitDB database without encryption options,
  OrbitDB doesn't throw errors or emit error events
- This makes it difficult to detect if a database is encrypted and needs
  a password, especially when opening databases via URL

Solution:
- Added isDatabaseEncrypted() function that detects encrypted databases
  by checking if entries exist but their values are undefined
- When opening encrypted DB without encryption: entries have hashes but
  values are undefined (encrypted data cannot be read)
- When opening unencrypted DB: entries have hashes AND readable values
- This enables applications to detect encrypted databases and prompt for
  password when needed

Changes:
- Added isDatabaseEncrypted() function to src/index.js
- Added comprehensive tests verifying the detection logic
- Test covers encrypted, unencrypted, and empty database scenarios
The nonce was derived alongside the key and reused for up to ivInterval
(32000) messages. In AES-GCM a repeated (key, nonce) pair discloses the
XOR of the plaintexts and allows the GHASH authentication subkey to be
recovered, so ciphertexts become forgeable as well as readable. The
rotation does not bound this — a single repeat is sufficient.

Generate the nonce per encrypt() call and keep caching the PBKDF2 key and
its salt, which is where the cost actually is. Salt rotation at ivInterval
is unchanged.

decrypt() cached the derived key against the nonce, which was harmless
only while the nonce never changed; with a nonce per message it would
re-run PBKDF2 for every entry. Keyed on the salt now, which is what the
derivation depends on.

test/iv.test.js asserted rotation at the interval; it now asserts that no
nonce repeats, that every message round-trips, and that the salt still
rotates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
NiKrause added a commit to NiKrause/simple-encryption that referenced this pull request Aug 29, 2026
NiKrause added a commit to NiKrause/simple-encryption that referenced this pull request Aug 29, 2026
Republishes with orbitdb#4 applied. 0.0.2 reused one
nonce for up to 32000 messages under the same key, which in AES-GCM makes
entries readable and forgeable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AES-GCM nonce is reused for up to 32000 messages under the same key

1 participant