Home

Awesome

caesium

caesium spectral lines

Clojars Project

Build Status codecov Dependencies Status

caesium is a modern cryptography library for Clojure. It is a direct jnr-ffi binding to libsodium, which in turn is a more convenient fork of the original NaCl library by djb.

NOTE: Install libsodium 1.0.18+ before trying to use caesium.

Minimum viable snippet

Here's a sample of how you can use secretbox:

(ns minimum-viable-secretbox
  (:require [caesium.crypto.secretbox :as sb]))

(def key (sb/new-key!))
(def plaintext "Hello caesium!")
(def nonce (sb/int->nonce 0))
(def ciphertext (sb/encrypt key nonce (.getBytes plaintext)))
(def roundtrip (String. (sb/decrypt key nonce ciphertext)))
(assert (= plaintext roundtrip))

Documentation

The most important documentation for caesium is actually the documentation for libsodium. Since it's all just relatively small wrappers around that, everything in it applies.

Password hashing

Here's an example of how you can use pwhash:

(ns pwhash-usage
  (:require [caesium.crypto.pwhash :as pwhash]
            [caesium.randombytes :as rb]
            [caesium.byte-bufs :as bb]
            [caesium.util :as u]
            [caesium.crypto.secretbox :as sb]))

;; helper function for creating salts from integers. may be useful for deterministic
;; key derivation, incrementing subkeys from 0.
(def int->salt (partial u/n->bytes pwhash/saltbytes))

;; hashing passwords
(def password "example")
(def hashed-password (pwhash/pwhash-str password 
                                        pwhash/opslimit-sensitive
                                        pwhash/memlimit-sensitive))
(assert (= 0 (pwhash/pwhash-str-verify hashed-password password)))

;; key derivation
(def salt (rb/randombytes pwhash/saltbytes)) ; changing salt means changed derived key
(def derived-key (pwhash/pwhash sb/keybytes
                                password
                                salt
                                pwhash/opslimit-sensitive
                                pwhash/memlimit-sensitive
                                pwhash/alg-default))
(def message (.getBytes "hello, world!"))
(def encrypted-message (sb/encrypt derived-key (sb/int->nonce 0) message))
(def decrypted-message (sb/decrypt derived-key (sb/int->nonce 0) encrypted-message))
(assert (bb/bytes= message decrypted-message))

Usage with Github Actions secrets

Here is how you can create or update a repository secret for GitHub actions:

(require '[caesium.crypto.box])
(require '[clj-http.client :as http])
(require '[jsonista.core :as json])
(import '(java.util Base64))

(def public-key
  "The public key of the repository of which you want to create or update a secret"
  (let [payload (-> {:request-method :get
                     :url "https://api.github.com/repos/{owner}/{repo}/actions/secrets/public-key"
                     :basic-auth ["{user}" "{GITHUB_TOKEN}"]
                     :headers {"Content-Type" "application/json"
                               "Accept" "application/vnd.github.v3+json"}}
                    http/request
                    :body
                    json/read-value)
        ^String encoded-key (get payload "key")]
    {:decoded-key (.decode (Base64/getDecoder) (.getBytes encoded-key))
     :key-id (get payload "key_id")}))

(let [{:keys [^String decoded-key ^String key-id]} public-key
      plaintext "MY_SECRET_VALUE"
      cyphertext (caesium.crypto.box/box-seal
                   (byte-streams/to-byte-array plaintext)
                   decoded-key)]
  (http/request
    {:request-method :put
     :url "https://api.github.com/repos/{owner}/{repo}/actions/secrets/{MY_SECRET}"
     :body (json/write-value-as-string
             {:encrypted_value (.encodeToString (Base64/getEncoder) cyphertext)
              :key_id key-id})
     :basic-auth ["{user}" "{GITHUB_TOKEN}"]
     :headers {"Content-Type" "application/json"
               "Accept" "application/vnd.github.v3+json"}}))

Differences with other bindings

Instead of making specific claims about specific libraries which may become outdated, here are a few properties you may care about:

caesium tries to just give you the libsodium experience from Clojure. C pseudo-namespaces are mapped to real Clojure namespaces. It usually maps fns to predictable names; sodium_crypto_secretbox_open_easy will be called caesium.crypto.secretbox/open-easy. Formally: take the C pseudo-namespace, turn it into a real namespace, replace the leading sodium with caesium, replace underscores with dashes. Exceptions where this doesn't work out:

Compatibility

caesium uses semver.

Since this is a security-sensitive library, I will actively remove functions or APIs that have serious security problems, instead of simply documenting the problem.

License

Copyright © the caesium authors (see AUTHORS)

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.