@@ -16,6 +16,31 @@ const THIRTY = new BigInteger(null)
1616THIRTY . fromInt ( 30 )
1717const 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