Skip to content

Commit 1d2c2e2

Browse files
committed
Remove std/node dependency
1 parent 8d062bc commit 1d2c2e2

File tree

3 files changed

+54
-61
lines changed

3 files changed

+54
-61
lines changed

cryptoRandomString.ts

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { randomBytes } from "./deps.ts";
2-
import { promisify } from "./deps.ts";
31
import {
42
ALLOWED_TYPES,
53
ALPHANUMERIC_CHARACTERS,
@@ -8,40 +6,50 @@ import {
86
NUMERIC_CHARACTERS,
97
URL_SAFE_CHARACTERS,
108
} from "./constants.ts";
9+
import { encodeToBase64, encodeToHex } from "./deps.ts";
1110

12-
const randomBytesAsync = promisify(randomBytes);
13-
14-
const generateForCustomCharacters = (length: number, characters: string[]) => {
15-
// Generating entropy is faster than complex math operations, so we use the simplest way
16-
const characterCount = characters.length;
17-
const maxValidSelector =
18-
(Math.floor(0x10000 / characterCount) * characterCount) - 1; // Using values above this will ruin distribution when using modular division
19-
const entropyLength = 2 * Math.ceil(1.1 * length); // Generating a bit more than required so chances we need more than one pass will be really low
20-
let string = "";
21-
let stringLength = 0;
22-
23-
while (stringLength < length) { // In case we had many bad values, which may happen for character sets of size above 0x8000 but close to it
24-
const entropy = randomBytes(entropyLength);
25-
let entropyPosition = 0;
11+
export interface GenerateRandomBytes {
12+
(
13+
byteLength: number,
14+
type: "hex" | "base64",
15+
length: number,
16+
): string;
17+
}
18+
19+
export interface GenerateForCustomCharacters {
20+
(length: number, characters: string[]): string;
21+
}
22+
23+
export const MAX_RANDOM_VALUES = 65536;
24+
export const MAX_SIZE = 4294967295;
25+
26+
function randomBytes(size: number) {
27+
if (size > MAX_SIZE) {
28+
throw new RangeError(
29+
`The value of "size" is out of range. It must be >= 0 && <= ${MAX_SIZE}. Received ${size}`,
30+
);
31+
}
2632

27-
while (entropyPosition < entropyLength && stringLength < length) {
28-
const entropyValue = entropy.readUInt16LE(entropyPosition);
29-
entropyPosition += 2;
30-
if (entropyValue > maxValidSelector) { // Skip values which will ruin distribution when using modular division
31-
continue;
32-
}
33+
const bytes = new Uint8Array(size);
3334

34-
string += characters[entropyValue % characterCount];
35-
stringLength++;
35+
//Work around for getRandomValues max generation
36+
if (size > MAX_RANDOM_VALUES) {
37+
for (let generated = 0; generated < size; generated += MAX_RANDOM_VALUES) {
38+
crypto.getRandomValues(
39+
bytes.subarray(generated, generated + MAX_RANDOM_VALUES),
40+
);
3641
}
42+
} else {
43+
crypto.getRandomValues(bytes);
3744
}
3845

39-
return string;
40-
};
46+
console.log(bytes);
47+
return bytes;
48+
}
4149

42-
const generateForCustomCharactersAsync = async (
43-
length: number,
44-
characters: string[],
50+
const generateForCustomCharacters: GenerateForCustomCharacters = (
51+
length,
52+
characters,
4553
) => {
4654
// Generating entropy is faster than complex math operations, so we use the simplest way
4755
const characterCount = characters.length;
@@ -52,11 +60,16 @@ const generateForCustomCharactersAsync = async (
5260
let stringLength = 0;
5361

5462
while (stringLength < length) { // In case we had many bad values, which may happen for character sets of size above 0x8000 but close to it
55-
const entropy = await randomBytesAsync(entropyLength); // eslint-disable-line no-await-in-loop
63+
const entropy = randomBytes(entropyLength);
5664
let entropyPosition = 0;
5765

5866
while (entropyPosition < entropyLength && stringLength < length) {
59-
const entropyValue = entropy.readUInt16LE(entropyPosition);
67+
const entropyValue = new DataView(
68+
entropy.buffer,
69+
entropy.byteOffset,
70+
entropy.byteLength,
71+
)
72+
.getUint16(entropyPosition, true);
6073
entropyPosition += 2;
6174
if (entropyValue > maxValidSelector) { // Skip values which will ruin distribution when using modular division
6275
continue;
@@ -70,24 +83,15 @@ const generateForCustomCharactersAsync = async (
7083
return string;
7184
};
7285

73-
const generateRandomBytes = (
74-
byteLength: number,
75-
type: string,
76-
length: number,
77-
) => randomBytes(byteLength).toString(type).slice(0, length);
78-
79-
const generateRandomBytesAsync = async (
80-
byteLength: number,
81-
type: string,
82-
length: number,
83-
) => {
84-
const buffer = await randomBytesAsync(byteLength);
85-
return buffer.toString(type).slice(0, length);
86+
const generateRandomBytes: GenerateRandomBytes = (byteLength, type, length) => {
87+
const bytes = randomBytes(byteLength);
88+
const str = type === "base64" ? encodeToBase64(bytes) : encodeToHex(bytes);
89+
return str.slice(0, length);
8690
};
8791

8892
const createGenerator = (
89-
generateForCustomCharacters: Function,
90-
generateRandomBytes: Function,
93+
generateForCustomCharacters: GenerateForCustomCharacters,
94+
generateRandomBytes: GenerateRandomBytes,
9195
) =>
9296
(
9397
{ length, type, characters }: {
@@ -165,9 +169,5 @@ const cryptoRandomString = createGenerator(
165169
generateForCustomCharacters,
166170
generateRandomBytes,
167171
);
168-
const cryptoRandomStringAsync = createGenerator(
169-
generateForCustomCharactersAsync,
170-
generateRandomBytesAsync,
171-
);
172172

173-
export { cryptoRandomString, cryptoRandomStringAsync };
173+
export { cryptoRandomString };

deps.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export { randomBytes } from "https://deno.land/std@0.83.0/node/crypto.ts";
2-
export { promisify } from "https://deno.land/std@0.83.0/node/util.ts";
1+
export { encodeToString as encodeToHex } from "https://deno.land/std@0.99.0/encoding/hex.ts";
2+
export { encode as encodeToBase64 } from "https://deno.land/std@0.99.0/encoding/base64.ts";

mod_test.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { assertEquals, assertMatch, assertThrows } from "./test_deps.ts";
2-
import { cryptoRandomString, cryptoRandomStringAsync } from "./mod.ts";
2+
import { cryptoRandomString } from "./mod.ts";
33

44
// Probabilistic, result is always less than or equal to actual set size, chance it is less is below 1e-256 for sizes up to 32656
55
const generatedCharacterSetSize = (
@@ -25,13 +25,6 @@ Deno.test("main", () => {
2525
assertEquals(generatedCharacterSetSize({}, 16), 16);
2626
});
2727

28-
Deno.test("async", async () => {
29-
assertEquals((await cryptoRandomStringAsync({ length: 0 })).length, 0);
30-
assertEquals((await cryptoRandomStringAsync({ length: 10 })).length, 10);
31-
assertEquals((await cryptoRandomStringAsync({ length: 100 })).length, 100);
32-
assertMatch(await cryptoRandomStringAsync({ length: 100 }), /^[a-f\d]*$/);
33-
});
34-
3528
Deno.test("hex", () => {
3629
assertEquals(cryptoRandomString({ length: 0, type: "hex" }).length, 0);
3730
assertEquals(cryptoRandomString({ length: 10, type: "hex" }).length, 10);

0 commit comments

Comments
 (0)