Skip to content

Commit 70fc096

Browse files
Adam Kaliszswannodette
authored andcommitted
CLJS-2386 random-uuid should use a cryptographically strong PRNG if available
Using cryptographically strong PRNG in most cases closes the gap to the implementation in Clojure where the implementation always uses a cryptographically strong PRNG. Practical exploitation of Math.random predictability on V8 is known. Most modern platforms have a randomUUID method, however this works only in a secure context. The getRandomValues method works as a secure fallback. If none of that works, the Math.random based is used as a last resort. Dereferencing the internal uuid-gen-method unveils the uuid generation strategy.
1 parent 0fee1c0 commit 70fc096

1 file changed

Lines changed: 58 additions & 5 deletions

File tree

src/main/cljs/cljs/core.cljs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11730,9 +11730,27 @@ reduces them without incurring seq initialization"
1173011730
(assert (string? s))
1173111731
(UUID. (.toLowerCase s) nil))
1173211732

11733-
(defn random-uuid
11734-
"Returns a pseudo-randomly generated UUID instance (i.e. type 4)."
11735-
[]
11733+
(defn- random-uuid-crypto-uuid [crypto]
11734+
(uuid (.randomUUID crypto)))
11735+
11736+
(defn- random-uuid-crypto-values [crypto]
11737+
(let [buf (js/Uint16Array. 8)]
11738+
(.getRandomValues crypto buf)
11739+
(letfn [(^string quad-hex [i]
11740+
(let [unpadded-hex ^string (.toString (aget buf i) 16)]
11741+
(case (count unpadded-hex)
11742+
1 (str_ "000" unpadded-hex)
11743+
2 (str_ "00" unpadded-hex)
11744+
3 (str_ "0" unpadded-hex)
11745+
unpadded-hex)))]
11746+
(let [ver-tripple-hex ^string (.toString (bit-or 0x4000 (bit-and 0x0fff (aget buf 3))) 16)
11747+
res-tripple-hex ^string (.toString (bit-or 0x8000 (bit-and 0x3fff (aget buf 4))) 16)]
11748+
(uuid
11749+
(str_ (quad-hex 0) (quad-hex 1) "-" (quad-hex 2) "-"
11750+
ver-tripple-hex "-" res-tripple-hex "-"
11751+
(quad-hex 5) (quad-hex 6) (quad-hex 7)))))))
11752+
11753+
(defn- random-uuid-math-random []
1173611754
(letfn [(^string quad-hex []
1173711755
(let [unpadded-hex ^string (.toString (rand-int 65536) 16)]
1173811756
(case (count unpadded-hex)
@@ -11744,8 +11762,43 @@ reduces them without incurring seq initialization"
1174411762
res-tripple-hex ^string (.toString (bit-or 0x8000 (bit-and 0x3fff (rand-int 65536))) 16)]
1174511763
(uuid
1174611764
(str_ (quad-hex) (quad-hex) "-" (quad-hex) "-"
11747-
ver-tripple-hex "-" res-tripple-hex "-"
11748-
(quad-hex) (quad-hex) (quad-hex))))))
11765+
ver-tripple-hex "-" res-tripple-hex "-"
11766+
(quad-hex) (quad-hex) (quad-hex))))))
11767+
11768+
(def ^:private uuid-gen-method (volatile! nil))
11769+
(def ^:private uuid-crypto-obj (volatile! nil))
11770+
11771+
(defn- init-uuid-strategy! []
11772+
(let [crypto-obj (cond
11773+
(exists? js/crypto) js/crypto
11774+
(identical? *target* "nodejs") (try (js/require "crypto") (catch :default _ nil)))]
11775+
11776+
(vreset! uuid-crypto-obj crypto-obj)
11777+
(vreset! uuid-gen-method
11778+
(cond
11779+
(and (some? crypto-obj)
11780+
(exists? (.-randomUUID crypto-obj))
11781+
(fn? (.-randomUUID crypto-obj)))
11782+
:random-uuid-crypto-uuid
11783+
11784+
(and (some? crypto-obj)
11785+
(exists? (.-getRandomValues crypto-obj))
11786+
(fn? (.-getRandomValues crypto-obj)))
11787+
:random-uuid-crypto-values
11788+
11789+
:else
11790+
:random-uuid-math-random))))
11791+
11792+
(defn random-uuid
11793+
"Returns a pseudo-randomly generated UUID instance (i.e. type 4)."
11794+
[]
11795+
(when (nil? @uuid-gen-method)
11796+
(init-uuid-strategy!))
11797+
11798+
(case @uuid-gen-method
11799+
:random-uuid-crypto-uuid (random-uuid-crypto-uuid @uuid-crypto-obj)
11800+
:random-uuid-crypto-values (random-uuid-crypto-values @uuid-crypto-obj)
11801+
(random-uuid-math-random)))
1174911802

1175011803
(defn uuid?
1175111804
"Return true if x is a UUID."

0 commit comments

Comments
 (0)