Skip to main content

Security

Security Architecture

This page documents how Inkrypt encrypts notes, written against the code that actually ships rather than a general description of good practice. It names every parameter, states exactly what our database holds, and describes the weaknesses of this design as directly as its strengths.

Last reviewed Reviewed by Inkrypt Security Team

Design goal

Inkrypt is built around a single constraint: our servers must never be able to read a note's contents, even under compulsion, even if fully breached, even if we wanted to. Everything else in the design follows from that, including the features we cannot offer.

The way to achieve that is not policy but arithmetic. If the decryption key never exists on our infrastructure, then reading your note is not a matter of access control — it is a matter of breaking AES-256, which nobody can do.

Cryptographic parameters

These are the values currently in use. They are recorded here so the documentation can be checked against behaviour, and so any future change is visible on the changelog.

CipherAES-256-GCM
Key length256 bits
Authentication tag128 bits
IV length96 bits (12 bytes)
IV generationCryptographically random, fresh per operation
Key derivation functionPBKDF2-HMAC-SHA256
KDF iterations310,000
Salt length128 bits (16 bytes)
Salt generationCryptographically random, fresh per operation
Key extractablefalse (non-extractable CryptoKey)
ImplementationWeb Crypto API — crypto.subtle
Execution locationThe user's browser

Why these values

310,000 PBKDF2-HMAC-SHA256 iterations is the OWASP Password Storage Cheat Sheet minimum. 12-byte IVs are the standard GCM construction, which avoids the additional GHASH derivation step other lengths require. 16-byte salts exceed the 8-byte minimum in RFC 8018 with negligible cost.

Key derivation

The password you type is never used directly as a key. It is stretched through PBKDF2-HMAC-SHA256 with a fresh 16-byte random salt and 310,000 iterations, producing a 256-bit AES-GCM key.

The derived key is created as a non-extractable CryptoKey. The browser's crypto layer will use it to encrypt and decrypt, but will not export the raw bytes back into JavaScript — including to our own code. That closes an entire category of accidental key leakage through logging or error reporting.

The iteration count is stored alongside each note as a label such as pbkdf2-310000, and decryption uses the parameters the note was created under. This means the count can be raised for new notes without stranding older ones — the mechanism for a future migration is already in place.

Encryption flow

  1. Generate randomness

    A 16-byte salt and a 12-byte IV are generated from the browser's cryptographically secure random number generator. Both are fresh for every single encryption, including every re-save of an existing note.
  2. Derive the key

    PBKDF2-HMAC-SHA256, 310,000 iterations, using the password and the new salt. Produces a 256-bit non-extractable AES-GCM key.
  3. Encrypt

    AES-256-GCM encrypts the UTF-8 encoded note using the derived key and the new IV, producing ciphertext and a 128-bit authentication tag.
  4. Encode and transmit

    Ciphertext, salt and IV are base64-encoded and sent to the server over TLS, together with the KDF parameter label. The password and the plaintext are not part of this request.
  5. Store

    The server writes those four values. It performs no cryptographic operation on the note and holds nothing that could decrypt it.

What the server stores

Per note, exactly four values relating to content. None of them, alone or together, permits decryption.

The complete set of content-related fields stored per note.
FieldContentsSecret?
ciphertextBase64 AES-256-GCM output including auth tagUnreadable without the key
saltBase64, 16 random bytesNo — not secret by design
ivBase64, 12 random bytesNo — not secret by design
kdfParameter label, e.g. pbkdf2-310000No
The complete set of content-related fields stored per note.

Salts and IVs are not secret in any correctly designed system — they exist to guarantee uniqueness, not confidentiality, and both must be available to reproduce decryption. Their security value comes entirely from being fresh and random, which they are.

Alongside these, the server holds operational metadata: the note's slug (which is its URL and therefore never private), creation time, expiry configuration, and a view counter where limits are set. What the server does not hold is any password, any password hash, any derived key, or any plaintext.

The slug is not encrypted

A note's name becomes its URL and is stored in the clear. URLs appear in browser history, server logs, referrer headers and link previews. Never put sensitive detail in a note name.

Transport and application security

  • TLS everywhere. All traffic is HTTPS. HTTP and non-canonical hostnames are 301-redirected to the canonical origin, and HSTS is set with a one-year max-age and includeSubDomains.
  • Content Security Policy, X-Frame-Options: DENY and frame-ancestors 'none' to prevent the interface being embedded and clickjacked.
  • `X-Content-Type-Options: nosniff` to prevent MIME confusion.
  • `Referrer-Policy: strict-origin-when-cross-origin`, so full note URLs are not leaked to third parties in referrer headers.
  • Permissions-Policy disabling camera, microphone, geolocation and interest-cohort.
  • No ads and no indexing on note or share pages. Advertising scripts are loaded only on editorial content pages; the note editor and share gate carry neither ads nor an index directive.

Known limitations

A security page that lists only strengths is marketing. These are the real constraints of this design, in rough order of how much they should affect your decision.

We serve the code that encrypts

This is the fundamental limitation of all browser-delivered cryptography. Your browser fetches our JavaScript on every visit and runs it. A compromised or malicious operator could serve a modified script that captures passwords before encryption. No amount of correct cryptography inside the page defends against the page itself being replaced.

Installed applications with signed, independently verifiable builds are genuinely stronger here. If your adversary is capable of compelling or compromising a service provider at the delivery layer, no browser-based tool — including this one — is the right choice.

Password strength is the binding constraint

PBKDF2 at 310,000 iterations makes each guess expensive; it does not make a weak password strong. An attacker holding exfiltrated ciphertext can attack it offline at their own pace. A note protected by a dictionary word is protected by a dictionary word, whatever the cipher.

PBKDF2 is not memory-hard

Argon2id resists GPU and ASIC attacks better because it demands memory as well as time. We use PBKDF2 because it is the only password-based KDF exposed natively by the Web Crypto API across all browsers we support, and a native constant-time primitive is preferable to a JavaScript Argon2 for the threats most users face. This is a considered trade-off, not an oversight.

Endpoint compromise defeats everything

Malware or a keylogger on your device observes plaintext as you type, before any encryption occurs. Client-side encryption cannot defend below its own layer.

No independent audit

Inkrypt has not been through a third-party cryptographic audit. The algorithms and parameters are standard and documented here so they can be evaluated, but you should weigh the absence of external review. We would rather state this than let the omission be inferred.

Reporting a problem

If you believe you have found a vulnerability, our responsible disclosure policy explains how to report it and what to expect. We would much rather hear from you than not.

Security FAQ

Can Inkrypt decrypt my notes if legally compelled?

No. We can be compelled to produce what we hold — ciphertext, a salt, an IV and a parameter label. We cannot be compelled to produce a key that has never existed on our systems. This is why the guarantee is architectural rather than a policy promise.

Why is the salt stored unencrypted?

Salts are not secret in any correct design. Their purpose is to make each derivation unique so precomputed tables are useless and cracking one note gives no advantage on another. Both salt and IV must be available to reproduce decryption.

Has Inkrypt been independently audited?

No. The algorithms and parameters are standard and fully documented on this page so they can be evaluated directly, but there has been no third-party cryptographic audit. You should factor that into your assessment.

Why PBKDF2 rather than Argon2id?

Argon2id is the stronger modern choice because it is memory-hard. PBKDF2 is the only password-based KDF the Web Crypto API exposes natively across all browsers we support, and a native constant-time primitive beats a JavaScript implementation for realistic threats. We track this as an open trade-off.

What happens if you raise the iteration count?

Nothing breaks. Each note stores the parameters it was created with, so existing notes continue to decrypt under their original settings while new notes use the new ones.

Do you log IP addresses?

Minimal technical logs exist for abuse prevention and operational health. They are never linked to note contents, which we cannot read, and there are no accounts to associate them with. See the privacy policy for detail.

Verify it yourself

Open your browser's developer tools, watch the Network tab, and save a note. The request body should contain ciphertext — not your text.

Open the notepad