-
Notifications
You must be signed in to change notification settings - Fork 0
/
webcrypto-random-int.js
163 lines (140 loc) · 4.02 KB
/
webcrypto-random-int.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// MIT license. https://github.com/bddjr/webcrypto-random-int
export const canIUse = !!globalThis.crypto?.getRandomValues;
export const notRandomTools = {
/**
* Bytes to bigint, not ramdom.
*
* It throw error if (!(bytes instanceof Uint8Array)).
*
* @param {Uint8Array} bytes
* @returns {bigint}
*/
uint8ArrayToBigint(bytes) {
if (!(bytes instanceof Uint8Array))
throw "webcrypto-random-int notRandomTools uint8ArrayToBigint: argument bytes is not Uint8Array";
var n = globalThis.BigInt(bytes[0]);
for (let i = 1; i < bytes.length; i++) {
n <<= 8n;
n |= globalThis.BigInt(bytes[i]);
}
return n;
},
/**
* Get bigint bitLength, not random.
*
* @param {bigint} n
* @returns {number}
*/
bigintGetBitlen(n) {
if (typeof n !== "bigint")
throw "webcrypto-random-int notRandomTools bigintGetBitlen: argument max is not bigint";
var bitLen = 0;
while (n > 0n) {
n >>= 1n;
bitLen++;
}
return bitLen;
},
};
/**
* Int returns a uniform random value in [0,max], excluding max itself.
*
* It throw error if (!canIUse || !Number.isInteger(max) || max <= 0 || max-1 > 0xffffffff) .
*
* @param {number} max
* @returns {number}
*/
export function numInt(max) {
if (!canIUse) throw "webcrypto-random-int numInt: webcrypto undefined";
if (!Number.isInteger(max))
throw "webcrypto-random-int numInt: argument max is not integer";
if (max <= 0) throw "webcrypto-random-int numInt: argument max is <= 0";
const n = max - 1;
if (n === 0) return 0;
if (n > 0xffffffff)
throw "webcrypto-random-int numInt: argument max-1 is > 0xffffffff";
var b = n;
b |= b >> 1;
b |= b >> 2;
b |= b >> 4;
/** @type {Uint8Array | Uint16Array | Uint32Array} */
var arr;
if (n <= 0xff) {
arr = new Uint8Array(1);
} else {
b |= b >> 8;
if (n <= 0xffff) {
arr = new Uint16Array(1);
} else {
b |= b >> 16;
arr = new Uint32Array(1);
}
}
while (true) {
globalThis.crypto.getRandomValues(arr);
// Clear bits in the first byte to increase the probability
// that the candidate is < max.
arr[0] &= b;
if (arr[0] < max) return arr[0];
}
}
/**
* bigInt returns a uniform random value in [0,max], excluding max itself.
*
* It throw error if (!canIUse || typeof max !== "bigint" || max <= 0).
*
* @param {bigint} max
* @returns {bigint}
*/
export function bigInt(max) {
if (!canIUse) throw "webcrypto-random-int numInt: webcrypto undefined";
if (typeof max !== "bigint")
throw "webcrypto-random-int numInt: argument max is not bigint";
if (max <= 0) throw "webcrypto-random-int numInt: argument max is <= 0";
var n = max - 1n;
if (n === 0n) return 0n;
// bitLen is the maximum bit length needed to encode a value < max.
var bitLen = notRandomTools.bigintGetBitlen(n);
// k is the maximum byte length needed to encode a value < max.
const k = Math.ceil(bitLen / 8);
// b is the number of bits in the most significant byte of max-1.
const b = (1 << (bitLen % 8 || 8)) - 1;
const bytes = new Uint8Array(k);
while (true) {
globalThis.crypto.getRandomValues(bytes);
// Clear bits in the first byte to increase the probability
// that the candidate is < max.
bytes[0] &= b;
n = notRandomTools.uint8ArrayToBigint(bytes);
if (n < max) return n;
}
}
/**
* ```
* str(32, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
* ```
*
* It throw error if (typeof letters !== "string" || !Number.isInteger(length) || !canIUse).
*
* @param {number} length
* @param {string} letters
* @returns {string}
*/
export function str(length, letters) {
if (typeof letters !== "string")
throw "webcrypto-random-int str: argument letters is not string";
if (!Number.isInteger(length))
throw "webcrypto-random-int str: argument length is not integer";
var str = "";
for (let i = 0; i < length; i++) {
str += letters[numInt(letters.length)];
}
return str;
}
export default {
canIUse,
notRandomTools,
numInt,
bigInt,
str,
};