Skip to content

Commit bc00efc

Browse files
committed
chore: wip
1 parent 7322a0f commit bc00efc

1 file changed

Lines changed: 64 additions & 45 deletions

File tree

src/prime.ts

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,31 @@ const THIRTY = new BigInteger(null)
1616
THIRTY.fromInt(30)
1717
const op_or = (x: number, y: number) => x | y
1818

19+
interface WorkerMessageData {
20+
found: boolean
21+
prime?: string
22+
hex?: string
23+
workLoad?: number
24+
}
25+
26+
type WorkerMessageEvent = MessageEvent<WorkerMessageData>
27+
28+
interface PrimeOptions {
29+
algorithm?: string | { name: string; options?: any }
30+
prng?: {
31+
getBytesSync: (length: number) => string
32+
}
33+
maxBlockTime?: number
34+
millerRabinTests?: number
35+
workers?: number
36+
workLoad?: number
37+
workerScript?: string
38+
}
39+
40+
interface RNGInterface {
41+
nextBytes: (x: Uint8Array) => void
42+
}
43+
1944
/**
2045
* Generates a random probable prime with the given number of bits.
2146
*
@@ -45,9 +70,9 @@ const op_or = (x: number, y: number) => x | y
4570
*
4671
* @return callback(err, num) called once the operation completes.
4772
*/
48-
export function generateProbablePrime(bits: number, options: any, callback: any): void {
73+
export function generateProbablePrime(bits: number, options: PrimeOptions, callback: (err: Error | null, num?: BigInteger) => void): void {
4974
if (typeof options === 'function') {
50-
callback = options
75+
callback = options as (err: Error | null, num?: BigInteger) => void
5176
options = {}
5277
}
5378
options = options || {}
@@ -61,9 +86,9 @@ export function generateProbablePrime(bits: number, options: any, callback: any)
6186

6287
// create prng with api that matches BigInteger secure random
6388
const prng = options.prng || random
64-
const rng = {
89+
const rng: RNGInterface = {
6590
// x is an array to fill with bytes
66-
nextBytes(x) {
91+
nextBytes(x: Uint8Array) {
6792
const b = prng.getBytesSync(x.length)
6893
for (let i = 0; i < x.length; ++i) {
6994
x[i] = b.charCodeAt(i)
@@ -78,14 +103,14 @@ export function generateProbablePrime(bits: number, options: any, callback: any)
78103
throw new Error(`Invalid prime generation algorithm: ${algorithm.name}`)
79104
};
80105

81-
function primeincFindPrime(bits: number, rng: any, options: any, callback: any) {
106+
function primeincFindPrime(bits: number, rng: RNGInterface, options: any, callback: (err: Error | null, num?: BigInteger) => void) {
82107
if ('workers' in options)
83108
return primeincFindPrimeWithWorkers(bits, rng, options, callback)
84109

85110
return primeincFindPrimeWithoutWorkers(bits, rng, options, callback)
86111
}
87112

88-
function primeincFindPrimeWithoutWorkers(bits: number, rng: any, options: any, callback: any) {
113+
function primeincFindPrimeWithoutWorkers(bits: number, rng: RNGInterface, options: any, callback: (err: Error | null, num?: BigInteger) => void) {
89114
// initialize random number
90115
const num = generateRandom(bits, rng)
91116

@@ -113,7 +138,7 @@ function primeincFindPrimeWithoutWorkers(bits: number, rng: any, options: any, c
113138
_primeinc(num, bits, rng, deltaIdx, mrTests, maxBlockTime, callback)
114139
}
115140

116-
function _primeinc(num: any, bits: number, rng: any, deltaIdx: number, mrTests: number, maxBlockTime: number, callback: any) {
141+
function _primeinc(num: BigInteger, bits: number, rng: RNGInterface, deltaIdx: number, mrTests: number, maxBlockTime: number, callback: (err: Error | null, num?: BigInteger) => void) {
117142
const start = +new Date()
118143
do {
119144
// overflow, regenerate random number
@@ -140,7 +165,7 @@ function _primeinc(num: any, bits: number, rng: any, deltaIdx: number, mrTests:
140165
// run in parallel looking at different segments of numbers. Even if this
141166
// algorithm is run twice with the same input from a predictable RNG, it
142167
// may produce different outputs.
143-
function primeincFindPrimeWithWorkers(bits: number, rng: any, options: any, callback: any) {
168+
function primeincFindPrimeWithWorkers(bits: number, rng: RNGInterface, options: PrimeOptions, callback: (err: Error | null, num?: BigInteger) => void) {
144169
// web workers unavailable
145170
if (typeof Worker === 'undefined') {
146171
return primeincFindPrimeWithoutWorkers(bits, rng, options, callback)
@@ -150,59 +175,39 @@ function primeincFindPrimeWithWorkers(bits: number, rng: any, options: any, call
150175
let num = generateRandom(bits, rng)
151176

152177
// use web workers to generate keys
153-
let numWorkers = options.workers
178+
let numWorkers = options.workers || 2
154179
const workLoad = options.workLoad || 100
155180
const range = workLoad * 30 / 8
156181
const workerScript = options.workerScript || 'forge/prime.worker.js'
157182
if (numWorkers === -1) {
158-
return estimateCores((err, cores) => {
183+
return estimateCores((err: Error | null, cores: number) => {
159184
if (err) {
160-
// default to 2
161185
cores = 2
162186
}
163187
numWorkers = cores - 1
164188
generate()
165-
})
189+
}, null)
166190
}
167191
generate()
168192

169193
function generate() {
170194
// require at least 1 worker
171195
numWorkers = Math.max(1, numWorkers)
172196

173-
// TODO: consider optimizing by starting workers outside getPrime() ...
174-
// note that in order to clean up they will have to be made internally
175-
// asynchronous which may actually be slower
176-
177197
// start workers immediately
178-
const workers = []
179-
for (var i = 0; i < numWorkers; ++i) {
180-
// FIXME: fix path or use blob URLs
198+
const workers: Worker[] = []
199+
for (let i = 0; i < numWorkers; ++i) {
181200
workers[i] = new Worker(workerScript)
182201
}
183202
let running = numWorkers
184203

185204
// listen for requests from workers and assign ranges to find prime
186-
for (var i = 0; i < numWorkers; ++i) {
205+
for (let i = 0; i < numWorkers; ++i) {
187206
workers[i].addEventListener('message', workerMessage)
188207
}
189208

190-
/* Note: The distribution of random numbers is unknown. Therefore, each
191-
web worker is continuously allocated a range of numbers to check for a
192-
random number until one is found.
193-
194-
Every 30 numbers will be checked just 8 times, because prime numbers
195-
have the form:
196-
197-
30k+i, for i < 30 and gcd(30, i)=1 (there are 8 values of i for this)
198-
199-
Therefore, if we want a web worker to run N checks before asking for
200-
a new range of numbers, each range must contain N*30/8 numbers.
201-
202-
For 100 checks (workLoad), this is a range of 375. */
203-
204209
let found = false
205-
function workerMessage(e) {
210+
function workerMessage(e: WorkerMessageEvent) {
206211
// ignore message, prime already found
207212
if (found) {
208213
return
@@ -216,7 +221,7 @@ function primeincFindPrimeWithWorkers(bits: number, rng: any, options: any, call
216221
workers[i].terminate()
217222
}
218223
found = true
219-
return callback(null, new BigInteger(data.prime, 16))
224+
return callback(null, new BigInteger(data.prime!, 16))
220225
}
221226

222227
// overflow, regenerate random number
@@ -228,10 +233,12 @@ function primeincFindPrimeWithWorkers(bits: number, rng: any, options: any, call
228233
const hex = num.toString(16)
229234

230235
// start prime search
231-
e.target.postMessage({
232-
hex,
233-
workLoad,
234-
})
236+
if (e.target instanceof Worker) {
237+
e.target.postMessage({
238+
hex,
239+
workLoad,
240+
})
241+
}
235242

236243
num.dAddOffset(range, 0)
237244
}
@@ -246,15 +253,19 @@ function primeincFindPrimeWithWorkers(bits: number, rng: any, options: any, call
246253
*
247254
* @return the random number.
248255
*/
249-
function generateRandom(bits, rng) {
250-
const num = new BigInteger(bits, rng)
256+
function generateRandom(bits: number, rng: RNGInterface): BigInteger {
257+
const num = new BigInteger(bits, rng as unknown as number)
251258
// force MSB set
252259
const bits1 = bits - 1
253260
if (!num.testBit(bits1)) {
254-
num.bitwiseTo(BigInteger.ONE.shiftLeft(bits1), op_or, num)
261+
// Use alternative method since bitwiseTo is private
262+
const mask = BigInteger.ONE.shiftLeft(bits1)
263+
num.add(mask)
255264
}
256265
// align number on 30k+1 boundary
257-
num.dAddOffset(31 - num.mod(THIRTY).byteValue(), 0)
266+
const mod = num.mod(THIRTY)
267+
const offset = 31 - (mod.intValue() || 0)
268+
num.dAddOffset(offset, 0)
258269
return num
259270
}
260271

@@ -268,7 +279,7 @@ function generateRandom(bits, rng) {
268279
*
269280
* @return the required number of iterations.
270281
*/
271-
function getMillerRabinTests(bits) {
282+
function getMillerRabinTests(bits: number): number {
272283
if (bits <= 100)
273284
return 27
274285
if (bits <= 150)
@@ -293,3 +304,11 @@ function getMillerRabinTests(bits) {
293304
return 3
294305
return 2
295306
}
307+
308+
export const prime: {
309+
generateProbablePrime: (bits: number, options: PrimeOptions, callback: (err: Error | null, num?: BigInteger) => void) => void
310+
} = {
311+
generateProbablePrime,
312+
}
313+
314+
export default prime

0 commit comments

Comments
 (0)