|
10 | 10 | (def ^{:doc "Convenience for java.util.Random"} |
11 | 11 | random (java.util.Random.)) |
12 | 12 |
|
13 | | -(defn- alphabetic-string |
14 | | - "Generate a random alphabetic string of the given length. |
15 | | -
|
16 | | - Chunks requests to stay within the BouncyCastle FIPS DRBG per-request limit |
17 | | - of 262144 bits. Apache Commons Lang3's CachedRandomBits allocates |
18 | | - (count * gapBits + 3) / 5 + 10 bytes per call. For nextAlphabetic |
19 | | - (A-Z + a-z = 52 chars, gapBits = ceil(log2(52)) = 6) the maximum count per call is: |
20 | | - (count * 6 + 3) / 5 + 10 <= 32768 => count <= 27298" |
21 | | - [length] |
22 | | - (let [fips-max-chunk 27298 |
23 | | - ^RandomStringUtils secure-random (RandomStringUtils/secure)] |
24 | | - (if (<= length fips-max-chunk) |
25 | | - (.nextAlphabetic secure-random length) |
26 | | - ;; Build incrementally to avoid intermediate vectors/strings for large values. |
27 | | - (let [^StringBuilder builder (StringBuilder. length)] |
28 | | - (loop [remaining length] |
29 | | - (if (<= remaining 0) |
30 | | - (.toString builder) |
31 | | - (let [chunk-size (min remaining fips-max-chunk)] |
32 | | - (.append builder (.nextAlphabetic secure-random chunk-size)) |
33 | | - (recur (- remaining chunk-size))))))))) |
34 | | - |
35 | | -(defn random-ascii-string |
36 | | - "Generate a random ASCII string of the given length. |
| 13 | +(defn- chunked-random-string |
| 14 | + "Generate a random alphabetic or ascii string of the given length. |
37 | 15 |
|
38 | 16 | Chunks requests to stay within the BouncyCastle FIPS DRBG per-request limit |
39 | 17 | of 262144 bits (32768 bytes). Apache Commons Lang3's RandomStringUtils |
40 | 18 | allocates a CachedRandomBits with size = (count * gapBits + 3) / 5 + 10 |
41 | | - bytes. For nextAscii (ASCII range 32-127, gap=95, gapBits=7) the maximum |
42 | | - count that keeps the cache within 32768 bytes is: |
43 | | - (count * 7 + 3) / 5 + 10 <= 32768 => count <= 23398" |
44 | | - [length] |
45 | | - (let [fips-max-chunk 23398 |
46 | | - ^RandomStringUtils secure-random (RandomStringUtils/secure)] |
| 19 | + bytes. |
| 20 | +
|
| 21 | + For nextAscii (ASCII range 32-127, gap=95, gapBits=7) |
| 22 | + the maximum count that keeps the cache within 32768 bytes is: |
| 23 | + (count * 7 + 3) / 5 + 10 <= 32768 => count <= 23398 |
| 24 | +
|
| 25 | + For nextAlphabetic (A-Z + a-z = 52 chars, gapBits = ceil(log2(52)) = 6) |
| 26 | + the maximum count per call is: |
| 27 | + (count * 6 + 3) / 5 + 10 <= 32768 => count <= 27298" |
| 28 | + [method length] |
| 29 | + (let [^RandomStringUtils secure-random (RandomStringUtils/secure) |
| 30 | + [gen-fn fips-max-chunk] (case method |
| 31 | + :ascii [#(.nextAscii ^RandomStringUtils secure-random %) 23398] |
| 32 | + :alphabetic [#(.nextAlphabetic ^RandomStringUtils secure-random %) 27298])] |
47 | 33 | (if (<= length fips-max-chunk) |
48 | | - (.nextAscii secure-random length) |
| 34 | + (gen-fn length) |
49 | 35 | ;; Build incrementally to avoid intermediate vectors/strings for large values. |
50 | 36 | (let [^StringBuilder builder (StringBuilder. length)] |
51 | 37 | (loop [remaining length] |
52 | 38 | (if (<= remaining 0) |
53 | 39 | (.toString builder) |
54 | 40 | (let [chunk-size (min remaining fips-max-chunk)] |
55 | | - (.append builder (.nextAscii secure-random chunk-size)) |
| 41 | + (.append builder (gen-fn chunk-size)) |
56 | 42 | (recur (- remaining chunk-size))))))))) |
57 | 43 |
|
| 44 | +(defn- alphabetic-string [length] |
| 45 | + (chunked-random-string :alphabetic length)) |
| 46 | + |
| 47 | +(defn random-ascii-string [length] |
| 48 | + (chunked-random-string :ascii length)) |
| 49 | + |
58 | 50 | (defn random-string |
59 | 51 | "Generate a random string of optional length" |
60 | 52 | ([] (alphabetic-string (inc (rand-int 10)))) |
|
0 commit comments