check the implementation

Inspect the code. Verify.

The verification tools help your team check encryption behaviour and compare downloaded code with published fingerprints. Run the browser checks below, then compare the results with the public source and reference examples.

↓
run the checks

Check what the software does.

One encrypted file (ciphertext), both passwords together and two control files that select the real message or a chosen fake (decoy). Run the examples locally, then compare downloaded code with its published fingerprint.

This page checks specific implementation behaviours and reference examples. It is not a third-party cryptographic audit. It does not prove a decoy is contextually plausible. It does not add primitive-layer authentication. The independent crypto audit remains on the roadmap.
A

Deniable round-trip

Encrypt a real secret, derive a decoy control file for a different plaintext against the same ciphertext and same password pair, then decrypt both. Password 1 and password 2 are one password pair. They do not select real vs decoy; the control file does.

Receipt appears here. Nothing leaves your browser.

B

Honey Mode reference checks

Known-answer tests (KATs) compare results with fixed examples. These cover seed derivation, the deterministic random-byte generator (DRBG), integer sampling, decoy generation and format checks. Your browser re-derives each value and compares byte-for-byte. Passing checks these examples; it is not a security proof or independent audit.

loading published vectors…

Pick a group and replay. Any mismatch is red.

D

Asset integrity self-check

Fetch the live manifest, hash each listed asset with SHA-384 in your browser, and compare to the manifest. The manifest is the source of truth; the static table below is explanatory only. A dot (●) marks assets currently executing on this page. If an attacker controls both the served asset and the manifest, this check cannot save you; reproducible build and signed release address that part of the chain.

manifest not yet fetched.

Hashes the downloaded files with SHA-384, a fingerprint function.

Run the tests yourself.

Every test executes in your browser using the same cryptographic engine that powers the API. Pick a seed phrase or use the random one.

0 / 22

Type your own seed phrase or use the random one. Every test runs live with your input. Nothing is pre-recorded.

running with:

Everything runs client-side. Nothing leaves your browser.

01

Statistical Indistinguishability

5 tests ▼
These statistical checks look for selected patterns in generated samples. Passing them does not prove that no method or external evidence can distinguish a decoy.
• Real vs deniable comparison entropy gap measurement

Byte value distribution: random data (blue) vs deniable control file (green). They should look the same.

• Chi-squared test byte frequency uniformity
• Shannon entropy information density per byte
• Kolmogorov-Smirnov test cumulative distribution fit
• Serial correlation adjacent byte patterns
02

Ciphertext Invariance

2 tests ▼
The encrypted blob never changes. Creating a decoy is a pure mathematical operation on the control data, not on the ciphertext.
• Ciphertext unchanged across deniable decryptions 5 variants, same blob
• No bit mutation during deny operations 20 iterations, zero drift
03

Length Independence

2 tests ▼
The length prefix is encrypted, but the overall file size remains visible. Size padding can reduce, not eliminate, length information.
• Different length messages from same ciphertext short + medium decoys
• Length prefix inside encrypted zone header + 4B + plaintext
04

Cross-implementation

1 test ▼
The browser engine produces identical output across all input types, including unicode.
• encrypt/decrypt round-trip incl. unicode + edge cases
05

Known-Answer Tests

3 tests ▼
Key derivation is deterministic and sensitive to every input: password, salt, and ordering.
• Key derivation is deterministic Argon2id reproducibility
• Different password pairs produce different results 3 password combos
• Password order matters pw1/pw2 not swappable
06

Fuzz Testing

2 tests ▼
Random messages, random passwords, random sizes. Every round: encrypt, decrypt, deny, verify. Plus all 256 single-byte values.
• Random input fuzz test random sizes + passwords
• Single-byte edge cases (0x00-0xFF) boundary values
07

Security Properties

5 tests ▼
Non-deterministic encryption, no error oracles, uncorrelated control files. Useful checks, not a complete security assessment.
• 10 encryptions = 10 unique ciphertexts random salt each time
• Wrong password produces garbage, not error no oracle leak
• Wrong control file = different message no accidental reveal
• Ciphertext entropy on repetitive plaintext 500 bytes of 'A'
• Control files uncorrelated XOR entropy check
08

Multiple Deniable Messages

2 tests ▼
One ciphertext. One pair of passwords. 20 different control files. 20 different decrypted messages. The original still works after all 20.
• 20 deniable messages from 1 ciphertext one encrypted file, several messages
• Original message still intact real secret survives
// supply-chain integrity

Source and download integrity.

The 22 tests above run inside your browser. The bundle that runs them is delivered from this server. If a sophisticated attacker compromises the delivery path, the tests still pass, but they pass on whatever JavaScript the attacker injected, not on the published source. Compare against an independently obtained trusted fingerprint, and check the available source and release evidence.

What's served right now.

The manifest lists file fingerprints for supported assets. Subresource Integrity (SRI) lets a browser compare a script or stylesheet with an expected fingerprint. These are the SHA-384 digests of the exact bytes our server is sending you. Recompute them locally, paste them into your integrity attribute, and your browser will refuse to execute any byte that doesn't match. This table is generated from the live manifest; for an authoritative, self-running check, use Proof D : Asset integrity self-check above, which hashes the served bytes in your browser and compares them to the manifest.

Asset Bytes SRI hash (sha384)
/js/argon2.js?v=75 216,086 sha384-6bLcbLILNvgBcX8TYQUYYOoePP/iXTwteWiGSloiTYvujkovkjXOM/ybjXgi9kmr
/js/crypto.js?v=79 16,058 sha384-9267+vM5ve4CM9j/SE70FDGIGtFTxgxd0LHcgzCHnV/MGGdtW/nHVjJYsnAqoKOv
/js/honey-engine.js?v=3 43,587 sha384-b/d4mz+H84rvFpuQdUAxl1+8L1FQampUlh3l2S9G8eL1CjXVKZoZhFzqzypF+UL8
/js/verify.js?v=75 50,009 sha384-5epvYgDfZGTHltCDtZtg2JJ03SgPCCt4e21NM1+gdkT4zi9KrMzLKKZ0B+nFzY5u
/js/verify-proof.js?v=3 24,318 sha384-sxzjQ1YJG/lPQBFWpWHJxPJ8wx/Y/InXR41tz7rvedc/voUowtwCqePvODV/iudO
/js/nav.js?v=77 1,652 sha384-BV81ynd3Lsr25LspYx1P9J+PcUjCi3PhBFYVUbUMY5VlqfHPlkIs9U36U3TCr/eL
/js/copy-code.js?v=76 997 sha384-opZldN672nncovrxNEDmQZCeaLHjrQVKVrgLiYOi2YA1jklqTk3jNqQXeONs6ISk
/js/back-to-top.js?v=74 654 sha384-GbBBXsssarGIR/ST7jZSFpjfdOD0M/czgfNyxspI9OOrDc+oBScVBg7W4hz/cdRY
/js/app.js?v=78 15,175 sha384-z/VYuwh6NjFd5UNsIHtMbgo4gO1br0AI+lVO5nRbRDQp9NpN575wAZ19yCkxNx6h
/css/style.css?v=210 146,910 sha384-NsWdQmf0ul3F/JOwHgnX5OQx1isE0tTILtswMnDs7PbRUeyxMs0mcgIXtO44FmPi
/css/v2.css?v=278 105,107 sha384-jHK68bO5e1fB5bBW6cZtHKJJPMtcU80QoDQtC8BFTQDNGV8hUdCiv5Dlg1qfgqwv

Recompute any of these locally with one command:

# pin verify.js by recomputing its hash and comparing
curl -sf https://deny.sh/js/verify.js?v=75 \
  | openssl dgst -sha384 -binary \
  | base64

# compare against the table above. mismatch = do not trust the bundle.

A live SRI manifest, machine-readable, is published at /.well-known/integrity.json and updated on every deploy. Pin against that if you're integrating deny.sh assets into another origin.

Reproduce the build.

Verification scope. Compare the source, served files and integrity manifest using the checks below. This page does not establish that an independent cryptographic audit is complete. Consult the linked release artefacts for their publication and signing status.

From the open beta onward, the published artefact (the JS bundle that powers the in-browser verifier and the SDK on npm) is built from a tagged commit on deny-sh-crypto/deny-js with a single deterministic command. Same source + same Node version + same dependency lockfile = byte-identical output. A mismatch needs investigation: source, dependencies, tooling and build configuration can all change the output.

# 1. clone the public source at the released tag
git clone https://github.com/deny-sh-crypto/deny-js
cd deny-js
git checkout v2.3.1

# 2. pin Node and reinstall from lockfile (no transitive surprises)
nvm use 22.22.2
npm ci

# 3. build the published bundle
npm run build:web

# 4. hash the bundled crypto.js the same way the server does
cat dist/web/crypto.js | openssl dgst -sha384 -binary | base64

# expect: OLz8xZ6aeZasfCql1pvERSZ2ijXb5SEFl+T4AMRoANaKVeMWym2Xc5IHfaV1IQ7w

Determinism details. The build pins Node v22.22.2 (LTS), uses npm ci against a committed package-lock.json, and disables timestamp-based dependency invalidation. The bundler (esbuild v0.25 in --minify --no-bundle-time-comment mode) writes byte-deterministic output for the same input. We don't ship sourcemap timestamps, build hostnames, or compile-time strings into the bundle.

How publishing and signing work.

Check the available evidence on each release surface:

  • npm registry. npm publish from a workstation that holds the publishing token. npm computes its own SHA-512 integrity field and stores it in the package metadata. npm install deny-sh@2.3.1 --integrity verifies on install. The published package.json includes the repository.url pointing at the public source mirror, so you can diff what npm shipped against what GitHub holds.
  • GitHub release. Check the release for its commit, tag and signing status. The release page on deny-sh-crypto/deny-js/releases may provide source archives and a SHA256SUMS file. Where a GPG signature is published, verify it with gpg --verify SHA256SUMS.sig SHA256SUMS.
  • This server. Compare the web bundle on the deny.sh origin with the relevant published source and release artefacts. The SRI manifest at /.well-known/integrity.json is regenerated on every deploy. A difference requires investigation; check which assets, commits and release artefacts are being compared.

Coordinated disclosure for any integrity issue (mismatched hash, unsigned release, source-vs-published divergence) goes to security@deny.sh per the disclosure policy.

Honest caveat. Reproducible builds protect against tampering between source and your browser. They don't protect against bugs in the source itself; that's what the in-browser test suite, the cross-language byte-compatibility KAT vectors in the four SDKs, the cryptographic construction write-up, and the external cryptographic audit on the roadmap are for. Different layers of defence; check all of them.

Ready to evaluate?

Understand the limits, then try the browser tool with non-sensitive sample data.

Encrypt something Get an API key
Limits and scope

What to know before production.

The tools above check implementation behaviour and asset fingerprints. They are not a security proof or an independent audit. An independent cryptographic audit is planned and is not complete.

Review evidence and current status ↗ · Threat model