1- import { randomBytes } from "./deps.ts" ;
2- import { promisify } from "./deps.ts" ;
31import {
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
8892const 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 } ;
0 commit comments