From 3ecbc420cd792340937f595297640895ad5edecb Mon Sep 17 00:00:00 2001 From: Peter Taoussanis Date: Tue, 11 Jun 2013 12:54:04 +0700 Subject: [PATCH] Add basic crypto support (alpha) --- README.md | 10 +-- benchmarks/chart.png | Bin 0 -> 16818 bytes benchmarks/chart1.png | Bin 13998 -> 0 bytes benchmarks/chart2.png | Bin 9909 -> 0 bytes project.clj | 2 +- src/taoensso/nippy.clj | 23 ++++-- src/taoensso/nippy/benchmarks.clj | 101 +++++++++++++++-------- src/taoensso/nippy/crypto.clj | 131 ++++++++++++++++++++++++++++++ src/taoensso/nippy/utils.clj | 30 ++++++- test/test_nippy/main.clj | 7 +- 10 files changed, 251 insertions(+), 53 deletions(-) create mode 100644 benchmarks/chart.png delete mode 100644 benchmarks/chart1.png delete mode 100644 benchmarks/chart2.png create mode 100644 src/taoensso/nippy/crypto.clj diff --git a/README.md b/README.md index 04a6584..e74ffa2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ Current [semantic](http://semver.org/) version: ```clojure -[com.taoensso/nippy "1.2.1"] +[com.taoensso/nippy "1.2.1"] ; Stable +[com.taoensso/nippy "1.3.0-alpha1"] ; Development (adds crypto support) ``` # Nippy, a Clojure serialization library @@ -17,6 +18,7 @@ Nippy is an attempt to provide a drop-in, high-performance alternative to the re * **Reader-fallback** for difficult/future types (including Clojure 1.4+ tagged literals). * **Full test coverage** for every supported type. * [Snappy](http://code.google.com/p/snappy/) **integrated de/compression** for efficient storage and network transfer. + * Enable **high-strength encryption** with a single option. (1.3.0+) ## Getting started @@ -102,9 +104,7 @@ Couldn't be simpler! ## Performance -![Performance comparison chart](https://github.com/ptaoussanis/nippy/raw/master/benchmarks/chart1.png) - -![Data size chart](https://github.com/ptaoussanis/nippy/raw/master/benchmarks/chart2.png) +![Comparison chart](https://github.com/ptaoussanis/nippy/raw/master/benchmarks/chart.png) [Detailed benchmark information](https://docs.google.com/spreadsheet/ccc?key=0AuSXb68FH4uhdE5kTTlocGZKSXppWG9sRzA5Y2pMVkE&pli=1#gid=0) is available on Google Docs. @@ -127,4 +127,4 @@ Otherwise reach me (Peter Taoussanis) at [taoensso.com](https://www.taoensso.com ## License -Copyright © 2012, 2013 Peter Taoussanis. Distributed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html), the same as Clojure. +Copyright © 2012, 2013 Peter Taoussanis. Distributed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html), the same as Clojure. \ No newline at end of file diff --git a/benchmarks/chart.png b/benchmarks/chart.png new file mode 100644 index 0000000000000000000000000000000000000000..77608db95a14bffdd13ac1db76a3427c02c92d56 GIT binary patch literal 16818 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 literal 0 HcmV?d00001 diff --git a/benchmarks/chart1.png b/benchmarks/chart1.png deleted file mode 100644 index 02ce9a1b7ab283e6880e57d0c5ad9f680920badc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13998 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 AA^-pY diff --git a/project.clj b/project.clj index 04227de..12f82bb 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject com.taoensso/nippy "1.2.1" +(defproject com.taoensso/nippy "1.3.0-alpha1" :description "Clojure serialization library" :url "https://github.com/ptaoussanis/nippy" :license {:name "Eclipse Public License" diff --git a/src/taoensso/nippy.clj b/src/taoensso/nippy.clj index 0817e23..6df0e03 100644 --- a/src/taoensso/nippy.clj +++ b/src/taoensso/nippy.clj @@ -2,7 +2,8 @@ "Simple, high-performance Clojure serialization library. Adapted from Deep-Freeze." {:author "Peter Taoussanis"} - (:require [taoensso.nippy.utils :as utils]) + (:require [taoensso.nippy.utils :as utils] + [taoensso.nippy.crypto :as crypto]) (:import [java.io DataInputStream DataOutputStream ByteArrayOutputStream ByteArrayInputStream] [clojure.lang Keyword BigInt Ratio PersistentQueue PersistentTreeMap @@ -166,14 +167,17 @@ (defn freeze-to-bytes "Serializes x to a byte array and returns the array." - ^bytes [x & {:keys [compress? print-dup?] - :or {compress? true + ^bytes [x & {:keys [crypto compress? print-dup? salt password] + :or {crypto crypto/crypto-default + compress? true print-dup? true}}] (let [ba (ByteArrayOutputStream.) stream (DataOutputStream. ba)] (freeze-to-stream! stream x print-dup?) - (let [ba (.toByteArray ba)] - (if compress? (utils/compress-bytes ba) ba)))) + (let [ba (.toByteArray ba) + ba (if compress? (utils/compress-bytes ba) ba) + ba (if password (crypto/encrypt crypto salt password ba) ba)] + ba))) ;;;; Thawing @@ -251,10 +255,13 @@ (defn thaw-from-bytes "Deserializes an object from given byte array." - [ba & {:keys [read-eval? compressed?] - :or {read-eval? false ; For `read-string` injection safety - NB!!! + [ba & {:keys [crypto compressed? read-eval? salt password] + :or {crypto crypto/crypto-default + read-eval? false ; For `read-string` injection safety - NB!!! compressed? true}}] - (-> (if compressed? (utils/uncompress-bytes ba) ba) + (-> (let [ba (if password (crypto/decrypt crypto salt password ba) ba) + ba (if compressed? (utils/uncompress-bytes ba) ba)] + ba) (ByteArrayInputStream.) (DataInputStream.) (thaw-from-stream! read-eval?))) diff --git a/src/taoensso/nippy/benchmarks.clj b/src/taoensso/nippy/benchmarks.clj index ea538f4..03f9c2e 100644 --- a/src/taoensso/nippy/benchmarks.clj +++ b/src/taoensso/nippy/benchmarks.clj @@ -1,7 +1,8 @@ (ns taoensso.nippy.benchmarks {:author "Peter Taoussanis"} (:use [taoensso.nippy :as nippy :only (freeze-to-bytes thaw-from-bytes)]) - (:require [taoensso.nippy.utils :as utils])) + (:require [taoensso.nippy.utils :as utils] + [taoensso.nippy.crypto :as crypto])) ;; Remove stuff from stress-data that breaks reader (def data (dissoc nippy/stress-data :queue :queue-empty :bytes)) @@ -10,52 +11,80 @@ (defn reader-freeze [x] (binding [*print-dup* false] (pr-str x))) (defn reader-thaw [x] (binding [*read-eval* false] (read-string x))) +(def reader-roundtrip (comp reader-thaw reader-freeze)) -(def roundtrip (comp thaw-from-bytes freeze-to-bytes)) -(def reader-roundtrip (comp reader-thaw reader-freeze)) +(def crypto-opts [:password "secret" :crypto crypto/crypto-default-cached]) -(defn autobench [] (bench (roundtrip data))) +(def roundtrip-defaults (comp nippy/thaw-from-bytes nippy/freeze-to-bytes)) +(def roundtrip-encrypted (comp #(apply nippy/thaw-from-bytes % crypto-opts) + #(apply nippy/freeze-to-bytes % crypto-opts))) +(def roundtrip-fast (comp #(nippy/thaw-from-bytes % :compressed? false) + #(nippy/freeze-to-bytes % :compress? false))) + +(defn autobench [] (bench (roundtrip-defaults data) + (roundtrip-encrypted data))) (comment - ;;; Times - (println - "---\n" - {:reader {:freeze (bench (reader-freeze data)) - :thaw (let [frozen (reader-freeze data)] - (bench (reader-thaw frozen))) - :round (bench (reader-roundtrip data))} + (do ; Roundtrip times + (println "Benching (this can take some time)...") + (println "-------------------------------------") - :nippy {:freeze (bench (freeze-to-bytes data)) - :thaw (let [frozen (freeze-to-bytes data)] - (bench (thaw-from-bytes frozen))) - :round (bench (roundtrip data))}}) + (println + {:reader + {:freeze (bench (reader-freeze data)) + :thaw (let [frozen (reader-freeze data)] + (bench (reader-thaw frozen))) + :round (bench (reader-roundtrip data)) + :data-size (count (.getBytes ^String (reader-freeze data) "UTF-8"))}}) - ;; Clojure 1.3.0, Nippy 0.9.2 - ;; {:reader {:freeze 28505, :thaw 36451, :round 59545}, - ;; :nippy {:freeze 3751, :thaw 4184, :round 7769}} - ;; (float (/ 59545 7769)) = 7.6644354 + (println + {:defaults + {:freeze (bench (freeze-to-bytes data)) + :thaw (let [frozen (freeze-to-bytes data)] + (bench (thaw-from-bytes frozen))) + :round (bench (roundtrip-defaults data)) + :data-size (count (freeze-to-bytes data))}}) - ;; Clojure 1.4.0, Nippy 1.0.0 (+ tagged-uuid, tagged-date) - ;; {:reader {:freeze 22595, :thaw 31148, :round 54059} - ;; :nippy {:freeze 3324, :thaw 3725, :round 6918}} - ;; (float (/ 54059 6918)) = 7.814253 + (println + {:encrypted + {:freeze (bench (apply freeze-to-bytes data crypto-opts)) + :thaw (let [frozen (apply freeze-to-bytes data crypto-opts)] + (bench (apply thaw-from-bytes frozen crypto-opts))) + :round (bench (roundtrip-encrypted data)) + :data-size (count (apply freeze-to-bytes data crypto-opts))}}) - ;; Clojure 1.5.1, Nippy 1.2.1 (+ sorted-set, sorted-map) + (println + {:fast + {:freeze (bench (freeze-to-bytes data :compress? false)) + :thaw (let [frozen (freeze-to-bytes data :compress? false)] + (bench (thaw-from-bytes frozen :compressed? false))) + :round (bench (roundtrip-fast data)) + :data-size (count (freeze-to-bytes data :compress? false))}}) + + (println "Done! (Time for cake?)")) + + ;;; 11 June 2013: Clojure 1.5.1, Nippy 1.3.0-alpha1 + ;; {:reader {:freeze 17042, :thaw 31579, :round 48379, :data-size 22954}} + ;; {:defaults {:freeze 3810, :thaw 5295, :round 9052, :data-size 12394}} + ;; {:encrypted {:freeze 5800, :thaw 6862, :round 12317, :data-size 12416}} + ;; {:fast {:freeze 3078, :thaw 4684, :round 8117, :data-size 13274}} + + ;;; Clojure 1.5.1, Nippy 1.2.1 (+ sorted-set, sorted-map) ;; (def data (dissoc data :sorted-set :sorted-map)) ;; {:reader {:freeze 15037, :thaw 27885, :round 43945}, ;; :nippy {:freeze 3194, :thaw 4734, :round 8380}} - ;; (float (/ 43945 8380)) = 5.2440333 + ;; {:reader-size 22975, :defaults-size 12400, :encrypted-size 12400} -;;; Data size - (let [frozen (reader-freeze data)] (count (.getBytes frozen "UTF8"))) - (let [frozen (freeze-to-bytes data)] (count frozen)) - ;; 22955, 12402 - ;; (float (/ 22955 12402)) = 1.8509111 + ;;; Clojure 1.4.0, Nippy 1.0.0 (+ tagged-uuid, tagged-date) + ;; {:reader {:freeze 22595, :thaw 31148, :round 54059} + ;; :nippy {:freeze 3324, :thaw 3725, :round 6918}} -;;; Snappy implementations - (println (bench (roundtrip data))) - ;; No Snappy: 6163 6064 6042 6176 - ;; Snappy JNI: 6489 6446 6542 6412 - ;; Snappy native array copy: 6569 6419 6414 6590 - ) \ No newline at end of file + ;;; Clojure 1.3.0, Nippy 0.9.2 + ;; {:reader {:freeze 28505, :thaw 36451, :round 59545}, + ;; :nippy {:freeze 3751, :thaw 4184, :round 7769}} + + (println (bench (roundtrip data))) ; Snappy implementations + ;; {:no-snappy [6163 6064 6042 6176] :JNI [6489 6446 6542 6412] + ;; :native-array-copy [6569 6419 6414 6590]} + ) \ No newline at end of file diff --git a/src/taoensso/nippy/crypto.clj b/src/taoensso/nippy/crypto.clj new file mode 100644 index 0000000..70606d8 --- /dev/null +++ b/src/taoensso/nippy/crypto.clj @@ -0,0 +1,131 @@ +(ns taoensso.nippy.crypto + "Alpha - subject to change. + Simple no-nonsense crypto with reasonable defaults. Because your Clojure data + deserves some privacy." + {:author "Peter Taoussanis"} + (:require [taoensso.nippy.utils :as utils])) + +(defprotocol ICrypto "Simple cryptography interface." + (gen-key ^javax.crypto.spec.SecretKeySpec [crypto salt pwd] + "Returns an appropriate SecretKeySpec.") + (encrypt ^bytes [crypto salt pwd ba] "Returns encrypted bytes.") + (decrypt ^bytes [crypto salt pwd ba] "Returns decrypted bytes.")) + +(defrecord CryptoAES [cipher-type default-salt key-gen-opts cache]) + +(def ^:private ^java.security.MessageDigest sha-md + (java.security.MessageDigest/getInstance "SHA-512")) + +(def ^:private ^:const aes128-block-size (int 16)) + +(defn- sha512-key + "Default SHA512-based key generator. Good JVM availability without extra + dependencies (PBKDF2, bcrypt, scrypt, etc.). Decent security with multiple + rounds. VERY aggressive multiples (>64) possible+recommended when cached." + [^String salted-pwd & [{:keys [rounds-multiple] + :or {rounds-multiple 5}}]] ; Cacheable + (loop [^bytes ba (.getBytes salted-pwd "UTF-8") + n (* (int Short/MAX_VALUE) (or rounds-multiple 5))] + (if-not (zero? n) + (recur (.digest sha-md ba) (dec n)) + (-> ba + ;; 128bit keys have good JVM availability and are + ;; entirely sufficient, Ref. http://goo.gl/2YRQG + (java.util.Arrays/copyOf aes128-block-size) + (javax.crypto.spec.SecretKeySpec. "AES"))))) + +(comment + (time (sha512-key "hi" {:rounds-multiple 1})) ; ~40ms per hash (fast) + (time (sha512-key "hi" {:rounds-multiple 5})) ; ~180ms (default) + (time (sha512-key "hi" {:rounds-multiple 32})) ; ~1200ms (conservative) + (time (sha512-key "hi" {:rounds-multiple 128})) ; ~4500ms (paranoid) + ) + +(def ^:private cipher* (memoize #(javax.crypto.Cipher/getInstance %))) +(defn- cipher ^javax.crypto.Cipher [cipher-type] (cipher* cipher-type)) + +(def ^:private ^java.security.SecureRandom rand-gen + (java.security.SecureRandom/getInstance "SHA1PRNG")) +(defn- rand-bytes [size] (let [seed (make-array Byte/TYPE size)] + (.nextBytes rand-gen seed) seed)) + +(extend-type CryptoAES + ICrypto + (gen-key [{:keys [default-salt key-gen-opts cache]} salt pwd] + (utils/apply-memoized cache + sha512-key (str (or salt default-salt) pwd) key-gen-opts)) + + (encrypt [{:keys [cipher-type cache] :as crypto} salt pwd ba] + (let [cipher (cipher cipher-type) + key (gen-key crypto salt pwd) + iv-ba (rand-bytes aes128-block-size) + iv (javax.crypto.spec.IvParameterSpec. iv-ba)] + (.init cipher javax.crypto.Cipher/ENCRYPT_MODE key iv) + (.doFinal cipher (utils/ba-concat iv-ba ba)))) + + (decrypt [{:keys [cipher-type cache] :as crypto} salt pwd ba] + (let [cipher (cipher cipher-type) + key (gen-key crypto salt pwd) + [iv-ba data-ba] (utils/ba-split ba aes128-block-size) + iv (javax.crypto.spec.IvParameterSpec. iv-ba)] + (.init cipher javax.crypto.Cipher/DECRYPT_MODE key iv) + (.doFinal cipher data-ba)))) + +(defn crypto-aes128 + "Returns a new CryptoAES object with options: + :default-salt - Shared fallback password salt when none is provided. If + the use case allows it, a unique random salt per + encrypted item is better. + :cache-keys? - IMPORTANT. DO enable this if and ONLY if your use case + involves only a small, finite number of unique secret + keys (salt+password)s. Dramatically improves `gen-key` + performance in those cases and (as a result) allows for + a *much* stronger `key-work-factor`. + :key-work-factor - O(n) CPU time needed to generate keys. Larger factors + provide more protection against brute-force attacks but + make encryption+decryption slower if `:cache-keys?` is + not enabled. + + Some sensible values (from fast to strong): + Without caching: 1, 5, 10 + With caching: 5, 32, 64, 128 + + See also `crypto-default` and `crypto-default-cached` for sensible ready-made + CryptoAES objects." + [& [{:keys [default-salt cache-keys? key-work-factor] + :or {default-salt "XA~I3(:]3'ck5!M[z\\m`l^0mltR~y/]Arq_d9+$`e#yJssN^8" + key-work-factor 5}}]] + (CryptoAES. "AES/CBC/PKCS5Padding" + default-salt + {:rounds-multiple (int key-work-factor)} + (when cache-keys? (atom {})))) + +(def crypto-default (crypto-aes128)) +(def crypto-default-cached (crypto-aes128 {:cache-keys? true + :key-work-factor 64})) + +(comment + (time (gen-key crypto-default "my-salt" "my-password")) + (time (gen-key crypto-default-cached "my-salt" "my-password")) + (time (->> (.getBytes "Secret message" "UTF-8") + (encrypt crypto-default "s" "p") + (encrypt crypto-default "s" "p") + (decrypt crypto-default "s" "p") + (decrypt crypto-default "s" "p") + (String.)))) + +;; TODO Nippy: Compress _then_ encode. Decode _then_ decompress. +;; TODO Move tests to actual unit tests ns. +;; TODO Update benchmarks with crypto (so 3 bars). +;; TODO Add tests for bad decryption (e.g. wrong key) +;; ;; (comment +;; ;; (let [data (dissoc nippy/stress-data :bytes)] +;; ;; (= data (->> data +;; ;; (nippy/freeze-to-bytes) +;; ;; (encrypt-aes "my-password") +;; ;; (decrypt-aes "my-password") +;; ;; (nippy/thaw-from-bytes)))) + +;; ;; (let [ba (nippy/freeze-to-bytes nippy/stress-data)] +;; ;; (time (dotimes [_ 10000] (->> ba (encrypt-aes "my-password") +;; ;; (decrypt-aes "my-password")))))) \ No newline at end of file diff --git a/src/taoensso/nippy/utils.clj b/src/taoensso/nippy/utils.clj index 30488e1..e3d90ff 100644 --- a/src/taoensso/nippy/utils.clj +++ b/src/taoensso/nippy/utils.clj @@ -59,4 +59,32 @@ (catch Exception _ false))) (defn compress-bytes [^bytes ba] (Snappy/compress ba)) -(defn uncompress-bytes [^bytes ba] (Snappy/uncompress ba 0 (alength ba))) \ No newline at end of file +(defn uncompress-bytes [^bytes ba] (Snappy/uncompress ba 0 (alength ba))) + +(defn apply-memoized + "A cross between `memoize` and `apply`. Operates like `apply` but accepts an + optional { ...} cache atom." + [cache f & args] + (if-not cache + (apply f args) + (if-let [dv (@cache args)] + @dv + (let [dv (delay (apply f args))] + (swap! cache assoc args dv) + @dv)))) + +(defn ba-concat ^bytes [^bytes ba1 ^bytes ba2] + (let [s1 (alength ba1) + s2 (alength ba2) + out (byte-array (+ s1 s2))] + (System/arraycopy ba1 0 out 0 s1) + (System/arraycopy ba2 0 out s1 s2) + out)) + +(defn ba-split [^bytes ba ^Integer idx] + [(java.util.Arrays/copyOfRange ba 0 idx) + (java.util.Arrays/copyOfRange ba idx (alength ba))]) + +(comment (String. (ba-concat (.getBytes "foo") (.getBytes "bar"))) + (let [[x y] (ba-split (.getBytes "foobar") 3)] + [(String. x) (String. y)])) \ No newline at end of file diff --git a/test/test_nippy/main.clj b/test/test_nippy/main.clj index 185bd68..7a7eb59 100644 --- a/test/test_nippy/main.clj +++ b/test/test_nippy/main.clj @@ -6,9 +6,12 @@ ;; Remove stuff from stress-data that breaks roundtrip equality (def test-data (dissoc nippy/stress-data :bytes)) -(def roundtrip (comp nippy/thaw-from-bytes nippy/freeze-to-bytes)) +(def roundtrip-defaults (comp nippy/thaw-from-bytes nippy/freeze-to-bytes)) +(def roundtrip-encrypted (comp #(nippy/thaw-from-bytes % :password "secret") + #(nippy/freeze-to-bytes % :password "secret"))) -(deftest test-roundtrip (is (= test-data (roundtrip test-data)))) +(deftest test-roundtrip-defaults (is (= test-data (roundtrip-defaults test-data)))) +(deftest test-roundtrip-encrypted (is (= test-data (roundtrip-encrypted test-data)))) (println "Benchmarking roundtrips (x3)") (println "----------------------------")