forked from EntropyString/JavaScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrandom.js
More file actions
148 lines (122 loc) · 3.96 KB
/
random.js
File metadata and controls
148 lines (122 loc) · 3.96 KB
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
import CharSet, {charSet32} from './charSet'
import Entropy from './entropy'
import WeakMap from 'weak-map'
const propMap = new WeakMap()
const BITS_PER_BYTE = 8
export default class {
constructor(arg) {
let charSet
if (arg === undefined) {
charSet = charSet32
}
else if (arg instanceof CharSet) {
charSet = arg
}
else if ((typeof arg === 'string' || arg instanceof String)) {
charSet = new CharSet(arg)
}
else {
throw new Error('Invalid arg: must be either valid CharSet or valid chars')
}
const hideProps = {
charSet
}
propMap.set(this, hideProps)
}
smallID(charSet = propMap.get(this).charSet) {
return this.string(29, charSet)
}
mediumID(charSet = propMap.get(this).charSet) {
return this.string(69, charSet)
}
largeID(charSet = propMap.get(this).charSet) {
return this.string(99, charSet)
}
sessionID(charSet = propMap.get(this).charSet) {
return this.string(128, charSet)
}
token(charSet = propMap.get(this).charSet) {
return this.string(256, charSet)
}
string(entropyBits, charSet = propMap.get(this).charSet) {
let bytesNeeded = charSet.bytesNeeded(entropyBits)
return this.stringWithBytes(entropyBits, _cryptoBytes(bytesNeeded), charSet)
}
stringRandom(entropyBits, charSet = propMap.get(this).charSet) {
let bytesNeeded = charSet.bytesNeeded(entropyBits)
return this.stringWithBytes(entropyBits, _randomBytes(bytesNeeded), charSet)
}
stringWithBytes(entropyBits, bytes, charSet = propMap.get(this).charSet) {
return _stringWithBytes(entropyBits, bytes, charSet)
}
bytesNeeded(entropyBits, charSet = propMap.get(this).charSet) {
return charSet.bytesNeeded(entropyBits)
}
chars() {
return propMap.get(this).charSet.chars()
}
use(charSet) {
if (!(charSet instanceof CharSet)) { throw new Error('Invalid CharSet') }
propMap.get(this).charSet = charSet
}
useChars(chars) {
if (!(typeof chars === 'string' || chars instanceof String)) {
throw new Error('Invalid chars: Must be string')
}
this.use(new CharSet(chars))
}
}
const _stringWithBytes = (entropyBits, bytes, charSet) => {
if (entropyBits <= 0) { return '' }
const bitsPerChar = charSet.getBitsPerChar()
const count = Math.ceil(entropyBits / bitsPerChar)
if (count <= 0) { return '' }
const need = Math.ceil(count * (bitsPerChar / BITS_PER_BYTE))
if (bytes.length < need) {
throw new Error('Insufficient bytes: need ' + need + ' and got ' + bytes.length)
}
const charsPerChunk = charSet.getCharsPerChunk()
const chunks = Math.floor(count / charsPerChunk)
const partials = count % charsPerChunk
const ndxFn = charSet.getNdxFn()
const chars = charSet.getChars()
let string = ''
for (let chunk = 0; chunk < chunks; chunk++) {
for (let slice = 0; slice < charsPerChunk; slice++) {
let ndx = ndxFn(chunk, slice, bytes)
string += chars[ndx]
}
}
for (let slice = 0; slice < partials; slice++) {
let ndx = ndxFn(chunks, slice, bytes)
string += chars[ndx]
}
return string
}
const _cryptoBytes = (count) => {
const crypto = require('crypto')
return Buffer.from(crypto.randomBytes(count))
}
const _randomBytes = (count) => {
let BYTES_USED_PER_RANDOM_CALL = 6
const randCount = Math.ceil(count / BYTES_USED_PER_RANDOM_CALL)
const buffer = new Buffer(count)
var dataView = new DataView(new ArrayBuffer(BITS_PER_BYTE))
for (let rNum = 0; rNum < randCount; rNum++) {
dataView.setFloat64(0, Math.random())
for (let n = 0; n < BYTES_USED_PER_RANDOM_CALL; n++) {
let fByteNum = _endianByteNum[n]
let bByteNum = rNum*BYTES_USED_PER_RANDOM_CALL + n
if (bByteNum < count) {
buffer[bByteNum] = dataView.getUint8(fByteNum)
}
}
}
return buffer
}
const _endianByteNum = (() => {
const buf32 = new Uint32Array(1)
const buf8 = new Uint8Array(buf32.buffer)
buf32[0] = 0xff
return (buf8[0] === 0xff) ? [2,3,4,5,6,7] : [0,1,2,3,6,7]
})()