Provides the core large integer arithmetic routines needed to implement multiplicative groups and elliptic curve groups over prime order fields. No additional functionality is provided. Although the main goal of this module is to be well-documented and clearly structured with proper encapsulation and without hidden assumptions, this is quite hard in a few routines.
WARNING! This module must be used with care due to the assumptions made by routines on inputs, but these assumptions are stated explicitly for each function, so the code is easy to follow.
Integers are represented as arrays of numbers constrained to WORDSIZE bits, where WORDSIZE is any even number between 4 and 30 and there are hardcoded constants derived from this when the script is generated, so do not attempt to change the wordsize in the generated code. These wordsizes are natural since JavaScript only allows bit operations on 32-bit signed integers. To see this, note that although we can do arithmetic on floating point numbers, e.g., by setting WORDSIZE = 24 we could do multiplications directly, it is expensive to recover parts of the result. Bit operations on 32-bit integers are provided in Javascript, but they are implemented on top of the native "number" datatype, i.e., numbers are cast to 32-bit signed integers, the bit operation is applied, and the result is cast back to a "number".
Using small wordsizes exposes certain types of arithmetic bugs, so providing this is not merely for educational purposes, it is also to lower the risk of structural bugs.
Functions are only implemented for unsigned integers and when called from external functions they assume that any result parameter is of a given length. All arithmetic functions guarantee that any leading unused words are set to zero.
A "limb" is an element of an array that may or may not store any single-precision integer. A word is a limb containing data, which may be zero if there are limbs at higher indices holding data. Thus, the number of limbs is the length of an array and the number of words is the index of the most significant word in the array plus one.
The workhorse routine is muladd_loop() which is generated for a given fixed wordsize. This routine determines the speed of multiplication and squaring. To a large extent it also determines the speed of division, but here div3by2() also plays an important role. These routines are generated from M4 macro code to allow using hard coded wordsize dependent constants for increased speed. The square_naive() routine also contains some generated code.
JavaScript is inherently difficult to optimize, since the JavaScript engines are moving targets, but it seems that the built-in arrays in Javascript are faster than the new typed arrays if they are handled properly. A first version of the library was based on Uint32Array for which, e.g., allocation of a fixed-size array is slower than a builtin array.
One notable observation is that it sometimes makes sense to inform the interpreter that a JavaScript "number" / float is really a 32-bit integer by saying, e.g., (x | 0) even if we are guaranteed that x is a 32-bit integer. This is important when accessing elements from arrays and it seems to prevent the interpreter from converting to and from floats.
We avoid dynamic memory allocation almost entirely by keeping scratch space as static variables of the functions. This is implemented using immediate function evaluation in JavaScript, but it is encapsulated to reduce complexity, i.e., calling functions remain unaware of this. This approach works well in our applications, since higher level routines work with integers of fixed bit length;
Handbook of Cryptography (HAC), Alfred J. Menezes, Paul C. van Oorschot and Scott A. Vanstone gives a straightforward introduction to the basic algorithms used and we try to follow their notation for easy reference. Division exploits the techniques of Improved division by invariant integers, Niels Moller and Torbjorn Granlund (MG). This is needed to implement div3by2() efficiently.
Reference | Operation | Comment |
---|---|---|
HAC 14.7. | Addition | |
HAC 14.9. | Subtraction | |
HAC 14.12. | Multiplication | Uses Karatsuba. |
HAC 14.16. | Squaring | Uses Karatsuba. |
HAC 14.20 and MG. | Division. | Uses reciprocals for invariant moduli. |
HAC 14.83. | Modular exponentiation | Left-to-right k-ary. |
- Source:
Methods
(static) add(w, x, y)
ASSUMES: x and y are positive and have B and B' bits and w can store (B + B' + 1) bits. A natural choice in general is to let w have (L + L' + 1) limbs if x and y have L and L' limbs, but the number of limbs can be arbitrary.
References: HAC 14.7.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Left term. | |
y |
Right term. |
- Source:
(static) cmp(x, x)
ASSUMES: x and y are positive.
Parameters:
Name | Type | Description |
---|---|---|
x |
Left array. | |
x |
Right array. |
- Source:
Returns:
(static) copyarray(x, len)
Parameters:
Name | Type | Description |
---|---|---|
x |
Original array. | |
len |
Maximal length of copy. |
- Source:
Returns:
(static) div3by2(r, u, d, neg_d, v)
ASSUMES: most significant bit of d is set, i.e., we have 2^(2 * 28)/2 <= d < 2^(2*28).
References: Algorithm DIV3BY2 in MG.
Parameters:
Name | Type | Description |
---|---|---|
r |
Two-word integer that ends up holding the remainder. | |
u |
Three-word dividend. | |
d |
Normalized divisor. | |
neg_d |
Negative of d in two's complement. | |
v |
3by2 reciprocal of d. |
- Source:
Returns:
(static) div_qr(q, x, y)
ASSUMES: x and y are positive, x has L words and at least L + 2 limbs (i.e., two leading unused zero words), y has L' limbs, and q has at least L'' = max{L - L', 0} + 1 limbs and will finally hold a result with at most L'' words and a leading zero limb.
References: HAC 14.20.
Parameters:
Name | Type | Description |
---|---|---|
q |
Holder of quotient. | |
x |
Divident and holder of remainder at end of computation. | |
y |
Divisor. |
- Source:
(static) getbit(x, index)
Parameters:
Name | Type | Description |
---|---|---|
x |
Array containing bit. | |
index |
Index of bit. |
- Source:
Returns:
(static) getuh(uh, x, i, wordsize)
Parameters:
Name | Type | Description |
---|---|---|
uh |
Holds the representation of word. | |
x |
Contains bits. | |
i |
Index of block of bits. | |
wordsize |
Number of bits in each block. |
- Source:
(static) hex(x)
Parameters:
Name | Type | Description |
---|---|---|
x |
Array of words. |
- Source:
Returns:
(static) iszero(x)
Parameters:
Name | Type | Description |
---|---|---|
x |
Array to inspect. |
- Source:
Returns:
(static) karatsuba_split(l, h, x)
Parameters:
Name | Type | Description |
---|---|---|
l |
Array holding most significant words of x. | |
h |
Array holding most significant words of x. | |
x |
Original array. |
- Source:
(static) lsbit(Array)
Parameters:
Name | Type | Description |
---|---|---|
Array |
containing bit. |
- Source:
Returns:
(static) modpow(w, b, e, m)
ASSUMES: b >= 0, e >= 0, and m > 1, and w, b and m have L limbs.
References: HAC 14.83.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
b |
Basis integer. | |
e |
Exponent. | |
m |
Modulus. |
- Source:
(static) modpow_naive(w, b, e, m)
ASSUMES: b >= 0, e >= 0, and m > 1, and w, b and m have L limbs.
References: HAC 14.82.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
b |
Basis integer. | |
e |
Exponent. | |
m |
Modulus. |
- Source:
(static) msbit(x)
Parameters:
Name | Type | Description |
---|---|---|
x |
Array containing bit. |
- Source:
Returns:
(static) msword(x)
Parameters:
Name | Type | Description |
---|---|---|
x |
Array containing word. |
- Source:
Returns:
(static) mul(w, x, y, len)
ASSUMES: x and y are both non-negative with L and L' limbs respectively, and that w has at least L + L' limbs.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Left factor. | |
y |
Right factor. | |
len |
Actual lengths of inputs. Useful when stored in longer arrays. |
- Source:
(static) mul_karatsuba(w, x, y, depth, len)
ASSUMES: x and y are both non-negative, with L and L' limbs respectively, and that w has at least L + L' limbs.
References: HAC
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Left factor. | |
y |
Right factor. | |
depth |
Recursion depth of the Karatsuba algorithm. | |
len |
Actual lengths of inputs. Useful when stored in longer arrays. |
- Source:
(static) mul_naive(w, x, y)
ASSUMES: x and y are both non-negative with L and L' limbs respectively, and that w has at least L + L' limbs.
References: HAC 14.12.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Left factor. | |
y |
Right factor. |
- Source:
(static) muladd_loop(w, x, start, end, Y, i, c)
Computes (pseudo-code) that due to limited precision and 32-bit bound bit operations does not work in JavaScript:
for (var j = start; j < end; j++) { tmp = x[j] * Y + w[i + j] + c; w[i + j] = tmp & 0xfffffff; c = tmp >>> 28; } return c;
Note that if Y < 2^(28 + 1), then the output carry c is only guaranteed to be smaller than 2^(28 + 1), which does not fit into a word.
ASSUMES: Y < 2^(28 + 1).
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding additive terms as input and the output. | |
x |
Array to be scaled. | |
start |
Start index into x. | |
end |
End index into x. | |
Y |
Scalar. | |
i |
Index into w. | |
c |
Input carry. |
- Source:
Returns:
(static) neg(w, x)
ASSUMES: w has at least as many limbs as x.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Integer. |
- Source:
(static) newarray(len)
Parameters:
Name | Type | Description |
---|---|---|
len |
Length of array. |
- Source:
Returns:
(static) normalize(x, mask_top)
Parameters:
Name | Type | Description |
---|---|---|
x |
Array to truncate. | |
mask_top |
Mask for a given wordsize with only most significant bit set. |
- Source:
(static) reciprocal_word(d)
ASSUMES: most significant bit of d is set, i.e., we have 2^28/2 <= d < 2^28.
References: Functionally equivalent to RECIPROCAL_WORD in MG.
Parameters:
Name | Type | Description |
---|---|---|
d |
Normalized divisor. |
- Source:
Returns:
(static) reciprocal_word_3by2(d)
ASSUMES: most significant bit of d is set, i.e., we have 2^(2 * 28)/2 <= d < 2^(2*28).
References: Algorithm RECIPROCAL_WORD_3BY2 in MG.
Parameters:
Name | Type | Description |
---|---|---|
d |
Normalized divisor. |
- Source:
Returns:
(static) resize(x, len)
Parameters:
Name | Type | Description |
---|---|---|
x |
Original array. | |
len |
New length. |
- Source:
(static) set(w, x)
Parameters:
Name | Type | Description |
---|---|---|
w |
Array or "number" holding result. | |
x |
Array holding value. |
- Source:
(static) setone(x)
Parameters:
Name | Type | Description |
---|---|---|
x |
Array to modify. |
- Source:
(static) setzero(x)
Parameters:
Name | Type | Description |
---|---|---|
x |
Array to modify. |
- Source:
(static) shiftleft(x, offset)
ASSUMES: offset >= 0.
Parameters:
Name | Type | Description |
---|---|---|
x |
Array to be shifted. | |
offset |
Number of bit positions to shift. |
- Source:
(static) shiftright(x, offset)
ASSUMES: offset >= 0.
Parameters:
Name | Type | Description |
---|---|---|
x |
Array to be shifted. | |
offset |
Number of bit positions to shift. |
- Source:
(static) square(w, x, len)
ASSUMES: x is non-negative with L and L' limbs respectively, and that w has at least L + L' limbs.
References: HAC 14.16.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Factor. | |
len |
Actual lengths of inputs. Useful when stored in longer arrays. |
- Source:
(static) square_karatsuba(w, x, depth)
ASSUMES: x is non-negative and has L limbs and w has at least 2 * L limbs.
References: HAC
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Factor. | |
depth |
Recursion depth of the Karatsuba algorithm. |
- Source:
(static) square_naive(w, x)
ASSUMES: x is non-negative with L and L' limbs respectively, and that w has at least L + L' limbs.
References: HAC 14.16.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Factor. |
- Source:
(static) sub(w, x, y)
ASSUMES: for normal use x >= y, and x and y have B and B' bits and w can store B bits. A natural choice is to use L >= L' limbs for x and y respectively and L limbs for w, but the number of limbs can be arbitrary.
References: HAC 14.9.
Parameters:
Name | Type | Description |
---|---|---|
w |
Array holding the result. | |
x |
Left term. | |
y |
Right term. |
- Source:
Returns:
(static) word_mul(w, x, y)
Parameters:
Name | Type | Description |
---|---|---|
w |
Destination long. | |
x |
Single word factor. | |
y |
Single word factor. |
- Source: