com.github.jlangch.venice.crypt.venice Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of venice Show documentation
Show all versions of venice Show documentation
Venice, a sandboxed Lisp implemented in Java.
;;;; __ __ _
;;;; \ \ / /__ _ __ (_) ___ ___
;;;; \ \/ / _ \ '_ \| |/ __/ _ \
;;;; \ / __/ | | | | (_| __/
;;;; \/ \___|_| |_|_|\___\___|
;;;;
;;;;
;;;; Copyright 2017-2024 Venice
;;;;
;;;; Licensed under the Apache License, Version 2.0 (the "License");
;;;; you may not use this file except in compliance with the License.
;;;; You may obtain a copy of the License at
;;;;
;;;; http://www.apache.org/licenses/LICENSE-2.0
;;;;
;;;; Unless required by applicable law or agreed to in writing, software
;;;; distributed under the License is distributed on an "AS IS" BASIS,
;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;;; See the License for the specific language governing permissions and
;;;; limitations under the License.
;;;; Venice crypt functions
(ns crypt)
(import :java.util.Base64)
(import :java.security.MessageDigest)
(import :java.security.spec.AlgorithmParameterSpec)
(import :javax.crypto.Cipher)
(import :javax.crypto.SecretKey)
(import :javax.crypto.SecretKeyFactory)
(import :javax.crypto.spec.PBEKeySpec)
(import :javax.crypto.spec.IvParameterSpec)
(import :javax.crypto.spec.PBEParameterSpec)
(import :javax.crypto.spec.GCMParameterSpec)
(import :javax.crypto.spec.SecretKeySpec)
(import :javax.net.ssl.SSLServerSocketFactory)
(import :com.github.jlangch.venice.util.crypt.FileEncryptor)
(import :com.github.jlangch.venice.util.crypt.FileHasher)
;; -----------------------------------------------------------------------------
;; Hashing
;; -----------------------------------------------------------------------------
(defn
^{ :arglists '(
"(crypt/md5-hash data)"
"(crypt/md5-hash data salt)")
:doc """
Hashes a string or a bytebuf using MD5 with an optional salt.
Note: MD5 is not safe any more use PBKDF2 to hash passwords!
"""
:examples (list
"""
(-> (crypt/md5-hash "hello world")
(str/bytebuf-to-hex :upper))
""",
"""
(-> (crypt/md5-hash "hello world" "-salt-")
(str/bytebuf-to-hex :upper))
""" )
:see-also '(
"crypt/sha1-hash"
"crypt/sha512-hash"
"crypt/pbkdf2-hash" ) }
crypt/md5-hash
([data] (hash "MD5" data nil))
([data salt] (hash "MD5" data salt)))
(defn
^{ :arglists '(
"(crypt/sha1-hash data)"
"(crypt/sha1-hash data salt)")
:doc "Hashes a string or a bytebuf using SHA1 with an optional salt."
:examples (list
"""
(-> (crypt/sha1-hash "hello world")
(str/bytebuf-to-hex :upper))
""",
"""
(-> (crypt/sha1-hash "hello world" "-salt-")
(str/bytebuf-to-hex :upper))
""" )
:see-also '(
"crypt/md5-hash"
"crypt/sha512-hash"
"crypt/pbkdf2-hash" ) }
crypt/sha1-hash
([data] (hash "SHA-1" data nil))
([data salt] (hash "SHA-1" data salt)))
(defn
^{ :arglists '(
"(crypt/sha512-hash data)"
"(crypt/sha512-hash data salt)")
:doc "Hashes a string or a bytebuf using SHA512 with an optional salt."
:examples (list
"""
(let [s (-> (crypt/sha512-hash "hello world")
(str/bytebuf-to-hex :upper))]
(str (str/nfirst s 32) "..." (str/nlast s 32)))
""",
"""
(let [s (-> (crypt/sha512-hash "hello world" "-salt-")
(str/bytebuf-to-hex :upper))]
(str (str/nfirst s 32) "..." (str/nlast s 32)))
""" )
:see-also '(
"crypt/md5-hash"
"crypt/sha1-hash"
"crypt/pbkdf2-hash" ) }
crypt/sha512-hash
([data] (hash "SHA-512" data nil))
([data salt] (hash "SHA-512" data salt)))
(defn
^{ :arglists '(
"(crypt/pbkdf2-hash data salt)"
"(crypt/pbkdf2-hash data salt iterations key-length)")
:doc """
Hashes a string using PBKDF2. iterations defaults to 1000,
key-length defaults to 256.
"""
:examples (list
"""
(-> (crypt/pbkdf2-hash "hello world" "-salt-")
(str/bytebuf-to-hex :upper))
""",
"""
(-> (crypt/pbkdf2-hash "hello world" "-salt-" 1000 256)
(str/bytebuf-to-hex :upper))
""" )
:see-also '(
"crypt/md5-hash"
"crypt/sha1-hash"
"crypt/sha512-hash" ) }
crypt/pbkdf2-hash
([text salt]
(crypt/pbkdf2-hash text salt 1000 256))
([text salt iterations key-length]
(assert (string? text)
"The passed text must be a string")
(assert (or (string? salt) (nil? salt))
"The passed salt must be either a string or nil")
(assert (or (long? iterations) (int? iterations))
"The passed iterations must be either a long or an integer")
(assert (or (long? key-length) (int? key-length))
"The passed key-length must be either a long or an integer")
(try
(let [spec (. :PBEKeySpec :new
text
(if (nil? salt) (bytebuf) (bytebuf-from-string salt :UTF-8))
iterations
key-length)]
(-> (. :SecretKeyFactory :getInstance "PBKDF2WithHmacSHA512")
(. :generateSecret spec)
(. :getEncoded)))
(catch :Exception ex
(throw (ex :VncException "Failed to compute PBKDF2 hash." ex))))))
;; -----------------------------------------------------------------------------
;; Encryption / Decryption
;; -----------------------------------------------------------------------------
(defn
^{ :arglists '(
"(crypt/encrypt algorithm passphrase & options)")
:doc """
Returns a new thread safe function to encrypt a string or a bytebuf
given the algorithm and passphrase. If a string is passed it is
encrypted and returned as a base64 encoded string. If a bytebuf
is passed the encryped bytebuf is returned.
Supported algorithms: "DES", "3DES", "AES256"
Options:
| [![width: 15%]] | [![width: 85%]] |
| :url-safe | The boolean option directs the base64 encoder to \
emit standard or URL safe base64 encoded strings. \
If `true` the base64 encoder will emit '-' and '_' \
instead of the usual '+' and '/' characters.¶ \
Defaults to false.¶ \
Note: no padding is added when encoding using the \
URL-safe alphabet. |
| :salt | An optional salt. A bytebuf or a string. ¶ \
DES and 3DES require exactly 8 bytes, AES256 1 or \
more bytes |
"""
:examples (list
"""
(do
(load-module :crypt)
(def encrypt (crypt/encrypt "3DES" "secret" :url-safe true))
(encrypt "hello") ; => "ndmW1NLsDHA="
(encrypt "world") ; => "KPYjndkZ8vM="
(encrypt (bytebuf [1 2 3 4]))) ; => [128 216 205 163 62 43 52 82]
""",
"""
(do
(load-module :crypt)
(def encrypt (crypt/encrypt "3DES" "secret" :url-safe true :salt "salty"))
(encrypt "hello") ; => "3MrQGcgbv00="
(encrypt "world") ; => "a6UyBZUnK4I="
(encrypt (bytebuf [1 2 3 4]))) ; => [86 66 56 135 239 120 10 150]
""" )
:see-also '(
"crypt/decrypt" ) }
crypt/encrypt
[algorithm passphrase & options]
(let [opts (apply hash-map options)
salt (:salt opts default-salt)
url-safe (:url-safe opts false)]
(create-encrypt-fn algorithm passphrase salt url-safe)))
(defn
^{ :arglists '(
"(crypt/decrypt algorithm passphrase & options)")
:doc """
Returns a new thread safe function to decrypt a string or a bytebuf \
given the algorithm and passphrase. If a string is passed it is base64 \
decoded, decrypted, and returned as string. If a bytebuf is passed the \
decrypted bytebuf is returned.
Supported algorithms: DES, 3DES, AES256
Options:
| [![width: 15%]] | [![width: 85%]] |
| :url-safe | The boolean option directs the base64 decoder to decode \
standard or URL safe base64 encoded strings. \
If enabled (true) the base64 decoder will convert '-' \
and '_' characters back to '+' and '/' before \
decoding.¶ \
Defaults to false. |
| :salt | An optional salt. A bytebuf or a string. ¶ \
DES and 3DES require exactly 8 bytes, AES256 1 or more \
bytes |
"""
:examples (list
"""
(do
(load-module :crypt)
(def encrypt (crypt/encrypt "AES256" "secret" :url-safe true))
(def decrypt (crypt/decrypt "AES256" "secret" :url-safe true))
(encrypt "hello") ; => "e4m1qe6Fyx3Rr7NTIZe97g=="
(decrypt "e4m1qe6Fyx3Rr7NTIZe97g==") ; => "hello"
(encrypt (bytebuf [128 216 205]))) ; => [43 195 99 118 231 225 142 76 132 194 129 237 158 12 12 203]
""",
"""
(do
(load-module :crypt)
(def encrypt (crypt/encrypt "AES256" "secret" :salt "salty"))
(def decrypt (crypt/decrypt "AES256" "secret" :salt "salty"))
(-> "hello"
(encrypt)
(decrypt)))
""" )
:see-also '(
"crypt/encrypt" ) }
crypt/decrypt
[algorithm passphrase & options]
(let [opts (apply hash-map options)
salt (:salt opts default-salt)
url-safe (:url-safe opts false)]
(create-decrypt-fn algorithm passphrase salt url-safe)))
;; -----------------------------------------------------------------------------
;; File encryption / decryption
;; -----------------------------------------------------------------------------
(defn
^{ :arglists '(
"(crypt/encrypt-file algorithm passphrase in)"
"(crypt/encrypt-file algorithm passphrase in out)")
:doc """
Encrypts a file.
Returns a byte buffer with the encrypted data if the 'out' argument
is missing. Otherwise returns nil and writes the encrypted file data
to the destination given by 'out'.
Supported algorithms:
* AES256-GCM ¹⁾
* AES256-CBC ²⁾
* ChaCha20 ³⁾
* ChaCha20-BC ⁴⁾
| ¹⁾ | Recommended by NIST |
| ²⁾ | AES256-CBC is regarded as a broken or risky \
cryptographic algorithm (CWE-327, CWE-328). Use AES256-GCM \
in production! |
| ³⁾ | 256 bit key, only available with Java 11+ |
| ⁴⁾ | 256 bit key, only available with BouncyCastle libraries but \
works with Java 8+ ¶\
Warning: files encrypted with ChaCha20 cannot be decrypted \
by ChaCha20-BC (and vice versa) due to different initial \
counter handling and the IV size (96bit vs 64bit) |
The ChaCha family of ciphers are an oder of magnitude more efficient
on servers that do not provide hardware acceleration. Apple Silicon
does not seem to have AES hardware acceleration probably due to its
RISC nature.
The arg 'in' may be a:
* string file path, e.g: "/temp/foo.json"
* bytebuf
* `java.io.File`, e.g: `(io/file "/temp/foo.json")`
* `java.io.InputStream`
The arg 'out' may be a:
* string file path, e.g: "/temp/foo.json"
* `java.io.File`, e.g: `(io/file "/temp/foo.json")`
* `java.io.OutputStream`
The 256 bit encryption key is derived from the passphrase using a
*PBKDF2WithHmacSHA256* secret key factory with a 16 byte random salt
and 65536 iterations. Carefully choose a long enough passphrase.
*Salt*, *IV*, *Nonce* and/or *Counter* are random and unique for every
call of `crypt/encrypt-file`.
While encrypting a file the random *Salt* (when a passphrase is used),
*IV*, *Nonce* and/or *Counter* are written to the start of the
encrypted file and read before decrypting the file:
```
AES256-GCM AES256-CBC ChaCha20
AES/GCM/NoPadding AES/CBC/PKCS5Padding
+--------------------+ +--------------------+ +--------------------+
| salt (16) | | salt (16) | | salt (16) |
+--------------------+ +--------------------+ +--------------------+
| iv (12) | | iv (12) | | nonce (12) |
+--------------------+ +--------------------+ +--------------------+
| data (n) | | data (n) | | counter (4) |
+--------------------+ +--------------------+ +--------------------+
| data (n) |
+--------------------+
```
"""
:examples (list
"""
(do
(load-module :crypt)
(load-module :hexdump)
(let [file-in (io/temp-file "test-", ".data")
file-out (io/temp-file "test-", ".data.enc")]
(io/delete-file-on-exit file-in file-out)
(io/spit file-in "1234567890")
(crypt/encrypt-file "AES256-GCM" "42" file-in file-out)
(-> (io/slurp file-out :binary false)
(bytebuf)
(hexdump/dump))))
""")
:see-also '(
"crypt/decrypt-file" ) }
encrypt-file
([algorithm passphrase in]
(as-> (io/slurp in :binary true) data
(. :FileEncryptor :encryptFileWithPassphrase algorithm passphrase data)))
([algorithm passphrase in out]
(as-> (io/slurp in :binary true) data
(. :FileEncryptor :encryptFileWithPassphrase algorithm passphrase data)
(io/spit out data)
nil)))
(defn
^{ :arglists '(
"(crypt/decrypt-file algorithm passphrase in)"
"(crypt/decrypt-file algorithm passphrase in out)")
:doc """
Decrypts an encrypted file that has been created by
`crypt/encrypt-file`.
Returns a byte buffer with the decrypted data if the 'out' argument
is missing. Otherwise returns nil and writes the decrypted file data
to the destination given by 'out'.
The arg 'algorithm' is one of: "AES256-GCM", "AES256-CBC", "ChaCha20"
The arg 'in' may be a:
* string file path, e.g: "/temp/foo.json"
* bytebuf
* `java.io.File`, e.g: `(io/file "/temp/foo.json")`
* `java.io.InputStream`
The arg 'out' may be a:
* string file path, e.g: "/temp/foo.json"
* `java.io.File`, e.g: `(io/file "/temp/foo.json")`
* `java.io.OutputStream`
"""
:examples (list
"""
(do
(load-module :crypt)
(let [file-in (io/temp-file "test-", ".data")
file-out (io/temp-file "test-", ".data.enc")
passphrase "42"]
(io/delete-file-on-exit file-in file-out)
(io/spit file-in "1234567890")
(crypt/encrypt-file "AES256-GCM" passphrase file-in file-out)
(-> (crypt/decrypt-file "AES256-GCM" passphrase file-out)
(bytebuf-to-string :UTF-8))))
""")
:see-also '(
"crypt/encrypt-file" ) }
decrypt-file
([algorithm passphrase in]
(as-> (io/slurp in :binary true) data
(. :FileEncryptor :decryptFileWithPassphrase algorithm passphrase data)))
([algorithm passphrase in out]
(as-> (io/slurp in :binary true) data
(. :FileEncryptor :decryptFileWithPassphrase algorithm passphrase data)
(io/spit out data)
nil)))
(defn encrypt-file-supported? [algorithm]
(. :FileEncryptor :supports algorithm))
;; -----------------------------------------------------------------------------
;; File hashing
;; -----------------------------------------------------------------------------
(defn
^{ :arglists '(
"(crypt/hash-file algorithm salt file)")
:doc """
Computes a hash for a file. The hash is used together with the
function `crypt/verify-file-hash` to detect file modifications.
Returns the hash Base64 encoded.
The functions uses the fast MD5 hash algorithm.
The arg 'file' may be a:
* string file path, e.g: "/temp/foo.json"
* bytebuf
* `java.io.File`, e.g: `(io/file "/temp/foo.json")`
* `java.io.InputStream`
Supported hash algorithms:
* MD5 (default)
* SHA-1
* SHA-512
MD5 is the fastest hash algorithm and precise enough to detect file
changes.
"""
:examples (list
"""
(do
(load-module :crypt)
(let [file (io/temp-file "test-", ".data")
data (bytebuf-allocate-random 1000)]
(io/delete-file-on-exit file)
(io/spit file data)
(crypt/hash-file "SHA-256" "-salt-" file)))
""")
:see-also '(
"crypt/verify-file-hash" ) }
hash-file [algorithm salt file]
(assert (string? algorithm) "The algorithm must be a string")
(assert (string? salt) "The salt must be a string")
(. :FileHasher :hashFile algorithm salt (io/slurp file :binary true)))
(defn
^{ :arglists '(
"(crypt/verify-file-hash algorithm salt file hash)")
:doc """
Verifies a file against a hash (Base64 encoded). Returns true if the
file's actual hash is equal to the given hash otherwise false.
The arg 'file' may be a:
* string file path, e.g: "/temp/foo.json"
* bytebuf
* `java.io.File`, e.g: `(io/file "/temp/foo.json")`
* `java.io.InputStream`
Supported hash algorithms:
* MD5
* SHA-1
* SHA-512
Warning: The MD5 hash function’s security is considered to be
severely compromised. Collisions can be found within seconds,
and they can be used for malicious purposes.
"""
:examples (list
"""
(do
(load-module :crypt)
(let [file (io/temp-file "test-", ".data")
data (bytebuf-allocate-random 1000)
salt "salt"]
(io/delete-file-on-exit file)
(io/spit file data)
(let [hash (crypt/hash-file "SHA-256" "-salt-" file)]
(crypt/verify-file-hash "SHA-256" "-salt-" file hash))))
""")
:see-also '(
"crypt/hash-file" ) }
verify-file-hash [algorithm salt file hash]
(assert (string? algorithm) "The algorithm must be a string")
(assert (string? salt) "The salt must be a string")
(assert (string? hash) "The hash must be a string")
(. :FileHasher :verifyFileHash algorithm salt (io/slurp file :binary true) hash))
;; -----------------------------------------------------------------------------
;; Ciphers
;; -----------------------------------------------------------------------------
(defn
^{ :arglists '(
"(crypt/max-key-size algorithm)")
:doc """
Returns the max allowed key size
"""
:examples (list
"""
(do
(load-module :crypt)
(crypt/max-key-size "AES"))
""" ) }
crypt/max-key-size [algorithm]
(. :Cipher :getMaxAllowedKeyLength algorithm))
(defn
^{ :arglists '(
"(crypt/ciphers)",
"(crypt/ciphers opt)")
:doc """
Returns a list of the ciphers the Java VM supports
Argument opt
| :default | returns the names of the cipher suites which are \
enabled by default. |
| :available | returns the names of the cipher suites which could be \
enabled for use on an SSL connection created by this \
`SSLServerSocketFactory`. |
"""
:examples (list
"""
(do
(load-module :crypt)
(crypt/ciphers :default))
""",
"""
(do
(load-module :crypt)
(crypt/ciphers :available))
""",
"""
(do
(load-module :crypt)
(docoll println (crypt/ciphers :available)))
""" ) }
crypt/ciphers
([] (crypt/ciphers :available))
([opt] (let [ssf (cast :SSLServerSocketFactory
(. :SSLServerSocketFactory :getDefault))
method (if (== opt :default)
:getDefaultCipherSuites
:getSupportedCipherSuites)]
(sort (. ssf method)))))
(defn
^{ :arglists '(
"(crypt/provider? name)")
:doc """
Returns true if the Java security provider with name exists, else
false.
"""
:examples (list
"""
(do
(load-module :crypt)
(crypt/provider? "BC"))
""" )
:see-also '(
"crypt/add-bouncy-castle-provider" ) }
crypt/provider? [name]
(. :FileEncryptor :hasProvider name))
(defn
^{ :arglists '(
"(crypt/add-bouncy-castle-provider")
:doc """
Adds the BouncyCastle provider to the Java security.
"""
:examples (list
"""
(do
(load-module :crypt)
(crypt/add-bouncy-castle-provider))
""" )
:see-also '(
"crypt/provider?" ) }
crypt/add-bouncy-castle-provider []
(. :FileEncryptor :addBouncyCastleProvider))
;; -----------------------------------------------------------------------------
;; Utils
;; -----------------------------------------------------------------------------
(def- default-salt (bytebuf [0xA9 0x9B 0xC8 0x32 0x56 0x35 0xE3 0x03]))
(defn- to-8-byte-salt [salt]
(if (string? salt) (bytebuf-from-string salt :UTF-8 8 0x00) salt))
(defn- to-bytes [data]
(if (string? data) (bytebuf-from-string data :UTF-8) data))
(defn- url-encode [s]
(-> s
(str/replace-all "+" "-")
(str/replace-all "/" "_")))
(defn- url-decode [s]
(-> s
(str/replace-all "-" "+")
(str/replace-all "_" "/")))
(defn- base64-encode [bytes url-safe padding]
(let [encoder (. :Base64 :getEncoder)]
(when-not padding
(. encoder :withoutPadding))
(if url-safe
(url-encode (. encoder :encodeToString bytes))
(. encoder :encodeToString bytes))))
(defn- base64-decode [s url-safe]
(-> (. :Base64 :getDecoder)
(. :decode (to-bytes (if url-safe (url-decode s) s)))))
(defn- crypt/hash
([algorithm data]
(hash algorithm data nil))
([algorithm data salt]
(assert (or (string? data) (bytebuf? data))
"The passed data must be either a string or a bytebuf")
(assert (or (string? salt) (bytebuf? salt) (nil? salt))
"The passed salt must be either a string, a bytebuf or nil")
(try
(let [md (. :MessageDigest :getInstance algorithm)]
(. md :reset)
(when (some? salt)
(. md :update (to-bytes salt)))
(. md :update (to-bytes data))
(. md :digest))
(catch :Exception ex
(throw (ex :VncException
(str "Failed to compute " algorithm " hash.")
ex))))))
;; -----------------------------------------------------------------------------
;; Cipher
;; -----------------------------------------------------------------------------
(def ENCRYPT_MODE 1)
(def DECRYPT_MODE 2)
(defmulti create-encrypt-fn (fn [algorithm passphrase salt url-safe]
(name algorithm)))
(defmethod create-encrypt-fn :default [algorithm passphrase salt url-safe]
(throw (ex :VncException
(str "Invalid cipher alogorithm '" algorithm "'"))))
(defmulti create-decrypt-fn (fn [algorithm passphrase salt url-safe]
(name algorithm)))
(defmethod create-decrypt-fn :default [algorithm passphrase salt url-safe]
(throw (ex :VncException
(str "Invalid cipher alogorithm '" algorithm "'"))))
(defn- cipher-encrypt [cipher data url-safe]
(try
(locking cipher
(cond
(empty? data) data
(string? data) (-<> (to-bytes data)
(. cipher :doFinal <>)
(base64-encode <> url-safe true))
(bytebuf? data) (. cipher :doFinal data)
:else (throw (ex :VncException
"Invalid datatype for data."))))
(catch :Exception ex
(throw (ex :VncException "Failed to encrypt data." ex)))))
(defn- cipher-decrypt [cipher data url-safe]
(try
(locking cipher
(cond
(empty? data) data
(string? data) (-<> (base64-decode data url-safe)
(. cipher :doFinal <>)
(bytebuf-to-string <> :UTF-8))
(bytebuf? data) (. cipher :doFinal data)
:else (throw (ex :VncException
"Invalid datatype for data."))))
(catch :Exception ex
(throw (ex :VncException "Failed to decrypt data." ex)))))
;; -----------------------------------------------------------------------------
;; DES
;; -----------------------------------------------------------------------------
(defn- iterations-DES [] 19)
(defn- create-secret-key-DES [passphrase salt]
(let [factory (. :SecretKeyFactory :getInstance "PBEWithMD5AndDES")
key-spec (. :PBEKeySpec :new passphrase salt (iterations-DES))
secret (. factory :generateSecret key-spec)]
secret))
(defn- create-cipher-DES [mode secret-key param-spec]
(doto (. :Cipher :getInstance (. secret-key :getAlgorithm))
(. :init mode secret-key param-spec)))
(defmethod create-encrypt-fn "DES" [algorithm passphrase salt url-safe]
(let [salt (to-8-byte-salt salt)
_ (assert (and (bytebuf? salt) (= 8 (count salt)))
"A DES salt must be a bytebuf with 8 bytes!")
secret-key (create-secret-key-DES passphrase salt)
param-spec (. :PBEParameterSpec :new salt (iterations-DES))
cipher (create-cipher-DES ENCRYPT_MODE secret-key param-spec)]
(fn [data] (cipher-encrypt cipher data url-safe))))
(defmethod create-decrypt-fn "DES" [algorithm passphrase salt url-safe]
(let [salt (to-8-byte-salt salt)
_ (assert (and (bytebuf? salt) (= 8 (count salt)))
"A DES salt must be a bytebuf with 8 bytes!")
secret-key (create-secret-key-DES passphrase salt)
param-spec (. :PBEParameterSpec :new salt (iterations-DES))
cipher (create-cipher-DES DECRYPT_MODE secret-key param-spec)]
(fn [data] (cipher-decrypt cipher data url-safe))))
;; -----------------------------------------------------------------------------
;; 3DES
;; -----------------------------------------------------------------------------
(defn- iterations-3DES [] 19)
(defn- create-secret-key-3DES [passphrase salt ]
(let [factory (. :SecretKeyFactory :getInstance "PBEWithMD5AndTripleDES")
key-spec (. :PBEKeySpec :new passphrase salt (iterations-3DES))
secret (. factory :generateSecret key-spec)]
secret))
(defn- create-cipher-3DES [mode secret-key param-spec]
(doto (. :Cipher :getInstance (. secret-key :getAlgorithm))
(. :init mode secret-key param-spec)))
(defmethod create-encrypt-fn "3DES" [algorithm passphrase salt url-safe]
(let [salt (to-8-byte-salt salt)
_ (assert (and (bytebuf? salt) (= 8 (count salt)))
"A 3DES salt must be a bytebuf with 8 bytes!")
secret-key (create-secret-key-3DES passphrase salt)
param-spec (. :PBEParameterSpec :new salt (iterations-3DES))
cipher (create-cipher-3DES ENCRYPT_MODE secret-key param-spec)]
(fn [data] (cipher-encrypt cipher data url-safe))))
(defmethod create-decrypt-fn "3DES" [algorithm passphrase salt url-safe]
(let [salt (to-8-byte-salt salt)
_ (assert (and (bytebuf? salt) (= 8 (count salt)))
"A 3DES salt must be a bytebuf with 8 bytes!")
secret-key (create-secret-key-3DES passphrase salt)
param-spec (. :PBEParameterSpec :new salt (iterations-3DES))
cipher (create-cipher-3DES DECRYPT_MODE secret-key param-spec)]
(fn [data] (cipher-decrypt cipher data url-safe))))
;; -----------------------------------------------------------------------------
;; AES256 (CBC)
;; -----------------------------------------------------------------------------
(defn- create-iv-AES256 []
(bytebuf [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]))
(defn- create-iv-spec-AES256 []
(. :IvParameterSpec :new (create-iv-AES256 )))
(defn- create-secret-key-AES256 [passphrase salt]
(let [factory (. :SecretKeyFactory :getInstance "PBKDF2WithHmacSHA256")
key-spec (. :PBEKeySpec :new passphrase salt 65536 256)
secret (. factory :generateSecret key-spec)
secret-key (. :SecretKeySpec :new (. secret :getEncoded) "AES")]
secret-key))
(defn- create-cipher-AES256 [mode secret-key iv-spec]
(doto (. :Cipher :getInstance "AES/CBC/PKCS5Padding")
(. :init mode secret-key iv-spec)))
(defmethod create-encrypt-fn "AES256" [algorithm passphrase salt url-safe]
(let [salt (to-bytes salt)
_ (assert (and (bytebuf? salt) (< 0 (count salt)))
"An AES256 salt must be a bytebuf with at least 1 byte!")
secret-key (create-secret-key-AES256 passphrase salt)
iv-spec (create-iv-spec-AES256)
cipher (create-cipher-AES256 ENCRYPT_MODE secret-key iv-spec)]
(fn [data] (cipher-encrypt cipher data url-safe))))
(defmethod create-decrypt-fn "AES256" [algorithm passphrase salt url-safe]
(let [salt (to-bytes salt)
_ (assert (and (bytebuf? salt) (< 0 (count salt)))
"An AES256 salt must be a bytebuf with at least 1 byte!")
secret-key (create-secret-key-AES256 passphrase salt)
iv-spec (create-iv-spec-AES256)
cipher (create-cipher-AES256 DECRYPT_MODE secret-key iv-spec)]
(fn [data] (cipher-decrypt cipher data url-safe))))