summaryrefslogtreecommitdiff
path: root/compojure-3.2/src/compojure/crypto.clj
blob: 937bad56f81bf87e5430cd490c41a50a47bdcad7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
;; Copyright (c) James Reeves. All rights reserved.
;; The use and distribution terms for this software are covered by the Eclipse
;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which
;; can be found in the file epl-v10.html at the root of this distribution. By
;; using this software in any fashion, you are agreeing to be bound by the
;; terms of this license. You must not remove this notice, or any other, from
;; this software.

(ns compojure.crypto
  "Functions for cryptographically signing, verifying and encrypting data."
  (:use compojure.encodings
        clojure.contrib.def
        clojure.contrib.java-utils)
  (:import java.security.SecureRandom
           [javax.crypto Cipher KeyGenerator Mac]
           [javax.crypto.spec SecretKeySpec IvParameterSpec]
           java.util.UUID))

(defvar hmac-defaults
  {:algorithm "HmacSHA256"}
  "Default options for HMACs.")

(defvar encrypt-defaults
  {:algorithm  "AES"
   :key-size   128
   :mode       "CBC"
   :padding    "PKCS5Padding"}
  "Default options for symmetric encryption.")

(defn secure-random-bytes
  "Returns a random byte array of the specified size. Can optionally supply
   an PRNG algorithm (defaults is SHA1PRNG)."
  ([size]
    (secure-random-bytes size "SHA1PRNG"))
  ([size algorithm]
     (let [seed (make-array Byte/TYPE size)]
       (.nextBytes (SecureRandom/getInstance algorithm) seed)
       seed)))

(defn gen-secret-key
  "Generate a random secret key from a map of encryption options."
  ([]
    (gen-secret-key {}))
  ([options]
    (secure-random-bytes (/ (options :key-size) 8))))

(defn gen-uuid
  "Generate a random UUID."
  []
  (str (UUID/randomUUID)))

(defn- to-bytes
  "Converts its argument into an array of bytes."
  [x]
  (cond
    (string? x)     (.getBytes x)
    (sequential? x) (into-array Byte/TYPE x)
    :else           x))

(defn hmac-bytes
  "Generate a HMAC byte array with the supplied key on a byte array of data.
  Takes an optional map of cryptography options."
  [options key data]
  (let [options   (merge hmac-defaults options)
        algorithm (options :algorithm)
        hmac      (doto (Mac/getInstance algorithm)
                    (.init (SecretKeySpec. key algorithm)))]
    (.doFinal hmac data)))

(defn hmac
  "Generate a Basc64-encoded HMAC with the supplied key on a byte array or
  string of data. Takes an optional map of cryptography options."
  [options key data]
  (base64-encode-bytes (hmac-bytes options key (to-bytes data))))

(defn- make-algorithm
  "Return an algorithm string suitable for JCE from a map of options."
  [options]
  (str "AES/" (options :mode) "/" (options :padding)))

(defn- make-cipher
  "Create an AES Cipher instance."
  [options]
  (Cipher/getInstance (make-algorithm options)))

(defn encrypt-bytes
  "Encrypts a byte array with the given key and encryption options."
  [options key data]
  (let [options    (merge encrypt-defaults options)
        cipher     (make-cipher options)
        secret-key (SecretKeySpec. key (options :algorithm))
        iv         (secure-random-bytes (.getBlockSize cipher))]
    (.init cipher Cipher/ENCRYPT_MODE secret-key (IvParameterSpec. iv))
    (to-bytes (concat iv (.doFinal cipher data)))))

(defn decrypt-bytes
  "Decrypts a byte array with the given key and encryption options."
  [options key data]
  (let [options    (merge encrypt-defaults options)
        cipher     (make-cipher options)
        [iv data]  (split-at (.getBlockSize cipher) data)
        iv-spec    (IvParameterSpec. (to-bytes iv))
        secret-key (SecretKeySpec. key (options :algorithm))]
    (.init cipher Cipher/DECRYPT_MODE secret-key iv-spec)
    (.doFinal cipher (to-bytes data))))

(defn encrypt
  "Encrypts a string or byte array with the given key and encryption options."
  [options key data]
  (base64-encode-bytes (encrypt-bytes options key (to-bytes data))))

(defn decrypt
  "Base64 encodes and encrypts a string with the given key and algorithm."
  [options key data]
  (String. (decrypt-bytes options key (base64-decode-bytes data))))

(defn seal
  "Seal a data structure into a cryptographically secure string. Ensures no-one
  looks at or tampers with the data inside."
  [key data]
  (let [data (encrypt {} key (marshal data))]
    (str data "--" (hmac {} key data))))

(defn unseal
  "Read a cryptographically sealed data structure."
  [key data]
  (let [[data mac] (.split data "--")]
    (if (= mac (hmac {} key data))
      (unmarshal (decrypt {} key data)))))