From 6986c31bd0d30bd072b59ed0f81f0a887edeae35 Mon Sep 17 00:00:00 2001 From: Adam Lippai Date: Sun, 21 Jan 2018 15:02:01 +0100 Subject: [PATCH] Use ES6 classes for BigNum and modulus classes --- src/bignum/bignum.js | 928 ++++++++++++++++++++++++++++++------------ src/bignum/exports.js | 5 +- src/bignum/extgcd.js | 12 + src/bignum/modulus.js | 175 -------- src/bignum/prime.js | 162 -------- src/rsa/genkey.js | 9 +- src/rsa/rsa.js | 3 +- test/bignum.js | 1 - 8 files changed, 680 insertions(+), 615 deletions(-) delete mode 100644 src/bignum/modulus.js delete mode 100644 src/bignum/prime.js diff --git a/src/bignum/bignum.js b/src/bignum/bignum.js index 16af327..982032c 100644 --- a/src/bignum/bignum.js +++ b/src/bignum/bignum.js @@ -1,6 +1,8 @@ +import {Random_getValues} from '../random/random'; import {bigint_asm} from './bigint.asm'; import {is_buffer, is_bytes, is_number, is_string, string_to_bytes} from '../utils'; import {IllegalArgumentError} from '../errors'; +import {BigNumber_extGCD, Number_extGCD} from './extgcd'; export function is_big_number ( a ) { return ( a instanceof BigNumber_constructor ); @@ -12,6 +14,9 @@ export var _bigint_stdlib = { Uint32Array: Uint32Array, Math: Math }; export var _bigint_heap = new Uint32Array(0x100000); export var _bigint_asm; +// Small primes for trail division +const _primes = [ 2, 3 /* and so on, computed lazily */ ]; + function _half_imul ( a, b ) { return a * b | 0; } @@ -27,445 +32,834 @@ else { /////////////////////////////////////////////////////////////////////////////// -var _BigNumber_ZERO_limbs = new Uint32Array(0); +const _BigNumber_ZERO_limbs = new Uint32Array(0); -export function BigNumber_constructor (num ) { - var limbs = _BigNumber_ZERO_limbs, - bitlen = 0, - sign = 0; +export class BigNumber_constructor { + /** + * @param {BigNumber_constructor|string|number|Uint8Array} num + * @return {BigNumber_constructor} + */ + constructor(num ) { + let limbs = _BigNumber_ZERO_limbs; + let bitlen = 0; + let sign = 0; if ( is_string(num) ) - num = string_to_bytes(num); + num = string_to_bytes(num); if ( is_buffer(num) ) - num = new Uint8Array(num); + num = new Uint8Array(num); if ( num === undefined ) { - // do nothing + // do nothing } else if ( is_number(num) ) { - var absnum = Math.abs(num); - if ( absnum > 0xffffffff ) { - limbs = new Uint32Array(2); - limbs[0] = absnum|0; - limbs[1] = (absnum/0x100000000)|0; - bitlen = 52; - } - else if ( absnum > 0 ) { - limbs = new Uint32Array(1); - limbs[0] = absnum; - bitlen = 32; - } - else { - limbs = _BigNumber_ZERO_limbs; - bitlen = 0; - } - sign = num < 0 ? -1 : 1; + var absnum = Math.abs(num); + if ( absnum > 0xffffffff ) { + limbs = new Uint32Array(2); + limbs[0] = absnum|0; + limbs[1] = (absnum/0x100000000)|0; + bitlen = 52; + } + else if ( absnum > 0 ) { + limbs = new Uint32Array(1); + limbs[0] = absnum; + bitlen = 32; + } + else { + limbs = _BigNumber_ZERO_limbs; + bitlen = 0; + } + sign = num < 0 ? -1 : 1; } else if ( is_bytes(num) ) { - for ( var i = 0; !num[i]; i++ ); + for ( var i = 0; !num[i]; i++ ); - bitlen = ( num.length - i ) * 8; - if ( !bitlen ) - return BigNumber_ZERO; - - limbs = new Uint32Array( (bitlen + 31) >> 5 ); - for ( var j = num.length-4; j >= i ; j -= 4 ) { - limbs[(num.length-4-j)>>2] = (num[j] << 24) | (num[j+1] << 16) | (num[j+2] << 8) | num[j+3]; - } - if ( i-j === 3 ) { - limbs[limbs.length-1] = num[i]; - } - else if ( i-j === 2 ) { - limbs[limbs.length-1] = (num[i] << 8) | num[i+1]; - } - else if ( i-j === 1 ) { - limbs[limbs.length-1] = (num[i] << 16) | (num[i+1] << 8) | num[i+2]; - } + bitlen = ( num.length - i ) * 8; + if ( !bitlen ) + return BigNumber_ZERO; - sign = 1; + limbs = new Uint32Array( (bitlen + 31) >> 5 ); + for ( var j = num.length-4; j >= i ; j -= 4 ) { + limbs[(num.length-4-j)>>2] = (num[j] << 24) | (num[j+1] << 16) | (num[j+2] << 8) | num[j+3]; + } + if ( i-j === 3 ) { + limbs[limbs.length-1] = num[i]; + } + else if ( i-j === 2 ) { + limbs[limbs.length-1] = (num[i] << 8) | num[i+1]; + } + else if ( i-j === 1 ) { + limbs[limbs.length-1] = (num[i] << 16) | (num[i+1] << 8) | num[i+2]; + } + + sign = 1; } else if ( typeof num === 'object' && num !== null ) { - limbs = new Uint32Array( num.limbs ); - bitlen = num.bitLength; - sign = num.sign; + limbs = new Uint32Array( num.limbs ); + bitlen = num.bitLength; + sign = num.sign; } else { - throw new TypeError("number is of unexpected type"); + throw new TypeError("number is of unexpected type"); } this.limbs = limbs; this.bitLength = bitlen; this.sign = sign; -} + } -function BigNumber_toString ( radix ) { + toString(radix ) { radix = radix || 16; - var limbs = this.limbs, - bitlen = this.bitLength, - str = ''; + const limbs = this.limbs; + const bitlen = this.bitLength; + let str = ''; if ( radix === 16 ) { - // FIXME clamp last limb to (bitlen % 32) - for ( var i = (bitlen+31>>5)-1; i >= 0; i-- ) { - var h = limbs[i].toString(16); - str += '00000000'.substr(h.length); - str += h; - } + // FIXME clamp last limb to (bitlen % 32) + for ( var i = (bitlen+31>>5)-1; i >= 0; i-- ) { + var h = limbs[i].toString(16); + str += '00000000'.substr(h.length); + str += h; + } - str = str.replace( /^0+/, '' ); + str = str.replace( /^0+/, '' ); - if ( !str.length ) - str = '0'; + if ( !str.length ) + str = '0'; } else { - throw new IllegalArgumentError("bad radix"); + throw new IllegalArgumentError("bad radix"); } if ( this.sign < 0 ) - str = '-' + str; + str = '-' + str; return str; -} + } -function BigNumber_toBytes () { - var bitlen = this.bitLength, - limbs = this.limbs; + toBytes() { + const bitlen = this.bitLength; + const limbs = this.limbs; if ( bitlen === 0 ) - return new Uint8Array(0); + return new Uint8Array(0); - var bytelen = ( bitlen + 7 ) >> 3, - bytes = new Uint8Array(bytelen); - for ( var i = 0; i < bytelen; i++ ) { - var j = bytelen - i - 1; - bytes[i] = limbs[j>>2] >> ( (j & 3) << 3 ); + const bytelen = ( bitlen + 7 ) >> 3; + const bytes = new Uint8Array(bytelen); + for ( let i = 0; i < bytelen; i++ ) { + let j = bytelen - i - 1; + bytes[i] = limbs[j>>2] >> ( (j & 3) << 3 ); } return bytes; -} - -// Downgrade to Number -function BigNumber_valueOf () { - var limbs = this.limbs, - bits = this.bitLength, - sign = this.sign; - - if ( !sign ) + } + + /** + * Downgrade to Number + * + * @return {number} + */ + valueOf() { + const limbs = this.limbs; + const bits = this.bitLength; + const sign = this.sign; + + if ( !sign ) return 0; - if ( bits <= 32 ) + if ( bits <= 32 ) return sign * (limbs[0]>>>0); - if ( bits <= 52 ) + if ( bits <= 52 ) return sign * ( 0x100000000 * (limbs[1]>>>0) + (limbs[0]>>>0) ); - // normalization - var i, l, e = 0; - for ( i = limbs.length-1; i >= 0; i-- ) { + // normalization + let i, l, e = 0; + for ( i = limbs.length-1; i >= 0; i-- ) { if ( (l = limbs[i]) === 0 ) continue; while ( ( (l << e) & 0x80000000 ) === 0 ) e++; break; - } + } - if ( i === 0 ) + if ( i === 0 ) return sign * (limbs[0]>>>0); - return sign * ( 0x100000 * (( (limbs[i] << e) | ( e ? limbs[i-1] >>> (32-e) : 0 ) )>>>0) - + (( (limbs[i-1] << e) | ( e && i > 1 ? limbs[i-2] >>> (32-e) : 0 ) )>>>12) - ) * Math.pow( 2, 32*i-e-52 ); -} + return sign * ( 0x100000 * (( (limbs[i] << e) | ( e ? limbs[i-1] >>> (32-e) : 0 ) )>>>0) + + (( (limbs[i-1] << e) | ( e && i > 1 ? limbs[i-2] >>> (32-e) : 0 ) )>>>12) + ) * Math.pow( 2, 32*i-e-52 ); + } -function BigNumber_clamp ( b ) { - var limbs = this.limbs, - bitlen = this.bitLength; + /** + * @param {number} b + * @return {BigNumber_constructor} + */ + clamp(b ) { + const limbs = this.limbs; + const bitlen = this.bitLength; - // FIXME check b is number and in a valid range + // FIXME check b is number and in a valid range - if ( b >= bitlen ) + if ( b >= bitlen ) return this; - var clamped = new BigNumber_constructor, - n = (b + 31) >> 5, - k = b % 32; + const clamped = new BigNumber_constructor; + let n = (b + 31) >> 5; + let k = b % 32; - clamped.limbs = new Uint32Array( limbs.subarray(0,n) ); - clamped.bitLength = b; - clamped.sign = this.sign; + clamped.limbs = new Uint32Array( limbs.subarray(0,n) ); + clamped.bitLength = b; + clamped.sign = this.sign; - if ( k ) clamped.limbs[n-1] &= (-1 >>> (32-k)); + if ( k ) clamped.limbs[n-1] &= (-1 >>> (32-k)); - return clamped; -} + return clamped; + } -function BigNumber_slice ( f, b ) { - if ( !is_number(f) ) + /** + * @param {number} f + * @param {number} [b] + * @return {BigNumber_constructor} + */ + slice(f, b ) { + if ( !is_number(f) ) throw new TypeError("TODO"); - if ( b !== undefined && !is_number(b) ) + if ( b !== undefined && !is_number(b) ) throw new TypeError("TODO"); - var limbs = this.limbs, - bitlen = this.bitLength; + const limbs = this.limbs; + const bitlen = this.bitLength; - if ( f < 0 ) + if ( f < 0 ) throw new RangeError("TODO"); - if ( f >= bitlen ) + if ( f >= bitlen ) return BigNumber_ZERO; - if ( b === undefined || b > bitlen - f ) + if ( b === undefined || b > bitlen - f ) b = bitlen - f; - var sliced = new BigNumber_constructor, slimbs, - n = f >> 5, m = (f + b + 31) >> 5, l = (b + 31) >> 5, - t = f % 32, k = b % 32; - - slimbs = new Uint32Array(l); - if ( t ) { - for ( var i = 0; i < m-n-1; i++ ) { - slimbs[i] = (limbs[n+i]>>>t) | ( limbs[n+i+1]<<(32-t) ); + const sliced = new BigNumber_constructor; + let n = f >> 5; + let m = (f + b + 31) >> 5; + let l = (b + 31) >> 5; + let t = f % 32; + let k = b % 32; + + const slimbs = new Uint32Array(l); + if ( t ) { + for ( let i = 0; i < m-n-1; i++ ) { + slimbs[i] = (limbs[n+i]>>>t) | ( limbs[n+i+1]<<(32-t) ); } slimbs[i] = limbs[n+i]>>>t; - } - else { + } + else { slimbs.set( limbs.subarray(n, m) ); - } + } - if ( k ) { + if ( k ) { slimbs[l-1] &= (-1 >>> (32-k)); - } - - sliced.limbs = slimbs - sliced.bitLength = b; - sliced.sign = this.sign; - - return sliced; -} - -/////////////////////////////////////////////////////////////////////////////// - -function BigNumber_negate () { - var negative = new BigNumber_constructor; - - negative.limbs = this.limbs; - negative.bitLength = this.bitLength; - negative.sign = -1 * this.sign; - - return negative; -} - -function BigNumber_compare ( that ) { - if ( !is_big_number(that) ) + } + + sliced.limbs = slimbs; + sliced.bitLength = b; + sliced.sign = this.sign; + + return sliced; + } + + /** + * @return {BigNumber_constructor} + */ + negate() { + const negative = new BigNumber_constructor; + + negative.limbs = this.limbs; + negative.bitLength = this.bitLength; + negative.sign = -1 * this.sign; + + return negative; + } + + /** + * @param {BigNumber_constructor} that + * @return {number} + */ + compare(that ) { + if ( !is_big_number(that) ) that = new BigNumber_constructor(that); - var alimbs = this.limbs, alimbcnt = alimbs.length, + var alimbs = this.limbs, alimbcnt = alimbs.length, blimbs = that.limbs, blimbcnt = blimbs.length, z = 0; - if ( this.sign < that.sign ) + if ( this.sign < that.sign ) return -1; - if ( this.sign > that.sign ) + if ( this.sign > that.sign ) return 1; - _bigint_heap.set( alimbs, 0 ); - _bigint_heap.set( blimbs, alimbcnt ); - z = _bigint_asm.cmp( 0, alimbcnt<<2, alimbcnt<<2, blimbcnt<<2 ); + _bigint_heap.set( alimbs, 0 ); + _bigint_heap.set( blimbs, alimbcnt ); + z = _bigint_asm.cmp( 0, alimbcnt<<2, alimbcnt<<2, blimbcnt<<2 ); - return z * this.sign; -} + return z * this.sign; + } -function BigNumber_add ( that ) { - if ( !is_big_number(that) ) + /** + * @param {BigNumber_constructor} that + * @return {BigNumber_constructor} + */ + add(that ) { + if ( !is_big_number(that) ) that = new BigNumber_constructor(that); - if ( !this.sign ) + if ( !this.sign ) return that; - if ( !that.sign ) + if ( !that.sign ) return this; - var abitlen = this.bitLength, alimbs = this.limbs, alimbcnt = alimbs.length, asign = this.sign, + var abitlen = this.bitLength, alimbs = this.limbs, alimbcnt = alimbs.length, asign = this.sign, bbitlen = that.bitLength, blimbs = that.limbs, blimbcnt = blimbs.length, bsign = that.sign, rbitlen, rlimbcnt, rsign, rof, result = new BigNumber_constructor; - rbitlen = ( abitlen > bbitlen ? abitlen : bbitlen ) + ( asign * bsign > 0 ? 1 : 0 ); - rlimbcnt = ( rbitlen + 31 ) >> 5; + rbitlen = ( abitlen > bbitlen ? abitlen : bbitlen ) + ( asign * bsign > 0 ? 1 : 0 ); + rlimbcnt = ( rbitlen + 31 ) >> 5; - _bigint_asm.sreset(); + _bigint_asm.sreset(); - var pA = _bigint_asm.salloc( alimbcnt<<2 ), + var pA = _bigint_asm.salloc( alimbcnt<<2 ), pB = _bigint_asm.salloc( blimbcnt<<2 ), pR = _bigint_asm.salloc( rlimbcnt<<2 ); - _bigint_asm.z( pR-pA+(rlimbcnt<<2), 0, pA ); + _bigint_asm.z( pR-pA+(rlimbcnt<<2), 0, pA ); - _bigint_heap.set( alimbs, pA>>2 ); - _bigint_heap.set( blimbs, pB>>2 ); + _bigint_heap.set( alimbs, pA>>2 ); + _bigint_heap.set( blimbs, pB>>2 ); - if ( asign * bsign > 0 ) { + if ( asign * bsign > 0 ) { _bigint_asm.add( pA, alimbcnt<<2, pB, blimbcnt<<2, pR, rlimbcnt<<2 ); rsign = asign; - } - else if ( asign > bsign ) { + } + else if ( asign > bsign ) { rof = _bigint_asm.sub( pA, alimbcnt<<2, pB, blimbcnt<<2, pR, rlimbcnt<<2 ); rsign = rof ? bsign : asign; - } - else { + } + else { rof = _bigint_asm.sub( pB, blimbcnt<<2, pA, alimbcnt<<2, pR, rlimbcnt<<2 ); rsign = rof ? asign : bsign; - } + } - if ( rof ) + if ( rof ) _bigint_asm.neg( pR, rlimbcnt<<2, pR, rlimbcnt<<2 ); - if ( _bigint_asm.tst( pR, rlimbcnt<<2 ) === 0 ) + if ( _bigint_asm.tst( pR, rlimbcnt<<2 ) === 0 ) return BigNumber_ZERO; - result.limbs = new Uint32Array( _bigint_heap.subarray( pR>>2, (pR>>2)+rlimbcnt ) ); - result.bitLength = rbitlen; - result.sign = rsign; + result.limbs = new Uint32Array( _bigint_heap.subarray( pR>>2, (pR>>2)+rlimbcnt ) ); + result.bitLength = rbitlen; + result.sign = rsign; - return result; -} + return result; + } -function BigNumber_subtract ( that ) { - if ( !is_big_number(that) ) + /** + * @param {BigNumber_constructor} that + * @return {BigNumber_constructor} + */ + subtract(that ) { + if ( !is_big_number(that) ) that = new BigNumber_constructor(that); - return this.add( that.negate() ); -} + return this.add( that.negate() ); + } -function BigNumber_multiply ( that ) { - if ( !is_big_number(that) ) - that = new BigNumber_constructor(that); - - if ( !this.sign || !that.sign ) + /** + * @return {BigNumber_constructor} + */ + square() { + if ( !this.sign ) return BigNumber_ZERO; - var abitlen = this.bitLength, alimbs = this.limbs, alimbcnt = alimbs.length, - bbitlen = that.bitLength, blimbs = that.limbs, blimbcnt = blimbs.length, + var abitlen = this.bitLength, alimbs = this.limbs, alimbcnt = alimbs.length, rbitlen, rlimbcnt, result = new BigNumber_constructor; - rbitlen = abitlen + bbitlen; - rlimbcnt = ( rbitlen + 31 ) >> 5; + rbitlen = abitlen << 1; + rlimbcnt = ( rbitlen + 31 ) >> 5; - _bigint_asm.sreset(); + _bigint_asm.sreset(); - var pA = _bigint_asm.salloc( alimbcnt<<2 ), - pB = _bigint_asm.salloc( blimbcnt<<2 ), + var pA = _bigint_asm.salloc( alimbcnt<<2 ), pR = _bigint_asm.salloc( rlimbcnt<<2 ); - _bigint_asm.z( pR-pA+(rlimbcnt<<2), 0, pA ); + _bigint_asm.z( pR-pA+(rlimbcnt<<2), 0, pA ); - _bigint_heap.set( alimbs, pA>>2 ); - _bigint_heap.set( blimbs, pB>>2 ); + _bigint_heap.set( alimbs, pA>>2 ); - _bigint_asm.mul( pA, alimbcnt<<2, pB, blimbcnt<<2, pR, rlimbcnt<<2 ); + _bigint_asm.sqr( pA, alimbcnt<<2, pR ); - result.limbs = new Uint32Array( _bigint_heap.subarray( pR>>2, (pR>>2)+rlimbcnt ) ); - result.sign = this.sign * that.sign; - result.bitLength = rbitlen; + result.limbs = new Uint32Array( _bigint_heap.subarray( pR>>2, (pR>>2)+rlimbcnt ) ); + result.bitLength = rbitlen; + result.sign = 1; - return result; -} + return result; + } -function BigNumber_square () { - if ( !this.sign ) - return BigNumber_ZERO; + /** + * @param {BigNumber_constructor} that + * @return {{quotient: BigNumber_constructor, remainder: BigNumber_constructor}} + */ + divide(that ) { + if ( !is_big_number(that) ) + that = new BigNumber_constructor(that); + + var abitlen = this.bitLength, alimbs = this.limbs, alimbcnt = alimbs.length, + bbitlen = that.bitLength, blimbs = that.limbs, blimbcnt = blimbs.length, + qlimbcnt, rlimbcnt, quotient = BigNumber_ZERO, remainder = BigNumber_ZERO; + + _bigint_asm.sreset(); + + var pA = _bigint_asm.salloc( alimbcnt<<2 ), + pB = _bigint_asm.salloc( blimbcnt<<2 ), + pQ = _bigint_asm.salloc( alimbcnt<<2 ); + + _bigint_asm.z( pQ-pA+(alimbcnt<<2), 0, pA ); + + _bigint_heap.set( alimbs, pA>>2 ); + _bigint_heap.set( blimbs, pB>>2 ); + + _bigint_asm.div( pA, alimbcnt<<2, pB, blimbcnt<<2, pQ ); + + qlimbcnt = _bigint_asm.tst( pQ, alimbcnt<<2 )>>2; + if ( qlimbcnt ) { + quotient = new BigNumber_constructor; + quotient.limbs = new Uint32Array( _bigint_heap.subarray( pQ>>2, (pQ>>2)+qlimbcnt ) ); + quotient.bitLength = abitlen < (qlimbcnt<<5) ? abitlen : (qlimbcnt<<5); + quotient.sign = this.sign * that.sign; + } + + rlimbcnt = _bigint_asm.tst( pA, blimbcnt<<2 )>>2; + if ( rlimbcnt ) { + remainder = new BigNumber_constructor; + remainder.limbs = new Uint32Array( _bigint_heap.subarray( pA>>2, (pA>>2)+rlimbcnt ) ); + remainder.bitLength = bbitlen < (rlimbcnt<<5) ? bbitlen : (rlimbcnt<<5); + remainder.sign = this.sign; + } + + return { + quotient: quotient, + remainder: remainder + }; + } + + /** + * @param {BigNumber_constructor} that + * @return {BigNumber_constructor} + */ + multiply ( that ) { + if ( !is_big_number(that) ) + that = new BigNumber_constructor(that); + + if ( !this.sign || !that.sign ) + return BigNumber_ZERO; var abitlen = this.bitLength, alimbs = this.limbs, alimbcnt = alimbs.length, - rbitlen, rlimbcnt, result = new BigNumber_constructor; + bbitlen = that.bitLength, blimbs = that.limbs, blimbcnt = blimbs.length, + rbitlen, rlimbcnt, result = new BigNumber_constructor; - rbitlen = abitlen << 1; + rbitlen = abitlen + bbitlen; rlimbcnt = ( rbitlen + 31 ) >> 5; _bigint_asm.sreset(); var pA = _bigint_asm.salloc( alimbcnt<<2 ), - pR = _bigint_asm.salloc( rlimbcnt<<2 ); + pB = _bigint_asm.salloc( blimbcnt<<2 ), + pR = _bigint_asm.salloc( rlimbcnt<<2 ); _bigint_asm.z( pR-pA+(rlimbcnt<<2), 0, pA ); _bigint_heap.set( alimbs, pA>>2 ); + _bigint_heap.set( blimbs, pB>>2 ); - _bigint_asm.sqr( pA, alimbcnt<<2, pR ); + _bigint_asm.mul( pA, alimbcnt<<2, pB, blimbcnt<<2, pR, rlimbcnt<<2 ); result.limbs = new Uint32Array( _bigint_heap.subarray( pR>>2, (pR>>2)+rlimbcnt ) ); + result.sign = this.sign * that.sign; result.bitLength = rbitlen; - result.sign = 1; return result; + } + + /** + * @param {number} rounds + * @return {boolean} + * @private + */ + _isMillerRabinProbablePrime (rounds ) { + var t = new BigNumber_constructor(this), + s = 0; + t.limbs[0] -= 1; + while ( t.limbs[s>>5] === 0 ) s += 32; + while ( ( ( t.limbs[s>>5] >> (s & 31) ) & 1 ) === 0 ) s++; + t = t.slice(s); + + var m = new Modulus(this), + m1 = this.subtract(BigNumber_ONE), + a = new BigNumber_constructor(this), + l = this.limbs.length-1; + while ( a.limbs[l] === 0 ) l--; + + while ( --rounds >= 0 ) { + Random_getValues(a.limbs); + if ( a.limbs[0] < 2 ) a.limbs[0] += 2; + while ( a.compare(m1) >= 0 ) a.limbs[l] >>>= 1; + + var x = m.power( a, t ); + if ( x.compare(BigNumber_ONE) === 0 ) continue; + if ( x.compare(m1) === 0 ) continue; + + var c = s; + while ( --c > 0 ) { + x = x.square().divide(m).remainder; + if ( x.compare(BigNumber_ONE) === 0 ) return false; + if ( x.compare(m1) === 0 ) break; + } + + if ( c === 0 ) return false; + } + + return true; + } + + /** + * @param {number} paranoia + * @return {boolean} + */ + isProbablePrime ( paranoia ) { + paranoia = paranoia || 80; + + var limbs = this.limbs, + i = 0; + + // Oddity test + // (50% false positive probability) + if ( ( limbs[0] & 1 ) === 0 ) return false; + if ( paranoia <= 1 ) return true; + + // Magic divisors (3, 5, 17) test + // (~25% false positive probability) + var s3 = 0, s5 = 0, s17 = 0; + for ( i = 0; i < limbs.length; i++ ) { + var l3 = limbs[i]; + while ( l3 ) { + s3 += (l3 & 3); + l3 >>>= 2; + } + + var l5 = limbs[i]; + while ( l5 ) { + s5 += (l5 & 3); + l5 >>>= 2; + s5 -= (l5 & 3); + l5 >>>= 2; + } + + var l17 = limbs[i]; + while ( l17 ) { + s17 += (l17 & 15); + l17 >>>= 4; + s17 -= (l17 & 15); + l17 >>>= 4; + } + } + if ( !(s3 % 3) || !(s5 % 5) || !(s17 % 17) ) return false; + if ( paranoia <= 2 ) return true; + + // Miller-Rabin test + // (≤ 4^(-k) false positive probability) + return this._isMillerRabinProbablePrime(paranoia >>> 1 ); + } } -function BigNumber_divide ( that ) { - if ( !is_big_number(that) ) - that = new BigNumber_constructor(that); +export const BigNumber_ZERO = new BigNumber_constructor(0); +export const BigNumber_ONE = new BigNumber_constructor(1); + +/** + * Returns an array populated with first n primes. + * + * @param {number} n + * @return {number[]} + * @private + */ +function _small_primes ( n ) { + if ( _primes.length >= n ) + return _primes.slice( 0, n ); + + for ( let p = _primes[_primes.length-1] + 2; _primes.length < n; p += 2 ) { + for ( let i = 0, d = _primes[i]; d*d <= p; d = _primes[++i] ) { + if ( p % d == 0 ) break; + } + if ( d*d > p ) _primes.push(p); + } - var abitlen = this.bitLength, alimbs = this.limbs, alimbcnt = alimbs.length, - bbitlen = that.bitLength, blimbs = that.limbs, blimbcnt = blimbs.length, - qlimbcnt, rlimbcnt, quotient = BigNumber_ZERO, remainder = BigNumber_ZERO; + return _primes; +} - _bigint_asm.sreset(); +/** + * Returns strong pseudoprime of a specified bit length + * + * @param {number} bitlen + * @param {function(p: BigNumber_constructor): number} filter + * @return {BigNumber_constructor} + */ +export function randomProbablePrime ( bitlen, filter ) { + let limbcnt = (bitlen + 31) >> 5; + const prime = new BigNumber_constructor({ sign: 1, bitLength: bitlen, limbs: limbcnt }); + const limbs = prime.limbs; + + // Number of small divisors to try that minimizes the total cost of the trial division + // along with the first round of Miller-Rabin test for a certain bit length. + let k = 10000; + if ( bitlen <= 512 ) k = 2200; + if ( bitlen <= 256 ) k = 600; + + let divisors = _small_primes(k); + const remainders = new Uint32Array(k); + + // Number of Miller-Rabin iterations for an error rate of less than 2^-80 + // Damgaard, Landrock, Pomerance: Average case error estimates for the strong probable prime test. + const s = (bitlen * Math.LN2) | 0; + let r = 27; + if ( bitlen >= 250 ) r = 12; + if ( bitlen >= 450 ) r = 6; + if ( bitlen >= 850 ) r = 3; + if ( bitlen >= 1300 ) r = 2; + + while ( true ) { + // populate `prime` with random bits, clamp to the appropriate bit length + Random_getValues(limbs); + limbs[0] |= 1; + limbs[limbcnt-1] |= 1 << ((bitlen - 1) & 31); + if ( bitlen & 31 ) limbs[limbcnt-1] &= pow2_ceil((bitlen + 1) & 31) - 1; + + // remainders from division to small primes + remainders[0] = 1; + for ( let i = 1; i < k; i++ ) { + remainders[i] = prime.divide( divisors[i] ).remainder.valueOf(); + } - var pA = _bigint_asm.salloc( alimbcnt<<2 ), - pB = _bigint_asm.salloc( blimbcnt<<2 ), - pQ = _bigint_asm.salloc( alimbcnt<<2 ); + // try no more than `s` subsequent candidates + seek: + for ( let j = 0; j < s; j += 2, limbs[0] += 2 ) { + // check for small factors + for ( let i = 1; i < k; i++ ) { + if ( ( remainders[i] + j ) % divisors[i] === 0 ) continue seek; + } - _bigint_asm.z( pQ-pA+(alimbcnt<<2), 0, pA ); + // additional check just before the heavy lifting + if ( typeof filter === 'function' && !filter(prime) ) continue; - _bigint_heap.set( alimbs, pA>>2 ); - _bigint_heap.set( blimbs, pB>>2 ); + // proceed to Miller-Rabin test + if ( prime._isMillerRabinProbablePrime( r ) ) return prime; + } + } +} - _bigint_asm.div( pA, alimbcnt<<2, pB, blimbcnt<<2, pQ ); +export class Modulus extends BigNumber_constructor { + /** + * Modulus + * + * @param {BigNumber_constructor|string|number|Uint8Array} number + */ + constructor(number) { + super(number); + + if (this.valueOf() < 1) + throw new RangeError(); + + if (this.bitLength <= 32) + return; + + let comodulus; + + if (this.limbs[0] & 1) { + const bitlen = ((this.bitLength + 31) & -32) + 1; + const limbs = new Uint32Array((bitlen + 31) >> 5); + limbs[limbs.length - 1] = 1; + comodulus = new BigNumber_constructor(); + comodulus.sign = 1; + comodulus.bitLength = bitlen; + comodulus.limbs = limbs; + + const k = Number_extGCD(0x100000000, this.limbs[0]).y; + this.coefficient = k < 0 ? -k : 0x100000000 - k; + } + else { + /** + * TODO even modulus reduction + * Modulus represented as `N = 2^U * V`, where `V` is odd and thus `GCD(2^U, V) = 1`. + * Calculation `A = TR' mod V` is made as for odd modulo using Montgomery method. + * Calculation `B = TR' mod 2^U` is easy as modulus is a power of 2. + * Using Chinese Remainder Theorem and Garner's Algorithm restore `TR' mod N` from `A` and `B`. + */ + return; + } - qlimbcnt = _bigint_asm.tst( pQ, alimbcnt<<2 )>>2; - if ( qlimbcnt ) { - quotient = new BigNumber_constructor; - quotient.limbs = new Uint32Array( _bigint_heap.subarray( pQ>>2, (pQ>>2)+qlimbcnt ) ); - quotient.bitLength = abitlen < (qlimbcnt<<5) ? abitlen : (qlimbcnt<<5); - quotient.sign = this.sign * that.sign; + this.comodulus = comodulus; + this.comodulusRemainder = comodulus.divide(this).remainder; + this.comodulusRemainderSquare = comodulus.square().divide(this).remainder; + } + + /** + * Modular reduction + * + * @param {BigNumber_constructor} a + * @return {BigNumber_constructor} + * @constructor + */ + reduce(a) { + if (!is_big_number(a)) + a = new BigNumber_constructor(a); + + if (a.bitLength <= 32 && this.bitLength <= 32) + return new BigNumber_constructor(a.valueOf() % this.valueOf()); + + if (a.compare(this) < 0) + return a; + + return a.divide(this).remainder; + } + + /** + * Modular inverse + * + * @param {BigNumber_constructor} a + * @return {BigNumber_constructor} + * @constructor + */ + inverse(a) { + a = this.reduce(a); + + const r = BigNumber_extGCD(this, a); + if (r.gcd.valueOf() !== 1) return null; + + if (r.y.sign < 0) return r.y.add(this).clamp(this.bitLength); + + return r.y; + } + + /** + * Modular exponentiation + * + * @param {BigNumber_constructor} g + * @param {BigNumber_constructor} e + * @return {BigNumber_constructor} + * @constructor + */ + power(g, e) { + if (!is_big_number(g)) + g = new BigNumber_constructor(g); + + if (!is_big_number(e)) + e = new BigNumber_constructor(e); + + // count exponent set bits + let c = 0; + for (let i = 0; i < e.limbs.length; i++) { + let t = e.limbs[i]; + while (t) { + if (t & 1) c++; + t >>>= 1; + } } - rlimbcnt = _bigint_asm.tst( pA, blimbcnt<<2 )>>2; - if ( rlimbcnt ) { - remainder = new BigNumber_constructor; - remainder.limbs = new Uint32Array( _bigint_heap.subarray( pA>>2, (pA>>2)+rlimbcnt ) );; - remainder.bitLength = bbitlen < (rlimbcnt<<5) ? bbitlen : (rlimbcnt<<5); - remainder.sign = this.sign; + // window size parameter + let k = 8; + if (e.bitLength <= 4536) k = 7; + if (e.bitLength <= 1736) k = 6; + if (e.bitLength <= 630) k = 5; + if (e.bitLength <= 210) k = 4; + if (e.bitLength <= 60) k = 3; + if (e.bitLength <= 12) k = 2; + if (c <= (1 << (k - 1))) k = 1; + + // montgomerize base + g = _Montgomery_reduce(this.reduce(g).multiply(this.comodulusRemainderSquare), this); + + // precompute odd powers + const g2 = _Montgomery_reduce(g.square(), this), + gn = new Array(1 << (k - 1)); + gn[0] = g; + gn[1] = _Montgomery_reduce(g.multiply(g2), this); + for (let i = 2; i < (1 << (k - 1)); i++) { + gn[i] = _Montgomery_reduce(gn[i - 1].multiply(g2), this); } - return { - quotient: quotient, - remainder: remainder - }; + // perform exponentiation + const u = this.comodulusRemainder; + let r = u; + for (let i = e.limbs.length - 1; i >= 0; i--) { + let t = e.limbs[i]; + for (let j = 32; j > 0;) { + if (t & 0x80000000) { + let n = t >>> (32 - k), l = k; + while ((n & 1) === 0) { + n >>>= 1; + l--; + } + var m = gn[n >>> 1]; + while (n) { + n >>>= 1; + if (r !== u) r = _Montgomery_reduce(r.square(), this); + } + r = (r !== u) ? _Montgomery_reduce(r.multiply(m), this) : m; + t <<= l, j -= l; + } + else { + if (r !== u) r = _Montgomery_reduce(r.square(), this); + t <<= 1, j--; + } + } + } + + // de-montgomerize result + return _Montgomery_reduce(r, this); + } } -/////////////////////////////////////////////////////////////////////////////// +/** + * @param {BigNumber_constructor} a + * @param {BigNumber_constructor} n + * @return {BigNumber_constructor} + * @private + */ +function _Montgomery_reduce(a, n) { + const alimbs = a.limbs; + const alimbcnt = alimbs.length; + const nlimbs = n.limbs; + const nlimbcnt = nlimbs.length; + const y = n.coefficient; -var BigNumberPrototype = BigNumber_constructor.prototype = new Number; -BigNumberPrototype.toString = BigNumber_toString; -BigNumberPrototype.toBytes = BigNumber_toBytes; -BigNumberPrototype.valueOf = BigNumber_valueOf; -BigNumberPrototype.clamp = BigNumber_clamp; -BigNumberPrototype.slice = BigNumber_slice; + _bigint_asm.sreset(); -/////////////////////////////////////////////////////////////////////////////// + const pA = _bigint_asm.salloc(alimbcnt << 2), + pN = _bigint_asm.salloc(nlimbcnt << 2), + pR = _bigint_asm.salloc(nlimbcnt << 2); -BigNumberPrototype.negate = BigNumber_negate; -BigNumberPrototype.compare = BigNumber_compare; -BigNumberPrototype.add = BigNumber_add; -BigNumberPrototype.subtract = BigNumber_subtract; -BigNumberPrototype.multiply = BigNumber_multiply; -BigNumberPrototype.square = BigNumber_square; -BigNumberPrototype.divide = BigNumber_divide; + _bigint_asm.z(pR - pA + (nlimbcnt << 2), 0, pA); -/////////////////////////////////////////////////////////////////////////////// + _bigint_heap.set(alimbs, pA >> 2); + _bigint_heap.set(nlimbs, pN >> 2); -export var BigNumber_ZERO = new BigNumber_constructor(0); -export var BigNumber_ONE = new BigNumber_constructor(1); + _bigint_asm.mredc(pA, alimbcnt << 2, pN, nlimbcnt << 2, y, pR); -Object.freeze(BigNumber_ZERO); -Object.freeze(BigNumber_ONE); + const result = new BigNumber_constructor(); + result.limbs = new Uint32Array(_bigint_heap.subarray(pR >> 2, (pR >> 2) + nlimbcnt)); + result.bitLength = n.bitLength; + result.sign = 1; + + return result; +} diff --git a/src/bignum/exports.js b/src/bignum/exports.js index 427ec16..a9b035e 100644 --- a/src/bignum/exports.js +++ b/src/bignum/exports.js @@ -1,6 +1,5 @@ -import {BigNumber_constructor, BigNumber_ONE, BigNumber_ZERO} from './bignum'; +import {BigNumber_constructor, BigNumber_ONE, BigNumber_ZERO, Modulus} from './bignum'; import {BigNumber_extGCD} from './extgcd'; -import "./prime"; export var BigNumber = BigNumber_constructor; BigNumber.ZERO = BigNumber_ZERO; @@ -8,4 +7,4 @@ BigNumber.ONE = BigNumber_ONE; BigNumber.extGCD = BigNumber_extGCD; -export { Modulus } from "./modulus"; +export { Modulus }; diff --git a/src/bignum/extgcd.js b/src/bignum/extgcd.js index bcfd50e..754fe7c 100644 --- a/src/bignum/extgcd.js +++ b/src/bignum/extgcd.js @@ -1,5 +1,11 @@ import {BigNumber_constructor, BigNumber_ONE, BigNumber_ZERO, is_big_number} from './bignum'; +/** + * @param {number} a + * @param {number} b + * @return {{gcd: number, x: number, y: number}} + * @constructor + */ export function Number_extGCD (a, b ) { var sa = ( a < 0 ) ? -1 : 1, sb = ( b < 0 ) ? -1 : 1, @@ -39,6 +45,12 @@ export function Number_extGCD (a, b ) { }; } +/** + * @param a + * @param b + * @return {{gcd: BigNumber_constructor, x: BigNumber_constructor, y: BigNumber_constructor}} + * @constructor + */ export function BigNumber_extGCD ( a, b ) { if ( !is_big_number(a) ) a = new BigNumber_constructor(a); diff --git a/src/bignum/modulus.js b/src/bignum/modulus.js deleted file mode 100644 index 0e7385b..0000000 --- a/src/bignum/modulus.js +++ /dev/null @@ -1,175 +0,0 @@ -import {BigNumber_constructor, is_big_number} from './bignum'; -import {BigNumber_extGCD, Number_extGCD} from './extgcd'; -import {_bigint_asm, _bigint_heap} from './bignum' - -/** - * Modulus - */ -export function Modulus () { - BigNumber_constructor.apply( this, arguments ); - - if ( this.valueOf() < 1 ) - throw new RangeError(); - - if ( this.bitLength <= 32 ) - return; - - var comodulus; - - if ( this.limbs[0] & 1 ) { - var bitlen = ( (this.bitLength+31) & -32 ) + 1, limbs = new Uint32Array( (bitlen+31) >> 5 ); - limbs[limbs.length-1] = 1; - comodulus = new BigNumber_constructor(); - comodulus.sign = 1; - comodulus.bitLength = bitlen; - comodulus.limbs = limbs; - - var k = Number_extGCD( 0x100000000, this.limbs[0] ).y; - this.coefficient = k < 0 ? -k : 0x100000000-k; - } - else { - /** - * TODO even modulus reduction - * Modulus represented as `N = 2^U * V`, where `V` is odd and thus `GCD(2^U, V) = 1`. - * Calculation `A = TR' mod V` is made as for odd modulo using Montgomery method. - * Calculation `B = TR' mod 2^U` is easy as modulus is a power of 2. - * Using Chinese Remainder Theorem and Garner's Algorithm restore `TR' mod N` from `A` and `B`. - */ - return; - } - - this.comodulus = comodulus; - this.comodulusRemainder = comodulus.divide(this).remainder; - this.comodulusRemainderSquare = comodulus.square().divide(this).remainder; -} - -/** - * Modular reduction - */ -function Modulus_reduce ( a ) { - if ( !is_big_number(a) ) - a = new BigNumber_constructor(a); - - if ( a.bitLength <= 32 && this.bitLength <= 32 ) - return new BigNumber_constructor( a.valueOf() % this.valueOf() ); - - if ( a.compare(this) < 0 ) - return a; - - return a.divide(this).remainder; -} - -/** - * Modular inverse - */ -function Modulus_inverse ( a ) { - a = this.reduce(a); - - var r = BigNumber_extGCD( this, a ); - if ( r.gcd.valueOf() !== 1 ) return null; - - r = r.y; - if ( r.sign < 0 ) r = r.add(this).clamp(this.bitLength); - - return r; -} - -/** - * Modular exponentiation - */ -function Modulus_power ( g, e ) { - if ( !is_big_number(g) ) - g = new BigNumber_constructor(g); - - if ( !is_big_number(e) ) - e = new BigNumber_constructor(e); - - // count exponent set bits - var c = 0; - for ( var i = 0; i < e.limbs.length; i++ ) { - var t = e.limbs[i]; - while ( t ) { - if ( t & 1 ) c++; - t >>>= 1; - } - } - - // window size parameter - var k = 8; - if ( e.bitLength <= 4536 ) k = 7; - if ( e.bitLength <= 1736 ) k = 6; - if ( e.bitLength <= 630 ) k = 5; - if ( e.bitLength <= 210 ) k = 4; - if ( e.bitLength <= 60 ) k = 3; - if ( e.bitLength <= 12 ) k = 2; - if ( c <= (1 << (k-1)) ) k = 1; - - // montgomerize base - g = _Montgomery_reduce( this.reduce(g).multiply(this.comodulusRemainderSquare), this ); - - // precompute odd powers - var g2 = _Montgomery_reduce( g.square(), this ), - gn = new Array( 1 << (k-1) ); - gn[0] = g; - gn[1] = _Montgomery_reduce( g.multiply(g2), this ); - for ( var i = 2; i < (1 << (k-1)); i++ ) { - gn[i] = _Montgomery_reduce( gn[i-1].multiply(g2), this ); - } - - // perform exponentiation - var u = this.comodulusRemainder, - r = u; - for ( var i = e.limbs.length-1; i >= 0; i-- ) { - var t = e.limbs[i]; - for ( var j = 32; j > 0; ) { - if ( t & 0x80000000 ) { - var n = t >>> (32-k), l = k; - while ( (n & 1) === 0 ) { n >>>= 1; l--; } - var m = gn[n>>>1]; - while ( n ) { n >>>= 1; if ( r !== u ) r = _Montgomery_reduce( r.square(), this ); } - r = ( r !== u ) ? _Montgomery_reduce( r.multiply(m), this ) : m; - t <<= l, j -= l; - } - else { - if ( r !== u ) r = _Montgomery_reduce( r.square(), this ); - t <<= 1, j--; - } - } - } - - // de-montgomerize result - r = _Montgomery_reduce( r, this ); - - return r; -} - -function _Montgomery_reduce ( a, n ) { - var alimbs = a.limbs, alimbcnt = alimbs.length, - nlimbs = n.limbs, nlimbcnt = nlimbs.length, - y = n.coefficient; - - _bigint_asm.sreset(); - - var pA = _bigint_asm.salloc( alimbcnt<<2 ), - pN = _bigint_asm.salloc( nlimbcnt<<2 ), - pR = _bigint_asm.salloc( nlimbcnt<<2 ); - - _bigint_asm.z( pR-pA+(nlimbcnt<<2), 0, pA ); - - _bigint_heap.set( alimbs, pA>>2 ); - _bigint_heap.set( nlimbs, pN>>2 ); - - _bigint_asm.mredc( pA, alimbcnt<<2, pN, nlimbcnt<<2, y, pR ); - - var result = new BigNumber_constructor(); - result.limbs = new Uint32Array( _bigint_heap.subarray( pR>>2, (pR>>2)+nlimbcnt ) ); - result.bitLength = n.bitLength; - result.sign = 1; - - return result; -} - -var ModulusPrototype = Modulus.prototype = new BigNumber_constructor; -ModulusPrototype.reduce = Modulus_reduce; -ModulusPrototype.inverse = Modulus_inverse; -ModulusPrototype.power = Modulus_power; diff --git a/src/bignum/prime.js b/src/bignum/prime.js deleted file mode 100644 index 952cba6..0000000 --- a/src/bignum/prime.js +++ /dev/null @@ -1,162 +0,0 @@ -// Tests if the number supplied is a Miller-Rabin strong probable prime -import {BigNumber_constructor, BigNumber_ONE} from './bignum'; -import {pow2_ceil} from '../utils'; -import {Random_getValues} from '../random/random'; -import {Modulus} from './modulus'; - -function _BigNumber_isMillerRabinProbablePrime (rounds ) { - var t = new BigNumber_constructor(this), - s = 0; - t.limbs[0] -= 1; - while ( t.limbs[s>>5] === 0 ) s += 32; - while ( ( ( t.limbs[s>>5] >> (s & 31) ) & 1 ) === 0 ) s++; - t = t.slice(s); - - var m = new Modulus(this), - m1 = this.subtract(BigNumber_ONE), - a = new BigNumber_constructor(this), - l = this.limbs.length-1; - while ( a.limbs[l] === 0 ) l--; - - while ( --rounds >= 0 ) { - Random_getValues(a.limbs); - if ( a.limbs[0] < 2 ) a.limbs[0] += 2; - while ( a.compare(m1) >= 0 ) a.limbs[l] >>>= 1; - - var x = m.power( a, t ); - if ( x.compare(BigNumber_ONE) === 0 ) continue; - if ( x.compare(m1) === 0 ) continue; - - var c = s; - while ( --c > 0 ) { - x = x.square().divide(m).remainder; - if ( x.compare(BigNumber_ONE) === 0 ) return false; - if ( x.compare(m1) === 0 ) break; - } - - if ( c === 0 ) return false; - } - - return true; -} - -function BigNumber_isProbablePrime ( paranoia ) { - paranoia = paranoia || 80; - - var limbs = this.limbs, - i = 0; - - // Oddity test - // (50% false positive probability) - if ( ( limbs[0] & 1 ) === 0 ) return false; - if ( paranoia <= 1 ) return true; - - // Magic divisors (3, 5, 17) test - // (~25% false positive probability) - var s3 = 0, s5 = 0, s17 = 0; - for ( i = 0; i < limbs.length; i++ ) { - var l3 = limbs[i]; - while ( l3 ) { - s3 += (l3 & 3); - l3 >>>= 2; - } - - var l5 = limbs[i]; - while ( l5 ) { - s5 += (l5 & 3); - l5 >>>= 2; - s5 -= (l5 & 3); - l5 >>>= 2; - } - - var l17 = limbs[i]; - while ( l17 ) { - s17 += (l17 & 15); - l17 >>>= 4; - s17 -= (l17 & 15); - l17 >>>= 4; - } - } - if ( !(s3 % 3) || !(s5 % 5) || !(s17 % 17) ) return false; - if ( paranoia <= 2 ) return true; - - // Miller-Rabin test - // (≤ 4^(-k) false positive probability) - return _BigNumber_isMillerRabinProbablePrime.call( this, paranoia >>> 1 ); -} - -// Small primes for trail division -var _primes = [ 2, 3 /* and so on, computed lazily */ ]; - -// Returns an array populated with first n primes. -function _small_primes ( n ) { - if ( _primes.length >= n ) - return _primes.slice( 0, n ); - - for ( var p = _primes[_primes.length-1] + 2; _primes.length < n; p += 2 ) { - for ( var i = 0, d = _primes[i]; d*d <= p; d = _primes[++i] ) { - if ( p % d == 0 ) break; - } - if ( d*d > p ) _primes.push(p); - } - - return _primes; -} - -// Returns strong pseudoprime of a specified bit length -export function BigNumber_randomProbablePrime ( bitlen, filter ) { - var limbcnt = (bitlen + 31) >> 5, - prime = new BigNumber_constructor({ sign: 1, bitLength: bitlen, limbs: limbcnt }), - limbs = prime.limbs; - - // Number of small divisors to try that minimizes the total cost of the trial division - // along with the first round of Miller-Rabin test for a certain bit length. - var k = 10000; - if ( bitlen <= 512 ) k = 2200; - if ( bitlen <= 256 ) k = 600; - - var divisors = _small_primes(k), - remainders = new Uint32Array(k); - - // Number of Miller-Rabin iterations for an error rate of less than 2^-80 - // Damgaard, Landrock, Pomerance: Average case error estimates for the strong probable prime test. - var s = (bitlen * Math.LN2) | 0, - r = 27; - if ( bitlen >= 250 ) r = 12; - if ( bitlen >= 450 ) r = 6; - if ( bitlen >= 850 ) r = 3; - if ( bitlen >= 1300 ) r = 2; - - while ( true ) { - // populate `prime` with random bits, clamp to the appropriate bit length - Random_getValues(limbs); - limbs[0] |= 1; - limbs[limbcnt-1] |= 1 << ((bitlen - 1) & 31); - if ( bitlen & 31 ) limbs[limbcnt-1] &= pow2_ceil((bitlen + 1) & 31) - 1; - - // remainders from division to small primes - remainders[0] = 1; - for ( var i = 1; i < k; i++ ) { - remainders[i] = prime.divide( divisors[i] ).remainder.valueOf(); - } - - // try no more than `s` subsequent candidates - seek: - for ( var j = 0; j < s; j += 2, limbs[0] += 2 ) { - // check for small factors - for ( var i = 1; i < k; i++ ) { - if ( ( remainders[i] + j ) % divisors[i] === 0 ) continue seek; - } - - // additional check just before the heavy lifting - if ( typeof filter === 'function' && !filter(prime) ) continue; - - // proceed to Miller-Rabin test - if ( _BigNumber_isMillerRabinProbablePrime.call( prime, r ) ) return prime; - } - } -} - -BigNumber_constructor.prototype.isProbablePrime = BigNumber_isProbablePrime; - -BigNumber_constructor.randomProbablePrime = BigNumber_randomProbablePrime; diff --git a/src/rsa/genkey.js b/src/rsa/genkey.js index 739a68d..00e53c8 100644 --- a/src/rsa/genkey.js +++ b/src/rsa/genkey.js @@ -5,10 +5,9 @@ * @param e public exponent, default is 65537 */ import {RSA} from './rsa'; -import {BigNumber_randomProbablePrime} from '../bignum/prime'; +import {randomProbablePrime} from '../bignum/bignum'; import {BigNumber_extGCD} from '../bignum/extgcd'; -import {BigNumber_constructor, is_big_number} from '../bignum/bignum'; -import {Modulus} from '../bignum/modulus'; +import {BigNumber_constructor, is_big_number, Modulus} from '../bignum/bignum'; import {is_buffer, is_bytes, is_number, is_string, string_to_bytes} from '../utils'; import {IllegalArgumentError} from '../errors'; @@ -37,7 +36,7 @@ export function RSA_generateKey (bitlen, e ) { var m, e, d, p, q, p1, q1, dp, dq, u; - p = BigNumber_randomProbablePrime( + p = randomProbablePrime( bitlen >> 1, function ( p ) { p1 = new BigNumber_constructor(p); p1.limbs[0] -= 1; @@ -45,7 +44,7 @@ export function RSA_generateKey (bitlen, e ) { } ); - q = BigNumber_randomProbablePrime( + q = randomProbablePrime( bitlen - (bitlen >> 1), function ( q ) { m = new Modulus( p.multiply(q) ); diff --git a/src/rsa/rsa.js b/src/rsa/rsa.js index 826a84a..e3b59eb 100644 --- a/src/rsa/rsa.js +++ b/src/rsa/rsa.js @@ -1,5 +1,4 @@ -import {Modulus} from '../bignum/modulus'; -import {BigNumber_constructor, is_big_number} from '../bignum/bignum'; +import {BigNumber_constructor, is_big_number, Modulus} from '../bignum/bignum'; import {is_buffer, is_bytes, is_string, string_to_bytes} from '../utils'; import {IllegalStateError} from '../errors'; diff --git a/test/bignum.js b/test/bignum.js index efcb3e7..8f681b2 100644 --- a/test/bignum.js +++ b/test/bignum.js @@ -247,7 +247,6 @@ if ( typeof asmCrypto.BigNumber !== 'undefined' ) var M = new asmCrypto.Modulus(123456789); ok( M, "new Modulus" ); - ok( M instanceof Number, "instanceof Number" ); equal( M.reduce(987654321).valueOf(), 9, "Modulus.reduce(small)" ); var M2 = new asmCrypto.Modulus(0xabcdabcdabcd);