Liking cljdoc? Tell your friends :D

dk.simongray.like-minded.store.encrypted

Any document store, with its documents encrypted at rest.

A server or a synced folder then holds only ciphertext, and nothing else changes. Names and versions pass through, and bodies are sealed on the way in and opened on the way out.

A body is sealed with AES-256-GCM under a key derived from the passphrase by PBKDF2 with HMAC-SHA256. Each document is a small EDN envelope with the salt, IV and iteration count that open it, so there's no key file to lose:

{:encrypted 1 :cipher "AES-256-GCM" :kdf "PBKDF2-HMAC-SHA256"
 :iterations 600000 :salt "…" :iv "…" :data "…"}

A body that isn't an envelope comes back as it is, so you can turn on encryption over a folder that already holds plain documents. A passphrase that doesn't open a document makes that call of the store fail with ::locked.

Since WebCrypto is asynchronous, the functions here and those of the store return promises in ClojureScript. Each platform opens the envelopes of the other.

Any document store, with its documents encrypted at rest.

A server or a synced folder then holds only ciphertext, and nothing else
changes. Names and versions pass through, and bodies are sealed on the
way in and opened on the way out.

A body is sealed with AES-256-GCM under a key derived from the passphrase
by PBKDF2 with HMAC-SHA256. Each document is a small EDN envelope with
the salt, IV and iteration count that open it, so there's no key file to
lose:

    {:encrypted 1 :cipher "AES-256-GCM" :kdf "PBKDF2-HMAC-SHA256"
     :iterations 600000 :salt "…" :iv "…" :data "…"}

A body that isn't an envelope comes back as it is, so you can turn on
encryption over a folder that already holds plain documents. A
passphrase that doesn't open a document makes that call of the store
fail with ::locked.

Since WebCrypto is asynchronous, the functions here and those of the
store return promises in ClojureScript. Each platform opens the
envelopes of the other.
raw docstring

decryptclj/s

(decrypt s passphrase)

The body in the envelope string s under passphrase, or s itself when it isn't an envelope, in ClojureScript as a promise.

A passphrase that doesn't open it throws ::locked, or in ClojureScript, rejects the promise with it.

The body in the envelope string `s` under `passphrase`, or `s` itself
when it isn't an envelope, in ClojureScript as a promise.

A passphrase that doesn't open it throws ::locked, or in ClojureScript,
rejects the promise with it.
sourceraw docstring

default-iterationsclj/s

The PBKDF2 iterations of a key, unless the options give others.

It's what the OWASP Password Storage Cheat Sheet recommends for HMAC-SHA256 as of September 2026. On a phone, a key takes about half a second, once per salt.

The PBKDF2 iterations of a key, unless the options give others.

It's what the OWASP Password Storage Cheat Sheet recommends for
HMAC-SHA256 as of September 2026. On a phone, a key takes about half a
second, once per salt.
sourceraw docstring

encryptclj/s

(encrypt body passphrase)
(encrypt body
         passphrase
         {:keys [iterations] :or {iterations default-iterations} :as opts})

The envelope string for body under passphrase, with a fresh salt and IV and the :iterations of opts, in ClojureScript as a promise. The iterations default to default-iterations.

The envelope string for `body` under `passphrase`, with a fresh salt and
IV and the :iterations of `opts`, in ClojureScript as a promise. The
iterations default to default-iterations.
sourceraw docstring

envelopeclj/s

(envelope s)

The envelope in the string s, or nil for anything else, a plain document included.

An envelope is a map of :encrypted, :cipher, :kdf, :iterations, :salt, :iv and :data. The last three are in base64, and the iterations are at least one and at most max-iterations.

The envelope in the string `s`, or nil for anything else, a plain
document included.

An envelope is a map of :encrypted, :cipher, :kdf, :iterations, :salt,
:iv and :data. The last three are in base64, and the iterations are at
least one and at most max-iterations.
sourceraw docstring

max-iterationsclj/s

The most PBKDF2 iterations an envelope can ask for, ten times the default. A document that asks for more isn't taken for an envelope, since deriving its key would hold up a device for minutes.

The most PBKDF2 iterations an envelope can ask for, ten times the
default. A document that asks for more isn't taken for an envelope, since
deriving its key would hold up a device for minutes.
sourceraw docstring

storeclj/s

(store inner passphrase)
(store inner
       passphrase
       {:keys [iterations except]
        :or {iterations default-iterations except #{}}
        :as opts})

The store inner with its documents encrypted at rest under passphrase, with the opts below.

The passphrase is a string or a function of no arguments that returns one. The string can be a hidden text of dk.simongray.wary-fetch.secret. The opts are:

  • :iterations, the PBKDF2 iterations of the key, default-iterations by default
  • :except, the names of documents to leave in the clear, e.g. an extra document of a backend that other apps should read

A key is derived once for each salt and iteration count, and cached. Each store seals with a salt of its own, and opens any salt that a document has. It writes, lists and deletes only when inner does.

When the passphrase is a function, a document that the cached keys don't open empties the cache. The document is then tried once more with what the function gives, so that a corrected passphrase is used from then on.

The store `inner` with its documents encrypted at rest under
`passphrase`, with the `opts` below.

The `passphrase` is a string or a function of no arguments that returns
one. The string can be a hidden text of
dk.simongray.wary-fetch.secret. The `opts` are:

- :iterations, the PBKDF2 iterations of the key, default-iterations by
  default
- :except, the names of documents to leave in the clear, e.g.
  an extra document of a backend that other apps should read

A key is derived once for each salt and iteration count, and cached.
Each store seals with a salt of its own, and opens any salt that a
document has. It writes, lists and deletes only when `inner` does.

When the passphrase is a function, a document that the cached keys
don't open empties the cache. The document is then tried once more with
what the function gives, so that a corrected passphrase is used from
then on.
sourceraw docstring

versionclj/s

The version of the envelope.

The version of the envelope.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close