(require '[babashka.pods :as pods])
(pods/load-pod 'org.babashka/buddy "0.3.4")
(require '[clojure.string :as str]
'[pod.babashka.buddy.core.codecs :as codecs]
'[pod.babashka.buddy.core.mac :as mac]
'[pod.babashka.buddy.core.nonce :as nonce])
(def hash-algorithm :hmac+sha256)
(def secret (nonce/random-bytes 64))
(let [timestamp (System/currentTimeMillis)
nonce (nonce/random-bytes 64)
nonce-hex (codecs/bytes->hex nonce)
payload (pr-str {:nonce nonce-hex :timestamp timestamp})
signature (codecs/bytes->hex (mac/hash payload {:alg hash-algorithm :key secret}))]
(prn (str/join "-" [nonce-hex timestamp signature])))This pod uses the namespace scheme buddy.x -> pod.babashka.buddy.x.
For buddy's documentation, go here and
here for buddy-hashers.
Available namespaces:
pod.babashka.buddy.core.cryptopod.babashka.buddy.core.codecspod.babashka.buddy.core.hashpod.babashka.buddy.core.kdfpod.babashka.buddy.core.keyspod.babashka.buddy.core.macpod.babashka.buddy.core.noncepod.babashka.buddy.sign.jwepod.babashka.buddy.sign.jwspod.babashka.buddy.sign.jwtpod.babashka.buddy.hashers
The .core part may be left out for brevity as the same namespaces are mirrored under the shorter names as well.
If you are missing functionality, please create an issue.
Note that pod.babashka.buddy.core.kdf deviates from buddy's documented API
because the buddy.core.kdf/engine fn returns an instance of
org.bouncycastle.crypto.generators.HKDFBytesGenerator which can't be
serialized back to the pod client.
Instead we wrap engine results in a call to buddy.core.kdf/get-bytes and
return the byte array.
The fn that does this is named pod.babashka.buddy.core.kdf/get-engine-bytes
and that is all that this pod exposes from that namespace.
You call it with a map just like engine, but you need to add a :length key
that gets passed to buddy.core.kdf/get-bytes.
Note that pod.babashka.buddy.core.crypto deviates from buddy's documented
API because the buddy.core.crypto/engine returns an instance of
org.bouncycastle.crypto.generators.HKDFBytesGenerator which can't be
serialized back to the pod client. The same happens with several of the
other low-level functions available in that namespace.
So the same approach used by for KDF functions is used in
pod.babashka.buddy.core.crypto: compose the problematic functions
together inside the pod function, and always return serializable values.
Just a very small subset of what is available in buddy.core.crypto
namespace is exposed. The rationale for this is that a babahska pod user
won't generally need or use the low-level cryptographic primitives, or know
what cryptographic constructs may offer which security properties (like
authenticated encryption, etc).
So the set of exposed functions are those that are sane and secure enough for most people, while still providing choice:
-
the block ciphers with authenticated encryption offered by
buddy.core.crypto/encryptandbuddy.core.crypto/decrypt. -
the stream ciphers offered by
buddy.core.crypto/stream-cipher(ChaCha20), even if they don't provide authenticated encryption at the moment. But oncebuddy.core.cryptoadds support for authenticated encryption constructs (e.g., ChaCha20+Poly1305) we could add support for them.
In order to use the block ciphers with authenticated encryption you need to
use pod.babashka.buddy.core.crypto/block-cipher-encrypt and
pod.babashka.buddy.core.crypto/block-cipher-decrypt functions. They
accept the same parameters as the buddy.core.crypto/encrypt and
buddy.core.crypto/decrypt functions. Both functions return a byte array.
In order to use the stream ciphers, you need to use
pod.babashka.buddy.core.crypto/stream-cipher-encrypt and
pod.babashka.buddy.core.crypto/stream-cipher-decrypt functions.
They accept an input argument (that is passed to
buddy.core.crypto/process-bytes! as its in argument), a key and iv
arguments (that are passed to buddy.core.crypto/init! as the same key
names in the params argument), and an alg argument (that is passed to
buddy.core.crypto/stream-cipher as its argument with the same name). The
idea is to keep the type of arguments and their ordering as similar to the
block cipher functions as possible.
Note that pod.babashka.buddy.core.keys deviates from buddy's documented API
because the function originally would return a Bouncy Castle key which can't
be serialized back to the pod client.
Instead the following functions will return the encoded byte-array of the key:
private-keypublic-keystr->public-keystr->private-keyjwk->private-keyjwk->public-key
Note that pod.babashka.buddy.sign.jws has some functions that have a key
as a parameter, these deviate from buddy's documented API and now will expect
a encoded byte-array of the key. The affected functions are:
signunsign
Note that pod.babashka.buddy.sign.jwt has some functions that have a key
as a parameter, these deviate from buddy's documented API and now will expect
a encoded byte-array of the key. The affected functions are:
signunsignencryptdecrypt
The derive and verify functions are exposed. Remember to set the :salt in
the options map supplied to derive or you'll get a different hash each time.
Run script/compile. This requires GRAALVM_HOME to be set.
To test the pod code with JVM clojure, run clojure -M test.clj.
To test the native image with bb, run bb test.clj.
Copyright © 2020 - 2023 Michiel Borkent
Distributed under the Apache 2.0 License. See LICENSE.