|
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. |
| 13 | +(defn- chunked-random-string |
| 14 | + "Generate a random alphabetic or ascii string of the given length. |
15 | 15 |
|
16 | 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)] |
| 17 | + of 262144 bits (32768 bytes). Apache Commons Lang3's RandomStringUtils |
| 18 | + allocates a CachedRandomBits with size = (count * gapBits + 3) / 5 + 10 |
| 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])] |
24 | 33 | (if (<= length fips-max-chunk) |
25 | | - (.nextAlphabetic secure-random length) |
| 34 | + (gen-fn length) |
26 | 35 | ;; Build incrementally to avoid intermediate vectors/strings for large values. |
27 | 36 | (let [^StringBuilder builder (StringBuilder. length)] |
28 | 37 | (loop [remaining length] |
29 | 38 | (if (<= remaining 0) |
30 | 39 | (.toString builder) |
31 | 40 | (let [chunk-size (min remaining fips-max-chunk)] |
32 | | - (.append builder (.nextAlphabetic secure-random chunk-size)) |
| 41 | + (.append builder (gen-fn chunk-size)) |
33 | 42 | (recur (- remaining chunk-size))))))))) |
34 | 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 | + |
35 | 50 | (defn random-string |
36 | 51 | "Generate a random string of optional length" |
37 | | - ([] (.nextAlphabetic (RandomStringUtils/secure) (inc (rand-int 10)))) |
| 52 | + ([] (alphabetic-string (inc (rand-int 10)))) |
38 | 53 | ([length] |
39 | 54 | (alphabetic-string length))) |
40 | 55 |
|
41 | 56 | (defn random-string-alpha |
42 | 57 | "Generate a random string of optional length, only lower case alphabet chars" |
43 | | - ([] (random-string (inc (rand-int 10)))) |
| 58 | + ([] (random-string-alpha (inc (rand-int 10)))) |
44 | 59 | ([length] |
45 | 60 | (.toLowerCase (alphabetic-string length)))) |
46 | 61 |
|
|
0 commit comments