node-forge 0.7.0 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/ed25519.js ADDED
@@ -0,0 +1,996 @@
1
+ /**
2
+ * JavaScript implementation of Ed25519.
3
+ *
4
+ * Copyright (c) 2017-2018 Digital Bazaar, Inc.
5
+ *
6
+ * This implementation is based on the most excellent TweetNaCl which is
7
+ * in the public domain. Many thanks to its contributors:
8
+ *
9
+ * https://github.com/dchest/tweetnacl-js
10
+ */
11
+ var forge = require('./forge');
12
+ require('./jsbn');
13
+ require('./random');
14
+ require('./sha512');
15
+ require('./util');
16
+
17
+ if(typeof BigInteger === 'undefined') {
18
+ var BigInteger = forge.jsbn.BigInteger;
19
+ }
20
+
21
+ var ByteBuffer = forge.util.ByteBuffer;
22
+ var NativeBuffer = typeof Buffer === 'undefined' ? Uint8Array : Buffer;
23
+
24
+ /*
25
+ * Ed25519 algorithms, see RFC 8032:
26
+ * https://tools.ietf.org/html/rfc8032
27
+ */
28
+ forge.pki = forge.pki || {};
29
+ module.exports = forge.pki.ed25519 = forge.ed25519 = forge.ed25519 || {};
30
+ var ed25519 = forge.ed25519;
31
+
32
+ ed25519.constants = {};
33
+ ed25519.constants.PUBLIC_KEY_BYTE_LENGTH = 32;
34
+ ed25519.constants.PRIVATE_KEY_BYTE_LENGTH = 64;
35
+ ed25519.constants.SEED_BYTE_LENGTH = 32;
36
+ ed25519.constants.SIGN_BYTE_LENGTH = 64;
37
+ ed25519.constants.HASH_BYTE_LENGTH = 64;
38
+
39
+ ed25519.generateKeyPair = function(options) {
40
+ options = options || {};
41
+ var seed = options.seed;
42
+ if(seed === undefined) {
43
+ // generate seed
44
+ seed = forge.random.getBytesSync(ed25519.constants.SEED_BYTE_LENGTH);
45
+ } else if(typeof seed === 'string') {
46
+ if(seed.length !== ed25519.constants.SEED_BYTE_LENGTH) {
47
+ throw new TypeError(
48
+ '"seed" must be ' + ed25519.constants.SEED_BYTE_LENGTH +
49
+ ' bytes in length.');
50
+ }
51
+ } else if(!(seed instanceof Uint8Array)) {
52
+ throw new TypeError(
53
+ '"seed" must be a node.js Buffer, Uint8Array, or a binary string.');
54
+ }
55
+
56
+ seed = messageToNativeBuffer({message: seed, encoding: 'binary'});
57
+
58
+ var pk = new NativeBuffer(ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);
59
+ var sk = new NativeBuffer(ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);
60
+ for(var i = 0; i < 32; ++i) {
61
+ sk[i] = seed[i];
62
+ }
63
+ crypto_sign_keypair(pk, sk);
64
+ return {publicKey: pk, privateKey: sk};
65
+ };
66
+
67
+ ed25519.publicKeyFromPrivateKey = function(options) {
68
+ options = options || {};
69
+ var privateKey = messageToNativeBuffer({
70
+ message: options.privateKey, encoding: 'binary'
71
+ });
72
+ if(privateKey.length !== ed25519.constants.PRIVATE_KEY_BYTE_LENGTH) {
73
+ throw new TypeError(
74
+ '"options.privateKey" must have a byte length of ' +
75
+ ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);
76
+ }
77
+
78
+ var pk = new NativeBuffer(ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);
79
+ for(var i = 0; i < pk.length; ++i) {
80
+ pk[i] = privateKey[32 + i];
81
+ }
82
+ return pk;
83
+ };
84
+
85
+ ed25519.sign = function(options) {
86
+ options = options || {};
87
+ var msg = messageToNativeBuffer(options);
88
+ var privateKey = messageToNativeBuffer({
89
+ message: options.privateKey,
90
+ encoding: 'binary'
91
+ });
92
+ if(privateKey.length !== ed25519.constants.PRIVATE_KEY_BYTE_LENGTH) {
93
+ throw new TypeError(
94
+ '"options.privateKey" must have a byte length of ' +
95
+ ed25519.constants.PRIVATE_KEY_BYTE_LENGTH);
96
+ }
97
+
98
+ var signedMsg = new NativeBuffer(
99
+ ed25519.constants.SIGN_BYTE_LENGTH + msg.length);
100
+ crypto_sign(signedMsg, msg, msg.length, privateKey);
101
+
102
+ var sig = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH);
103
+ for(var i = 0; i < sig.length; ++i) {
104
+ sig[i] = signedMsg[i];
105
+ }
106
+ return sig;
107
+ };
108
+
109
+ ed25519.verify = function(options) {
110
+ options = options || {};
111
+ var msg = messageToNativeBuffer(options);
112
+ if(options.signature === undefined) {
113
+ throw new TypeError(
114
+ '"options.signature" must be a node.js Buffer, a Uint8Array, a forge ' +
115
+ 'ByteBuffer, or a binary string.');
116
+ }
117
+ var sig = messageToNativeBuffer({
118
+ message: options.signature,
119
+ encoding: 'binary'
120
+ });
121
+ if(sig.length !== ed25519.constants.SIGN_BYTE_LENGTH) {
122
+ throw new TypeError(
123
+ '"options.signature" must have a byte length of ' +
124
+ ed25519.constants.SIGN_BYTE_LENGTH);
125
+ }
126
+ var publicKey = messageToNativeBuffer({
127
+ message: options.publicKey,
128
+ encoding: 'binary'
129
+ });
130
+ if(publicKey.length !== ed25519.constants.PUBLIC_KEY_BYTE_LENGTH) {
131
+ throw new TypeError(
132
+ '"options.publicKey" must have a byte length of ' +
133
+ ed25519.constants.PUBLIC_KEY_BYTE_LENGTH);
134
+ }
135
+
136
+ var sm = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH + msg.length);
137
+ var m = new NativeBuffer(ed25519.constants.SIGN_BYTE_LENGTH + msg.length);
138
+ var i;
139
+ for(i = 0; i < ed25519.constants.SIGN_BYTE_LENGTH; ++i) {
140
+ sm[i] = sig[i];
141
+ }
142
+ for(i = 0; i < msg.length; ++i) {
143
+ sm[i + ed25519.constants.SIGN_BYTE_LENGTH] = msg[i];
144
+ }
145
+ return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0);
146
+ };
147
+
148
+ function messageToNativeBuffer(options) {
149
+ var message = options.message;
150
+ if(message instanceof Uint8Array) {
151
+ return message;
152
+ }
153
+
154
+ var encoding = options.encoding;
155
+ if(message === undefined) {
156
+ if(options.md) {
157
+ // TODO: more rigorous validation that `md` is a MessageDigest
158
+ message = options.md.digest().getBytes();
159
+ encoding = 'binary';
160
+ } else {
161
+ throw new TypeError('"options.message" or "options.md" not specified.');
162
+ }
163
+ }
164
+
165
+ if(typeof message === 'string' && !encoding) {
166
+ throw new TypeError('"options.encoding" must be "binary" or "utf8".');
167
+ }
168
+
169
+ if(typeof message === 'string') {
170
+ if(typeof Buffer !== 'undefined') {
171
+ return new Buffer(message, encoding);
172
+ }
173
+ message = new ByteBuffer(message, encoding);
174
+ } else if(!(message instanceof ByteBuffer)) {
175
+ throw new TypeError(
176
+ '"options.message" must be a node.js Buffer, a Uint8Array, a forge ' +
177
+ 'ByteBuffer, or a string with "options.encoding" specifying its ' +
178
+ 'encoding.');
179
+ }
180
+
181
+ // convert to native buffer
182
+ var buffer = new NativeBuffer(message.length());
183
+ for(var i = 0; i < buffer.length; ++i) {
184
+ buffer[i] = message.at(i);
185
+ }
186
+ return buffer;
187
+ }
188
+
189
+ var gf0 = gf();
190
+ var gf1 = gf([1]);
191
+ var D = gf([
192
+ 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,
193
+ 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203]);
194
+ var D2 = gf([
195
+ 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0,
196
+ 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406]);
197
+ var X = gf([
198
+ 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c,
199
+ 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169]);
200
+ var Y = gf([
201
+ 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666,
202
+ 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666]);
203
+ var L = new Float64Array([
204
+ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
205
+ 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
206
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10]);
207
+ var I = gf([
208
+ 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,
209
+ 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83]);
210
+
211
+ // TODO: update forge buffer implementation to use `Buffer` or `Uint8Array`,
212
+ // whichever is available, to improve performance
213
+ function sha512(msg, msgLen) {
214
+ // Note: `out` and `msg` are NativeBuffer
215
+ var md = forge.md.sha512.create();
216
+ var buffer = new ByteBuffer(msg);
217
+ md.update(buffer.getBytes(msgLen), 'binary');
218
+ const hash = md.digest().getBytes();
219
+ if(typeof Buffer !== 'undefined') {
220
+ return new Buffer(hash, 'binary');
221
+ }
222
+ var out = new NativeBuffer(ed25519.constants.HASH_BYTE_LENGTH);
223
+ for(var i = 0; i < 64; ++i) {
224
+ out[i] = hash.charCodeAt(i);
225
+ }
226
+ return out;
227
+ }
228
+
229
+ function crypto_sign_keypair(pk, sk) {
230
+ var p = [gf(), gf(), gf(), gf()];
231
+ var i;
232
+
233
+ var d = sha512(sk, 32);
234
+ d[0] &= 248;
235
+ d[31] &= 127;
236
+ d[31] |= 64;
237
+
238
+ scalarbase(p, d);
239
+ pack(pk, p);
240
+
241
+ for(i = 0; i < 32; ++i) {
242
+ sk[i + 32] = pk[i];
243
+ }
244
+ return 0;
245
+ }
246
+
247
+ // Note: difference from C - smlen returned, not passed as argument.
248
+ function crypto_sign(sm, m, n, sk) {
249
+ var i, j, x = new Float64Array(64);
250
+ var p = [gf(), gf(), gf(), gf()];
251
+
252
+ var d = sha512(sk, 32);
253
+ d[0] &= 248;
254
+ d[31] &= 127;
255
+ d[31] |= 64;
256
+
257
+ var smlen = n + 64;
258
+ for(i = 0; i < n; ++i) {
259
+ sm[64 + i] = m[i];
260
+ }
261
+ for(i = 0; i < 32; ++i) {
262
+ sm[32 + i] = d[32 + i];
263
+ }
264
+
265
+ var r = sha512(sm.subarray(32), n + 32);
266
+ reduce(r);
267
+ scalarbase(p, r);
268
+ pack(sm, p);
269
+
270
+ for(i = 32; i < 64; ++i) {
271
+ sm[i] = sk[i];
272
+ }
273
+ var h = sha512(sm, n + 64);
274
+ reduce(h);
275
+
276
+ for(i = 32; i < 64; ++i) {
277
+ x[i] = 0;
278
+ }
279
+ for(i = 0; i < 32; ++i) {
280
+ x[i] = r[i];
281
+ }
282
+ for(i = 0; i < 32; ++i) {
283
+ for(j = 0; j < 32; j++) {
284
+ x[i + j] += h[i] * d[j];
285
+ }
286
+ }
287
+
288
+ modL(sm.subarray(32), x);
289
+ return smlen;
290
+ }
291
+
292
+ function crypto_sign_open(m, sm, n, pk) {
293
+ var i, mlen;
294
+ var t = new NativeBuffer(32);
295
+ var p = [gf(), gf(), gf(), gf()],
296
+ q = [gf(), gf(), gf(), gf()];
297
+
298
+ mlen = -1;
299
+ if(n < 64) {
300
+ return -1;
301
+ }
302
+
303
+ if(unpackneg(q, pk)) {
304
+ return -1;
305
+ }
306
+
307
+ for(i = 0; i < n; ++i) {
308
+ m[i] = sm[i];
309
+ }
310
+ for(i = 0; i < 32; ++i) {
311
+ m[i + 32] = pk[i];
312
+ }
313
+ var h = sha512(m, n);
314
+ reduce(h);
315
+ scalarmult(p, q, h);
316
+
317
+ scalarbase(q, sm.subarray(32));
318
+ add(p, q);
319
+ pack(t, p);
320
+
321
+ n -= 64;
322
+ if(crypto_verify_32(sm, 0, t, 0)) {
323
+ for(i = 0; i < n; ++i) {
324
+ m[i] = 0;
325
+ }
326
+ return -1;
327
+ }
328
+
329
+ for(i = 0; i < n; ++i) {
330
+ m[i] = sm[i + 64];
331
+ }
332
+ mlen = n;
333
+ return mlen;
334
+ }
335
+
336
+ function modL(r, x) {
337
+ var carry, i, j, k;
338
+ for(i = 63; i >= 32; --i) {
339
+ carry = 0;
340
+ for(j = i - 32, k = i - 12; j < k; ++j) {
341
+ x[j] += carry - 16 * x[i] * L[j - (i - 32)];
342
+ carry = (x[j] + 128) >> 8;
343
+ x[j] -= carry * 256;
344
+ }
345
+ x[j] += carry;
346
+ x[i] = 0;
347
+ }
348
+ carry = 0;
349
+ for(j = 0; j < 32; ++j) {
350
+ x[j] += carry - (x[31] >> 4) * L[j];
351
+ carry = x[j] >> 8;
352
+ x[j] &= 255;
353
+ }
354
+ for(j = 0; j < 32; ++j) {
355
+ x[j] -= carry * L[j];
356
+ }
357
+ for(i = 0; i < 32; ++i) {
358
+ x[i + 1] += x[i] >> 8;
359
+ r[i] = x[i] & 255;
360
+ }
361
+ }
362
+
363
+ function reduce(r) {
364
+ var x = new Float64Array(64);
365
+ for(var i = 0; i < 64; ++i) {
366
+ x[i] = r[i];
367
+ r[i] = 0;
368
+ }
369
+ modL(r, x);
370
+ }
371
+
372
+ function add(p, q) {
373
+ var a = gf(), b = gf(), c = gf(),
374
+ d = gf(), e = gf(), f = gf(),
375
+ g = gf(), h = gf(), t = gf();
376
+
377
+ Z(a, p[1], p[0]);
378
+ Z(t, q[1], q[0]);
379
+ M(a, a, t);
380
+ A(b, p[0], p[1]);
381
+ A(t, q[0], q[1]);
382
+ M(b, b, t);
383
+ M(c, p[3], q[3]);
384
+ M(c, c, D2);
385
+ M(d, p[2], q[2]);
386
+ A(d, d, d);
387
+ Z(e, b, a);
388
+ Z(f, d, c);
389
+ A(g, d, c);
390
+ A(h, b, a);
391
+
392
+ M(p[0], e, f);
393
+ M(p[1], h, g);
394
+ M(p[2], g, f);
395
+ M(p[3], e, h);
396
+ }
397
+
398
+ function cswap(p, q, b) {
399
+ for(var i = 0; i < 4; ++i) {
400
+ sel25519(p[i], q[i], b);
401
+ }
402
+ }
403
+
404
+ function pack(r, p) {
405
+ var tx = gf(), ty = gf(), zi = gf();
406
+ inv25519(zi, p[2]);
407
+ M(tx, p[0], zi);
408
+ M(ty, p[1], zi);
409
+ pack25519(r, ty);
410
+ r[31] ^= par25519(tx) << 7;
411
+ }
412
+
413
+ function pack25519(o, n) {
414
+ var i, j, b;
415
+ var m = gf(), t = gf();
416
+ for(i = 0; i < 16; ++i) {
417
+ t[i] = n[i];
418
+ }
419
+ car25519(t);
420
+ car25519(t);
421
+ car25519(t);
422
+ for(j = 0; j < 2; ++j) {
423
+ m[0] = t[0] - 0xffed;
424
+ for(i = 1; i < 15; ++i) {
425
+ m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
426
+ m[i-1] &= 0xffff;
427
+ }
428
+ m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
429
+ b = (m[15] >> 16) & 1;
430
+ m[14] &= 0xffff;
431
+ sel25519(t, m, 1 - b);
432
+ }
433
+ for (i = 0; i < 16; i++) {
434
+ o[2 * i] = t[i] & 0xff;
435
+ o[2 * i + 1] = t[i] >> 8;
436
+ }
437
+ }
438
+
439
+ function unpackneg(r, p) {
440
+ var t = gf(), chk = gf(), num = gf(),
441
+ den = gf(), den2 = gf(), den4 = gf(),
442
+ den6 = gf();
443
+
444
+ set25519(r[2], gf1);
445
+ unpack25519(r[1], p);
446
+ S(num, r[1]);
447
+ M(den, num, D);
448
+ Z(num, num, r[2]);
449
+ A(den, r[2], den);
450
+
451
+ S(den2, den);
452
+ S(den4, den2);
453
+ M(den6, den4, den2);
454
+ M(t, den6, num);
455
+ M(t, t, den);
456
+
457
+ pow2523(t, t);
458
+ M(t, t, num);
459
+ M(t, t, den);
460
+ M(t, t, den);
461
+ M(r[0], t, den);
462
+
463
+ S(chk, r[0]);
464
+ M(chk, chk, den);
465
+ if(neq25519(chk, num)) {
466
+ M(r[0], r[0], I);
467
+ }
468
+
469
+ S(chk, r[0]);
470
+ M(chk, chk, den);
471
+ if(neq25519(chk, num)) {
472
+ return -1;
473
+ }
474
+
475
+ if(par25519(r[0]) === (p[31] >> 7)) {
476
+ Z(r[0], gf0, r[0]);
477
+ }
478
+
479
+ M(r[3], r[0], r[1]);
480
+ return 0;
481
+ }
482
+
483
+ function unpack25519(o, n) {
484
+ var i;
485
+ for(i = 0; i < 16; ++i) {
486
+ o[i] = n[2 * i] + (n[2 * i + 1] << 8);
487
+ }
488
+ o[15] &= 0x7fff;
489
+ }
490
+
491
+ function pow2523(o, i) {
492
+ var c = gf();
493
+ var a;
494
+ for(a = 0; a < 16; ++a) {
495
+ c[a] = i[a];
496
+ }
497
+ for(a = 250; a >= 0; --a) {
498
+ S(c, c);
499
+ if(a !== 1) {
500
+ M(c, c, i);
501
+ }
502
+ }
503
+ for(a = 0; a < 16; ++a) {
504
+ o[a] = c[a];
505
+ }
506
+ }
507
+
508
+ function neq25519(a, b) {
509
+ var c = new NativeBuffer(32);
510
+ var d = new NativeBuffer(32);
511
+ pack25519(c, a);
512
+ pack25519(d, b);
513
+ return crypto_verify_32(c, 0, d, 0);
514
+ }
515
+
516
+ function crypto_verify_32(x, xi, y, yi) {
517
+ return vn(x, xi, y, yi, 32);
518
+ }
519
+
520
+ function vn(x, xi, y, yi, n) {
521
+ var i, d = 0;
522
+ for(i = 0; i < n; ++i) {
523
+ d |= x[xi + i] ^ y[yi + i];
524
+ }
525
+ return (1 & ((d - 1) >>> 8)) - 1;
526
+ }
527
+
528
+ function par25519(a) {
529
+ var d = new NativeBuffer(32);
530
+ pack25519(d, a);
531
+ return d[0] & 1;
532
+ }
533
+
534
+ function scalarmult(p, q, s) {
535
+ var b, i;
536
+ set25519(p[0], gf0);
537
+ set25519(p[1], gf1);
538
+ set25519(p[2], gf1);
539
+ set25519(p[3], gf0);
540
+ for(i = 255; i >= 0; --i) {
541
+ b = (s[(i / 8)|0] >> (i & 7)) & 1;
542
+ cswap(p, q, b);
543
+ add(q, p);
544
+ add(p, p);
545
+ cswap(p, q, b);
546
+ }
547
+ }
548
+
549
+ function scalarbase(p, s) {
550
+ var q = [gf(), gf(), gf(), gf()];
551
+ set25519(q[0], X);
552
+ set25519(q[1], Y);
553
+ set25519(q[2], gf1);
554
+ M(q[3], X, Y);
555
+ scalarmult(p, q, s);
556
+ }
557
+
558
+ function set25519(r, a) {
559
+ var i;
560
+ for(i = 0; i < 16; i++) {
561
+ r[i] = a[i] | 0;
562
+ }
563
+ }
564
+
565
+ function inv25519(o, i) {
566
+ var c = gf();
567
+ var a;
568
+ for(a = 0; a < 16; ++a) {
569
+ c[a] = i[a];
570
+ }
571
+ for(a = 253; a >= 0; --a) {
572
+ S(c, c);
573
+ if(a !== 2 && a !== 4) {
574
+ M(c, c, i);
575
+ }
576
+ }
577
+ for(a = 0; a < 16; ++a) {
578
+ o[a] = c[a];
579
+ }
580
+ }
581
+
582
+ function car25519(o) {
583
+ var i, v, c = 1;
584
+ for(i = 0; i < 16; ++i) {
585
+ v = o[i] + c + 65535;
586
+ c = Math.floor(v / 65536);
587
+ o[i] = v - c * 65536;
588
+ }
589
+ o[0] += c - 1 + 37 * (c - 1);
590
+ }
591
+
592
+ function sel25519(p, q, b) {
593
+ var t, c = ~(b - 1);
594
+ for(var i = 0; i < 16; ++i) {
595
+ t = c & (p[i] ^ q[i]);
596
+ p[i] ^= t;
597
+ q[i] ^= t;
598
+ }
599
+ }
600
+
601
+ function gf(init) {
602
+ var i, r = new Float64Array(16);
603
+ if(init) {
604
+ for(i = 0; i < init.length; ++i) {
605
+ r[i] = init[i];
606
+ }
607
+ }
608
+ return r;
609
+ }
610
+
611
+ function A(o, a, b) {
612
+ for(var i = 0; i < 16; ++i) {
613
+ o[i] = a[i] + b[i];
614
+ }
615
+ }
616
+
617
+ function Z(o, a, b) {
618
+ for(var i = 0; i < 16; ++i) {
619
+ o[i] = a[i] - b[i];
620
+ }
621
+ }
622
+
623
+ function S(o, a) {
624
+ M(o, a, a);
625
+ }
626
+
627
+ function M(o, a, b) {
628
+ var v, c,
629
+ t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
630
+ t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0,
631
+ t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0,
632
+ t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0,
633
+ b0 = b[0],
634
+ b1 = b[1],
635
+ b2 = b[2],
636
+ b3 = b[3],
637
+ b4 = b[4],
638
+ b5 = b[5],
639
+ b6 = b[6],
640
+ b7 = b[7],
641
+ b8 = b[8],
642
+ b9 = b[9],
643
+ b10 = b[10],
644
+ b11 = b[11],
645
+ b12 = b[12],
646
+ b13 = b[13],
647
+ b14 = b[14],
648
+ b15 = b[15];
649
+
650
+ v = a[0];
651
+ t0 += v * b0;
652
+ t1 += v * b1;
653
+ t2 += v * b2;
654
+ t3 += v * b3;
655
+ t4 += v * b4;
656
+ t5 += v * b5;
657
+ t6 += v * b6;
658
+ t7 += v * b7;
659
+ t8 += v * b8;
660
+ t9 += v * b9;
661
+ t10 += v * b10;
662
+ t11 += v * b11;
663
+ t12 += v * b12;
664
+ t13 += v * b13;
665
+ t14 += v * b14;
666
+ t15 += v * b15;
667
+ v = a[1];
668
+ t1 += v * b0;
669
+ t2 += v * b1;
670
+ t3 += v * b2;
671
+ t4 += v * b3;
672
+ t5 += v * b4;
673
+ t6 += v * b5;
674
+ t7 += v * b6;
675
+ t8 += v * b7;
676
+ t9 += v * b8;
677
+ t10 += v * b9;
678
+ t11 += v * b10;
679
+ t12 += v * b11;
680
+ t13 += v * b12;
681
+ t14 += v * b13;
682
+ t15 += v * b14;
683
+ t16 += v * b15;
684
+ v = a[2];
685
+ t2 += v * b0;
686
+ t3 += v * b1;
687
+ t4 += v * b2;
688
+ t5 += v * b3;
689
+ t6 += v * b4;
690
+ t7 += v * b5;
691
+ t8 += v * b6;
692
+ t9 += v * b7;
693
+ t10 += v * b8;
694
+ t11 += v * b9;
695
+ t12 += v * b10;
696
+ t13 += v * b11;
697
+ t14 += v * b12;
698
+ t15 += v * b13;
699
+ t16 += v * b14;
700
+ t17 += v * b15;
701
+ v = a[3];
702
+ t3 += v * b0;
703
+ t4 += v * b1;
704
+ t5 += v * b2;
705
+ t6 += v * b3;
706
+ t7 += v * b4;
707
+ t8 += v * b5;
708
+ t9 += v * b6;
709
+ t10 += v * b7;
710
+ t11 += v * b8;
711
+ t12 += v * b9;
712
+ t13 += v * b10;
713
+ t14 += v * b11;
714
+ t15 += v * b12;
715
+ t16 += v * b13;
716
+ t17 += v * b14;
717
+ t18 += v * b15;
718
+ v = a[4];
719
+ t4 += v * b0;
720
+ t5 += v * b1;
721
+ t6 += v * b2;
722
+ t7 += v * b3;
723
+ t8 += v * b4;
724
+ t9 += v * b5;
725
+ t10 += v * b6;
726
+ t11 += v * b7;
727
+ t12 += v * b8;
728
+ t13 += v * b9;
729
+ t14 += v * b10;
730
+ t15 += v * b11;
731
+ t16 += v * b12;
732
+ t17 += v * b13;
733
+ t18 += v * b14;
734
+ t19 += v * b15;
735
+ v = a[5];
736
+ t5 += v * b0;
737
+ t6 += v * b1;
738
+ t7 += v * b2;
739
+ t8 += v * b3;
740
+ t9 += v * b4;
741
+ t10 += v * b5;
742
+ t11 += v * b6;
743
+ t12 += v * b7;
744
+ t13 += v * b8;
745
+ t14 += v * b9;
746
+ t15 += v * b10;
747
+ t16 += v * b11;
748
+ t17 += v * b12;
749
+ t18 += v * b13;
750
+ t19 += v * b14;
751
+ t20 += v * b15;
752
+ v = a[6];
753
+ t6 += v * b0;
754
+ t7 += v * b1;
755
+ t8 += v * b2;
756
+ t9 += v * b3;
757
+ t10 += v * b4;
758
+ t11 += v * b5;
759
+ t12 += v * b6;
760
+ t13 += v * b7;
761
+ t14 += v * b8;
762
+ t15 += v * b9;
763
+ t16 += v * b10;
764
+ t17 += v * b11;
765
+ t18 += v * b12;
766
+ t19 += v * b13;
767
+ t20 += v * b14;
768
+ t21 += v * b15;
769
+ v = a[7];
770
+ t7 += v * b0;
771
+ t8 += v * b1;
772
+ t9 += v * b2;
773
+ t10 += v * b3;
774
+ t11 += v * b4;
775
+ t12 += v * b5;
776
+ t13 += v * b6;
777
+ t14 += v * b7;
778
+ t15 += v * b8;
779
+ t16 += v * b9;
780
+ t17 += v * b10;
781
+ t18 += v * b11;
782
+ t19 += v * b12;
783
+ t20 += v * b13;
784
+ t21 += v * b14;
785
+ t22 += v * b15;
786
+ v = a[8];
787
+ t8 += v * b0;
788
+ t9 += v * b1;
789
+ t10 += v * b2;
790
+ t11 += v * b3;
791
+ t12 += v * b4;
792
+ t13 += v * b5;
793
+ t14 += v * b6;
794
+ t15 += v * b7;
795
+ t16 += v * b8;
796
+ t17 += v * b9;
797
+ t18 += v * b10;
798
+ t19 += v * b11;
799
+ t20 += v * b12;
800
+ t21 += v * b13;
801
+ t22 += v * b14;
802
+ t23 += v * b15;
803
+ v = a[9];
804
+ t9 += v * b0;
805
+ t10 += v * b1;
806
+ t11 += v * b2;
807
+ t12 += v * b3;
808
+ t13 += v * b4;
809
+ t14 += v * b5;
810
+ t15 += v * b6;
811
+ t16 += v * b7;
812
+ t17 += v * b8;
813
+ t18 += v * b9;
814
+ t19 += v * b10;
815
+ t20 += v * b11;
816
+ t21 += v * b12;
817
+ t22 += v * b13;
818
+ t23 += v * b14;
819
+ t24 += v * b15;
820
+ v = a[10];
821
+ t10 += v * b0;
822
+ t11 += v * b1;
823
+ t12 += v * b2;
824
+ t13 += v * b3;
825
+ t14 += v * b4;
826
+ t15 += v * b5;
827
+ t16 += v * b6;
828
+ t17 += v * b7;
829
+ t18 += v * b8;
830
+ t19 += v * b9;
831
+ t20 += v * b10;
832
+ t21 += v * b11;
833
+ t22 += v * b12;
834
+ t23 += v * b13;
835
+ t24 += v * b14;
836
+ t25 += v * b15;
837
+ v = a[11];
838
+ t11 += v * b0;
839
+ t12 += v * b1;
840
+ t13 += v * b2;
841
+ t14 += v * b3;
842
+ t15 += v * b4;
843
+ t16 += v * b5;
844
+ t17 += v * b6;
845
+ t18 += v * b7;
846
+ t19 += v * b8;
847
+ t20 += v * b9;
848
+ t21 += v * b10;
849
+ t22 += v * b11;
850
+ t23 += v * b12;
851
+ t24 += v * b13;
852
+ t25 += v * b14;
853
+ t26 += v * b15;
854
+ v = a[12];
855
+ t12 += v * b0;
856
+ t13 += v * b1;
857
+ t14 += v * b2;
858
+ t15 += v * b3;
859
+ t16 += v * b4;
860
+ t17 += v * b5;
861
+ t18 += v * b6;
862
+ t19 += v * b7;
863
+ t20 += v * b8;
864
+ t21 += v * b9;
865
+ t22 += v * b10;
866
+ t23 += v * b11;
867
+ t24 += v * b12;
868
+ t25 += v * b13;
869
+ t26 += v * b14;
870
+ t27 += v * b15;
871
+ v = a[13];
872
+ t13 += v * b0;
873
+ t14 += v * b1;
874
+ t15 += v * b2;
875
+ t16 += v * b3;
876
+ t17 += v * b4;
877
+ t18 += v * b5;
878
+ t19 += v * b6;
879
+ t20 += v * b7;
880
+ t21 += v * b8;
881
+ t22 += v * b9;
882
+ t23 += v * b10;
883
+ t24 += v * b11;
884
+ t25 += v * b12;
885
+ t26 += v * b13;
886
+ t27 += v * b14;
887
+ t28 += v * b15;
888
+ v = a[14];
889
+ t14 += v * b0;
890
+ t15 += v * b1;
891
+ t16 += v * b2;
892
+ t17 += v * b3;
893
+ t18 += v * b4;
894
+ t19 += v * b5;
895
+ t20 += v * b6;
896
+ t21 += v * b7;
897
+ t22 += v * b8;
898
+ t23 += v * b9;
899
+ t24 += v * b10;
900
+ t25 += v * b11;
901
+ t26 += v * b12;
902
+ t27 += v * b13;
903
+ t28 += v * b14;
904
+ t29 += v * b15;
905
+ v = a[15];
906
+ t15 += v * b0;
907
+ t16 += v * b1;
908
+ t17 += v * b2;
909
+ t18 += v * b3;
910
+ t19 += v * b4;
911
+ t20 += v * b5;
912
+ t21 += v * b6;
913
+ t22 += v * b7;
914
+ t23 += v * b8;
915
+ t24 += v * b9;
916
+ t25 += v * b10;
917
+ t26 += v * b11;
918
+ t27 += v * b12;
919
+ t28 += v * b13;
920
+ t29 += v * b14;
921
+ t30 += v * b15;
922
+
923
+ t0 += 38 * t16;
924
+ t1 += 38 * t17;
925
+ t2 += 38 * t18;
926
+ t3 += 38 * t19;
927
+ t4 += 38 * t20;
928
+ t5 += 38 * t21;
929
+ t6 += 38 * t22;
930
+ t7 += 38 * t23;
931
+ t8 += 38 * t24;
932
+ t9 += 38 * t25;
933
+ t10 += 38 * t26;
934
+ t11 += 38 * t27;
935
+ t12 += 38 * t28;
936
+ t13 += 38 * t29;
937
+ t14 += 38 * t30;
938
+ // t15 left as is
939
+
940
+ // first car
941
+ c = 1;
942
+ v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
943
+ v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
944
+ v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
945
+ v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
946
+ v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
947
+ v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
948
+ v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
949
+ v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
950
+ v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
951
+ v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
952
+ v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
953
+ v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
954
+ v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
955
+ v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
956
+ v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
957
+ v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
958
+ t0 += c-1 + 37 * (c-1);
959
+
960
+ // second car
961
+ c = 1;
962
+ v = t0 + c + 65535; c = Math.floor(v / 65536); t0 = v - c * 65536;
963
+ v = t1 + c + 65535; c = Math.floor(v / 65536); t1 = v - c * 65536;
964
+ v = t2 + c + 65535; c = Math.floor(v / 65536); t2 = v - c * 65536;
965
+ v = t3 + c + 65535; c = Math.floor(v / 65536); t3 = v - c * 65536;
966
+ v = t4 + c + 65535; c = Math.floor(v / 65536); t4 = v - c * 65536;
967
+ v = t5 + c + 65535; c = Math.floor(v / 65536); t5 = v - c * 65536;
968
+ v = t6 + c + 65535; c = Math.floor(v / 65536); t6 = v - c * 65536;
969
+ v = t7 + c + 65535; c = Math.floor(v / 65536); t7 = v - c * 65536;
970
+ v = t8 + c + 65535; c = Math.floor(v / 65536); t8 = v - c * 65536;
971
+ v = t9 + c + 65535; c = Math.floor(v / 65536); t9 = v - c * 65536;
972
+ v = t10 + c + 65535; c = Math.floor(v / 65536); t10 = v - c * 65536;
973
+ v = t11 + c + 65535; c = Math.floor(v / 65536); t11 = v - c * 65536;
974
+ v = t12 + c + 65535; c = Math.floor(v / 65536); t12 = v - c * 65536;
975
+ v = t13 + c + 65535; c = Math.floor(v / 65536); t13 = v - c * 65536;
976
+ v = t14 + c + 65535; c = Math.floor(v / 65536); t14 = v - c * 65536;
977
+ v = t15 + c + 65535; c = Math.floor(v / 65536); t15 = v - c * 65536;
978
+ t0 += c-1 + 37 * (c-1);
979
+
980
+ o[ 0] = t0;
981
+ o[ 1] = t1;
982
+ o[ 2] = t2;
983
+ o[ 3] = t3;
984
+ o[ 4] = t4;
985
+ o[ 5] = t5;
986
+ o[ 6] = t6;
987
+ o[ 7] = t7;
988
+ o[ 8] = t8;
989
+ o[ 9] = t9;
990
+ o[10] = t10;
991
+ o[11] = t11;
992
+ o[12] = t12;
993
+ o[13] = t13;
994
+ o[14] = t14;
995
+ o[15] = t15;
996
+ }