├── .gitignore ├── yarn.lock ├── README.md ├── test.js ├── LICENSE ├── package.json ├── lib ├── LICENSE-jsbn ├── sec.js └── ec.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | jsbn@~0.1.0: 6 | version "0.1.1" 7 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ecc-jsbn 2 | ======== 3 | 4 | ECC package based on [jsbn](https://github.com/andyperlitch/jsbn) from [Tom Wu](http://www-cs-students.stanford.edu/~tjw/). 5 | 6 | This is a subset of the same interface as the [node compiled module](https://github.com/quartzjer/ecc), but works in the browser too. 7 | 8 | Also uses point compression now from [https://github.com/kaielvin](https://github.com/kaielvin/jsbn-ec-point-compression). 9 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const assert = require("assert"); 4 | const { ECKey, ECCurves } = require("./index.js"); 5 | 6 | const testCurve = curve => { 7 | const key1 = new ECKey(curve); 8 | const key2 = new ECKey(curve); 9 | 10 | const key3 = new ECKey(curve, key1.PrivateKey); 11 | const key4 = new ECKey(curve, key2.PublicKey, true); 12 | 13 | assert.strictEqual( 14 | Buffer.compare( 15 | key1.deriveSharedSecret(key2), 16 | key3.deriveSharedSecret(key4) 17 | ), 18 | 0 19 | ); 20 | }; 21 | 22 | try { 23 | Object.keys(ECCurves) 24 | .map(curveName => ECCurves[curveName]) 25 | .forEach(testCurve); 26 | 27 | console.log("Test OK"); 28 | } catch (e) { 29 | console.error(e); 30 | 31 | console.log("Test FAILURE"); 32 | process.exit(1); 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jeremie Miller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecc-jsbn", 3 | "version": "0.2.0", 4 | "description": "ECC JS code based on JSBN", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/quartzjer/ecc-jsbn.git" 9 | }, 10 | "keywords": [ 11 | "jsbn", 12 | "ecc", 13 | "browserify" 14 | ], 15 | "author": { 16 | "name": "Jeremie Miller", 17 | "email": "jeremie@jabber.org", 18 | "url": "http://jeremie.com/" 19 | }, 20 | "maintainers": [ 21 | { 22 | "name": "Jeremie Miller", 23 | "email": "jeremie@jabber.org", 24 | "url": "http://jeremie.com/" 25 | }, 26 | { 27 | "name": "Ryan Bennett", 28 | "url": "https://github.com/rynomad" 29 | }, 30 | { 31 | "name": "Antoine du Hamel", 32 | "email": "duhamelantoine1995@gmail.com" 33 | } 34 | ], 35 | "scripts": { 36 | "preversion": "node ./test.js" 37 | }, 38 | "dependencies": { 39 | "jsbn": "~0.1.0" 40 | }, 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/quartzjer/ecc-jsbn/issues" 44 | }, 45 | "engines": { 46 | "node": ">=6.0.0" 47 | }, 48 | "homepage": "https://github.com/quartzjer/ecc-jsbn" 49 | } 50 | -------------------------------------------------------------------------------- /lib/LICENSE-jsbn: -------------------------------------------------------------------------------- 1 | Licensing 2 | --------- 3 | 4 | This software is covered under the following copyright: 5 | 6 | /* 7 | * Copyright (c) 2003-2005 Tom Wu 8 | * All Rights Reserved. 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining 11 | * a copy of this software and associated documentation files (the 12 | * "Software"), to deal in the Software without restriction, including 13 | * without limitation the rights to use, copy, modify, merge, publish, 14 | * distribute, sublicense, and/or sell copies of the Software, and to 15 | * permit persons to whom the Software is furnished to do so, subject to 16 | * the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 23 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 24 | * 25 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, 26 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER 27 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF 28 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT 29 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30 | * 31 | * In addition, the following condition applies: 32 | * 33 | * All redistributions must retain an intact copy of this copyright notice 34 | * and disclaimer. 35 | */ 36 | 37 | Address all questions regarding this license to: 38 | 39 | Tom Wu 40 | tjw@cs.Stanford.EDU 41 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const crypto = require("crypto"); 4 | const { BigInteger } = require("jsbn"); 5 | 6 | exports.ECCurves = require("./lib/sec.js"); 7 | 8 | // zero prepad 9 | function unstupid(hex, len) { 10 | return hex.length >= len ? hex : unstupid("0" + hex, len); 11 | } 12 | 13 | exports.ECKey = function(curve, key, isPublic) { 14 | var priv; 15 | var c = curve(); 16 | var n = c.getN(); 17 | var bytes = Math.floor(n.bitLength() / 8); 18 | 19 | if (key) { 20 | if (isPublic) { 21 | var curve = c.getCurve(); 22 | // var x = key.slice(1,bytes+1); // skip the 04 for uncompressed format 23 | // var y = key.slice(bytes+1); 24 | // this.P = new ECPointFp(curve, 25 | // curve.fromBigInteger(new BigInteger(x.toString("hex"), 16)), 26 | // curve.fromBigInteger(new BigInteger(y.toString("hex"), 16))); 27 | this.P = curve.decodePointHex(key.toString("hex")); 28 | } else { 29 | if (key.length != bytes) return false; 30 | priv = new BigInteger(key.toString("hex"), 16); 31 | } 32 | } else { 33 | var n1 = n.subtract(BigInteger.ONE); 34 | var r = new BigInteger(crypto.randomBytes(n.bitLength())); 35 | priv = r.mod(n1).add(BigInteger.ONE); 36 | this.P = c.getG().multiply(priv); 37 | } 38 | if (this.P) { 39 | // var pubhex = unstupid(this.P.getX().toBigInteger().toString(16),bytes*2)+unstupid(this.P.getY().toBigInteger().toString(16),bytes*2); 40 | // this.PublicKey = Buffer.from("04"+pubhex,"hex"); 41 | this.PublicKey = Buffer.from( 42 | c.getCurve().encodeCompressedPointHex(this.P), 43 | "hex" 44 | ); 45 | } 46 | if (priv) { 47 | this.PrivateKey = Buffer.from( 48 | unstupid(priv.toString(16), bytes * 2), 49 | "hex" 50 | ); 51 | this.deriveSharedSecret = function(key) { 52 | if (!key || !key.P) return false; 53 | var S = key.P.multiply(priv); 54 | return Buffer.from( 55 | unstupid( 56 | S.getX() 57 | .toBigInteger() 58 | .toString(16), 59 | bytes * 2 60 | ), 61 | "hex" 62 | ); 63 | }; 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /lib/sec.js: -------------------------------------------------------------------------------- 1 | // Named EC curves 2 | "use strict"; 3 | 4 | // Requires ec.js, jsbn.js, and jsbn2.js 5 | var { BigInteger } = require("jsbn"); 6 | var { ECCurveFp } = require("./ec.js"); 7 | 8 | // ---------------- 9 | // X9ECParameters 10 | 11 | // constructor 12 | function X9ECParameters(curve, g, n, h) { 13 | this.curve = curve; 14 | this.g = g; 15 | this.n = n; 16 | this.h = h; 17 | } 18 | 19 | function x9getCurve() { 20 | return this.curve; 21 | } 22 | 23 | function x9getG() { 24 | return this.g; 25 | } 26 | 27 | function x9getN() { 28 | return this.n; 29 | } 30 | 31 | function x9getH() { 32 | return this.h; 33 | } 34 | 35 | X9ECParameters.prototype.getCurve = x9getCurve; 36 | X9ECParameters.prototype.getG = x9getG; 37 | X9ECParameters.prototype.getN = x9getN; 38 | X9ECParameters.prototype.getH = x9getH; 39 | 40 | // ---------------- 41 | // SECNamedCurves 42 | 43 | const fromHex = string => new BigInteger(string, 16); 44 | 45 | function secp128r1() { 46 | // p = 2^128 - 2^97 - 1 47 | var p = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF"); 48 | var a = fromHex("FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC"); 49 | var b = fromHex("E87579C11079F43DD824993C2CEE5ED3"); 50 | //byte[] S = Hex.decode("000E0D4D696E6768756151750CC03A4473D03679"); 51 | var n = fromHex("FFFFFFFE0000000075A30D1B9038A115"); 52 | var h = BigInteger.ONE; 53 | var curve = new ECCurveFp(p, a, b); 54 | var G = curve.decodePointHex( 55 | "04" + 56 | "161FF7528B899B2D0C28607CA52C5B86" + 57 | "CF5AC8395BAFEB13C02DA292DDED7A83" 58 | ); 59 | return new X9ECParameters(curve, G, n, h); 60 | } 61 | 62 | function secp160k1() { 63 | // p = 2^160 - 2^32 - 2^14 - 2^12 - 2^9 - 2^8 - 2^7 - 2^3 - 2^2 - 1 64 | var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73"); 65 | var a = BigInteger.ZERO; 66 | var b = fromHex("7"); 67 | //byte[] S = null; 68 | var n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"); 69 | var h = BigInteger.ONE; 70 | var curve = new ECCurveFp(p, a, b); 71 | var G = curve.decodePointHex( 72 | "04" + 73 | "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB" + 74 | "938CF935318FDCED6BC28286531733C3F03C4FEE" 75 | ); 76 | return new X9ECParameters(curve, G, n, h); 77 | } 78 | 79 | function secp160r1() { 80 | // p = 2^160 - 2^31 - 1 81 | var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF"); 82 | var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC"); 83 | var b = fromHex("1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45"); 84 | //byte[] S = Hex.decode("1053CDE42C14D696E67687561517533BF3F83345"); 85 | var n = fromHex("0100000000000000000001F4C8F927AED3CA752257"); 86 | var h = BigInteger.ONE; 87 | var curve = new ECCurveFp(p, a, b); 88 | var G = curve.decodePointHex( 89 | "04" + 90 | "4A96B5688EF573284664698968C38BB913CBFC82" + 91 | "23A628553168947D59DCC912042351377AC5FB32" 92 | ); 93 | return new X9ECParameters(curve, G, n, h); 94 | } 95 | 96 | function secp192k1() { 97 | // p = 2^192 - 2^32 - 2^12 - 2^8 - 2^7 - 2^6 - 2^3 - 1 98 | var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37"); 99 | var a = BigInteger.ZERO; 100 | var b = fromHex("3"); 101 | //byte[] S = null; 102 | var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"); 103 | var h = BigInteger.ONE; 104 | var curve = new ECCurveFp(p, a, b); 105 | var G = curve.decodePointHex( 106 | "04" + 107 | "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D" + 108 | "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D" 109 | ); 110 | return new X9ECParameters(curve, G, n, h); 111 | } 112 | 113 | function secp192r1() { 114 | // p = 2^192 - 2^64 - 1 115 | var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF"); 116 | var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC"); 117 | var b = fromHex("64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1"); 118 | //byte[] S = Hex.decode("3045AE6FC8422F64ED579528D38120EAE12196D5"); 119 | var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"); 120 | var h = BigInteger.ONE; 121 | var curve = new ECCurveFp(p, a, b); 122 | var G = curve.decodePointHex( 123 | "04" + 124 | "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012" + 125 | "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811" 126 | ); 127 | return new X9ECParameters(curve, G, n, h); 128 | } 129 | 130 | function secp224r1() { 131 | // p = 2^224 - 2^96 + 1 132 | var p = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001"); 133 | var a = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE"); 134 | var b = fromHex("B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4"); 135 | //byte[] S = Hex.decode("BD71344799D5C7FCDC45B59FA3B9AB8F6A948BC5"); 136 | var n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"); 137 | var h = BigInteger.ONE; 138 | var curve = new ECCurveFp(p, a, b); 139 | var G = curve.decodePointHex( 140 | "04" + 141 | "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21" + 142 | "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34" 143 | ); 144 | return new X9ECParameters(curve, G, n, h); 145 | } 146 | 147 | function secp256r1() { 148 | // p = 2^224 (2^32 - 1) + 2^192 + 2^96 - 1 149 | var p = fromHex( 150 | "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF" 151 | ); 152 | var a = fromHex( 153 | "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC" 154 | ); 155 | var b = fromHex( 156 | "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B" 157 | ); 158 | //byte[] S = Hex.decode("C49D360886E704936A6678E1139D26B7819F7E90"); 159 | var n = fromHex( 160 | "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551" 161 | ); 162 | var h = BigInteger.ONE; 163 | var curve = new ECCurveFp(p, a, b); 164 | var G = curve.decodePointHex( 165 | "04" + 166 | "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + 167 | "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5" 168 | ); 169 | return new X9ECParameters(curve, G, n, h); 170 | } 171 | 172 | module.exports = { 173 | secp128r1, 174 | secp160k1, 175 | secp160r1, 176 | secp192k1, 177 | secp192r1, 178 | secp224r1, 179 | secp256r1, 180 | }; 181 | -------------------------------------------------------------------------------- /lib/ec.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Basic Javascript Elliptic Curve implementation 4 | // Ported loosely from BouncyCastle's Java EC code 5 | // Only Fp curves implemented for now 6 | 7 | // Requires jsbn.js and jsbn2.js 8 | const { randomBytes } = require("crypto"); 9 | const { BigInteger } = require("jsbn"); 10 | const { Barrett } = BigInteger.prototype; 11 | 12 | class SecureRandom { 13 | nextBytes(bytes) { 14 | const { length } = bytes; 15 | const rand = randomBytes(length); 16 | for (let i = 0; i < length; ++i) { 17 | bytes.push(rand[i]); 18 | } 19 | } 20 | } 21 | 22 | // ---------------- 23 | // ECFieldElementFp 24 | 25 | // constructor 26 | function ECFieldElementFp(q, x) { 27 | this.x = x; 28 | // TODO if(x.compareTo(this.q) >= 0) error 29 | this.q = q; 30 | } 31 | 32 | function feFpEquals(other) { 33 | if (other == this) return true; 34 | return this.q.equals(other.q) && this.x.equals(other.x); 35 | } 36 | 37 | function feFpToBigInteger() { 38 | return this.x; 39 | } 40 | 41 | function feFpNegate() { 42 | return new ECFieldElementFp(this.q, this.x.negate().mod(this.q)); 43 | } 44 | 45 | function feFpAdd(b) { 46 | return new ECFieldElementFp(this.q, this.x.add(b.toBigInteger()).mod(this.q)); 47 | } 48 | 49 | function feFpSubtract(b) { 50 | return new ECFieldElementFp( 51 | this.q, 52 | this.x.subtract(b.toBigInteger()).mod(this.q) 53 | ); 54 | } 55 | 56 | function feFpMultiply(b) { 57 | return new ECFieldElementFp( 58 | this.q, 59 | this.x.multiply(b.toBigInteger()).mod(this.q) 60 | ); 61 | } 62 | 63 | function feFpSquare() { 64 | return new ECFieldElementFp(this.q, this.x.square().mod(this.q)); 65 | } 66 | 67 | function feFpDivide(b) { 68 | return new ECFieldElementFp( 69 | this.q, 70 | this.x.multiply(b.toBigInteger().modInverse(this.q)).mod(this.q) 71 | ); 72 | } 73 | 74 | ECFieldElementFp.prototype.equals = feFpEquals; 75 | ECFieldElementFp.prototype.toBigInteger = feFpToBigInteger; 76 | ECFieldElementFp.prototype.negate = feFpNegate; 77 | ECFieldElementFp.prototype.add = feFpAdd; 78 | ECFieldElementFp.prototype.subtract = feFpSubtract; 79 | ECFieldElementFp.prototype.multiply = feFpMultiply; 80 | ECFieldElementFp.prototype.square = feFpSquare; 81 | ECFieldElementFp.prototype.divide = feFpDivide; 82 | ECFieldElementFp.prototype.modDouble = function(x) { 83 | let _2x = x.shiftLeft(1); 84 | if (_2x.compareTo(this.q) >= 0) { 85 | _2x = _2x.subtract(this.q); 86 | } 87 | return _2x; 88 | }; 89 | 90 | // ---------------- 91 | // ECPointFp 92 | 93 | // constructor 94 | function ECPointFp(curve, x, y, z) { 95 | this.curve = curve; 96 | this.x = x; 97 | this.y = y; 98 | // Projective coordinates: either zinv == null or z * zinv == 1 99 | // z and zinv are just BigIntegers, not fieldElements 100 | if (z == null) { 101 | this.z = BigInteger.ONE; 102 | } else { 103 | this.z = z; 104 | } 105 | this.zinv = null; 106 | //TODO: compression flag 107 | } 108 | 109 | function pointFpGetX() { 110 | if (this.zinv == null) { 111 | this.zinv = this.z.modInverse(this.curve.q); 112 | } 113 | var r = this.x.toBigInteger().multiply(this.zinv); 114 | this.curve.reduce(r); 115 | return this.curve.fromBigInteger(r); 116 | } 117 | 118 | function pointFpGetY() { 119 | if (this.zinv == null) { 120 | this.zinv = this.z.modInverse(this.curve.q); 121 | } 122 | var r = this.y.toBigInteger().multiply(this.zinv); 123 | this.curve.reduce(r); 124 | return this.curve.fromBigInteger(r); 125 | } 126 | 127 | function pointFpEquals(other) { 128 | if (other == this) return true; 129 | if (this.isInfinity()) return other.isInfinity(); 130 | if (other.isInfinity()) return this.isInfinity(); 131 | var u, v; 132 | // u = Y2 * Z1 - Y1 * Z2 133 | u = other.y 134 | .toBigInteger() 135 | .multiply(this.z) 136 | .subtract(this.y.toBigInteger().multiply(other.z)) 137 | .mod(this.curve.q); 138 | if (!u.equals(BigInteger.ZERO)) return false; 139 | // v = X2 * Z1 - X1 * Z2 140 | v = other.x 141 | .toBigInteger() 142 | .multiply(this.z) 143 | .subtract(this.x.toBigInteger().multiply(other.z)) 144 | .mod(this.curve.q); 145 | return v.equals(BigInteger.ZERO); 146 | } 147 | 148 | function pointFpIsInfinity() { 149 | if (this.x == null && this.y == null) return true; 150 | return ( 151 | this.z.equals(BigInteger.ZERO) && 152 | !this.y.toBigInteger().equals(BigInteger.ZERO) 153 | ); 154 | } 155 | 156 | function pointFpNegate() { 157 | return new ECPointFp(this.curve, this.x, this.y.negate(), this.z); 158 | } 159 | 160 | function pointFpAdd(b) { 161 | if (this.isInfinity()) return b; 162 | if (b.isInfinity()) return this; 163 | 164 | // u = Y2 * Z1 - Y1 * Z2 165 | var u = b.y 166 | .toBigInteger() 167 | .multiply(this.z) 168 | .subtract(this.y.toBigInteger().multiply(b.z)) 169 | .mod(this.curve.q); 170 | // v = X2 * Z1 - X1 * Z2 171 | var v = b.x 172 | .toBigInteger() 173 | .multiply(this.z) 174 | .subtract(this.x.toBigInteger().multiply(b.z)) 175 | .mod(this.curve.q); 176 | 177 | if (BigInteger.ZERO.equals(v)) { 178 | if (BigInteger.ZERO.equals(u)) { 179 | return this.twice(); // this == b, so double 180 | } 181 | return this.curve.getInfinity(); // this = -b, so infinity 182 | } 183 | 184 | var THREE = new BigInteger("3"); 185 | var x1 = this.x.toBigInteger(); 186 | var y1 = this.y.toBigInteger(); 187 | var x2 = b.x.toBigInteger(); 188 | var y2 = b.y.toBigInteger(); 189 | 190 | var v2 = v.square(); 191 | var v3 = v2.multiply(v); 192 | var x1v2 = x1.multiply(v2); 193 | var zu2 = u.square().multiply(this.z); 194 | 195 | // x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3) 196 | var x3 = zu2 197 | .subtract(x1v2.shiftLeft(1)) 198 | .multiply(b.z) 199 | .subtract(v3) 200 | .multiply(v) 201 | .mod(this.curve.q); 202 | // y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3 203 | var y3 = x1v2 204 | .multiply(THREE) 205 | .multiply(u) 206 | .subtract(y1.multiply(v3)) 207 | .subtract(zu2.multiply(u)) 208 | .multiply(b.z) 209 | .add(u.multiply(v3)) 210 | .mod(this.curve.q); 211 | // z3 = v^3 * z1 * z2 212 | var z3 = v3 213 | .multiply(this.z) 214 | .multiply(b.z) 215 | .mod(this.curve.q); 216 | 217 | return new ECPointFp( 218 | this.curve, 219 | this.curve.fromBigInteger(x3), 220 | this.curve.fromBigInteger(y3), 221 | z3 222 | ); 223 | } 224 | 225 | function pointFpTwice() { 226 | if (this.isInfinity()) return this; 227 | if (this.y.toBigInteger().signum() == 0) return this.curve.getInfinity(); 228 | 229 | // TODO: optimized handling of constants 230 | var THREE = new BigInteger("3"); 231 | var x1 = this.x.toBigInteger(); 232 | var y1 = this.y.toBigInteger(); 233 | 234 | var y1z1 = y1.multiply(this.z); 235 | var y1sqz1 = y1z1.multiply(y1).mod(this.curve.q); 236 | var a = this.curve.a.toBigInteger(); 237 | 238 | // w = 3 * x1^2 + a * z1^2 239 | var w = x1.square().multiply(THREE); 240 | if (!BigInteger.ZERO.equals(a)) { 241 | w = w.add(this.z.square().multiply(a)); 242 | } 243 | w = w.mod(this.curve.q); 244 | //this.curve.reduce(w); 245 | // x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1) 246 | var x3 = w 247 | .square() 248 | .subtract(x1.shiftLeft(3).multiply(y1sqz1)) 249 | .shiftLeft(1) 250 | .multiply(y1z1) 251 | .mod(this.curve.q); 252 | // y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3 253 | var y3 = w 254 | .multiply(THREE) 255 | .multiply(x1) 256 | .subtract(y1sqz1.shiftLeft(1)) 257 | .shiftLeft(2) 258 | .multiply(y1sqz1) 259 | .subtract(w.square().multiply(w)) 260 | .mod(this.curve.q); 261 | // z3 = 8 * (y1 * z1)^3 262 | var z3 = y1z1 263 | .square() 264 | .multiply(y1z1) 265 | .shiftLeft(3) 266 | .mod(this.curve.q); 267 | 268 | return new ECPointFp( 269 | this.curve, 270 | this.curve.fromBigInteger(x3), 271 | this.curve.fromBigInteger(y3), 272 | z3 273 | ); 274 | } 275 | 276 | // Simple NAF (Non-Adjacent Form) multiplication algorithm 277 | // TODO: modularize the multiplication algorithm 278 | function pointFpMultiply(k) { 279 | if (this.isInfinity()) return this; 280 | if (k.signum() == 0) return this.curve.getInfinity(); 281 | 282 | var e = k; 283 | var h = e.multiply(new BigInteger("3")); 284 | 285 | var neg = this.negate(); 286 | var R = this; 287 | 288 | var i; 289 | for (i = h.bitLength() - 2; i > 0; --i) { 290 | R = R.twice(); 291 | 292 | var hBit = h.testBit(i); 293 | var eBit = e.testBit(i); 294 | 295 | if (hBit != eBit) { 296 | R = R.add(hBit ? this : neg); 297 | } 298 | } 299 | 300 | return R; 301 | } 302 | 303 | // Compute this*j + x*k (simultaneous multiplication) 304 | function pointFpMultiplyTwo(j, x, k) { 305 | var i; 306 | if (j.bitLength() > k.bitLength()) i = j.bitLength() - 1; 307 | else i = k.bitLength() - 1; 308 | 309 | var R = this.curve.getInfinity(); 310 | var both = this.add(x); 311 | while (i >= 0) { 312 | R = R.twice(); 313 | if (j.testBit(i)) { 314 | if (k.testBit(i)) { 315 | R = R.add(both); 316 | } else { 317 | R = R.add(this); 318 | } 319 | } else { 320 | if (k.testBit(i)) { 321 | R = R.add(x); 322 | } 323 | } 324 | --i; 325 | } 326 | 327 | return R; 328 | } 329 | 330 | ECPointFp.prototype.getX = pointFpGetX; 331 | ECPointFp.prototype.getY = pointFpGetY; 332 | ECPointFp.prototype.equals = pointFpEquals; 333 | ECPointFp.prototype.isInfinity = pointFpIsInfinity; 334 | ECPointFp.prototype.negate = pointFpNegate; 335 | ECPointFp.prototype.add = pointFpAdd; 336 | ECPointFp.prototype.twice = pointFpTwice; 337 | ECPointFp.prototype.multiply = pointFpMultiply; 338 | ECPointFp.prototype.multiplyTwo = pointFpMultiplyTwo; 339 | 340 | // ---------------- 341 | // ECCurveFp 342 | 343 | // constructor 344 | function ECCurveFp(q, a, b) { 345 | this.q = q; 346 | this.a = this.fromBigInteger(a); 347 | this.b = this.fromBigInteger(b); 348 | this.infinity = new ECPointFp(this, null, null); 349 | this.reducer = new Barrett(this.q); 350 | } 351 | 352 | function curveFpGetQ() { 353 | return this.q; 354 | } 355 | 356 | function curveFpGetA() { 357 | return this.a; 358 | } 359 | 360 | function curveFpGetB() { 361 | return this.b; 362 | } 363 | 364 | function curveFpEquals(other) { 365 | if (other == this) return true; 366 | return ( 367 | this.q.equals(other.q) && this.a.equals(other.a) && this.b.equals(other.b) 368 | ); 369 | } 370 | 371 | function curveFpGetInfinity() { 372 | return this.infinity; 373 | } 374 | 375 | function curveFpFromBigInteger(x) { 376 | return new ECFieldElementFp(this.q, x); 377 | } 378 | 379 | function curveReduce(x) { 380 | this.reducer.reduce(x); 381 | } 382 | 383 | function curveFpEncodePointHex(p) { 384 | if (p.isInfinity()) return "00"; 385 | var xHex = p 386 | .getX() 387 | .toBigInteger() 388 | .toString(16); 389 | var yHex = p 390 | .getY() 391 | .toBigInteger() 392 | .toString(16); 393 | var oLen = this.getQ().toString(16).length; 394 | if (oLen % 2 != 0) oLen++; 395 | while (xHex.length < oLen) { 396 | xHex = "0" + xHex; 397 | } 398 | while (yHex.length < oLen) { 399 | yHex = "0" + yHex; 400 | } 401 | return "04" + xHex + yHex; 402 | } 403 | 404 | ECCurveFp.prototype.getQ = curveFpGetQ; 405 | ECCurveFp.prototype.getA = curveFpGetA; 406 | ECCurveFp.prototype.getB = curveFpGetB; 407 | ECCurveFp.prototype.equals = curveFpEquals; 408 | ECCurveFp.prototype.getInfinity = curveFpGetInfinity; 409 | ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger; 410 | ECCurveFp.prototype.reduce = curveReduce; 411 | ECCurveFp.prototype.encodePointHex = curveFpEncodePointHex; 412 | 413 | // from: https://github.com/kaielvin/jsbn-ec-point-compression 414 | ECCurveFp.prototype.decodePointHex = function(s) { 415 | var yIsEven; 416 | switch ( 417 | parseInt(s.substr(0, 2), 16) // first byte 418 | ) { 419 | case 0: 420 | return this.infinity; 421 | case 2: 422 | yIsEven = false; 423 | case 3: 424 | if (yIsEven == undefined) yIsEven = true; 425 | var len = s.length - 2; 426 | var xHex = s.substr(2, len); 427 | var x = this.fromBigInteger(new BigInteger(xHex, 16)); 428 | var alpha = x.multiply(x.square().add(this.getA())).add(this.getB()); 429 | var beta = alpha.sqrt(); 430 | 431 | if (beta == null) throw "Invalid point compression"; 432 | 433 | var betaValue = beta.toBigInteger(); 434 | if (betaValue.testBit(0) != yIsEven) { 435 | // Use the other root 436 | beta = this.fromBigInteger(this.getQ().subtract(betaValue)); 437 | } 438 | return new ECPointFp(this, x, beta); 439 | case 4: 440 | case 6: 441 | case 7: 442 | var len = (s.length - 2) / 2; 443 | var xHex = s.substr(2, len); 444 | var yHex = s.substr(len + 2, len); 445 | 446 | return new ECPointFp( 447 | this, 448 | this.fromBigInteger(new BigInteger(xHex, 16)), 449 | this.fromBigInteger(new BigInteger(yHex, 16)) 450 | ); 451 | 452 | default: 453 | // unsupported 454 | return null; 455 | } 456 | }; 457 | ECCurveFp.prototype.encodeCompressedPointHex = function(p) { 458 | if (p.isInfinity()) return "00"; 459 | var xHex = p 460 | .getX() 461 | .toBigInteger() 462 | .toString(16); 463 | var oLen = this.getQ().toString(16).length; 464 | if (oLen % 2 != 0) oLen++; 465 | while (xHex.length < oLen) xHex = "0" + xHex; 466 | var yPrefix; 467 | if ( 468 | p 469 | .getY() 470 | .toBigInteger() 471 | .isEven() 472 | ) 473 | yPrefix = "02"; 474 | else yPrefix = "03"; 475 | 476 | return yPrefix + xHex; 477 | }; 478 | 479 | ECFieldElementFp.prototype.getR = function() { 480 | if (this.r != undefined) return this.r; 481 | 482 | this.r = null; 483 | var bitLength = this.q.bitLength(); 484 | if (bitLength > 128) { 485 | var firstWord = this.q.shiftRight(bitLength - 64); 486 | if (firstWord.intValue() == -1) { 487 | this.r = BigInteger.ONE.shiftLeft(bitLength).subtract(this.q); 488 | } 489 | } 490 | return this.r; 491 | }; 492 | ECFieldElementFp.prototype.modMult = function(x1, x2) { 493 | return this.modReduce(x1.multiply(x2)); 494 | }; 495 | ECFieldElementFp.prototype.modReduce = function(x) { 496 | if (this.getR() != null) { 497 | var qLen = this.q.bitLength(); 498 | while (x.bitLength() > qLen + 1) { 499 | var u = x.shiftRight(qLen); 500 | var v = x.subtract(u.shiftLeft(qLen)); 501 | if (!this.getR().equals(BigInteger.ONE)) { 502 | u = u.multiply(this.getR()); 503 | } 504 | x = u.add(v); 505 | } 506 | while (x.compareTo(this.q) >= 0) { 507 | x = x.subtract(this.q); 508 | } 509 | } else { 510 | x = x.mod(this.q); 511 | } 512 | return x; 513 | }; 514 | ECFieldElementFp.prototype.sqrt = function() { 515 | if (!this.q.testBit(0)) throw "unsupported"; 516 | 517 | // p mod 4 == 3 518 | if (this.q.testBit(1)) { 519 | var z = new ECFieldElementFp( 520 | this.q, 521 | this.x.modPow(this.q.shiftRight(2).add(BigInteger.ONE), this.q) 522 | ); 523 | return z.square().equals(this) ? z : null; 524 | } 525 | 526 | // p mod 4 == 1 527 | var qMinusOne = this.q.subtract(BigInteger.ONE); 528 | 529 | var legendreExponent = qMinusOne.shiftRight(1); 530 | if (!this.x.modPow(legendreExponent, this.q).equals(BigInteger.ONE)) { 531 | return null; 532 | } 533 | 534 | var u = qMinusOne.shiftRight(2); 535 | var k = u.shiftLeft(1).add(BigInteger.ONE); 536 | 537 | var Q = this.x; 538 | var fourQ = this.modDouble(this.modDouble(Q)); 539 | 540 | var U, V; 541 | do { 542 | var P; 543 | do { 544 | P = new BigInteger(this.q.bitLength(), new SecureRandom()); 545 | } while ( 546 | P.compareTo(this.q) >= 0 || 547 | !P.multiply(P) 548 | .subtract(fourQ) 549 | .modPow(legendreExponent, this.q) 550 | .equals(qMinusOne) 551 | ); 552 | 553 | var result = this.lucasSequence(P, Q, k); 554 | U = result[0]; 555 | V = result[1]; 556 | 557 | if (this.modMult(V, V).equals(fourQ)) { 558 | // Integer division by 2, mod q 559 | if (V.testBit(0)) { 560 | V = V.add(this.q); 561 | } 562 | 563 | V = V.shiftRight(1); 564 | 565 | return new ECFieldElementFp(this.q, V); 566 | } 567 | } while (U.equals(BigInteger.ONE) || U.equals(qMinusOne)); 568 | 569 | return null; 570 | }; 571 | ECFieldElementFp.prototype.lucasSequence = function(P, Q, k) { 572 | var n = k.bitLength(); 573 | var s = k.getLowestSetBit(); 574 | 575 | var Uh = BigInteger.ONE; 576 | var Vl = BigInteger.ONE.add(BigInteger.ONE); 577 | var Vh = P; 578 | var Ql = BigInteger.ONE; 579 | var Qh = BigInteger.ONE; 580 | 581 | for (var j = n - 1; j >= s + 1; --j) { 582 | Ql = this.modMult(Ql, Qh); 583 | 584 | if (k.testBit(j)) { 585 | Qh = this.modMult(Ql, Q); 586 | Uh = this.modMult(Uh, Vh); 587 | Vl = this.modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql))); 588 | Vh = this.modReduce(Vh.multiply(Vh).subtract(Qh.shiftLeft(1))); 589 | } else { 590 | Qh = Ql; 591 | Uh = this.modReduce(Uh.multiply(Vl).subtract(Ql)); 592 | Vh = this.modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql))); 593 | Vl = this.modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1))); 594 | } 595 | } 596 | 597 | Ql = this.modMult(Ql, Qh); 598 | Qh = this.modMult(Ql, Q); 599 | Uh = this.modReduce(Uh.multiply(Vl).subtract(Ql)); 600 | Vl = this.modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql))); 601 | Ql = this.modMult(Ql, Qh); 602 | 603 | for (var j = 1; j <= s; ++j) { 604 | Uh = this.modMult(Uh, Vl); 605 | Vl = this.modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1))); 606 | Ql = this.modMult(Ql, Ql); 607 | } 608 | 609 | return [Uh, Vl]; 610 | }; 611 | 612 | module.exports = { 613 | ECCurveFp, 614 | ECPointFp, 615 | ECFieldElementFp, 616 | }; 617 | --------------------------------------------------------------------------------