Portable cryptography for Clojure and ClojureScript (browser + Node) — one API, native on each platform.
⚠️ Security status — read this first
v1 (
geheimnis.aes,geheimnis.rsa,geheimnis.md5) is deprecated and NOT safe for new use. It provides unauthenticated AES-CBC (no integrity — a tampered ciphertext is undetectable), textbook RSA (no padding — malleable, unsafe), and an empty MD5. Do not use these for anything security-sensitive.v2 (
org.replikativ.geheimnis.*) is a ground-up modern rewrite in progress — authenticated encryption (AES-256-GCM), Ed25519 signatures, X25519 key agreement, HMAC/HKDF, a CSPRNG-only randomness source, portable across JVM / browser / Node. Status below.
The full v2 surface — synchronous foundation, AES-256-GCM (AEAD), Ed25519 signatures, and X25519 key agreement — is landed and verified on JVM and Node against NIST/RFC vectors and JVM↔Node interop KATs.
| namespace | provides | sync? | status |
|---|---|---|---|
…geheimnis.core | random-bytes (CSPRNG), ct-equal? | sync | ✅ |
…geheimnis.codec | base64url / hex / utf8, byte-array utils | sync | ✅ |
…geheimnis.hash | hmac-sha256, hkdf; sha256/sha512 (via hasch) | sync | ✅ |
…geheimnis.aead | AES-256-GCM authenticated encryption | async | ✅ |
…geheimnis.sign | Ed25519 sign / verify | async | ✅ |
…geheimnis.dh | X25519 key agreement | async | ✅ |
Design principle — sync where possible, async only where forced. Hashing,
HMAC and HKDF are synchronous on both platforms (goog.crypt / java.security),
so token verification (HS256) needs no async. Only AEAD and the elliptic curves
use the async tier (Web Crypto on CLJS, java.security on the JVM), returning
core.async channels.
Randomness is CSPRNG-only — SecureRandom on the JVM,
crypto.getRandomValues in the browser/Node; it throws loudly rather than fall
back to Math.random.
Raw SHA digests live in hasch (the content-hashing library) and are re-exported here — geheimnis owns the keyed / secret crypto, hasch owns unkeyed content hashing.
;; deps.edn
org.replikativ/geheimnis {:mvn/version "…"}
(require '[org.replikativ.geheimnis.core :as g]
'[org.replikativ.geheimnis.hash :as h]
'[org.replikativ.geheimnis.codec :as codec])
(g/random-bytes 32) ; 32 CSPRNG bytes
(h/hmac-sha256 key msg) ; HMAC-SHA-256
(h/hkdf ikm salt info 32) ; derive 32 bytes (RFC 5869)
(codec/bytes->b64url some-bytes) ; unpadded base64url
A byte value is a JVM byte[] / CLJS Uint8Array; the codec helpers convert.
# JVM
clj -M:test -e "(require 'org.replikativ.geheimnis.v2-hash-test) \
(clojure.test/run-tests 'org.replikativ.geheimnis.v2-hash-test)"
# CLJS on Node
npx shadow-cljs compile node-test && node target/node-test.js
# format
clj -M:format # check · clj -M:ffix # fix
Copyright © 2016-2026 Christian Weilbach, Konrad Kühne
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this software except in compliance with the License. You may obtain a copy of
the License at http://www.apache.org/licenses/LICENSE-2.0. See the LICENSE and
NOTICE files for details.
Can you improve this documentation? These fine people already did:
Christian Weilbach & Konrad KühneEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |