├── dist └── .gitkeep ├── .gitignore ├── .npmignore ├── lib ├── elliptic │ ├── curve │ │ ├── index.js │ │ ├── mont.js │ │ ├── base.js │ │ ├── edwards.js │ │ └── short.js │ ├── eddsa │ │ ├── signature.js │ │ ├── key.js │ │ └── index.js │ ├── utils.js │ ├── ec │ │ ├── signature.js │ │ ├── key.js │ │ └── index.js │ ├── curves.js │ └── precomputed │ │ └── secp256k1.js └── elliptic.js ├── test ├── index.js ├── api-test.js ├── ecdh-test.js ├── unittests.html ├── ed25519-test.js ├── curve-test.js └── ecdsa-test.js ├── .codeclimate.yml ├── benchmarks ├── package.json └── index.js ├── .travis.yml ├── .jscsrc ├── package.json ├── Gruntfile.js ├── .jshintrc └── README.md /dist/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/lib/ 2 | node_modules/ 3 | coverage/ 4 | npm-debug.log 5 | 1.js 6 | v8.log 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | test/lib/ 3 | node_modules/ 4 | coverage/ 5 | npm-debug.log 6 | 1.js 7 | v8.log 8 | -------------------------------------------------------------------------------- /lib/elliptic/curve/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var curve = exports; 4 | 5 | curve.base = require('./base'); 6 | curve.short = require('./short'); 7 | curve.mont = require('./mont'); 8 | curve.edwards = require('./edwards'); 9 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | describe('Test specs', function () { 2 | require('./api-test.js'); 3 | require('./curve-test.js'); 4 | require('./ecdh-test.js'); 5 | require('./ecdsa-test.js'); 6 | require('./ed25519-test.js'); 7 | }); 8 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - javascript 8 | eslint: 9 | enabled: true 10 | fixme: 11 | enabled: true 12 | ratings: 13 | paths: 14 | - "lib/**/*" 15 | -------------------------------------------------------------------------------- /lib/elliptic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var elliptic = exports; 4 | 5 | elliptic.version = require('../package.json').version; 6 | elliptic.utils = require('./elliptic/utils'); 7 | elliptic.rand = require('brorand'); 8 | elliptic.curve = require('./elliptic/curve'); 9 | elliptic.curves = require('./elliptic/curves'); 10 | 11 | // Protocols 12 | elliptic.ec = require('./elliptic/ec'); 13 | elliptic.eddsa = require('./elliptic/eddsa'); 14 | -------------------------------------------------------------------------------- /test/api-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var elliptic = require('../'); 3 | 4 | describe('EC API', function() { 5 | it('should instantiate with valid curve (secp256k1)', function() { 6 | var ec = new elliptic.ec('secp256k1'); 7 | 8 | assert(ec); 9 | assert(typeof ec === 'object'); 10 | }); 11 | 12 | it('should throw error with invalid curve', function() { 13 | assert.throws(function() { 14 | var ec = new elliptic.ec('nonexistent-curve'); 15 | }, Error); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /benchmarks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bn.js-benchmark", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "benchmark": "^1.0.0", 13 | "eccjs": "^0.3.1", 14 | "ecdsa": "^0.6.0", 15 | "eckey": "^0.8.0", 16 | "hash.js": "^0.3.2", 17 | "jodid25519": "git://github.com/meganz/jodid25519.git", 18 | "secp256k1": "~0.0.13", 19 | "sjcl": "^1.0.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/ecdh-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var elliptic = require('../'); 3 | var hash = require('hash.js'); 4 | 5 | describe('ECDH', function() { 6 | function test(name) { 7 | it('should work with ' + name + ' curve', function() { 8 | var ecdh = new elliptic.ec(name); 9 | var s1 = ecdh.genKeyPair(); 10 | var s2 = ecdh.genKeyPair(); 11 | var sh1 = s1.derive(s2.getPublic()); 12 | var sh2 = s2.derive(s1.getPublic()); 13 | 14 | assert.equal(sh1.toString(16), sh2.toString(16)); 15 | 16 | var sh1 = s1.derive(ecdh.keyFromPublic(s2.getPublic('hex'), 'hex') 17 | .getPublic()); 18 | var sh2 = s2.derive(ecdh.keyFromPublic(s1.getPublic('hex'), 'hex') 19 | .getPublic()); 20 | assert.equal(sh1.toString(16), sh2.toString(16)); 21 | }); 22 | } 23 | 24 | test('curve25519'); 25 | test('ed25519'); 26 | test('secp256k1'); 27 | }); 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | language: node_js 5 | node_js: 6 | - "0.10" 7 | - "0.11" 8 | - "0.12" 9 | - "4" 10 | - "5" 11 | env: 12 | global: 13 | - secure: ewqsRnX7z3+g6q+mM6peRPhuTlTlkJIRmVG/Ya9AcDXEb7as49KDTdqfMcOu7+FxF50ehqla+TRRCLRPOcVkWqs72OVUXEsjRASp9jQwpQGl+g/vZhyTMwlM2ENEkyiFQtobI1hYKgVW1ogftkc6vm7ywvoyTrFp/leVJBrTuHk= 14 | - secure: UcurJwykdbpUrAsrMox4BfiSGOWDIcI8ytBdxgK2vOC2biZJmkFXG4WfB6X2PwNvK4hx0z2s+VhxNVQRwtyl9mqyeAoAWjTpDg+NOoBgTu6Bacsobg29m65tD4x4Qc6iSmylRnfdb04Iob4rsnNNLZbIbERv3j3PFJMENUD3acQ= 15 | - secure: S8vAesx9V9o0/cJRmENJ76JrzSPLo8mtKAfDVHgcVG7Okfl7ANs+0cW1BtjZjBR63uQKJSmQEYC6N9eQlnNhCjeM7KXtFEbtD76OxvAABj4Sr9pIY06KUYLvBaGdYNmyT0WMIAaRN8SzY2uZEUaxMHmiEd1yOw6qQYBMCjbx0xg= 16 | matrix: 17 | - TEST_SUITE='npm run unit' 18 | matrix: 19 | include: 20 | - node_js: "4" 21 | env: TEST_SUITE='npm run lint' 22 | - node_js: "4" 23 | env: TEST_SUITE='grunt coveralls' 24 | - node_js: "4" 25 | env: TEST_SUITE='grunt saucelabs' 26 | allow_failures: 27 | - env: TEST_SUITE='grunt saucelabs' 28 | fast_finish: true 29 | cache: 30 | directories: 31 | - node_modules 32 | before_script: 33 | - npm install -g grunt-cli 34 | script: 35 | - $TEST_SUITE 36 | -------------------------------------------------------------------------------- /test/unittests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | scrypt-async Unit Tests 6 | 7 | 8 | 9 |
10 | 11 | 15 | 16 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowKeywordsOnNewLine": [ "else" ], 3 | "disallowMixedSpacesAndTabs": true, 4 | "disallowMultipleLineStrings": true, 5 | "disallowMultipleVarDecl": true, 6 | "disallowNewlineBeforeBlockStatements": true, 7 | "disallowQuotedKeysInObjects": true, 8 | "disallowSpaceAfterObjectKeys": true, 9 | "disallowSpaceAfterPrefixUnaryOperators": true, 10 | "disallowSpaceBeforePostfixUnaryOperators": true, 11 | "disallowSpacesInCallExpression": true, 12 | "disallowTrailingComma": true, 13 | "disallowTrailingWhitespace": true, 14 | "disallowYodaConditions": true, 15 | 16 | "requireCommaBeforeLineBreak": true, 17 | "requireOperatorBeforeLineBreak": true, 18 | "requireSpaceAfterBinaryOperators": true, 19 | "requireSpaceAfterKeywords": [ "if", "for", "while", "else", "try", "catch" ], 20 | "requireSpaceAfterLineComment": true, 21 | "requireSpaceBeforeBinaryOperators": true, 22 | "requireSpaceBeforeBlockStatements": true, 23 | "requireSpaceBeforeKeywords": [ "else", "catch" ], 24 | "requireSpaceBeforeObjectValues": true, 25 | "requireSpaceBetweenArguments": true, 26 | "requireSpacesInAnonymousFunctionExpression": { 27 | "beforeOpeningCurlyBrace": true 28 | }, 29 | "requireSpacesInFunctionDeclaration": { 30 | "beforeOpeningCurlyBrace": true 31 | }, 32 | "requireSpacesInFunctionExpression": { 33 | "beforeOpeningCurlyBrace": true 34 | }, 35 | "requireSpacesInConditionalExpression": true, 36 | "requireSpacesInForStatement": true, 37 | "requireSpacesInsideArrayBrackets": "all", 38 | "requireSpacesInsideObjectBrackets": "all", 39 | "requireDotNotation": true, 40 | 41 | "maximumLineLength": 80, 42 | "validateIndentation": 2, 43 | "validateLineBreaks": "LF", 44 | "validateParameterSeparator": ", ", 45 | "validateQuoteMarks": "'" 46 | } 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elliptic", 3 | "version": "6.4.0", 4 | "description": "EC cryptography", 5 | "main": "lib/elliptic.js", 6 | "files": [ 7 | "lib" 8 | ], 9 | "scripts": { 10 | "jscs": "jscs benchmarks/*.js lib/*.js lib/**/*.js lib/**/**/*.js test/index.js", 11 | "jshint": "jscs benchmarks/*.js lib/*.js lib/**/*.js lib/**/**/*.js test/index.js", 12 | "lint": "npm run jscs && npm run jshint", 13 | "unit": "istanbul test _mocha --reporter=spec test/index.js", 14 | "test": "npm run lint && npm run unit", 15 | "version": "grunt dist && git add dist/" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:indutny/elliptic" 20 | }, 21 | "keywords": [ 22 | "EC", 23 | "Elliptic", 24 | "curve", 25 | "Cryptography" 26 | ], 27 | "author": "Fedor Indutny ", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/indutny/elliptic/issues" 31 | }, 32 | "homepage": "https://github.com/indutny/elliptic", 33 | "devDependencies": { 34 | "brfs": "^1.4.3", 35 | "coveralls": "^2.11.3", 36 | "grunt": "^0.4.5", 37 | "grunt-browserify": "^5.0.0", 38 | "grunt-cli": "^1.2.0", 39 | "grunt-contrib-connect": "^1.0.0", 40 | "grunt-contrib-copy": "^1.0.0", 41 | "grunt-contrib-uglify": "^1.0.1", 42 | "grunt-mocha-istanbul": "^3.0.1", 43 | "grunt-saucelabs": "^8.6.2", 44 | "istanbul": "^0.4.2", 45 | "jscs": "^2.9.0", 46 | "jshint": "^2.6.0", 47 | "mocha": "^2.1.0" 48 | }, 49 | "dependencies": { 50 | "bn.js": "^4.4.0", 51 | "brorand": "^1.0.1", 52 | "hash.js": "^1.0.0", 53 | "hmac-drbg": "^1.0.0", 54 | "inherits": "^2.0.1", 55 | "minimalistic-assert": "^1.0.0", 56 | "minimalistic-crypto-utils": "^1.0.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/elliptic/eddsa/signature.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('bn.js'); 4 | var elliptic = require('../../elliptic'); 5 | var utils = elliptic.utils; 6 | var assert = utils.assert; 7 | var cachedProperty = utils.cachedProperty; 8 | var parseBytes = utils.parseBytes; 9 | 10 | /** 11 | * @param {EDDSA} eddsa - eddsa instance 12 | * @param {Array|Object} sig - 13 | * @param {Array|Point} [sig.R] - R point as Point or bytes 14 | * @param {Array|bn} [sig.S] - S scalar as bn or bytes 15 | * @param {Array} [sig.Rencoded] - R point encoded 16 | * @param {Array} [sig.Sencoded] - S scalar encoded 17 | */ 18 | function Signature(eddsa, sig) { 19 | this.eddsa = eddsa; 20 | 21 | if (typeof sig !== 'object') 22 | sig = parseBytes(sig); 23 | 24 | if (Array.isArray(sig)) { 25 | sig = { 26 | R: sig.slice(0, eddsa.encodingLength), 27 | S: sig.slice(eddsa.encodingLength) 28 | }; 29 | } 30 | 31 | assert(sig.R && sig.S, 'Signature without R or S'); 32 | 33 | if (eddsa.isPoint(sig.R)) 34 | this._R = sig.R; 35 | if (sig.S instanceof BN) 36 | this._S = sig.S; 37 | 38 | this._Rencoded = Array.isArray(sig.R) ? sig.R : sig.Rencoded; 39 | this._Sencoded = Array.isArray(sig.S) ? sig.S : sig.Sencoded; 40 | } 41 | 42 | cachedProperty(Signature, 'S', function S() { 43 | return this.eddsa.decodeInt(this.Sencoded()); 44 | }); 45 | 46 | cachedProperty(Signature, 'R', function R() { 47 | return this.eddsa.decodePoint(this.Rencoded()); 48 | }); 49 | 50 | cachedProperty(Signature, 'Rencoded', function Rencoded() { 51 | return this.eddsa.encodePoint(this.R()); 52 | }); 53 | 54 | cachedProperty(Signature, 'Sencoded', function Sencoded() { 55 | return this.eddsa.encodeInt(this.S()); 56 | }); 57 | 58 | Signature.prototype.toBytes = function toBytes() { 59 | return this.Rencoded().concat(this.Sencoded()); 60 | }; 61 | 62 | Signature.prototype.toHex = function toHex() { 63 | return utils.encode(this.toBytes(), 'hex').toUpperCase(); 64 | }; 65 | 66 | module.exports = Signature; 67 | -------------------------------------------------------------------------------- /lib/elliptic/eddsa/key.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var elliptic = require('../../elliptic'); 4 | var utils = elliptic.utils; 5 | var assert = utils.assert; 6 | var parseBytes = utils.parseBytes; 7 | var cachedProperty = utils.cachedProperty; 8 | 9 | /** 10 | * @param {EDDSA} eddsa - instance 11 | * @param {Object} params - public/private key parameters 12 | * 13 | * @param {Array} [params.secret] - secret seed bytes 14 | * @param {Point} [params.pub] - public key point (aka `A` in eddsa terms) 15 | * @param {Array} [params.pub] - public key point encoded as bytes 16 | * 17 | */ 18 | function KeyPair(eddsa, params) { 19 | this.eddsa = eddsa; 20 | this._secret = parseBytes(params.secret); 21 | if (eddsa.isPoint(params.pub)) 22 | this._pub = params.pub; 23 | else 24 | this._pubBytes = parseBytes(params.pub); 25 | } 26 | 27 | KeyPair.fromPublic = function fromPublic(eddsa, pub) { 28 | if (pub instanceof KeyPair) 29 | return pub; 30 | return new KeyPair(eddsa, { pub: pub }); 31 | }; 32 | 33 | KeyPair.fromSecret = function fromSecret(eddsa, secret) { 34 | if (secret instanceof KeyPair) 35 | return secret; 36 | return new KeyPair(eddsa, { secret: secret }); 37 | }; 38 | 39 | KeyPair.prototype.secret = function secret() { 40 | return this._secret; 41 | }; 42 | 43 | cachedProperty(KeyPair, 'pubBytes', function pubBytes() { 44 | return this.eddsa.encodePoint(this.pub()); 45 | }); 46 | 47 | cachedProperty(KeyPair, 'pub', function pub() { 48 | if (this._pubBytes) 49 | return this.eddsa.decodePoint(this._pubBytes); 50 | return this.eddsa.g.mul(this.priv()); 51 | }); 52 | 53 | cachedProperty(KeyPair, 'privBytes', function privBytes() { 54 | var eddsa = this.eddsa; 55 | var hash = this.hash(); 56 | var lastIx = eddsa.encodingLength - 1; 57 | 58 | var a = hash.slice(0, eddsa.encodingLength); 59 | a[0] &= 248; 60 | a[lastIx] &= 127; 61 | a[lastIx] |= 64; 62 | 63 | return a; 64 | }); 65 | 66 | cachedProperty(KeyPair, 'priv', function priv() { 67 | return this.eddsa.decodeInt(this.privBytes()); 68 | }); 69 | 70 | cachedProperty(KeyPair, 'hash', function hash() { 71 | return this.eddsa.hash().update(this.secret()).digest(); 72 | }); 73 | 74 | cachedProperty(KeyPair, 'messagePrefix', function messagePrefix() { 75 | return this.hash().slice(this.eddsa.encodingLength); 76 | }); 77 | 78 | KeyPair.prototype.sign = function sign(message) { 79 | assert(this._secret, 'KeyPair can only verify'); 80 | return this.eddsa.sign(message, this); 81 | }; 82 | 83 | KeyPair.prototype.verify = function verify(message, sig) { 84 | return this.eddsa.verify(message, sig, this); 85 | }; 86 | 87 | KeyPair.prototype.getSecret = function getSecret(enc) { 88 | assert(this._secret, 'KeyPair is public only'); 89 | return utils.encode(this.secret(), enc); 90 | }; 91 | 92 | KeyPair.prototype.getPublic = function getPublic(enc) { 93 | return utils.encode(this.pubBytes(), enc); 94 | }; 95 | 96 | module.exports = KeyPair; 97 | -------------------------------------------------------------------------------- /lib/elliptic/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var utils = exports; 4 | var BN = require('bn.js'); 5 | var minAssert = require('minimalistic-assert'); 6 | var minUtils = require('minimalistic-crypto-utils'); 7 | 8 | utils.assert = minAssert; 9 | utils.toArray = minUtils.toArray; 10 | utils.zero2 = minUtils.zero2; 11 | utils.toHex = minUtils.toHex; 12 | utils.encode = minUtils.encode; 13 | 14 | // Represent num in a w-NAF form 15 | function getNAF(num, w) { 16 | var naf = []; 17 | var ws = 1 << (w + 1); 18 | var k = num.clone(); 19 | while (k.cmpn(1) >= 0) { 20 | var z; 21 | if (k.isOdd()) { 22 | var mod = k.andln(ws - 1); 23 | if (mod > (ws >> 1) - 1) 24 | z = (ws >> 1) - mod; 25 | else 26 | z = mod; 27 | k.isubn(z); 28 | } else { 29 | z = 0; 30 | } 31 | naf.push(z); 32 | 33 | // Optimization, shift by word if possible 34 | var shift = (k.cmpn(0) !== 0 && k.andln(ws - 1) === 0) ? (w + 1) : 1; 35 | for (var i = 1; i < shift; i++) 36 | naf.push(0); 37 | k.iushrn(shift); 38 | } 39 | 40 | return naf; 41 | } 42 | utils.getNAF = getNAF; 43 | 44 | // Represent k1, k2 in a Joint Sparse Form 45 | function getJSF(k1, k2) { 46 | var jsf = [ 47 | [], 48 | [] 49 | ]; 50 | 51 | k1 = k1.clone(); 52 | k2 = k2.clone(); 53 | var d1 = 0; 54 | var d2 = 0; 55 | while (k1.cmpn(-d1) > 0 || k2.cmpn(-d2) > 0) { 56 | 57 | // First phase 58 | var m14 = (k1.andln(3) + d1) & 3; 59 | var m24 = (k2.andln(3) + d2) & 3; 60 | if (m14 === 3) 61 | m14 = -1; 62 | if (m24 === 3) 63 | m24 = -1; 64 | var u1; 65 | if ((m14 & 1) === 0) { 66 | u1 = 0; 67 | } else { 68 | var m8 = (k1.andln(7) + d1) & 7; 69 | if ((m8 === 3 || m8 === 5) && m24 === 2) 70 | u1 = -m14; 71 | else 72 | u1 = m14; 73 | } 74 | jsf[0].push(u1); 75 | 76 | var u2; 77 | if ((m24 & 1) === 0) { 78 | u2 = 0; 79 | } else { 80 | var m8 = (k2.andln(7) + d2) & 7; 81 | if ((m8 === 3 || m8 === 5) && m14 === 2) 82 | u2 = -m24; 83 | else 84 | u2 = m24; 85 | } 86 | jsf[1].push(u2); 87 | 88 | // Second phase 89 | if (2 * d1 === u1 + 1) 90 | d1 = 1 - d1; 91 | if (2 * d2 === u2 + 1) 92 | d2 = 1 - d2; 93 | k1.iushrn(1); 94 | k2.iushrn(1); 95 | } 96 | 97 | return jsf; 98 | } 99 | utils.getJSF = getJSF; 100 | 101 | function cachedProperty(obj, name, computer) { 102 | var key = '_' + name; 103 | obj.prototype[name] = function cachedProperty() { 104 | return this[key] !== undefined ? this[key] : 105 | this[key] = computer.call(this); 106 | }; 107 | } 108 | utils.cachedProperty = cachedProperty; 109 | 110 | function parseBytes(bytes) { 111 | return typeof bytes === 'string' ? utils.toArray(bytes, 'hex') : 112 | bytes; 113 | } 114 | utils.parseBytes = parseBytes; 115 | 116 | function intFromLE(bytes) { 117 | return new BN(bytes, 'hex', 'le'); 118 | } 119 | utils.intFromLE = intFromLE; 120 | 121 | -------------------------------------------------------------------------------- /lib/elliptic/ec/signature.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('bn.js'); 4 | 5 | var elliptic = require('../../elliptic'); 6 | var utils = elliptic.utils; 7 | var assert = utils.assert; 8 | 9 | function Signature(options, enc) { 10 | if (options instanceof Signature) 11 | return options; 12 | 13 | if (this._importDER(options, enc)) 14 | return; 15 | 16 | assert(options.r && options.s, 'Signature without r or s'); 17 | this.r = new BN(options.r, 16); 18 | this.s = new BN(options.s, 16); 19 | if (options.recoveryParam === undefined) 20 | this.recoveryParam = null; 21 | else 22 | this.recoveryParam = options.recoveryParam; 23 | } 24 | module.exports = Signature; 25 | 26 | function Position() { 27 | this.place = 0; 28 | } 29 | 30 | function getLength(buf, p) { 31 | var initial = buf[p.place++]; 32 | if (!(initial & 0x80)) { 33 | return initial; 34 | } 35 | var octetLen = initial & 0xf; 36 | var val = 0; 37 | for (var i = 0, off = p.place; i < octetLen; i++, off++) { 38 | val <<= 8; 39 | val |= buf[off]; 40 | } 41 | p.place = off; 42 | return val; 43 | } 44 | 45 | function rmPadding(buf) { 46 | var i = 0; 47 | var len = buf.length - 1; 48 | while (!buf[i] && !(buf[i + 1] & 0x80) && i < len) { 49 | i++; 50 | } 51 | if (i === 0) { 52 | return buf; 53 | } 54 | return buf.slice(i); 55 | } 56 | 57 | Signature.prototype._importDER = function _importDER(data, enc) { 58 | data = utils.toArray(data, enc); 59 | var p = new Position(); 60 | if (data[p.place++] !== 0x30) { 61 | return false; 62 | } 63 | var len = getLength(data, p); 64 | if ((len + p.place) !== data.length) { 65 | return false; 66 | } 67 | if (data[p.place++] !== 0x02) { 68 | return false; 69 | } 70 | var rlen = getLength(data, p); 71 | var r = data.slice(p.place, rlen + p.place); 72 | p.place += rlen; 73 | if (data[p.place++] !== 0x02) { 74 | return false; 75 | } 76 | var slen = getLength(data, p); 77 | if (data.length !== slen + p.place) { 78 | return false; 79 | } 80 | var s = data.slice(p.place, slen + p.place); 81 | if (r[0] === 0 && (r[1] & 0x80)) { 82 | r = r.slice(1); 83 | } 84 | if (s[0] === 0 && (s[1] & 0x80)) { 85 | s = s.slice(1); 86 | } 87 | 88 | this.r = new BN(r); 89 | this.s = new BN(s); 90 | this.recoveryParam = null; 91 | 92 | return true; 93 | }; 94 | 95 | function constructLength(arr, len) { 96 | if (len < 0x80) { 97 | arr.push(len); 98 | return; 99 | } 100 | var octets = 1 + (Math.log(len) / Math.LN2 >>> 3); 101 | arr.push(octets | 0x80); 102 | while (--octets) { 103 | arr.push((len >>> (octets << 3)) & 0xff); 104 | } 105 | arr.push(len); 106 | } 107 | 108 | Signature.prototype.toDER = function toDER(enc) { 109 | var r = this.r.toArray(); 110 | var s = this.s.toArray(); 111 | 112 | // Pad values 113 | if (r[0] & 0x80) 114 | r = [ 0 ].concat(r); 115 | // Pad values 116 | if (s[0] & 0x80) 117 | s = [ 0 ].concat(s); 118 | 119 | r = rmPadding(r); 120 | s = rmPadding(s); 121 | 122 | while (!s[0] && !(s[1] & 0x80)) { 123 | s = s.slice(1); 124 | } 125 | var arr = [ 0x02 ]; 126 | constructLength(arr, r.length); 127 | arr = arr.concat(r); 128 | arr.push(0x02); 129 | constructLength(arr, s.length); 130 | var backHalf = arr.concat(s); 131 | var res = [ 0x30 ]; 132 | constructLength(res, backHalf.length); 133 | res = res.concat(backHalf); 134 | return utils.encode(res, enc); 135 | }; 136 | -------------------------------------------------------------------------------- /lib/elliptic/ec/key.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('bn.js'); 4 | var elliptic = require('../../elliptic'); 5 | var utils = elliptic.utils; 6 | var assert = utils.assert; 7 | 8 | function KeyPair(ec, options) { 9 | this.ec = ec; 10 | this.priv = null; 11 | this.pub = null; 12 | 13 | // KeyPair(ec, { priv: ..., pub: ... }) 14 | if (options.priv) 15 | this._importPrivate(options.priv, options.privEnc); 16 | if (options.pub) 17 | this._importPublic(options.pub, options.pubEnc); 18 | } 19 | module.exports = KeyPair; 20 | 21 | KeyPair.fromPublic = function fromPublic(ec, pub, enc) { 22 | if (pub instanceof KeyPair) 23 | return pub; 24 | 25 | return new KeyPair(ec, { 26 | pub: pub, 27 | pubEnc: enc 28 | }); 29 | }; 30 | 31 | KeyPair.fromPrivate = function fromPrivate(ec, priv, enc) { 32 | if (priv instanceof KeyPair) 33 | return priv; 34 | 35 | return new KeyPair(ec, { 36 | priv: priv, 37 | privEnc: enc 38 | }); 39 | }; 40 | 41 | KeyPair.prototype.validate = function validate() { 42 | var pub = this.getPublic(); 43 | 44 | if (pub.isInfinity()) 45 | return { result: false, reason: 'Invalid public key' }; 46 | if (!pub.validate()) 47 | return { result: false, reason: 'Public key is not a point' }; 48 | if (!pub.mul(this.ec.curve.n).isInfinity()) 49 | return { result: false, reason: 'Public key * N != O' }; 50 | 51 | return { result: true, reason: null }; 52 | }; 53 | 54 | KeyPair.prototype.getPublic = function getPublic(compact, enc) { 55 | // compact is optional argument 56 | if (typeof compact === 'string') { 57 | enc = compact; 58 | compact = null; 59 | } 60 | 61 | if (!this.pub) 62 | this.pub = this.ec.g.mul(this.priv); 63 | 64 | if (!enc) 65 | return this.pub; 66 | 67 | return this.pub.encode(enc, compact); 68 | }; 69 | 70 | KeyPair.prototype.getPrivate = function getPrivate(enc) { 71 | if (enc === 'hex') 72 | return this.priv.toString(16, 2); 73 | else 74 | return this.priv; 75 | }; 76 | 77 | KeyPair.prototype._importPrivate = function _importPrivate(key, enc) { 78 | this.priv = new BN(key, enc || 16); 79 | 80 | // Ensure that the priv won't be bigger than n, otherwise we may fail 81 | // in fixed multiplication method 82 | this.priv = this.priv.umod(this.ec.curve.n); 83 | }; 84 | 85 | KeyPair.prototype._importPublic = function _importPublic(key, enc) { 86 | if (key.x || key.y) { 87 | // Montgomery points only have an `x` coordinate. 88 | // Weierstrass/Edwards points on the other hand have both `x` and 89 | // `y` coordinates. 90 | if (this.ec.curve.type === 'mont') { 91 | assert(key.x, 'Need x coordinate'); 92 | } else if (this.ec.curve.type === 'short' || 93 | this.ec.curve.type === 'edwards') { 94 | assert(key.x && key.y, 'Need both x and y coordinate'); 95 | } 96 | this.pub = this.ec.curve.point(key.x, key.y); 97 | return; 98 | } 99 | this.pub = this.ec.curve.decodePoint(key, enc); 100 | }; 101 | 102 | // ECDH 103 | KeyPair.prototype.derive = function derive(pub) { 104 | return pub.mul(this.priv).getX(); 105 | }; 106 | 107 | // ECDSA 108 | KeyPair.prototype.sign = function sign(msg, enc, options) { 109 | return this.ec.sign(msg, this, enc, options); 110 | }; 111 | 112 | KeyPair.prototype.verify = function verify(msg, signature) { 113 | return this.ec.verify(msg, signature, this); 114 | }; 115 | 116 | KeyPair.prototype.inspect = function inspect() { 117 | return ''; 119 | }; 120 | -------------------------------------------------------------------------------- /lib/elliptic/eddsa/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var hash = require('hash.js'); 4 | var elliptic = require('../../elliptic'); 5 | var utils = elliptic.utils; 6 | var assert = utils.assert; 7 | var parseBytes = utils.parseBytes; 8 | var KeyPair = require('./key'); 9 | var Signature = require('./signature'); 10 | 11 | function EDDSA(curve) { 12 | assert(curve === 'ed25519', 'only tested with ed25519 so far'); 13 | 14 | if (!(this instanceof EDDSA)) 15 | return new EDDSA(curve); 16 | 17 | var curve = elliptic.curves[curve].curve; 18 | this.curve = curve; 19 | this.g = curve.g; 20 | this.g.precompute(curve.n.bitLength() + 1); 21 | 22 | this.pointClass = curve.point().constructor; 23 | this.encodingLength = Math.ceil(curve.n.bitLength() / 8); 24 | this.hash = hash.sha512; 25 | } 26 | 27 | module.exports = EDDSA; 28 | 29 | /** 30 | * @param {Array|String} message - message bytes 31 | * @param {Array|String|KeyPair} secret - secret bytes or a keypair 32 | * @returns {Signature} - signature 33 | */ 34 | EDDSA.prototype.sign = function sign(message, secret) { 35 | message = parseBytes(message); 36 | var key = this.keyFromSecret(secret); 37 | var r = this.hashInt(key.messagePrefix(), message); 38 | var R = this.g.mul(r); 39 | var Rencoded = this.encodePoint(R); 40 | var s_ = this.hashInt(Rencoded, key.pubBytes(), message) 41 | .mul(key.priv()); 42 | var S = r.add(s_).umod(this.curve.n); 43 | return this.makeSignature({ R: R, S: S, Rencoded: Rencoded }); 44 | }; 45 | 46 | /** 47 | * @param {Array} message - message bytes 48 | * @param {Array|String|Signature} sig - sig bytes 49 | * @param {Array|String|Point|KeyPair} pub - public key 50 | * @returns {Boolean} - true if public key matches sig of message 51 | */ 52 | EDDSA.prototype.verify = function verify(message, sig, pub) { 53 | message = parseBytes(message); 54 | sig = this.makeSignature(sig); 55 | var key = this.keyFromPublic(pub); 56 | var h = this.hashInt(sig.Rencoded(), key.pubBytes(), message); 57 | var SG = this.g.mul(sig.S()); 58 | var RplusAh = sig.R().add(key.pub().mul(h)); 59 | return RplusAh.eq(SG); 60 | }; 61 | 62 | EDDSA.prototype.hashInt = function hashInt() { 63 | var hash = this.hash(); 64 | for (var i = 0; i < arguments.length; i++) 65 | hash.update(arguments[i]); 66 | return utils.intFromLE(hash.digest()).umod(this.curve.n); 67 | }; 68 | 69 | EDDSA.prototype.keyFromPublic = function keyFromPublic(pub) { 70 | return KeyPair.fromPublic(this, pub); 71 | }; 72 | 73 | EDDSA.prototype.keyFromSecret = function keyFromSecret(secret) { 74 | return KeyPair.fromSecret(this, secret); 75 | }; 76 | 77 | EDDSA.prototype.makeSignature = function makeSignature(sig) { 78 | if (sig instanceof Signature) 79 | return sig; 80 | return new Signature(this, sig); 81 | }; 82 | 83 | /** 84 | * * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03#section-5.2 85 | * 86 | * EDDSA defines methods for encoding and decoding points and integers. These are 87 | * helper convenience methods, that pass along to utility functions implied 88 | * parameters. 89 | * 90 | */ 91 | EDDSA.prototype.encodePoint = function encodePoint(point) { 92 | var enc = point.getY().toArray('le', this.encodingLength); 93 | enc[this.encodingLength - 1] |= point.getX().isOdd() ? 0x80 : 0; 94 | return enc; 95 | }; 96 | 97 | EDDSA.prototype.decodePoint = function decodePoint(bytes) { 98 | bytes = utils.parseBytes(bytes); 99 | 100 | var lastIx = bytes.length - 1; 101 | var normed = bytes.slice(0, lastIx).concat(bytes[lastIx] & ~0x80); 102 | var xIsOdd = (bytes[lastIx] & 0x80) !== 0; 103 | 104 | var y = utils.intFromLE(normed); 105 | return this.curve.pointFromY(y, xIsOdd); 106 | }; 107 | 108 | EDDSA.prototype.encodeInt = function encodeInt(num) { 109 | return num.toArray('le', this.encodingLength); 110 | }; 111 | 112 | EDDSA.prototype.decodeInt = function decodeInt(bytes) { 113 | return utils.intFromLE(bytes); 114 | }; 115 | 116 | EDDSA.prototype.isPoint = function isPoint(val) { 117 | return val instanceof this.pointClass; 118 | }; 119 | -------------------------------------------------------------------------------- /benchmarks/index.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var benchmark = require('benchmark'); 3 | var crypto = require('crypto'); 4 | var hash = require('hash.js'); 5 | var elliptic = require('../'); 6 | var eccjs = require('eccjs'); 7 | var jodid = require('./deps/jodid'); 8 | var ecdsa = require('ecdsa'); 9 | var ECKey = require('eckey'); 10 | var secp256k1 = require('secp256k1'); 11 | 12 | var benchmarks = []; 13 | var maxTime = 10; 14 | 15 | function add(op, obj) { 16 | benchmarks.push({ 17 | name: op, 18 | start: function start() { 19 | var suite = new benchmark.Suite; 20 | 21 | console.log('Benchmarking: ' + op); 22 | Object.keys(obj).forEach(function(key) { 23 | suite.add(key + '#' + op, obj[key], { maxTime: maxTime }) 24 | }); 25 | 26 | suite 27 | .on('cycle', function(event) { 28 | console.log(String(event.target)); 29 | }) 30 | .on('complete', function() { 31 | console.log('------------------------'); 32 | console.log('Fastest is ' + this.filter('fastest').pluck('name')); 33 | }) 34 | .run(); 35 | console.log('========================'); 36 | } 37 | }); 38 | } 39 | 40 | function start() { 41 | var re = process.argv[2] ? new RegExp(process.argv[2], 'i') : /./; 42 | 43 | benchmarks.filter(function(b) { 44 | return re.test(b.name); 45 | }).forEach(function(b) { 46 | b.start(); 47 | }); 48 | } 49 | 50 | var str = 'big benchmark against elliptic'; 51 | 52 | var m1 = hash.sha256().update(str).digest(); 53 | var c1 = elliptic.ec(elliptic.curves.secp256k1); 54 | var k1 = c1.genKeyPair(); 55 | var s1 = c1.sign(m1, k1); 56 | var ok = '-----BEGIN EC PARAMETERS-----\n' + 57 | 'BgUrgQQACg==\n' + 58 | '-----END EC PARAMETERS-----\n' + 59 | '-----BEGIN EC PRIVATE KEY-----\n' + 60 | 'MHQCAQEEIEWQfsvf8TdY+F2ziHut3Fl+LVdCHAQBLxeKn3v79M1LoAcGBSuBBAAK\n' + 61 | 'oUQDQgAEM7DB6MG6T5nGSwougzGIXVypXb81EerftCyqTc7v/rjmLobz5ZJHGALh\n' + 62 | 'ne+0Gdz5ZytFHQCTkUlU85Mz8SPjWg==\n' + 63 | '-----END EC PRIVATE KEY-----'; 64 | assert(c1.verify(m1, s1, k1)); 65 | 66 | var m2 = eccjs.sjcl.hash.sha256.hash('big benchmark against elliptic'); 67 | var c2 = eccjs.sjcl.ecc.curves.k256; 68 | var k2 = eccjs.sjcl.ecc.ecdsa.generateKeys(c2, 0); 69 | var s2 = k2.sec.sign(m2, 0); 70 | assert(k2.pub.verify(m2, s2)); 71 | 72 | var m3 = crypto.createHash('sha256').update(str).digest(); 73 | var k3 = new ECKey(crypto.randomBytes(32)); 74 | var s3 = ecdsa.sign(m3, k3.privateKey); 75 | assert(ecdsa.verify(m3, s3, k3.publicKey)); 76 | 77 | var m4 = crypto.createHash('sha256').update(str).digest(); 78 | var k4priv = crypto.randomBytes(32); 79 | var k4pub = secp256k1.createPublicKey(k4priv); 80 | var s4 = secp256k1.sign(k4priv, m4); 81 | assert(secp256k1.verify(k4pub, m4, s4) > 0); 82 | 83 | add('sign', { 84 | elliptic: function() { 85 | c1.sign(m1, k1); 86 | }, 87 | sjcl: function() { 88 | k2.sec.sign(m2, 0); 89 | }, 90 | openssl: function() { 91 | crypto.createSign('RSA-SHA256').update(str).sign(ok); 92 | }, 93 | ecdsa: function() { 94 | ecdsa.sign(m3, k3.privateKey); 95 | }, 96 | secp256k1: function() { 97 | secp256k1.sign(k4priv, m4); 98 | } 99 | }); 100 | 101 | var os1 = crypto.createSign('RSA-SHA256').update(str).sign(ok); 102 | var opk = '-----BEGIN PUBLIC KEY-----\n' + 103 | 'MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEM7DB6MG6T5nGSwougzGIXVypXb81Eerf\n' + 104 | 'tCyqTc7v/rjmLobz5ZJHGALhne+0Gdz5ZytFHQCTkUlU85Mz8SPjWg==\n' + 105 | '-----END PUBLIC KEY-----'; 106 | add('verify', { 107 | elliptic: function() { 108 | c1.verify(m1, s1, k1); 109 | }, 110 | sjcl: function() { 111 | k2.pub.verify(m2, s2); 112 | }, 113 | openssl: function() { 114 | crypto.createVerify('RSA-SHA256').update(str).verify(opk, os1); 115 | }, 116 | ecdsa: function() { 117 | ecdsa.verify(m3, s3, k3.publicKey); 118 | }, 119 | secp256k1: function() { 120 | secp256k1.verify(k4pub, m4, s4); 121 | } 122 | }); 123 | 124 | add('gen', { 125 | elliptic: function() { 126 | c1.genKeyPair().getPublic(); 127 | }, 128 | sjcl: function() { 129 | eccjs.sjcl.ecc.ecdsa.generateKeys(c2, 0); 130 | } 131 | }); 132 | 133 | add('ecdh', { 134 | elliptic: function() { 135 | c1.genKeyPair().derive(k1.getPublic()); 136 | } 137 | }); 138 | 139 | var cu1 = elliptic.ec('curve25519'); 140 | var ku1 = cu1.genKeyPair(); 141 | var kp2 = jodid.eddsa.genKeySeed(); 142 | 143 | add('curve25519', { 144 | elliptic: function() { 145 | var s = ku1.derive(cu1.genKeyPair().getPublic()); 146 | }, 147 | jodid: function() { 148 | var s = jodid.dh.computeKey(kp2, 149 | jodid.dh.publicKey(jodid.eddsa.genKeySeed())); 150 | } 151 | }); 152 | 153 | start(); 154 | -------------------------------------------------------------------------------- /test/ed25519-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var elliptic = require('../'); 5 | var utils = elliptic.utils; 6 | var toArray = elliptic.utils.toArray; 7 | var eddsa = elliptic.eddsa; 8 | 9 | function toHex(arr) { 10 | return elliptic.utils.toHex(arr).toUpperCase(); 11 | } 12 | 13 | var MAX_PROGRAMMATIC = process.env.CI ? Infinity : 50; 14 | 15 | describe('ed25519 derivations', function() { 16 | var expectedTests = 256; 17 | var ed25519; 18 | var derivations; 19 | 20 | before(function() { 21 | ed25519 = new eddsa('ed25519'); 22 | derivations = require('./fixtures/derivation-fixtures'); 23 | assert.equal(derivations.length, expectedTests); 24 | }); 25 | 26 | function testFactory(i) { 27 | it('can compute correct a and A for secret: ' + i, function() { 28 | var test = derivations[i]; 29 | var secret = utils.toArray(test.secret_hex, 'hex'); 30 | var key = ed25519.keyFromSecret(secret); 31 | assert.equal(toHex(key.privBytes()), test.a_hex); 32 | var xRecovered = toHex(ed25519.encodeInt( 33 | ed25519.decodePoint(key.pubBytes()).getX())); 34 | assert.equal(xRecovered, test.A_P.x); 35 | assert.equal(toHex(key.pubBytes()), test.A_hex); 36 | }); 37 | } 38 | 39 | for (var i = 0; i < Math.min(expectedTests, MAX_PROGRAMMATIC); i++) 40 | testFactory(i); 41 | }); 42 | 43 | describe('sign.input ed25519 test vectors', function() { 44 | var expectedTests = 1024; 45 | var ed25519; 46 | var lines; 47 | 48 | before(function(done) { 49 | ed25519 = new eddsa('ed25519'); 50 | require('fs').readFile(__dirname + '/fixtures/sign.input', function(err, f) { 51 | lines = f.toString().split('\n'); 52 | assert.equal(lines.length, expectedTests + 1 /*blank line*/); 53 | done(); 54 | }); 55 | }); 56 | 57 | function testFactory(i) { 58 | it('vector ' + i, function() { 59 | var split = lines[i].toUpperCase().split(':'); 60 | var key = ed25519.keyFromSecret(split[0].slice(0, 64)); 61 | var expectedPk = split[0].slice(64); 62 | 63 | assert.equal(toHex(key.pubBytes()), expectedPk); 64 | 65 | var msg = toArray(split[2], 'hex'); 66 | var sig = key.sign(msg).toHex(); 67 | var sigR = sig.slice(0, 64); 68 | var sigS = sig.slice(64); 69 | 70 | assert.equal(sigR, split[3].slice(0, 64)); 71 | assert.equal(sigS, split[3].slice(64, 128)); 72 | assert(key.verify(msg, sig)); 73 | 74 | var forged = msg.length === 0 ? [ 0x78 ] /*ord('x')*/: 75 | msg.slice(0, msg.length - 1).concat( 76 | (msg[(msg.length - 1)] + 1) % 256); 77 | 78 | assert.equal(msg.length || 1, forged.length); 79 | assert(!key.verify(forged, sig)); 80 | }); 81 | } 82 | for (var i = 0; i < Math.min(expectedTests, MAX_PROGRAMMATIC); i++) 83 | testFactory(i); 84 | }); 85 | 86 | describe('EDDSA(\'ed25519\')', function() { 87 | var ed25519; 88 | 89 | before(function() { 90 | ed25519 = new eddsa('ed25519'); 91 | }); 92 | 93 | it('has encodingLength of 32', function() { 94 | assert.equal(32, ed25519.encodingLength); 95 | }); 96 | 97 | it('can sign/verify messages', function() { 98 | var secret = toArray(new Array(65).join('0'), 'hex'); 99 | assert(secret.length === 32); 100 | var msg = [ 0xB, 0xE, 0xE, 0xF ]; 101 | var key = ed25519.keyFromSecret(secret); 102 | var sig = key.sign(msg).toHex(); 103 | 104 | var R = '8F1B9A7FDB22BCD2C15D4695B1CE2B063CBFAEC9B00BE360427BAC9533943F6C'; 105 | var S = '5F0B380FD7F2E43B70AB2FA29F6C6E3FFC1012710E174786814012324BF19B0C'; 106 | 107 | assert.equal(sig.slice(0, 64), R); 108 | assert.equal(sig.slice(64), S); 109 | 110 | assert(key.verify(msg, sig)); 111 | }); 112 | 113 | describe('KeyPair', function() { 114 | var pair; 115 | var secret = '00000000000000000000000000000000' + 116 | '00000000000000000000000000000000'; 117 | 118 | before(function() { 119 | pair = ed25519.keyFromSecret(secret); 120 | }); 121 | 122 | it('can be created with keyFromSecret/keyFromPublic', function() { 123 | var pubKey = ed25519.keyFromPublic(toHex(pair.pubBytes())); 124 | assert(pubKey.pub() instanceof ed25519.pointClass); 125 | assert(pubKey.pub().eq(pair.pub())); 126 | }); 127 | it('#getSecret returns bytes with optional encoding', function() { 128 | assert(Array.isArray(pair.getSecret())); 129 | assert(pair.getSecret('hex') === secret); 130 | }); 131 | it('#getPub returns bytes with optional encoding', function() { 132 | assert(Array.isArray(pair.getPublic())); 133 | assert.equal(pair.getPublic('hex'), 134 | '3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29'); 135 | }); 136 | }); 137 | }); 138 | -------------------------------------------------------------------------------- /lib/elliptic/curve/mont.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var curve = require('../curve'); 4 | var BN = require('bn.js'); 5 | var inherits = require('inherits'); 6 | var Base = curve.base; 7 | 8 | var elliptic = require('../../elliptic'); 9 | var utils = elliptic.utils; 10 | 11 | function MontCurve(conf) { 12 | Base.call(this, 'mont', conf); 13 | 14 | this.a = new BN(conf.a, 16).toRed(this.red); 15 | this.b = new BN(conf.b, 16).toRed(this.red); 16 | this.i4 = new BN(4).toRed(this.red).redInvm(); 17 | this.two = new BN(2).toRed(this.red); 18 | this.a24 = this.i4.redMul(this.a.redAdd(this.two)); 19 | } 20 | inherits(MontCurve, Base); 21 | module.exports = MontCurve; 22 | 23 | MontCurve.prototype.validate = function validate(point) { 24 | var x = point.normalize().x; 25 | var x2 = x.redSqr(); 26 | var rhs = x2.redMul(x).redAdd(x2.redMul(this.a)).redAdd(x); 27 | var y = rhs.redSqrt(); 28 | 29 | return y.redSqr().cmp(rhs) === 0; 30 | }; 31 | 32 | function Point(curve, x, z) { 33 | Base.BasePoint.call(this, curve, 'projective'); 34 | if (x === null && z === null) { 35 | this.x = this.curve.one; 36 | this.z = this.curve.zero; 37 | } else { 38 | this.x = new BN(x, 16); 39 | this.z = new BN(z, 16); 40 | if (!this.x.red) 41 | this.x = this.x.toRed(this.curve.red); 42 | if (!this.z.red) 43 | this.z = this.z.toRed(this.curve.red); 44 | } 45 | } 46 | inherits(Point, Base.BasePoint); 47 | 48 | MontCurve.prototype.decodePoint = function decodePoint(bytes, enc) { 49 | return this.point(utils.toArray(bytes, enc), 1); 50 | }; 51 | 52 | MontCurve.prototype.point = function point(x, z) { 53 | return new Point(this, x, z); 54 | }; 55 | 56 | MontCurve.prototype.pointFromJSON = function pointFromJSON(obj) { 57 | return Point.fromJSON(this, obj); 58 | }; 59 | 60 | Point.prototype.precompute = function precompute() { 61 | // No-op 62 | }; 63 | 64 | Point.prototype._encode = function _encode() { 65 | return this.getX().toArray('be', this.curve.p.byteLength()); 66 | }; 67 | 68 | Point.fromJSON = function fromJSON(curve, obj) { 69 | return new Point(curve, obj[0], obj[1] || curve.one); 70 | }; 71 | 72 | Point.prototype.inspect = function inspect() { 73 | if (this.isInfinity()) 74 | return ''; 75 | return ''; 77 | }; 78 | 79 | Point.prototype.isInfinity = function isInfinity() { 80 | // XXX This code assumes that zero is always zero in red 81 | return this.z.cmpn(0) === 0; 82 | }; 83 | 84 | Point.prototype.dbl = function dbl() { 85 | // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#doubling-dbl-1987-m-3 86 | // 2M + 2S + 4A 87 | 88 | // A = X1 + Z1 89 | var a = this.x.redAdd(this.z); 90 | // AA = A^2 91 | var aa = a.redSqr(); 92 | // B = X1 - Z1 93 | var b = this.x.redSub(this.z); 94 | // BB = B^2 95 | var bb = b.redSqr(); 96 | // C = AA - BB 97 | var c = aa.redSub(bb); 98 | // X3 = AA * BB 99 | var nx = aa.redMul(bb); 100 | // Z3 = C * (BB + A24 * C) 101 | var nz = c.redMul(bb.redAdd(this.curve.a24.redMul(c))); 102 | return this.curve.point(nx, nz); 103 | }; 104 | 105 | Point.prototype.add = function add() { 106 | throw new Error('Not supported on Montgomery curve'); 107 | }; 108 | 109 | Point.prototype.diffAdd = function diffAdd(p, diff) { 110 | // http://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#diffadd-dadd-1987-m-3 111 | // 4M + 2S + 6A 112 | 113 | // A = X2 + Z2 114 | var a = this.x.redAdd(this.z); 115 | // B = X2 - Z2 116 | var b = this.x.redSub(this.z); 117 | // C = X3 + Z3 118 | var c = p.x.redAdd(p.z); 119 | // D = X3 - Z3 120 | var d = p.x.redSub(p.z); 121 | // DA = D * A 122 | var da = d.redMul(a); 123 | // CB = C * B 124 | var cb = c.redMul(b); 125 | // X5 = Z1 * (DA + CB)^2 126 | var nx = diff.z.redMul(da.redAdd(cb).redSqr()); 127 | // Z5 = X1 * (DA - CB)^2 128 | var nz = diff.x.redMul(da.redISub(cb).redSqr()); 129 | return this.curve.point(nx, nz); 130 | }; 131 | 132 | Point.prototype.mul = function mul(k) { 133 | var t = k.clone(); 134 | var a = this; // (N / 2) * Q + Q 135 | var b = this.curve.point(null, null); // (N / 2) * Q 136 | var c = this; // Q 137 | 138 | for (var bits = []; t.cmpn(0) !== 0; t.iushrn(1)) 139 | bits.push(t.andln(1)); 140 | 141 | for (var i = bits.length - 1; i >= 0; i--) { 142 | if (bits[i] === 0) { 143 | // N * Q + Q = ((N / 2) * Q + Q)) + (N / 2) * Q 144 | a = a.diffAdd(b, c); 145 | // N * Q = 2 * ((N / 2) * Q + Q)) 146 | b = b.dbl(); 147 | } else { 148 | // N * Q = ((N / 2) * Q + Q) + ((N / 2) * Q) 149 | b = a.diffAdd(b, c); 150 | // N * Q + Q = 2 * ((N / 2) * Q + Q) 151 | a = a.dbl(); 152 | } 153 | } 154 | return b; 155 | }; 156 | 157 | Point.prototype.mulAdd = function mulAdd() { 158 | throw new Error('Not supported on Montgomery curve'); 159 | }; 160 | 161 | Point.prototype.jumlAdd = function jumlAdd() { 162 | throw new Error('Not supported on Montgomery curve'); 163 | }; 164 | 165 | Point.prototype.eq = function eq(other) { 166 | return this.getX().cmp(other.getX()) === 0; 167 | }; 168 | 169 | Point.prototype.normalize = function normalize() { 170 | this.x = this.x.redMul(this.z.redInvm()); 171 | this.z = this.curve.one; 172 | return this; 173 | }; 174 | 175 | Point.prototype.getX = function getX() { 176 | // Normalize coordinates 177 | this.normalize(); 178 | 179 | return this.x.fromRed(); 180 | }; 181 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | browserify: { 4 | unittests: { 5 | files: { 6 | 'test/lib/unittests-bundle.js': [ 'test/index.js'] 7 | }, 8 | options: { 9 | transform: ['brfs'] 10 | } 11 | }, 12 | dist: { 13 | files: { 14 | 'dist/elliptic.js': [ 'lib/elliptic.js' ] 15 | }, 16 | options: { 17 | browserifyOptions: { 18 | standalone: 'elliptic' 19 | } 20 | } 21 | } 22 | }, 23 | connect: { 24 | server: { 25 | options: { 26 | port: 3000, 27 | base: './test' 28 | } 29 | } 30 | }, 31 | copy: { 32 | test: { 33 | expand: true, 34 | flatten: true, 35 | cwd: 'node_modules/', 36 | src: ['mocha/mocha.css', 'mocha/mocha.js'], 37 | dest: 'test/lib/' 38 | } 39 | }, 40 | mocha_istanbul: { 41 | coverage: { 42 | src: ['test'], 43 | options: { 44 | coverage: false, 45 | timeout: 6000, 46 | reportFormats: ['cobertura','lcovonly'] 47 | } 48 | }, 49 | coveralls: { 50 | src: ['test'], 51 | options: { 52 | coverage: true, 53 | timeout: 6000, 54 | reportFormats: ['cobertura','lcovonly'] 55 | } 56 | } 57 | }, 58 | 'saucelabs-mocha': { 59 | all: { 60 | options: { 61 | username: process.env.SAUCE_USERNAME, 62 | key: process.env.SAUCE_ACCESS_KEY, 63 | urls: ['http://127.0.0.1:3000/unittests.html'], 64 | build: process.env.TRAVIS_JOB_ID, 65 | testname: 'Sauce Unit Test for ellipticjs', 66 | browsers: [ 67 | { 68 | browserName: "safari", 69 | platform: "OS X 10.11", 70 | version: "9" 71 | }, 72 | { 73 | browserName: "safari", 74 | platform: "OS X 10.10", 75 | version: "8" 76 | }, 77 | { 78 | browserName: "microsoftedge", 79 | version: "13.10586", 80 | platform: "Windows 10" 81 | }, 82 | { 83 | browserName: "internet explorer", 84 | version: "11", 85 | platform: "Windows 8.1" 86 | }, 87 | { 88 | browserName: "internet explorer", 89 | version: "10", 90 | platform: "Windows 8" 91 | }, 92 | { 93 | browserName: "internet explorer", 94 | version: "9", 95 | platform: "Windows 7" 96 | }, 97 | { 98 | browserName: "internet explorer", 99 | version: "8", 100 | platform: "Windows 7" 101 | }, 102 | { 103 | browserName: "android", 104 | platform: "Linux", 105 | version: "5.1" 106 | }, 107 | { 108 | browserName: "android", 109 | platform: "Linux", 110 | version: "4.4" 111 | }, 112 | { 113 | browserName: "iphone", 114 | platform: "OS X 10.10", 115 | version: "7.1" 116 | }, 117 | { 118 | browserName: "iphone", 119 | platform: "OS X 10.10", 120 | version: "9.2" 121 | }, 122 | { 123 | browserName: "chrome", 124 | platform: "Linux", 125 | version: "38" 126 | }, 127 | { 128 | browserName: "chrome", 129 | platform: "Linux", 130 | version: "47" 131 | }, 132 | { 133 | browserName: "chrome", 134 | platform: "Linux", 135 | version: "beta" 136 | }, 137 | { 138 | browserName: "firefox", 139 | platform: "Linux", 140 | version: "38" 141 | }, 142 | { 143 | browserName: "firefox", 144 | platform: "Linux", 145 | version: "43" 146 | }, 147 | { 148 | browserName: "firefox", 149 | platform: "Linux", 150 | version: "beta" 151 | } 152 | ], 153 | public: "public", 154 | maxRetries: 3, 155 | throttled: 2, 156 | pollInterval: 4000, 157 | statusCheckAttempts: 200 158 | } 159 | }, 160 | }, 161 | uglify: { 162 | dist: { 163 | files: { 164 | 'dist/elliptic.min.js' : [ 'dist/elliptic.js' ] 165 | } 166 | } 167 | } 168 | }); 169 | 170 | grunt.loadNpmTasks('grunt-browserify'); 171 | grunt.loadNpmTasks('grunt-contrib-connect'); 172 | grunt.loadNpmTasks('grunt-contrib-copy'); 173 | grunt.loadNpmTasks('grunt-contrib-uglify'); 174 | grunt.loadNpmTasks('grunt-mocha-istanbul'); 175 | grunt.loadNpmTasks('grunt-saucelabs'); 176 | 177 | grunt.event.on('coverage', function(lcov, done){ 178 | require('coveralls').handleInput(lcov, function(err){ 179 | if (err) { 180 | return done(err); 181 | } 182 | done(); 183 | }); 184 | }); 185 | 186 | grunt.registerTask('dist', ['browserify', 'uglify']); 187 | grunt.registerTask('coverage', ['browserify', 'copy:test', 'mocha_istanbul:coverage']); 188 | grunt.registerTask('coveralls', ['browserify', 'copy:test', 'mocha_istanbul:coveralls']); 189 | grunt.registerTask('saucelabs', ['browserify', 'copy:test', 'connect', 'saucelabs-mocha']); 190 | }; 191 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 50, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : false, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : true, // true: Identifiers must be in camelCase 10 | "curly" : false, // true: Require {} for every new block or scope 11 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 12 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 14 | "immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "indent" : 2, // {int} Number of spaces to use for indentation 16 | "latedef" : true, // true: Require variables/functions to be defined before being used 17 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` 18 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 19 | "noempty" : false, // true: Prohibit use of empty blocks 20 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 21 | "nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment) 22 | "plusplus" : false, // true: Prohibit use of `++` & `--` 23 | "quotmark" : "single", // Quotation mark consistency: 24 | // false : do nothing (default) 25 | // true : ensure whatever is used is consistent 26 | // "single" : require single quotes 27 | // "double" : require double quotes 28 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 29 | "unused" : true, // true: Require all defined variables be used 30 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 31 | "maxparams" : false, // {int} Max number of formal params allowed per function 32 | "maxdepth" : 4, // {int} Max depth of nested blocks (within functions) 33 | "maxstatements" : false, // {int} Max number statements per function 34 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 35 | "maxlen" : false, // {int} Max number of characters per line 36 | 37 | // Relaxing 38 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 39 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 40 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 41 | "eqnull" : false, // true: Tolerate use of `== null` 42 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 43 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 44 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 45 | // (ex: `for each`, multiple try/catch, function expression…) 46 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 47 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 48 | "funcscope" : false, // true: Tolerate defining variables inside control statements 49 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 50 | "iterator" : false, // true: Tolerate using the `__iterator__` property 51 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 52 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 53 | "laxcomma" : false, // true: Tolerate comma-first style coding 54 | "loopfunc" : false, // true: Tolerate functions being defined in loops 55 | "multistr" : false, // true: Tolerate multi-line strings 56 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 57 | "notypeof" : false, // true: Tolerate invalid typeof operator values 58 | "proto" : false, // true: Tolerate using the `__proto__` property 59 | "scripturl" : false, // true: Tolerate script-targeted URLs 60 | "shadow" : true, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 61 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 62 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 63 | "validthis" : false, // true: Tolerate using this in a non-constructor function 64 | 65 | // Environments 66 | "browser" : true, // Web Browser (window, document, etc) 67 | "browserify" : true, // Browserify (node.js code in the browser) 68 | "couch" : false, // CouchDB 69 | "devel" : true, // Development/debugging (alert, confirm, etc) 70 | "dojo" : false, // Dojo Toolkit 71 | "jasmine" : false, // Jasmine 72 | "jquery" : false, // jQuery 73 | "mocha" : true, // Mocha 74 | "mootools" : false, // MooTools 75 | "node" : true, // Node.js 76 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 77 | "prototypejs" : false, // Prototype and Scriptaculous 78 | "qunit" : false, // QUnit 79 | "rhino" : false, // Rhino 80 | "shelljs" : false, // ShellJS 81 | "worker" : false, // Web Workers 82 | "wsh" : false, // Windows Scripting Host 83 | "yui" : false, // Yahoo User Interface 84 | 85 | // Custom Globals 86 | "globals" : { 87 | "module": true 88 | } // additional predefined global variables 89 | } 90 | -------------------------------------------------------------------------------- /lib/elliptic/curves.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var curves = exports; 4 | 5 | var hash = require('hash.js'); 6 | var elliptic = require('../elliptic'); 7 | 8 | var assert = elliptic.utils.assert; 9 | 10 | function PresetCurve(options) { 11 | if (options.type === 'short') 12 | this.curve = new elliptic.curve.short(options); 13 | else if (options.type === 'edwards') 14 | this.curve = new elliptic.curve.edwards(options); 15 | else 16 | this.curve = new elliptic.curve.mont(options); 17 | this.g = this.curve.g; 18 | this.n = this.curve.n; 19 | this.hash = options.hash; 20 | 21 | assert(this.g.validate(), 'Invalid curve'); 22 | assert(this.g.mul(this.n).isInfinity(), 'Invalid curve, G*N != O'); 23 | } 24 | curves.PresetCurve = PresetCurve; 25 | 26 | function defineCurve(name, options) { 27 | Object.defineProperty(curves, name, { 28 | configurable: true, 29 | enumerable: true, 30 | get: function() { 31 | var curve = new PresetCurve(options); 32 | Object.defineProperty(curves, name, { 33 | configurable: true, 34 | enumerable: true, 35 | value: curve 36 | }); 37 | return curve; 38 | } 39 | }); 40 | } 41 | 42 | defineCurve('p192', { 43 | type: 'short', 44 | prime: 'p192', 45 | p: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff', 46 | a: 'ffffffff ffffffff ffffffff fffffffe ffffffff fffffffc', 47 | b: '64210519 e59c80e7 0fa7e9ab 72243049 feb8deec c146b9b1', 48 | n: 'ffffffff ffffffff ffffffff 99def836 146bc9b1 b4d22831', 49 | hash: hash.sha256, 50 | gRed: false, 51 | g: [ 52 | '188da80e b03090f6 7cbf20eb 43a18800 f4ff0afd 82ff1012', 53 | '07192b95 ffc8da78 631011ed 6b24cdd5 73f977a1 1e794811' 54 | ] 55 | }); 56 | 57 | defineCurve('p224', { 58 | type: 'short', 59 | prime: 'p224', 60 | p: 'ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001', 61 | a: 'ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff fffffffe', 62 | b: 'b4050a85 0c04b3ab f5413256 5044b0b7 d7bfd8ba 270b3943 2355ffb4', 63 | n: 'ffffffff ffffffff ffffffff ffff16a2 e0b8f03e 13dd2945 5c5c2a3d', 64 | hash: hash.sha256, 65 | gRed: false, 66 | g: [ 67 | 'b70e0cbd 6bb4bf7f 321390b9 4a03c1d3 56c21122 343280d6 115c1d21', 68 | 'bd376388 b5f723fb 4c22dfe6 cd4375a0 5a074764 44d58199 85007e34' 69 | ] 70 | }); 71 | 72 | defineCurve('p256', { 73 | type: 'short', 74 | prime: null, 75 | p: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff ffffffff', 76 | a: 'ffffffff 00000001 00000000 00000000 00000000 ffffffff ffffffff fffffffc', 77 | b: '5ac635d8 aa3a93e7 b3ebbd55 769886bc 651d06b0 cc53b0f6 3bce3c3e 27d2604b', 78 | n: 'ffffffff 00000000 ffffffff ffffffff bce6faad a7179e84 f3b9cac2 fc632551', 79 | hash: hash.sha256, 80 | gRed: false, 81 | g: [ 82 | '6b17d1f2 e12c4247 f8bce6e5 63a440f2 77037d81 2deb33a0 f4a13945 d898c296', 83 | '4fe342e2 fe1a7f9b 8ee7eb4a 7c0f9e16 2bce3357 6b315ece cbb64068 37bf51f5' 84 | ] 85 | }); 86 | 87 | defineCurve('p384', { 88 | type: 'short', 89 | prime: null, 90 | p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 91 | 'fffffffe ffffffff 00000000 00000000 ffffffff', 92 | a: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 93 | 'fffffffe ffffffff 00000000 00000000 fffffffc', 94 | b: 'b3312fa7 e23ee7e4 988e056b e3f82d19 181d9c6e fe814112 0314088f ' + 95 | '5013875a c656398d 8a2ed19d 2a85c8ed d3ec2aef', 96 | n: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff c7634d81 ' + 97 | 'f4372ddf 581a0db2 48b0a77a ecec196a ccc52973', 98 | hash: hash.sha384, 99 | gRed: false, 100 | g: [ 101 | 'aa87ca22 be8b0537 8eb1c71e f320ad74 6e1d3b62 8ba79b98 59f741e0 82542a38 ' + 102 | '5502f25d bf55296c 3a545e38 72760ab7', 103 | '3617de4a 96262c6f 5d9e98bf 9292dc29 f8f41dbd 289a147c e9da3113 b5f0b8c0 ' + 104 | '0a60b1ce 1d7e819d 7a431d7c 90ea0e5f' 105 | ] 106 | }); 107 | 108 | defineCurve('p521', { 109 | type: 'short', 110 | prime: null, 111 | p: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 112 | 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 113 | 'ffffffff ffffffff ffffffff ffffffff ffffffff', 114 | a: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 115 | 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 116 | 'ffffffff ffffffff ffffffff ffffffff fffffffc', 117 | b: '00000051 953eb961 8e1c9a1f 929a21a0 b68540ee a2da725b ' + 118 | '99b315f3 b8b48991 8ef109e1 56193951 ec7e937b 1652c0bd ' + 119 | '3bb1bf07 3573df88 3d2c34f1 ef451fd4 6b503f00', 120 | n: '000001ff ffffffff ffffffff ffffffff ffffffff ffffffff ' + 121 | 'ffffffff ffffffff fffffffa 51868783 bf2f966b 7fcc0148 ' + 122 | 'f709a5d0 3bb5c9b8 899c47ae bb6fb71e 91386409', 123 | hash: hash.sha512, 124 | gRed: false, 125 | g: [ 126 | '000000c6 858e06b7 0404e9cd 9e3ecb66 2395b442 9c648139 ' + 127 | '053fb521 f828af60 6b4d3dba a14b5e77 efe75928 fe1dc127 ' + 128 | 'a2ffa8de 3348b3c1 856a429b f97e7e31 c2e5bd66', 129 | '00000118 39296a78 9a3bc004 5c8a5fb4 2c7d1bd9 98f54449 ' + 130 | '579b4468 17afbd17 273e662c 97ee7299 5ef42640 c550b901 ' + 131 | '3fad0761 353c7086 a272c240 88be9476 9fd16650' 132 | ] 133 | }); 134 | 135 | defineCurve('curve25519', { 136 | type: 'mont', 137 | prime: 'p25519', 138 | p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', 139 | a: '76d06', 140 | b: '1', 141 | n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', 142 | hash: hash.sha256, 143 | gRed: false, 144 | g: [ 145 | '9' 146 | ] 147 | }); 148 | 149 | defineCurve('ed25519', { 150 | type: 'edwards', 151 | prime: 'p25519', 152 | p: '7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed', 153 | a: '-1', 154 | c: '1', 155 | // -121665 * (121666^(-1)) (mod P) 156 | d: '52036cee2b6ffe73 8cc740797779e898 00700a4d4141d8ab 75eb4dca135978a3', 157 | n: '1000000000000000 0000000000000000 14def9dea2f79cd6 5812631a5cf5d3ed', 158 | hash: hash.sha256, 159 | gRed: false, 160 | g: [ 161 | '216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a', 162 | 163 | // 4/5 164 | '6666666666666666666666666666666666666666666666666666666666666658' 165 | ] 166 | }); 167 | 168 | var pre; 169 | try { 170 | pre = require('./precomputed/secp256k1'); 171 | } catch (e) { 172 | pre = undefined; 173 | } 174 | 175 | defineCurve('secp256k1', { 176 | type: 'short', 177 | prime: 'k256', 178 | p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f', 179 | a: '0', 180 | b: '7', 181 | n: 'ffffffff ffffffff ffffffff fffffffe baaedce6 af48a03b bfd25e8c d0364141', 182 | h: '1', 183 | hash: hash.sha256, 184 | 185 | // Precomputed endomorphism 186 | beta: '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee', 187 | lambda: '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72', 188 | basis: [ 189 | { 190 | a: '3086d221a7d46bcde86c90e49284eb15', 191 | b: '-e4437ed6010e88286f547fa90abfe4c3' 192 | }, 193 | { 194 | a: '114ca50f7a8e2f3f657c1108d9d44cfd8', 195 | b: '3086d221a7d46bcde86c90e49284eb15' 196 | } 197 | ], 198 | 199 | gRed: false, 200 | g: [ 201 | '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 202 | '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', 203 | pre 204 | ] 205 | }); 206 | -------------------------------------------------------------------------------- /lib/elliptic/ec/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('bn.js'); 4 | var HmacDRBG = require('hmac-drbg'); 5 | var elliptic = require('../../elliptic'); 6 | var utils = elliptic.utils; 7 | var assert = utils.assert; 8 | 9 | var KeyPair = require('./key'); 10 | var Signature = require('./signature'); 11 | 12 | function EC(options) { 13 | if (!(this instanceof EC)) 14 | return new EC(options); 15 | 16 | // Shortcut `elliptic.ec(curve-name)` 17 | if (typeof options === 'string') { 18 | assert(elliptic.curves.hasOwnProperty(options), 'Unknown curve ' + options); 19 | 20 | options = elliptic.curves[options]; 21 | } 22 | 23 | // Shortcut for `elliptic.ec(elliptic.curves.curveName)` 24 | if (options instanceof elliptic.curves.PresetCurve) 25 | options = { curve: options }; 26 | 27 | this.curve = options.curve.curve; 28 | this.n = this.curve.n; 29 | this.nh = this.n.ushrn(1); 30 | this.g = this.curve.g; 31 | 32 | // Point on curve 33 | this.g = options.curve.g; 34 | this.g.precompute(options.curve.n.bitLength() + 1); 35 | 36 | // Hash for function for DRBG 37 | this.hash = options.hash || options.curve.hash; 38 | } 39 | module.exports = EC; 40 | 41 | EC.prototype.keyPair = function keyPair(options) { 42 | return new KeyPair(this, options); 43 | }; 44 | 45 | EC.prototype.keyFromPrivate = function keyFromPrivate(priv, enc) { 46 | return KeyPair.fromPrivate(this, priv, enc); 47 | }; 48 | 49 | EC.prototype.keyFromPublic = function keyFromPublic(pub, enc) { 50 | return KeyPair.fromPublic(this, pub, enc); 51 | }; 52 | 53 | EC.prototype.genKeyPair = function genKeyPair(options) { 54 | if (!options) 55 | options = {}; 56 | 57 | // Instantiate Hmac_DRBG 58 | var drbg = new HmacDRBG({ 59 | hash: this.hash, 60 | pers: options.pers, 61 | persEnc: options.persEnc || 'utf8', 62 | entropy: options.entropy || elliptic.rand(this.hash.hmacStrength), 63 | entropyEnc: options.entropy && options.entropyEnc || 'utf8', 64 | nonce: this.n.toArray() 65 | }); 66 | 67 | var bytes = this.n.byteLength(); 68 | var ns2 = this.n.sub(new BN(2)); 69 | do { 70 | var priv = new BN(drbg.generate(bytes)); 71 | if (priv.cmp(ns2) > 0) 72 | continue; 73 | 74 | priv.iaddn(1); 75 | return this.keyFromPrivate(priv); 76 | } while (true); 77 | }; 78 | 79 | EC.prototype._truncateToN = function truncateToN(msg, truncOnly) { 80 | var delta = msg.byteLength() * 8 - this.n.bitLength(); 81 | if (delta > 0) 82 | msg = msg.ushrn(delta); 83 | if (!truncOnly && msg.cmp(this.n) >= 0) 84 | return msg.sub(this.n); 85 | else 86 | return msg; 87 | }; 88 | 89 | EC.prototype.sign = function sign(msg, key, enc, options) { 90 | if (typeof enc === 'object') { 91 | options = enc; 92 | enc = null; 93 | } 94 | if (!options) 95 | options = {}; 96 | 97 | key = this.keyFromPrivate(key, enc); 98 | msg = this._truncateToN(new BN(msg, 16)); 99 | 100 | // Zero-extend key to provide enough entropy 101 | var bytes = this.n.byteLength(); 102 | var bkey = key.getPrivate().toArray('be', bytes); 103 | 104 | // Zero-extend nonce to have the same byte size as N 105 | var nonce = msg.toArray('be', bytes); 106 | 107 | // Instantiate Hmac_DRBG 108 | var drbg = new HmacDRBG({ 109 | hash: this.hash, 110 | entropy: bkey, 111 | nonce: nonce, 112 | pers: options.pers, 113 | persEnc: options.persEnc || 'utf8' 114 | }); 115 | 116 | // Number of bytes to generate 117 | var ns1 = this.n.sub(new BN(1)); 118 | 119 | for (var iter = 0; true; iter++) { 120 | var k = options.k ? 121 | options.k(iter) : 122 | new BN(drbg.generate(this.n.byteLength())); 123 | k = this._truncateToN(k, true); 124 | if (k.cmpn(1) <= 0 || k.cmp(ns1) >= 0) 125 | continue; 126 | 127 | var kp = this.g.mul(k); 128 | if (kp.isInfinity()) 129 | continue; 130 | 131 | var kpX = kp.getX(); 132 | var r = kpX.umod(this.n); 133 | if (r.cmpn(0) === 0) 134 | continue; 135 | 136 | var s = k.invm(this.n).mul(r.mul(key.getPrivate()).iadd(msg)); 137 | s = s.umod(this.n); 138 | if (s.cmpn(0) === 0) 139 | continue; 140 | 141 | var recoveryParam = (kp.getY().isOdd() ? 1 : 0) | 142 | (kpX.cmp(r) !== 0 ? 2 : 0); 143 | 144 | // Use complement of `s`, if it is > `n / 2` 145 | if (options.canonical && s.cmp(this.nh) > 0) { 146 | s = this.n.sub(s); 147 | recoveryParam ^= 1; 148 | } 149 | 150 | return new Signature({ r: r, s: s, recoveryParam: recoveryParam }); 151 | } 152 | }; 153 | 154 | EC.prototype.verify = function verify(msg, signature, key, enc) { 155 | msg = this._truncateToN(new BN(msg, 16)); 156 | key = this.keyFromPublic(key, enc); 157 | signature = new Signature(signature, 'hex'); 158 | 159 | // Perform primitive values validation 160 | var r = signature.r; 161 | var s = signature.s; 162 | if (r.cmpn(1) < 0 || r.cmp(this.n) >= 0) 163 | return false; 164 | if (s.cmpn(1) < 0 || s.cmp(this.n) >= 0) 165 | return false; 166 | 167 | // Validate signature 168 | var sinv = s.invm(this.n); 169 | var u1 = sinv.mul(msg).umod(this.n); 170 | var u2 = sinv.mul(r).umod(this.n); 171 | 172 | if (!this.curve._maxwellTrick) { 173 | var p = this.g.mulAdd(u1, key.getPublic(), u2); 174 | if (p.isInfinity()) 175 | return false; 176 | 177 | return p.getX().umod(this.n).cmp(r) === 0; 178 | } 179 | 180 | // NOTE: Greg Maxwell's trick, inspired by: 181 | // https://git.io/vad3K 182 | 183 | var p = this.g.jmulAdd(u1, key.getPublic(), u2); 184 | if (p.isInfinity()) 185 | return false; 186 | 187 | // Compare `p.x` of Jacobian point with `r`, 188 | // this will do `p.x == r * p.z^2` instead of multiplying `p.x` by the 189 | // inverse of `p.z^2` 190 | return p.eqXToP(r); 191 | }; 192 | 193 | EC.prototype.recoverPubKey = function(msg, signature, j, enc) { 194 | assert((3 & j) === j, 'The recovery param is more than two bits'); 195 | signature = new Signature(signature, enc); 196 | 197 | var n = this.n; 198 | var e = new BN(msg); 199 | var r = signature.r; 200 | var s = signature.s; 201 | 202 | // A set LSB signifies that the y-coordinate is odd 203 | var isYOdd = j & 1; 204 | var isSecondKey = j >> 1; 205 | if (r.cmp(this.curve.p.umod(this.curve.n)) >= 0 && isSecondKey) 206 | throw new Error('Unable to find sencond key candinate'); 207 | 208 | // 1.1. Let x = r + jn. 209 | if (isSecondKey) 210 | r = this.curve.pointFromX(r.add(this.curve.n), isYOdd); 211 | else 212 | r = this.curve.pointFromX(r, isYOdd); 213 | 214 | var rInv = signature.r.invm(n); 215 | var s1 = n.sub(e).mul(rInv).umod(n); 216 | var s2 = s.mul(rInv).umod(n); 217 | 218 | // 1.6.1 Compute Q = r^-1 (sR - eG) 219 | // Q = r^-1 (sR + -eG) 220 | return this.g.mulAdd(s1, r, s2); 221 | }; 222 | 223 | EC.prototype.getKeyRecoveryParam = function(e, signature, Q, enc) { 224 | signature = new Signature(signature, enc); 225 | if (signature.recoveryParam !== null) 226 | return signature.recoveryParam; 227 | 228 | for (var i = 0; i < 4; i++) { 229 | var Qprime; 230 | try { 231 | Qprime = this.recoverPubKey(e, signature, i); 232 | } catch (e) { 233 | continue; 234 | } 235 | 236 | if (Qprime.eq(Q)) 237 | return i; 238 | } 239 | throw new Error('Unable to find valid recovery factor'); 240 | }; 241 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elliptic [![Build Status](https://secure.travis-ci.org/indutny/elliptic.png)](http://travis-ci.org/indutny/elliptic) [![Coverage Status](https://coveralls.io/repos/indutny/elliptic/badge.svg?branch=master&service=github)](https://coveralls.io/github/indutny/elliptic?branch=master) [![Code Climate](https://codeclimate.com/github/indutny/elliptic/badges/gpa.svg)](https://codeclimate.com/github/indutny/elliptic) 2 | 3 | [![Saucelabs Test Status](https://saucelabs.com/browser-matrix/gh-indutny-elliptic.svg)](https://saucelabs.com/u/gh-indutny-elliptic) 4 | 5 | Fast elliptic-curve cryptography in a plain javascript implementation. 6 | 7 | NOTE: Please take a look at http://safecurves.cr.yp.to/ before choosing a curve 8 | for your cryptography operations. 9 | 10 | ## Incentive 11 | 12 | ECC is much slower than regular RSA cryptography, the JS implementations are 13 | even more slower. 14 | 15 | ## Benchmarks 16 | 17 | ```bash 18 | $ node benchmarks/index.js 19 | Benchmarking: sign 20 | elliptic#sign x 262 ops/sec ±0.51% (177 runs sampled) 21 | eccjs#sign x 55.91 ops/sec ±0.90% (144 runs sampled) 22 | ------------------------ 23 | Fastest is elliptic#sign 24 | ======================== 25 | Benchmarking: verify 26 | elliptic#verify x 113 ops/sec ±0.50% (166 runs sampled) 27 | eccjs#verify x 48.56 ops/sec ±0.36% (125 runs sampled) 28 | ------------------------ 29 | Fastest is elliptic#verify 30 | ======================== 31 | Benchmarking: gen 32 | elliptic#gen x 294 ops/sec ±0.43% (176 runs sampled) 33 | eccjs#gen x 62.25 ops/sec ±0.63% (129 runs sampled) 34 | ------------------------ 35 | Fastest is elliptic#gen 36 | ======================== 37 | Benchmarking: ecdh 38 | elliptic#ecdh x 136 ops/sec ±0.85% (156 runs sampled) 39 | ------------------------ 40 | Fastest is elliptic#ecdh 41 | ======================== 42 | ``` 43 | 44 | ## API 45 | 46 | ### ECDSA 47 | 48 | ```javascript 49 | var EC = require('elliptic').ec; 50 | 51 | // Create and initialize EC context 52 | // (better do it once and reuse it) 53 | var ec = new EC('secp256k1'); 54 | 55 | // Generate keys 56 | var key = ec.genKeyPair(); 57 | 58 | // Sign the message's hash (input must be an array, or a hex-string) 59 | var msgHash = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 60 | var signature = key.sign(msgHash); 61 | 62 | // Export DER encoded signature in Array 63 | var derSign = signature.toDER(); 64 | 65 | // Verify signature 66 | console.log(key.verify(msgHash, derSign)); 67 | 68 | // CHECK WITH NO PRIVATE KEY 69 | 70 | var pubPoint = key.getPublic(); 71 | var x = pubPoint.getX(); 72 | var y = pubPoint.getY(); 73 | 74 | // Public Key MUST be either: 75 | // 1) '04' + hex string of x + hex string of y; or 76 | // 2) object with two hex string properties (x and y); or 77 | // 3) object with two buffer properties (x and y) 78 | var pub = pubPoint.encode('hex'); // case 1 79 | var pub = { x: x.toString('hex'), y: y.toString('hex') }; // case 2 80 | var pub = { x: x.toBuffer(), y: y.toBuffer() }; // case 3 81 | var pub = { x: x.toArrayLike(Buffer), y: y.toArrayLike(Buffer) }; // case 3 82 | 83 | // Import public key 84 | var key = ec.keyFromPublic(pub, 'hex'); 85 | 86 | // Signature MUST be either: 87 | // 1) DER-encoded signature as hex-string; or 88 | // 2) DER-encoded signature as buffer; or 89 | // 3) object with two hex-string properties (r and s); or 90 | // 4) object with two buffer properties (r and s) 91 | 92 | var signature = '3046022100...'; // case 1 93 | var signature = new Buffer('...'); // case 2 94 | var signature = { r: 'b1fc...', s: '9c42...' }; // case 3 95 | 96 | // Verify signature 97 | console.log(key.verify(msgHash, signature)); 98 | ``` 99 | 100 | ### EdDSA 101 | 102 | ```javascript 103 | var EdDSA = require('elliptic').eddsa; 104 | 105 | // Create and initialize EdDSA context 106 | // (better do it once and reuse it) 107 | var ec = new EdDSA('ed25519'); 108 | 109 | // Create key pair from secret 110 | var key = ec.keyFromSecret('693e3c...'); // hex string, array or Buffer 111 | 112 | // Sign the message's hash (input must be an array, or a hex-string) 113 | var msgHash = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 114 | var signature = key.sign(msgHash).toHex(); 115 | 116 | // Verify signature 117 | console.log(key.verify(msgHash, signature)); 118 | 119 | // CHECK WITH NO PRIVATE KEY 120 | 121 | // Import public key 122 | var pub = '0a1af638...'; 123 | var key = ec.keyFromPublic(pub, 'hex'); 124 | 125 | // Verify signature 126 | var signature = '70bed1...'; 127 | console.log(key.verify(msgHash, signature)); 128 | ``` 129 | 130 | ### ECDH 131 | 132 | ```javascript 133 | var EC = require('elliptic').ec; 134 | var ec = new EC('curve25519'); 135 | 136 | // Generate keys 137 | var key1 = ec.genKeyPair(); 138 | var key2 = ec.genKeyPair(); 139 | 140 | var shared1 = key1.derive(key2.getPublic()); 141 | var shared2 = key2.derive(key1.getPublic()); 142 | 143 | console.log('Both shared secrets are BN instances'); 144 | console.log(shared1.toString(16)); 145 | console.log(shared2.toString(16)); 146 | ``` 147 | 148 | three and more members: 149 | ```javascript 150 | var EC = require('elliptic').ec; 151 | var ec = new EC('curve25519'); 152 | 153 | var A = ec.genKeyPair(); 154 | var B = ec.genKeyPair(); 155 | var C = ec.genKeyPair(); 156 | 157 | var AB = A.getPublic().mul(B.getPrivate()) 158 | var BC = B.getPublic().mul(C.getPrivate()) 159 | var CA = C.getPublic().mul(A.getPrivate()) 160 | 161 | var ABC = AB.mul(C.getPrivate()) 162 | var BCA = BC.mul(A.getPrivate()) 163 | var CAB = CA.mul(B.getPrivate()) 164 | 165 | console.log(ABC.getX().toString(16)) 166 | console.log(BCA.getX().toString(16)) 167 | console.log(CAB.getX().toString(16)) 168 | ``` 169 | 170 | NOTE: `.derive()` returns a [BN][1] instance. 171 | 172 | ## Supported curves 173 | 174 | Elliptic.js support following curve types: 175 | 176 | * Short Weierstrass 177 | * Montgomery 178 | * Edwards 179 | * Twisted Edwards 180 | 181 | Following curve 'presets' are embedded into the library: 182 | 183 | * `secp256k1` 184 | * `p192` 185 | * `p224` 186 | * `p256` 187 | * `p384` 188 | * `p521` 189 | * `curve25519` 190 | * `ed25519` 191 | 192 | NOTE: That `curve25519` could not be used for ECDSA, use `ed25519` instead. 193 | 194 | ### Implementation details 195 | 196 | ECDSA is using deterministic `k` value generation as per [RFC6979][0]. Most of 197 | the curve operations are performed on non-affine coordinates (either projective 198 | or extended), various windowing techniques are used for different cases. 199 | 200 | All operations are performed in reduction context using [bn.js][1], hashing is 201 | provided by [hash.js][2] 202 | 203 | ### Related projects 204 | 205 | * [eccrypto][3]: isomorphic implementation of ECDSA, ECDH and ECIES for both 206 | browserify and node (uses `elliptic` for browser and [secp256k1-node][4] for 207 | node) 208 | 209 | #### LICENSE 210 | 211 | This software is licensed under the MIT License. 212 | 213 | Copyright Fedor Indutny, 2014. 214 | 215 | Permission is hereby granted, free of charge, to any person obtaining a 216 | copy of this software and associated documentation files (the 217 | "Software"), to deal in the Software without restriction, including 218 | without limitation the rights to use, copy, modify, merge, publish, 219 | distribute, sublicense, and/or sell copies of the Software, and to permit 220 | persons to whom the Software is furnished to do so, subject to the 221 | following conditions: 222 | 223 | The above copyright notice and this permission notice shall be included 224 | in all copies or substantial portions of the Software. 225 | 226 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 227 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 228 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 229 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 230 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 231 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 232 | USE OR OTHER DEALINGS IN THE SOFTWARE. 233 | 234 | [0]: http://tools.ietf.org/html/rfc6979 235 | [1]: https://github.com/indutny/bn.js 236 | [2]: https://github.com/indutny/hash.js 237 | [3]: https://github.com/bitchan/eccrypto 238 | [4]: https://github.com/wanderer/secp256k1-node 239 | -------------------------------------------------------------------------------- /test/curve-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var BN = require('bn.js'); 5 | var elliptic = require('../'); 6 | 7 | describe('Curve', function() { 8 | it('should work with example curve', function() { 9 | var curve = new elliptic.curve.short({ 10 | p: '1d', 11 | a: '4', 12 | b: '14' 13 | }); 14 | 15 | var p = curve.point('18', '16'); 16 | assert(p.validate()); 17 | assert(p.dbl().validate()); 18 | assert(p.dbl().add(p).validate()); 19 | assert(p.dbl().add(p.dbl()).validate()); 20 | assert(p.dbl().add(p.dbl()).eq(p.add(p).add(p).add(p))); 21 | }); 22 | 23 | it('should work with secp112k1', function() { 24 | var curve = new elliptic.curve.short({ 25 | p: 'db7c 2abf62e3 5e668076 bead208b', 26 | a: 'db7c 2abf62e3 5e668076 bead2088', 27 | b: '659e f8ba0439 16eede89 11702b22' 28 | }); 29 | 30 | var p = curve.point( 31 | '0948 7239995a 5ee76b55 f9c2f098', 32 | 'a89c e5af8724 c0a23e0e 0ff77500'); 33 | assert(p.validate()); 34 | assert(p.dbl().validate()); 35 | }); 36 | 37 | it('should work with secp256k1', function() { 38 | var curve = new elliptic.curve.short({ 39 | p: 'ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe ' + 40 | 'fffffc2f', 41 | a: '0', 42 | b: '7', 43 | n: 'ffffffff ffffffff ffffffff fffffffe ' + 44 | 'baaedce6 af48a03b bfd25e8c d0364141', 45 | g: [ 46 | '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 47 | '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' 48 | ] 49 | }); 50 | 51 | var p = curve.point( 52 | '79be667e f9dcbbac 55a06295 ce870b07 029bfcdb 2dce28d9 59f2815b 16f81798', 53 | '483ada77 26a3c465 5da4fbfc 0e1108a8 fd17b448 a6855419 9c47d08f fb10d4b8' 54 | ); 55 | assert(p.validate()); 56 | assert(p.dbl().validate()); 57 | assert(p.toJ().dbl().toP().validate()); 58 | assert(p.mul(new BN('79be667e f9dcbbac 55a06295 ce870b07', 16)).validate()); 59 | 60 | var j = p.toJ(); 61 | assert(j.trpl().eq(j.dbl().add(j))); 62 | 63 | // Endomorphism test 64 | assert(curve.endo); 65 | assert.equal( 66 | curve.endo.beta.fromRed().toString(16), 67 | '7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee'); 68 | assert.equal( 69 | curve.endo.lambda.toString(16), 70 | '5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72'); 71 | 72 | var k = new BN('1234567890123456789012345678901234', 16); 73 | var split = curve._endoSplit(k); 74 | 75 | var testK = split.k1.add(split.k2.mul(curve.endo.lambda)).umod(curve.n); 76 | assert.equal(testK.toString(16), k.toString(16)); 77 | }); 78 | 79 | it('should compute this problematic secp256k1 multiplication', function() { 80 | var curve = elliptic.curves.secp256k1.curve; 81 | var g1 = curve.g; // precomputed g 82 | assert(g1.precomputed); 83 | var g2 = curve.point(g1.getX(), g1.getY()); // not precomputed g 84 | assert(!g2.precomputed); 85 | var a = new BN( 86 | '6d1229a6b24c2e775c062870ad26bc261051e0198c67203167273c7c62538846', 16); 87 | var p1 = g1.mul(a); 88 | var p2 = g2.mul(a); 89 | assert(p1.eq(p2)); 90 | }); 91 | 92 | it('should not use fixed NAF when k is too large', function() { 93 | var curve = elliptic.curves.secp256k1.curve; 94 | var g1 = curve.g; // precomputed g 95 | assert(g1.precomputed); 96 | var g2 = curve.point(g1.getX(), g1.getY()); // not precomputed g 97 | assert(!g2.precomputed); 98 | 99 | var a = new BN( 100 | '6d1229a6b24c2e775c062870ad26bc26' + 101 | '1051e0198c67203167273c7c6253884612345678', 102 | 16); 103 | var p1 = g1.mul(a); 104 | var p2 = g2.mul(a); 105 | assert(p1.eq(p2)); 106 | }); 107 | 108 | it('should not fail on secp256k1 regression', function() { 109 | var curve = elliptic.curves.secp256k1.curve; 110 | var k1 = new BN( 111 | '32efeba414cd0c830aed727749e816a01c471831536fd2fce28c56b54f5a3bb1', 16); 112 | var k2 = new BN( 113 | '5f2e49b5d64e53f9811545434706cde4de528af97bfd49fde1f6cf792ee37a8c', 16); 114 | 115 | var p1 = curve.g.mul(k1); 116 | var p2 = curve.g.mul(k2); 117 | 118 | // 2 + 2 + 1 = 2 + 1 + 2 119 | var two = p2.dbl(); 120 | var five = two.dbl().add(p2); 121 | var three = two.add(p2); 122 | var maybeFive = three.add(two); 123 | 124 | assert(maybeFive.eq(five)); 125 | 126 | p1 = p1.mul(k2); 127 | p2 = p2.mul(k1); 128 | 129 | assert(p1.validate()); 130 | assert(p2.validate()); 131 | assert(p1.eq(p2)); 132 | }); 133 | 134 | it('should correctly double the affine point on secp256k1', function() { 135 | var bad = { 136 | x: '026a2073b1ef6fab47ace18e60e728a05180a82755bbcec9a0abc08ad9f7a3d4', 137 | y: '9cd8cb48c3281596139f147c1364a3ede88d3f310fdb0eb98c924e599ca1b3c9', 138 | z: 'd78587ad45e4102f48b54b5d85598296e069ce6085002e169c6bad78ddc6d9bd' 139 | }; 140 | 141 | var good = { 142 | x: 'e7789226739ac2eb3c7ccb2a9a910066beeed86cdb4e0f8a7fee8eeb29dc7016', 143 | y: '4b76b191fd6d47d07828ea965e275b76d0e3e0196cd5056d38384fbb819f9fcb', 144 | z: 'cbf8d99056618ba132d6145b904eee1ce566e0feedb9595139c45f84e90cfa7d' 145 | }; 146 | 147 | var curve = elliptic.curves.secp256k1.curve; 148 | bad = curve.jpoint(bad.x, bad.y, bad.z); 149 | good = curve.jpoint(good.x, good.y, good.z); 150 | 151 | // They are the same points 152 | assert(bad.add(good.neg()).isInfinity()); 153 | 154 | // But doubling borks them out 155 | assert(bad.dbl().add(good.dbl().neg()).isInfinity()); 156 | }); 157 | 158 | it('should store precomputed values correctly on negation', function() { 159 | var curve = elliptic.curves.secp256k1.curve; 160 | var p = curve.g.mul('2'); 161 | p.precompute(); 162 | var neg = p.neg(true); 163 | var neg2 = neg.neg(true); 164 | assert(p.eq(neg2)); 165 | }); 166 | }); 167 | 168 | describe('Point codec', function () { 169 | function makeShortTest(definition) { 170 | var curve = elliptic.curves.secp256k1.curve; 171 | 172 | return function() { 173 | var co = definition.coordinates; 174 | var p = curve.point(co.x, co.y); 175 | 176 | // Encodes as expected 177 | assert.equal(p.encode('hex'), definition.encoded); 178 | assert.equal(p.encodeCompressed('hex'), definition.compactEncoded); 179 | 180 | // Decodes as expected 181 | assert(curve.decodePoint(definition.encoded, 'hex').eq(p)); 182 | assert(curve.decodePoint(definition.compactEncoded, 'hex').eq(p)); 183 | assert(curve.decodePoint(definition.hybrid, 'hex').eq(p)); 184 | }; 185 | } 186 | 187 | function makeMontTest(definition) { 188 | var curve = elliptic.curves.curve25519.curve; 189 | 190 | return function() { 191 | var co = definition.coordinates; 192 | var p = curve.point(co.x, co.z); 193 | var encoded = p.encode('hex'); 194 | var decoded = curve.decodePoint(encoded, 'hex'); 195 | assert(decoded.eq(p)); 196 | assert.equal(encoded, definition.encoded); 197 | }; 198 | } 199 | 200 | var shortPointEvenY = { 201 | coordinates: { 202 | x: '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 203 | y: '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' 204 | }, 205 | compactEncoded: 206 | '02' + 207 | '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798', 208 | encoded: 209 | '04' + 210 | '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798' + 211 | '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8', 212 | hybrid: 213 | '06' + 214 | '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798' + 215 | '483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8' 216 | }; 217 | 218 | var shortPointOddY = { 219 | coordinates: { 220 | x: 'fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556', 221 | y: 'ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297' 222 | }, 223 | compactEncoded: 224 | '03' + 225 | 'fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556', 226 | encoded: 227 | '04' + 228 | 'fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556' + 229 | 'ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297', 230 | hybrid: 231 | '07' + 232 | 'fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556' + 233 | 'ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297' 234 | }; 235 | 236 | it('should throw when trying to decode random bytes', function() { 237 | assert.throws(function() { 238 | elliptic.curves.secp256k1.curve.decodePoint( 239 | '05' + 240 | '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'); 241 | }); 242 | }); 243 | 244 | it('should be able to encode/decode a short curve point with even Y', 245 | makeShortTest(shortPointEvenY)); 246 | 247 | it('should be able to encode/decode a short curve point with odd Y', 248 | makeShortTest(shortPointOddY)); 249 | 250 | it('should be able to encode/decode a mont curve point', makeMontTest({ 251 | coordinates: { 252 | // curve25519.curve.g.mul(new BN('6')).getX().toString(16, 2) 253 | x: '26954ccdc99ebf34f8f1dde5e6bb080685fec73640494c28f9fe0bfa8c794531', 254 | z: '1' 255 | }, 256 | encoded: 257 | '26954ccdc99ebf34f8f1dde5e6bb080685fec73640494c28f9fe0bfa8c794531' 258 | })); 259 | }); 260 | -------------------------------------------------------------------------------- /lib/elliptic/curve/base.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BN = require('bn.js'); 4 | var elliptic = require('../../elliptic'); 5 | var utils = elliptic.utils; 6 | var getNAF = utils.getNAF; 7 | var getJSF = utils.getJSF; 8 | var assert = utils.assert; 9 | 10 | function BaseCurve(type, conf) { 11 | this.type = type; 12 | this.p = new BN(conf.p, 16); 13 | 14 | // Use Montgomery, when there is no fast reduction for the prime 15 | this.red = conf.prime ? BN.red(conf.prime) : BN.mont(this.p); 16 | 17 | // Useful for many curves 18 | this.zero = new BN(0).toRed(this.red); 19 | this.one = new BN(1).toRed(this.red); 20 | this.two = new BN(2).toRed(this.red); 21 | 22 | // Curve configuration, optional 23 | this.n = conf.n && new BN(conf.n, 16); 24 | this.g = conf.g && this.pointFromJSON(conf.g, conf.gRed); 25 | 26 | // Temporary arrays 27 | this._wnafT1 = new Array(4); 28 | this._wnafT2 = new Array(4); 29 | this._wnafT3 = new Array(4); 30 | this._wnafT4 = new Array(4); 31 | 32 | // Generalized Greg Maxwell's trick 33 | var adjustCount = this.n && this.p.div(this.n); 34 | if (!adjustCount || adjustCount.cmpn(100) > 0) { 35 | this.redN = null; 36 | } else { 37 | this._maxwellTrick = true; 38 | this.redN = this.n.toRed(this.red); 39 | } 40 | } 41 | module.exports = BaseCurve; 42 | 43 | BaseCurve.prototype.point = function point() { 44 | throw new Error('Not implemented'); 45 | }; 46 | 47 | BaseCurve.prototype.validate = function validate() { 48 | throw new Error('Not implemented'); 49 | }; 50 | 51 | BaseCurve.prototype._fixedNafMul = function _fixedNafMul(p, k) { 52 | assert(p.precomputed); 53 | var doubles = p._getDoubles(); 54 | 55 | var naf = getNAF(k, 1); 56 | var I = (1 << (doubles.step + 1)) - (doubles.step % 2 === 0 ? 2 : 1); 57 | I /= 3; 58 | 59 | // Translate into more windowed form 60 | var repr = []; 61 | for (var j = 0; j < naf.length; j += doubles.step) { 62 | var nafW = 0; 63 | for (var k = j + doubles.step - 1; k >= j; k--) 64 | nafW = (nafW << 1) + naf[k]; 65 | repr.push(nafW); 66 | } 67 | 68 | var a = this.jpoint(null, null, null); 69 | var b = this.jpoint(null, null, null); 70 | for (var i = I; i > 0; i--) { 71 | for (var j = 0; j < repr.length; j++) { 72 | var nafW = repr[j]; 73 | if (nafW === i) 74 | b = b.mixedAdd(doubles.points[j]); 75 | else if (nafW === -i) 76 | b = b.mixedAdd(doubles.points[j].neg()); 77 | } 78 | a = a.add(b); 79 | } 80 | return a.toP(); 81 | }; 82 | 83 | BaseCurve.prototype._wnafMul = function _wnafMul(p, k) { 84 | var w = 4; 85 | 86 | // Precompute window 87 | var nafPoints = p._getNAFPoints(w); 88 | w = nafPoints.wnd; 89 | var wnd = nafPoints.points; 90 | 91 | // Get NAF form 92 | var naf = getNAF(k, w); 93 | 94 | // Add `this`*(N+1) for every w-NAF index 95 | var acc = this.jpoint(null, null, null); 96 | for (var i = naf.length - 1; i >= 0; i--) { 97 | // Count zeroes 98 | for (var k = 0; i >= 0 && naf[i] === 0; i--) 99 | k++; 100 | if (i >= 0) 101 | k++; 102 | acc = acc.dblp(k); 103 | 104 | if (i < 0) 105 | break; 106 | var z = naf[i]; 107 | assert(z !== 0); 108 | if (p.type === 'affine') { 109 | // J +- P 110 | if (z > 0) 111 | acc = acc.mixedAdd(wnd[(z - 1) >> 1]); 112 | else 113 | acc = acc.mixedAdd(wnd[(-z - 1) >> 1].neg()); 114 | } else { 115 | // J +- J 116 | if (z > 0) 117 | acc = acc.add(wnd[(z - 1) >> 1]); 118 | else 119 | acc = acc.add(wnd[(-z - 1) >> 1].neg()); 120 | } 121 | } 122 | return p.type === 'affine' ? acc.toP() : acc; 123 | }; 124 | 125 | BaseCurve.prototype._wnafMulAdd = function _wnafMulAdd(defW, 126 | points, 127 | coeffs, 128 | len, 129 | jacobianResult) { 130 | var wndWidth = this._wnafT1; 131 | var wnd = this._wnafT2; 132 | var naf = this._wnafT3; 133 | 134 | // Fill all arrays 135 | var max = 0; 136 | for (var i = 0; i < len; i++) { 137 | var p = points[i]; 138 | var nafPoints = p._getNAFPoints(defW); 139 | wndWidth[i] = nafPoints.wnd; 140 | wnd[i] = nafPoints.points; 141 | } 142 | 143 | // Comb small window NAFs 144 | for (var i = len - 1; i >= 1; i -= 2) { 145 | var a = i - 1; 146 | var b = i; 147 | if (wndWidth[a] !== 1 || wndWidth[b] !== 1) { 148 | naf[a] = getNAF(coeffs[a], wndWidth[a]); 149 | naf[b] = getNAF(coeffs[b], wndWidth[b]); 150 | max = Math.max(naf[a].length, max); 151 | max = Math.max(naf[b].length, max); 152 | continue; 153 | } 154 | 155 | var comb = [ 156 | points[a], /* 1 */ 157 | null, /* 3 */ 158 | null, /* 5 */ 159 | points[b] /* 7 */ 160 | ]; 161 | 162 | // Try to avoid Projective points, if possible 163 | if (points[a].y.cmp(points[b].y) === 0) { 164 | comb[1] = points[a].add(points[b]); 165 | comb[2] = points[a].toJ().mixedAdd(points[b].neg()); 166 | } else if (points[a].y.cmp(points[b].y.redNeg()) === 0) { 167 | comb[1] = points[a].toJ().mixedAdd(points[b]); 168 | comb[2] = points[a].add(points[b].neg()); 169 | } else { 170 | comb[1] = points[a].toJ().mixedAdd(points[b]); 171 | comb[2] = points[a].toJ().mixedAdd(points[b].neg()); 172 | } 173 | 174 | var index = [ 175 | -3, /* -1 -1 */ 176 | -1, /* -1 0 */ 177 | -5, /* -1 1 */ 178 | -7, /* 0 -1 */ 179 | 0, /* 0 0 */ 180 | 7, /* 0 1 */ 181 | 5, /* 1 -1 */ 182 | 1, /* 1 0 */ 183 | 3 /* 1 1 */ 184 | ]; 185 | 186 | var jsf = getJSF(coeffs[a], coeffs[b]); 187 | max = Math.max(jsf[0].length, max); 188 | naf[a] = new Array(max); 189 | naf[b] = new Array(max); 190 | for (var j = 0; j < max; j++) { 191 | var ja = jsf[0][j] | 0; 192 | var jb = jsf[1][j] | 0; 193 | 194 | naf[a][j] = index[(ja + 1) * 3 + (jb + 1)]; 195 | naf[b][j] = 0; 196 | wnd[a] = comb; 197 | } 198 | } 199 | 200 | var acc = this.jpoint(null, null, null); 201 | var tmp = this._wnafT4; 202 | for (var i = max; i >= 0; i--) { 203 | var k = 0; 204 | 205 | while (i >= 0) { 206 | var zero = true; 207 | for (var j = 0; j < len; j++) { 208 | tmp[j] = naf[j][i] | 0; 209 | if (tmp[j] !== 0) 210 | zero = false; 211 | } 212 | if (!zero) 213 | break; 214 | k++; 215 | i--; 216 | } 217 | if (i >= 0) 218 | k++; 219 | acc = acc.dblp(k); 220 | if (i < 0) 221 | break; 222 | 223 | for (var j = 0; j < len; j++) { 224 | var z = tmp[j]; 225 | var p; 226 | if (z === 0) 227 | continue; 228 | else if (z > 0) 229 | p = wnd[j][(z - 1) >> 1]; 230 | else if (z < 0) 231 | p = wnd[j][(-z - 1) >> 1].neg(); 232 | 233 | if (p.type === 'affine') 234 | acc = acc.mixedAdd(p); 235 | else 236 | acc = acc.add(p); 237 | } 238 | } 239 | // Zeroify references 240 | for (var i = 0; i < len; i++) 241 | wnd[i] = null; 242 | 243 | if (jacobianResult) 244 | return acc; 245 | else 246 | return acc.toP(); 247 | }; 248 | 249 | function BasePoint(curve, type) { 250 | this.curve = curve; 251 | this.type = type; 252 | this.precomputed = null; 253 | } 254 | BaseCurve.BasePoint = BasePoint; 255 | 256 | BasePoint.prototype.eq = function eq(/*other*/) { 257 | throw new Error('Not implemented'); 258 | }; 259 | 260 | BasePoint.prototype.validate = function validate() { 261 | return this.curve.validate(this); 262 | }; 263 | 264 | BaseCurve.prototype.decodePoint = function decodePoint(bytes, enc) { 265 | bytes = utils.toArray(bytes, enc); 266 | 267 | var len = this.p.byteLength(); 268 | 269 | // uncompressed, hybrid-odd, hybrid-even 270 | if ((bytes[0] === 0x04 || bytes[0] === 0x06 || bytes[0] === 0x07) && 271 | bytes.length - 1 === 2 * len) { 272 | if (bytes[0] === 0x06) 273 | assert(bytes[bytes.length - 1] % 2 === 0); 274 | else if (bytes[0] === 0x07) 275 | assert(bytes[bytes.length - 1] % 2 === 1); 276 | 277 | var res = this.point(bytes.slice(1, 1 + len), 278 | bytes.slice(1 + len, 1 + 2 * len)); 279 | 280 | return res; 281 | } else if ((bytes[0] === 0x02 || bytes[0] === 0x03) && 282 | bytes.length - 1 === len) { 283 | return this.pointFromX(bytes.slice(1, 1 + len), bytes[0] === 0x03); 284 | } 285 | throw new Error('Unknown point format'); 286 | }; 287 | 288 | BasePoint.prototype.encodeCompressed = function encodeCompressed(enc) { 289 | return this.encode(enc, true); 290 | }; 291 | 292 | BasePoint.prototype._encode = function _encode(compact) { 293 | var len = this.curve.p.byteLength(); 294 | var x = this.getX().toArray('be', len); 295 | 296 | if (compact) 297 | return [ this.getY().isEven() ? 0x02 : 0x03 ].concat(x); 298 | 299 | return [ 0x04 ].concat(x, this.getY().toArray('be', len)) ; 300 | }; 301 | 302 | BasePoint.prototype.encode = function encode(enc, compact) { 303 | return utils.encode(this._encode(compact), enc); 304 | }; 305 | 306 | BasePoint.prototype.precompute = function precompute(power) { 307 | if (this.precomputed) 308 | return this; 309 | 310 | var precomputed = { 311 | doubles: null, 312 | naf: null, 313 | beta: null 314 | }; 315 | precomputed.naf = this._getNAFPoints(8); 316 | precomputed.doubles = this._getDoubles(4, power); 317 | precomputed.beta = this._getBeta(); 318 | this.precomputed = precomputed; 319 | 320 | return this; 321 | }; 322 | 323 | BasePoint.prototype._hasDoubles = function _hasDoubles(k) { 324 | if (!this.precomputed) 325 | return false; 326 | 327 | var doubles = this.precomputed.doubles; 328 | if (!doubles) 329 | return false; 330 | 331 | return doubles.points.length >= Math.ceil((k.bitLength() + 1) / doubles.step); 332 | }; 333 | 334 | BasePoint.prototype._getDoubles = function _getDoubles(step, power) { 335 | if (this.precomputed && this.precomputed.doubles) 336 | return this.precomputed.doubles; 337 | 338 | var doubles = [ this ]; 339 | var acc = this; 340 | for (var i = 0; i < power; i += step) { 341 | for (var j = 0; j < step; j++) 342 | acc = acc.dbl(); 343 | doubles.push(acc); 344 | } 345 | return { 346 | step: step, 347 | points: doubles 348 | }; 349 | }; 350 | 351 | BasePoint.prototype._getNAFPoints = function _getNAFPoints(wnd) { 352 | if (this.precomputed && this.precomputed.naf) 353 | return this.precomputed.naf; 354 | 355 | var res = [ this ]; 356 | var max = (1 << wnd) - 1; 357 | var dbl = max === 1 ? null : this.dbl(); 358 | for (var i = 1; i < max; i++) 359 | res[i] = res[i - 1].add(dbl); 360 | return { 361 | wnd: wnd, 362 | points: res 363 | }; 364 | }; 365 | 366 | BasePoint.prototype._getBeta = function _getBeta() { 367 | return null; 368 | }; 369 | 370 | BasePoint.prototype.dblp = function dblp(k) { 371 | var r = this; 372 | for (var i = 0; i < k; i++) 373 | r = r.dbl(); 374 | return r; 375 | }; 376 | -------------------------------------------------------------------------------- /lib/elliptic/curve/edwards.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var curve = require('../curve'); 4 | var elliptic = require('../../elliptic'); 5 | var BN = require('bn.js'); 6 | var inherits = require('inherits'); 7 | var Base = curve.base; 8 | 9 | var assert = elliptic.utils.assert; 10 | 11 | function EdwardsCurve(conf) { 12 | // NOTE: Important as we are creating point in Base.call() 13 | this.twisted = (conf.a | 0) !== 1; 14 | this.mOneA = this.twisted && (conf.a | 0) === -1; 15 | this.extended = this.mOneA; 16 | 17 | Base.call(this, 'edwards', conf); 18 | 19 | this.a = new BN(conf.a, 16).umod(this.red.m); 20 | this.a = this.a.toRed(this.red); 21 | this.c = new BN(conf.c, 16).toRed(this.red); 22 | this.c2 = this.c.redSqr(); 23 | this.d = new BN(conf.d, 16).toRed(this.red); 24 | this.dd = this.d.redAdd(this.d); 25 | 26 | assert(!this.twisted || this.c.fromRed().cmpn(1) === 0); 27 | this.oneC = (conf.c | 0) === 1; 28 | } 29 | inherits(EdwardsCurve, Base); 30 | module.exports = EdwardsCurve; 31 | 32 | EdwardsCurve.prototype._mulA = function _mulA(num) { 33 | if (this.mOneA) 34 | return num.redNeg(); 35 | else 36 | return this.a.redMul(num); 37 | }; 38 | 39 | EdwardsCurve.prototype._mulC = function _mulC(num) { 40 | if (this.oneC) 41 | return num; 42 | else 43 | return this.c.redMul(num); 44 | }; 45 | 46 | // Just for compatibility with Short curve 47 | EdwardsCurve.prototype.jpoint = function jpoint(x, y, z, t) { 48 | return this.point(x, y, z, t); 49 | }; 50 | 51 | EdwardsCurve.prototype.pointFromX = function pointFromX(x, odd) { 52 | x = new BN(x, 16); 53 | if (!x.red) 54 | x = x.toRed(this.red); 55 | 56 | var x2 = x.redSqr(); 57 | var rhs = this.c2.redSub(this.a.redMul(x2)); 58 | var lhs = this.one.redSub(this.c2.redMul(this.d).redMul(x2)); 59 | 60 | var y2 = rhs.redMul(lhs.redInvm()); 61 | var y = y2.redSqrt(); 62 | if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) 63 | throw new Error('invalid point'); 64 | 65 | var isOdd = y.fromRed().isOdd(); 66 | if (odd && !isOdd || !odd && isOdd) 67 | y = y.redNeg(); 68 | 69 | return this.point(x, y); 70 | }; 71 | 72 | EdwardsCurve.prototype.pointFromY = function pointFromY(y, odd) { 73 | y = new BN(y, 16); 74 | if (!y.red) 75 | y = y.toRed(this.red); 76 | 77 | // x^2 = (y^2 - 1) / (d y^2 + 1) 78 | var y2 = y.redSqr(); 79 | var lhs = y2.redSub(this.one); 80 | var rhs = y2.redMul(this.d).redAdd(this.one); 81 | var x2 = lhs.redMul(rhs.redInvm()); 82 | 83 | if (x2.cmp(this.zero) === 0) { 84 | if (odd) 85 | throw new Error('invalid point'); 86 | else 87 | return this.point(this.zero, y); 88 | } 89 | 90 | var x = x2.redSqrt(); 91 | if (x.redSqr().redSub(x2).cmp(this.zero) !== 0) 92 | throw new Error('invalid point'); 93 | 94 | if (x.isOdd() !== odd) 95 | x = x.redNeg(); 96 | 97 | return this.point(x, y); 98 | }; 99 | 100 | EdwardsCurve.prototype.validate = function validate(point) { 101 | if (point.isInfinity()) 102 | return true; 103 | 104 | // Curve: A * X^2 + Y^2 = C^2 * (1 + D * X^2 * Y^2) 105 | point.normalize(); 106 | 107 | var x2 = point.x.redSqr(); 108 | var y2 = point.y.redSqr(); 109 | var lhs = x2.redMul(this.a).redAdd(y2); 110 | var rhs = this.c2.redMul(this.one.redAdd(this.d.redMul(x2).redMul(y2))); 111 | 112 | return lhs.cmp(rhs) === 0; 113 | }; 114 | 115 | function Point(curve, x, y, z, t) { 116 | Base.BasePoint.call(this, curve, 'projective'); 117 | if (x === null && y === null && z === null) { 118 | this.x = this.curve.zero; 119 | this.y = this.curve.one; 120 | this.z = this.curve.one; 121 | this.t = this.curve.zero; 122 | this.zOne = true; 123 | } else { 124 | this.x = new BN(x, 16); 125 | this.y = new BN(y, 16); 126 | this.z = z ? new BN(z, 16) : this.curve.one; 127 | this.t = t && new BN(t, 16); 128 | if (!this.x.red) 129 | this.x = this.x.toRed(this.curve.red); 130 | if (!this.y.red) 131 | this.y = this.y.toRed(this.curve.red); 132 | if (!this.z.red) 133 | this.z = this.z.toRed(this.curve.red); 134 | if (this.t && !this.t.red) 135 | this.t = this.t.toRed(this.curve.red); 136 | this.zOne = this.z === this.curve.one; 137 | 138 | // Use extended coordinates 139 | if (this.curve.extended && !this.t) { 140 | this.t = this.x.redMul(this.y); 141 | if (!this.zOne) 142 | this.t = this.t.redMul(this.z.redInvm()); 143 | } 144 | } 145 | } 146 | inherits(Point, Base.BasePoint); 147 | 148 | EdwardsCurve.prototype.pointFromJSON = function pointFromJSON(obj) { 149 | return Point.fromJSON(this, obj); 150 | }; 151 | 152 | EdwardsCurve.prototype.point = function point(x, y, z, t) { 153 | return new Point(this, x, y, z, t); 154 | }; 155 | 156 | Point.fromJSON = function fromJSON(curve, obj) { 157 | return new Point(curve, obj[0], obj[1], obj[2]); 158 | }; 159 | 160 | Point.prototype.inspect = function inspect() { 161 | if (this.isInfinity()) 162 | return ''; 163 | return ''; 166 | }; 167 | 168 | Point.prototype.isInfinity = function isInfinity() { 169 | // XXX This code assumes that zero is always zero in red 170 | return this.x.cmpn(0) === 0 && 171 | this.y.cmp(this.z) === 0; 172 | }; 173 | 174 | Point.prototype._extDbl = function _extDbl() { 175 | // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html 176 | // #doubling-dbl-2008-hwcd 177 | // 4M + 4S 178 | 179 | // A = X1^2 180 | var a = this.x.redSqr(); 181 | // B = Y1^2 182 | var b = this.y.redSqr(); 183 | // C = 2 * Z1^2 184 | var c = this.z.redSqr(); 185 | c = c.redIAdd(c); 186 | // D = a * A 187 | var d = this.curve._mulA(a); 188 | // E = (X1 + Y1)^2 - A - B 189 | var e = this.x.redAdd(this.y).redSqr().redISub(a).redISub(b); 190 | // G = D + B 191 | var g = d.redAdd(b); 192 | // F = G - C 193 | var f = g.redSub(c); 194 | // H = D - B 195 | var h = d.redSub(b); 196 | // X3 = E * F 197 | var nx = e.redMul(f); 198 | // Y3 = G * H 199 | var ny = g.redMul(h); 200 | // T3 = E * H 201 | var nt = e.redMul(h); 202 | // Z3 = F * G 203 | var nz = f.redMul(g); 204 | return this.curve.point(nx, ny, nz, nt); 205 | }; 206 | 207 | Point.prototype._projDbl = function _projDbl() { 208 | // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html 209 | // #doubling-dbl-2008-bbjlp 210 | // #doubling-dbl-2007-bl 211 | // and others 212 | // Generally 3M + 4S or 2M + 4S 213 | 214 | // B = (X1 + Y1)^2 215 | var b = this.x.redAdd(this.y).redSqr(); 216 | // C = X1^2 217 | var c = this.x.redSqr(); 218 | // D = Y1^2 219 | var d = this.y.redSqr(); 220 | 221 | var nx; 222 | var ny; 223 | var nz; 224 | if (this.curve.twisted) { 225 | // E = a * C 226 | var e = this.curve._mulA(c); 227 | // F = E + D 228 | var f = e.redAdd(d); 229 | if (this.zOne) { 230 | // X3 = (B - C - D) * (F - 2) 231 | nx = b.redSub(c).redSub(d).redMul(f.redSub(this.curve.two)); 232 | // Y3 = F * (E - D) 233 | ny = f.redMul(e.redSub(d)); 234 | // Z3 = F^2 - 2 * F 235 | nz = f.redSqr().redSub(f).redSub(f); 236 | } else { 237 | // H = Z1^2 238 | var h = this.z.redSqr(); 239 | // J = F - 2 * H 240 | var j = f.redSub(h).redISub(h); 241 | // X3 = (B-C-D)*J 242 | nx = b.redSub(c).redISub(d).redMul(j); 243 | // Y3 = F * (E - D) 244 | ny = f.redMul(e.redSub(d)); 245 | // Z3 = F * J 246 | nz = f.redMul(j); 247 | } 248 | } else { 249 | // E = C + D 250 | var e = c.redAdd(d); 251 | // H = (c * Z1)^2 252 | var h = this.curve._mulC(this.c.redMul(this.z)).redSqr(); 253 | // J = E - 2 * H 254 | var j = e.redSub(h).redSub(h); 255 | // X3 = c * (B - E) * J 256 | nx = this.curve._mulC(b.redISub(e)).redMul(j); 257 | // Y3 = c * E * (C - D) 258 | ny = this.curve._mulC(e).redMul(c.redISub(d)); 259 | // Z3 = E * J 260 | nz = e.redMul(j); 261 | } 262 | return this.curve.point(nx, ny, nz); 263 | }; 264 | 265 | Point.prototype.dbl = function dbl() { 266 | if (this.isInfinity()) 267 | return this; 268 | 269 | // Double in extended coordinates 270 | if (this.curve.extended) 271 | return this._extDbl(); 272 | else 273 | return this._projDbl(); 274 | }; 275 | 276 | Point.prototype._extAdd = function _extAdd(p) { 277 | // hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html 278 | // #addition-add-2008-hwcd-3 279 | // 8M 280 | 281 | // A = (Y1 - X1) * (Y2 - X2) 282 | var a = this.y.redSub(this.x).redMul(p.y.redSub(p.x)); 283 | // B = (Y1 + X1) * (Y2 + X2) 284 | var b = this.y.redAdd(this.x).redMul(p.y.redAdd(p.x)); 285 | // C = T1 * k * T2 286 | var c = this.t.redMul(this.curve.dd).redMul(p.t); 287 | // D = Z1 * 2 * Z2 288 | var d = this.z.redMul(p.z.redAdd(p.z)); 289 | // E = B - A 290 | var e = b.redSub(a); 291 | // F = D - C 292 | var f = d.redSub(c); 293 | // G = D + C 294 | var g = d.redAdd(c); 295 | // H = B + A 296 | var h = b.redAdd(a); 297 | // X3 = E * F 298 | var nx = e.redMul(f); 299 | // Y3 = G * H 300 | var ny = g.redMul(h); 301 | // T3 = E * H 302 | var nt = e.redMul(h); 303 | // Z3 = F * G 304 | var nz = f.redMul(g); 305 | return this.curve.point(nx, ny, nz, nt); 306 | }; 307 | 308 | Point.prototype._projAdd = function _projAdd(p) { 309 | // hyperelliptic.org/EFD/g1p/auto-twisted-projective.html 310 | // #addition-add-2008-bbjlp 311 | // #addition-add-2007-bl 312 | // 10M + 1S 313 | 314 | // A = Z1 * Z2 315 | var a = this.z.redMul(p.z); 316 | // B = A^2 317 | var b = a.redSqr(); 318 | // C = X1 * X2 319 | var c = this.x.redMul(p.x); 320 | // D = Y1 * Y2 321 | var d = this.y.redMul(p.y); 322 | // E = d * C * D 323 | var e = this.curve.d.redMul(c).redMul(d); 324 | // F = B - E 325 | var f = b.redSub(e); 326 | // G = B + E 327 | var g = b.redAdd(e); 328 | // X3 = A * F * ((X1 + Y1) * (X2 + Y2) - C - D) 329 | var tmp = this.x.redAdd(this.y).redMul(p.x.redAdd(p.y)).redISub(c).redISub(d); 330 | var nx = a.redMul(f).redMul(tmp); 331 | var ny; 332 | var nz; 333 | if (this.curve.twisted) { 334 | // Y3 = A * G * (D - a * C) 335 | ny = a.redMul(g).redMul(d.redSub(this.curve._mulA(c))); 336 | // Z3 = F * G 337 | nz = f.redMul(g); 338 | } else { 339 | // Y3 = A * G * (D - C) 340 | ny = a.redMul(g).redMul(d.redSub(c)); 341 | // Z3 = c * F * G 342 | nz = this.curve._mulC(f).redMul(g); 343 | } 344 | return this.curve.point(nx, ny, nz); 345 | }; 346 | 347 | Point.prototype.add = function add(p) { 348 | if (this.isInfinity()) 349 | return p; 350 | if (p.isInfinity()) 351 | return this; 352 | 353 | if (this.curve.extended) 354 | return this._extAdd(p); 355 | else 356 | return this._projAdd(p); 357 | }; 358 | 359 | Point.prototype.mul = function mul(k) { 360 | if (this._hasDoubles(k)) 361 | return this.curve._fixedNafMul(this, k); 362 | else 363 | return this.curve._wnafMul(this, k); 364 | }; 365 | 366 | Point.prototype.mulAdd = function mulAdd(k1, p, k2) { 367 | return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, false); 368 | }; 369 | 370 | Point.prototype.jmulAdd = function jmulAdd(k1, p, k2) { 371 | return this.curve._wnafMulAdd(1, [ this, p ], [ k1, k2 ], 2, true); 372 | }; 373 | 374 | Point.prototype.normalize = function normalize() { 375 | if (this.zOne) 376 | return this; 377 | 378 | // Normalize coordinates 379 | var zi = this.z.redInvm(); 380 | this.x = this.x.redMul(zi); 381 | this.y = this.y.redMul(zi); 382 | if (this.t) 383 | this.t = this.t.redMul(zi); 384 | this.z = this.curve.one; 385 | this.zOne = true; 386 | return this; 387 | }; 388 | 389 | Point.prototype.neg = function neg() { 390 | return this.curve.point(this.x.redNeg(), 391 | this.y, 392 | this.z, 393 | this.t && this.t.redNeg()); 394 | }; 395 | 396 | Point.prototype.getX = function getX() { 397 | this.normalize(); 398 | return this.x.fromRed(); 399 | }; 400 | 401 | Point.prototype.getY = function getY() { 402 | this.normalize(); 403 | return this.y.fromRed(); 404 | }; 405 | 406 | Point.prototype.eq = function eq(other) { 407 | return this === other || 408 | this.getX().cmp(other.getX()) === 0 && 409 | this.getY().cmp(other.getY()) === 0; 410 | }; 411 | 412 | Point.prototype.eqXToP = function eqXToP(x) { 413 | var rx = x.toRed(this.curve.red).redMul(this.z); 414 | if (this.x.cmp(rx) === 0) 415 | return true; 416 | 417 | var xc = x.clone(); 418 | var t = this.curve.redN.redMul(this.z); 419 | for (;;) { 420 | xc.iadd(this.curve.n); 421 | if (xc.cmp(this.curve.p) >= 0) 422 | return false; 423 | 424 | rx.redIAdd(t); 425 | if (this.x.cmp(rx) === 0) 426 | return true; 427 | } 428 | }; 429 | 430 | // Compatibility with BaseCurve 431 | Point.prototype.toP = Point.prototype.normalize; 432 | Point.prototype.mixedAdd = Point.prototype.add; 433 | -------------------------------------------------------------------------------- /test/ecdsa-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var elliptic = require('../'); 3 | var Signature = require('../lib/elliptic/ec/signature') 4 | var BN = require('bn.js'); 5 | var hash = require('hash.js'); 6 | 7 | var entropy = [ 8 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 9 | 21, 22, 23, 24, 25 10 | ]; 11 | 12 | var msg = 'deadbeef'; 13 | 14 | describe('ECDSA', function() { 15 | function test(name) { 16 | describe('curve ' + name, function() { 17 | var curve; 18 | var ecdsa; 19 | 20 | beforeEach(function() { 21 | curve = elliptic.curves[name]; 22 | assert(curve); 23 | 24 | ecdsa = new elliptic.ec(curve); 25 | keys = ecdsa.genKeyPair({ 26 | entropy: entropy 27 | }); 28 | }); 29 | 30 | it('should generate proper key pair', function() { 31 | var keylen = 64; 32 | if (name === 'p384') { 33 | keylen = 96; 34 | } else if (name === 'p521') { 35 | keylen = 132 36 | } 37 | // Get keys out of pair 38 | assert(keys.getPublic().x && keys.getPublic().y); 39 | assert(keys.getPrivate().length > 0); 40 | assert.equal(keys.getPrivate('hex').length, keylen); 41 | assert(keys.getPublic('hex').length > 0); 42 | assert(keys.getPrivate('hex').length > 0); 43 | assert(keys.validate().result); 44 | }); 45 | 46 | it('should sign and verify', function() { 47 | var signature = ecdsa.sign(msg, keys); 48 | assert(ecdsa.verify(msg, signature, keys), 'Normal verify'); 49 | }); 50 | 51 | it('should sign and verify using key\'s methods', function() { 52 | var signature = keys.sign(msg); 53 | assert(keys.verify(msg, signature), 'On-key verify'); 54 | }); 55 | 56 | it('should load private key from the hex value', function() { 57 | var copy = ecdsa.keyFromPrivate(keys.getPrivate('hex'), 'hex'); 58 | var signature = ecdsa.sign(msg, copy); 59 | assert(ecdsa.verify(msg, signature, copy), 'hex-private verify'); 60 | }); 61 | 62 | it('should have `signature.s <= keys.ec.nh`', function() { 63 | // key.sign(msg, options) 64 | var sign = keys.sign('hello', { canonical: true }); 65 | assert(sign.s.cmp(keys.ec.nh) <= 0); 66 | }); 67 | 68 | it('should support `options.k`', function() { 69 | var sign = keys.sign(msg, { 70 | k: function(iter) { 71 | assert(iter >= 0); 72 | return new BN(1358); 73 | } 74 | }); 75 | assert(ecdsa.verify(msg, sign, keys), 'custom-k verify'); 76 | }); 77 | 78 | it('should have another signature with pers', function () { 79 | var sign1 = keys.sign(msg); 80 | var sign2 = keys.sign(msg, { pers: '1234', persEnc: 'hex' }); 81 | assert.notEqual(sign1.r.toArray().concat(sign1.s.toArray()), 82 | sign2.r.toArray().concat(sign2.s.toArray())); 83 | }); 84 | 85 | it('should load public key from compact hex value', function() { 86 | var pub = keys.getPublic(true, 'hex'); 87 | var copy = ecdsa.keyFromPublic(pub, 'hex'); 88 | assert.equal(copy.getPublic(true, 'hex'), pub); 89 | }); 90 | 91 | it('should load public key from hex value', function() { 92 | var pub = keys.getPublic('hex'); 93 | var copy = ecdsa.keyFromPublic(pub, 'hex'); 94 | assert.equal(copy.getPublic('hex'), pub); 95 | }); 96 | 97 | it('should support hex DER encoding of signatures', function() { 98 | var signature = ecdsa.sign(msg, keys); 99 | var dsign = signature.toDER('hex'); 100 | assert(ecdsa.verify(msg, dsign, keys), 'hex-DER encoded verify'); 101 | }); 102 | 103 | it('should support DER encoding of signatures', function() { 104 | var signature = ecdsa.sign(msg, keys); 105 | var dsign = signature.toDER(); 106 | assert(ecdsa.verify(msg, dsign, keys), 'DER encoded verify'); 107 | }); 108 | 109 | it('should not verify signature with wrong public key', function() { 110 | var signature = ecdsa.sign(msg, keys); 111 | 112 | var wrong = ecdsa.genKeyPair(); 113 | assert(!ecdsa.verify(msg, signature, wrong), 'Wrong key verify'); 114 | }); 115 | 116 | it('should not verify signature with wrong private key', function() { 117 | var signature = ecdsa.sign(msg, keys); 118 | 119 | var wrong = ecdsa.keyFromPrivate(keys.getPrivate('hex') + 120 | keys.getPrivate('hex')); 121 | assert(!ecdsa.verify(msg, signature, wrong), 'Wrong key verify'); 122 | }); 123 | }); 124 | } 125 | test('secp256k1'); 126 | test('ed25519'); 127 | test('p256'); 128 | test('p384'); 129 | test('p521'); 130 | 131 | describe('RFC6979 vector', function() { 132 | function test(opt) { 133 | opt.cases.forEach(function(c) { 134 | var ecdsa = elliptic.ec({ 135 | curve: opt.curve, 136 | hash: c.hash 137 | }); 138 | var descr = 'should not fail on "' + opt.name + '" ' + 139 | 'and hash ' + c.hash.name + ' on "' + c.message + '"'; 140 | it(descr, function() { 141 | var dgst = c.hash().update(c.message).digest(); 142 | var sign = ecdsa.sign(dgst, opt.key); 143 | assert.equal(sign.r.toString(16), c.r); 144 | assert.equal(sign.s.toString(16), c.s); 145 | assert.ok(ecdsa.keyFromPublic(opt.pub).validate().result, 146 | 'Invalid public key'); 147 | assert.ok(ecdsa.verify(dgst, sign, opt.pub), 148 | 'Invalid signature'); 149 | }); 150 | }); 151 | } 152 | 153 | test({ 154 | name: 'ECDSA, 192 Bits (Prime Field)', 155 | curve: elliptic.curves.p192, 156 | key: '6fab034934e4c0fc9ae67f5b5659a9d7d1fefd187ee09fd4', 157 | pub: { 158 | x: 'ac2c77f529f91689fea0ea5efec7f210d8eea0b9e047ed56', 159 | y: '3bc723e57670bd4887ebc732c523063d0a7c957bc97c1c43' 160 | }, 161 | cases: [ 162 | { 163 | message: 'sample', 164 | hash: hash.sha224, 165 | r: 'a1f00dad97aeec91c95585f36200c65f3c01812aa60378f5', 166 | s: 'e07ec1304c7c6c9debbe980b9692668f81d4de7922a0f97a' 167 | }, 168 | { 169 | message: 'sample', 170 | hash: hash.sha256, 171 | r: '4b0b8ce98a92866a2820e20aa6b75b56382e0f9bfd5ecb55', 172 | s: 'ccdb006926ea9565cbadc840829d8c384e06de1f1e381b85' 173 | }, 174 | { 175 | message: 'test', 176 | hash: hash.sha224, 177 | r: '6945a1c1d1b2206b8145548f633bb61cef04891baf26ed34', 178 | s: 'b7fb7fdfc339c0b9bd61a9f5a8eaf9be58fc5cba2cb15293' 179 | }, 180 | { 181 | message: 'test', 182 | hash: hash.sha256, 183 | r: '3a718bd8b4926c3b52ee6bbe67ef79b18cb6eb62b1ad97ae', 184 | s: '5662e6848a4a19b1f1ae2f72acd4b8bbe50f1eac65d9124f' 185 | } 186 | ] 187 | }); 188 | 189 | test({ 190 | name: 'ECDSA, 224 Bits (Prime Field)', 191 | curve: elliptic.curves.p224, 192 | key: 'f220266e1105bfe3083e03ec7a3a654651f45e37167e88600bf257c1', 193 | pub: { 194 | x: '00cf08da5ad719e42707fa431292dea11244d64fc51610d94b130d6c', 195 | y: 'eeab6f3debe455e3dbf85416f7030cbd94f34f2d6f232c69f3c1385a' 196 | }, 197 | cases: [ 198 | { 199 | message: 'sample', 200 | hash: hash.sha224, 201 | r: '1cdfe6662dde1e4a1ec4cdedf6a1f5a2fb7fbd9145c12113e6abfd3e', 202 | s: 'a6694fd7718a21053f225d3f46197ca699d45006c06f871808f43ebc' 203 | }, 204 | { 205 | message: 'sample', 206 | hash: hash.sha256, 207 | r: '61aa3da010e8e8406c656bc477a7a7189895e7e840cdfe8ff42307ba', 208 | s: 'bc814050dab5d23770879494f9e0a680dc1af7161991bde692b10101' 209 | }, 210 | { 211 | message: 'test', 212 | hash: hash.sha224, 213 | r: 'c441ce8e261ded634e4cf84910e4c5d1d22c5cf3b732bb204dbef019', 214 | s: '902f42847a63bdc5f6046ada114953120f99442d76510150f372a3f4' 215 | }, 216 | { 217 | message: 'test', 218 | hash: hash.sha256, 219 | r: 'ad04dde87b84747a243a631ea47a1ba6d1faa059149ad2440de6fba6', 220 | s: '178d49b1ae90e3d8b629be3db5683915f4e8c99fdf6e666cf37adcfd' 221 | } 222 | ] 223 | }); 224 | 225 | test({ 226 | name: 'ECDSA, 256 Bits (Prime Field)', 227 | curve: elliptic.curves.p256, 228 | key: 'c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721', 229 | pub: { 230 | x: '60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6', 231 | y: '7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299' 232 | }, 233 | cases: [ 234 | { 235 | message: 'sample', 236 | hash: hash.sha224, 237 | r: '53b2fff5d1752b2c689df257c04c40a587fababb3f6fc2702f1343af7ca9aa3f', 238 | s: 'b9afb64fdc03dc1a131c7d2386d11e349f070aa432a4acc918bea988bf75c74c' 239 | }, 240 | { 241 | message: 'sample', 242 | hash: hash.sha256, 243 | r: 'efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716', 244 | s: 'f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8' 245 | }, 246 | { 247 | message: 'test', 248 | hash: hash.sha224, 249 | r: 'c37edb6f0ae79d47c3c27e962fa269bb4f441770357e114ee511f662ec34a692', 250 | s: 'c820053a05791e521fcaad6042d40aea1d6b1a540138558f47d0719800e18f2d' 251 | }, 252 | { 253 | message: 'test', 254 | hash: hash.sha256, 255 | r: 'f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367', 256 | s: '19f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083' 257 | } 258 | ] 259 | }); 260 | 261 | test({ 262 | name: 'ECDSA, 384 Bits (Prime Field)', 263 | curve: elliptic.curves.p384, 264 | key: '6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa4774078713' + 265 | '7d896d5724e4c70a825f872c9ea60d2edf5', 266 | pub: { 267 | x: 'ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b30' + 268 | '0c64def8f0ea9055866064a254515480bc13', 269 | y: '8015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f' + 270 | '1c9db1288b231c3ae0d4fe7344fd2533264720' 271 | }, 272 | cases: [ 273 | { 274 | message: 'sample', 275 | hash: hash.sha224, 276 | r: '42356e76b55a6d9b4631c865445dbe54e056d3b3431766d05092447' + 277 | '93c3f9366450f76ee3de43f5a125333a6be060122', 278 | s: '9da0c81787064021e78df658f2fbb0b042bf304665db721f077a429' + 279 | '8b095e4834c082c03d83028efbf93a3c23940ca8d' 280 | }, 281 | { 282 | message: 'sample', 283 | hash: hash.sha384, 284 | r: '94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d6' + 285 | '4c4ea95ad133c81a648152e44acf96e36dd1e80fabe46', 286 | s: '99ef4aeb15f178cea1fe40db2603138f130e740a19624526203b' + 287 | '6351d0a3a94fa329c145786e679e7b82c71a38628ac8' 288 | }, 289 | { 290 | message: 'test', 291 | hash: hash.sha384, 292 | r: '8203b63d3c853e8d77227fb377bcf7b7b772e97892a80f36a' + 293 | 'b775d509d7a5feb0542a7f0812998da8f1dd3ca3cf023db', 294 | s: 'ddd0760448d42d8a43af45af836fce4de8be06b485e9b61b827c2f13' + 295 | '173923e06a739f040649a667bf3b828246baa5a5' 296 | } 297 | ] 298 | }); 299 | 300 | test({ 301 | name: 'ECDSA, 521 Bits (Prime Field)', 302 | curve: elliptic.curves.p521, 303 | key: '0fad06daa62ba3b25d2fb40133da757205de67f5bb0018fee8c86e1b68c7e75' + 304 | 'caa896eb32f1f47c70855836a6d16fcc1466f6d8fbec67db89ec0c08b0e996b' + 305 | '83538', 306 | pub: { 307 | x: '1894550d0785932e00eaa23b694f213f8c3121f86dc97a04e5a7167db4e5bcd3' + 308 | '71123d46e45db6b5d5370a7f20fb633155d38ffa16d2bd761dcac474b9a2f502' + 309 | '3a4', 310 | y: '0493101c962cd4d2fddf782285e64584139c2f91b47f87ff82354d6630f746a2' + 311 | '8a0db25741b5b34a828008b22acc23f924faafbd4d33f81ea66956dfeaa2bfdfcf5' 312 | }, 313 | cases: [ 314 | { 315 | message: 'sample', 316 | hash: hash.sha384, 317 | r: '1ea842a0e17d2de4f92c15315c63ddf72685c18195c2bb95e572b9c5136ca4' + 318 | 'b4b576ad712a52be9730627d16054ba40cc0b8d3ff035b12ae75168397f5' + 319 | 'd50c67451', 320 | s: '1f21a3cee066e1961025fb048bd5fe2b7924d0cd797babe0a83b66f1e35ee' + 321 | 'af5fde143fa85dc394a7dee766523393784484bdf3e00114a1c857cde1aa2' + 322 | '03db65d61' 323 | }, 324 | { 325 | message: 'sample', 326 | hash: hash.sha512, 327 | r: 'c328fafcbd79dd77850370c46325d987cb525569fb63c5d3bc53950e6d4c5f1' + 328 | '74e25a1ee9017b5d450606add152b534931d7d4e8455cc91f9b15bf05ec36e3' + 329 | '77fa', 330 | s: '617cce7cf5064806c467f678d3b4080d6f1cc50af26ca209417308281b68af2' + 331 | '82623eaa63e5b5c0723d8b8c37ff0777b1a20f8ccb1dccc43997f1ee0e44da4' + 332 | 'a67a' 333 | }, 334 | { 335 | message: 'test', 336 | hash: hash.sha512, 337 | r: '13e99020abf5cee7525d16b69b229652ab6bdf2affcaef38773b4b7d087' + 338 | '25f10cdb93482fdcc54edcee91eca4166b2a7c6265ef0ce2bd7051b7cef945' + 339 | 'babd47ee6d', 340 | s: '1fbd0013c674aa79cb39849527916ce301c66ea7ce8b80682786ad60f98' + 341 | 'f7e78a19ca69eff5c57400e3b3a0ad66ce0978214d13baf4e9ac60752f7b15' + 342 | '5e2de4dce3' 343 | } 344 | ] 345 | }); 346 | }); 347 | 348 | describe('Maxwell\'s trick', function() { 349 | var p256 = elliptic.curves.p256; 350 | assert(p256); 351 | var p384 = elliptic.curves.p384; 352 | assert(p384); 353 | 354 | var msg = 355 | 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; 356 | 357 | var vectors = [ 358 | { 359 | curve: p256, 360 | pub: '041548fc88953e06cd34d4b300804c5322cb48c24aaaa4d0' + 361 | '7a541b0f0ccfeedeb0ae4991b90519ea405588bdf699f5e6' + 362 | 'd0c6b2d5217a5c16e8371062737aa1dae1', 363 | message: msg, 364 | sig: '3006020106020104', 365 | result: true 366 | }, 367 | { 368 | curve: p256, 369 | pub: '04ad8f60e4ec1ebdb6a260b559cb55b1e9d2c5ddd43a41a2' + 370 | 'd11b0741ef2567d84e166737664104ebbc337af3d861d352' + 371 | '4cfbc761c12edae974a0759750c8324f9a', 372 | message: msg, 373 | sig: '3006020106020104', 374 | result: true 375 | }, 376 | { 377 | curve: p256, 378 | pub: '0445bd879143a64af5746e2e82aa65fd2ea07bba4e355940' + 379 | '95a981b59984dacb219d59697387ac721b1f1eccf4b11f43' + 380 | 'ddc39e8367147abab3084142ed3ea170e4', 381 | message: msg, 382 | sig: '301502104319055358e8617b0c46353d039cdaae020104', 383 | result: true 384 | }, 385 | { 386 | curve: p256, 387 | pub: '040feb5df4cc78b35ec9c180cc0de5842f75f088b4845697' + 388 | '8ffa98e716d94883e1e6500b2a1f6c1d9d493428d7ae7d9a' + 389 | '8a560fff30a3d14aa160be0c5e7edcd887', 390 | message: msg, 391 | sig: '301502104319055358e8617b0c46353d039cdaae020104', 392 | result: false 393 | }, 394 | { 395 | curve: p384, 396 | pub: '0425e299eea9927b39fa92417705391bf17e8110b4615e9e' + 397 | 'b5da471b57be0c30e7d89dbdc3e5da4eae029b300344d385' + 398 | '1548b59ed8be668813905105e673319d59d32f574e180568' + 399 | '463c6186864888f6c0b67b304441f82aab031279e48f047c31', 400 | message: msg, 401 | sig: '3006020103020104', 402 | result: true 403 | }, 404 | { 405 | curve: p384, 406 | pub: '04a328f65c22307188b4af65779c1d2ec821c6748c6bd8dc' + 407 | '0e6a008135f048f832df501f7f3f79966b03d5bef2f187ec' + 408 | '34d85f6a934af465656fb4eea8dd9176ab80fbb4a27a649f' + 409 | '526a7dfe616091b78d293552bc093dfde9b31cae69d51d3afb', 410 | message: msg, 411 | sig: '3006020103020104', 412 | result: true 413 | }, 414 | { 415 | curve: p384, 416 | pub: '04242e8585eaa7a28cc6062cab4c9c5fd536f46b17be1728' + 417 | '288a2cda5951df4941aed1d712defda023d10aca1c5ee014' + 418 | '43e8beacd821f7efa27847418ab95ce2c514b2b6b395ee73' + 419 | '417c83dbcad631421f360d84d64658c98a62d685b220f5aad4', 420 | message: msg, 421 | sig: '301d0218389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68e020104', 422 | result: true 423 | }, 424 | { 425 | curve: p384, 426 | pub: '04cdf865dd743fe1c23757ec5e65fd5e4038b472ded2af26' + 427 | '1e3d8343c595c8b69147df46379c7ca40e60e80170d34a11' + 428 | '88dbb2b6f7d3934c23d2f78cfb0db3f3219959fad63c9b61' + 429 | '2ef2f20d679777b84192ce86e781c14b1bbb77eacd6e0520e2', 430 | message: msg, 431 | sig: '301d0218389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68e020104', 432 | result: false 433 | } 434 | ]; 435 | 436 | vectors.forEach(function(vector, i) { 437 | it('should pass on vector#' + i, function() { 438 | var ecdsa = new elliptic.ec(vector.curve); 439 | var key = ecdsa.keyFromPublic(vector.pub, 'hex'); 440 | var msg = vector.message; 441 | var sig = vector.sig; 442 | 443 | var actual = ecdsa.verify(msg, sig, key); 444 | assert.equal(actual, vector.result); 445 | }); 446 | }); 447 | }); 448 | 449 | it('should deterministically generate private key', function() { 450 | var curve = elliptic.curves.secp256k1; 451 | assert(curve); 452 | 453 | var ecdsa = new elliptic.ec(curve); 454 | var keys = ecdsa.genKeyPair({ 455 | pers: 'my.pers.string', 456 | entropy: hash.sha256().update('hello world').digest() 457 | }); 458 | assert.equal( 459 | keys.getPrivate('hex'), 460 | '6160edb2b218b7f1394b9ca8eb65a72831032a1f2f3dc2d99291c2f7950ed887'); 461 | }); 462 | 463 | it('should recover the public key from a signature', function() { 464 | var ec = new elliptic.ec('secp256k1'); 465 | var key = ec.genKeyPair(); 466 | var msg = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; 467 | var signature = key.sign(msg); 468 | var recid = ec.getKeyRecoveryParam(msg, signature, key.getPublic()); 469 | var r = ec.recoverPubKey(msg, signature, recid); 470 | assert(key.getPublic().eq(r), 'the keys should match'); 471 | }); 472 | 473 | it('should fail to recover key when no quadratic residue available', 474 | function() { 475 | var ec = new elliptic.ec('secp256k1'); 476 | 477 | var message = 478 | 'f75c6b18a72fabc0f0b888c3da58e004f0af1fe14f7ca5d8c897fe164925d5e9'; 479 | 480 | assert.throws(function() { 481 | ecdsa.recoverPubKey(message, { 482 | r: 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 483 | s: '8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3' 484 | }, 0); 485 | }); 486 | }); 487 | 488 | describe('Signature', function () { 489 | it('recoveryParam is 0', function () { 490 | var sig = new Signature({ r: '00', s: '00', recoveryParam: 0 }); 491 | assert.equal(sig.recoveryParam, 0); 492 | }); 493 | 494 | it('recoveryParam is 1', function () { 495 | var sig = new Signature({ r: '00', s: '00', recoveryParam: 1 }); 496 | assert.equal(sig.recoveryParam, 1); 497 | }); 498 | }); 499 | }); 500 | -------------------------------------------------------------------------------- /lib/elliptic/curve/short.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var curve = require('../curve'); 4 | var elliptic = require('../../elliptic'); 5 | var BN = require('bn.js'); 6 | var inherits = require('inherits'); 7 | var Base = curve.base; 8 | 9 | var assert = elliptic.utils.assert; 10 | 11 | function ShortCurve(conf) { 12 | Base.call(this, 'short', conf); 13 | 14 | this.a = new BN(conf.a, 16).toRed(this.red); 15 | this.b = new BN(conf.b, 16).toRed(this.red); 16 | this.tinv = this.two.redInvm(); 17 | 18 | this.zeroA = this.a.fromRed().cmpn(0) === 0; 19 | this.threeA = this.a.fromRed().sub(this.p).cmpn(-3) === 0; 20 | 21 | // If the curve is endomorphic, precalculate beta and lambda 22 | this.endo = this._getEndomorphism(conf); 23 | this._endoWnafT1 = new Array(4); 24 | this._endoWnafT2 = new Array(4); 25 | } 26 | inherits(ShortCurve, Base); 27 | module.exports = ShortCurve; 28 | 29 | ShortCurve.prototype._getEndomorphism = function _getEndomorphism(conf) { 30 | // No efficient endomorphism 31 | if (!this.zeroA || !this.g || !this.n || this.p.modn(3) !== 1) 32 | return; 33 | 34 | // Compute beta and lambda, that lambda * P = (beta * Px; Py) 35 | var beta; 36 | var lambda; 37 | if (conf.beta) { 38 | beta = new BN(conf.beta, 16).toRed(this.red); 39 | } else { 40 | var betas = this._getEndoRoots(this.p); 41 | // Choose the smallest beta 42 | beta = betas[0].cmp(betas[1]) < 0 ? betas[0] : betas[1]; 43 | beta = beta.toRed(this.red); 44 | } 45 | if (conf.lambda) { 46 | lambda = new BN(conf.lambda, 16); 47 | } else { 48 | // Choose the lambda that is matching selected beta 49 | var lambdas = this._getEndoRoots(this.n); 50 | if (this.g.mul(lambdas[0]).x.cmp(this.g.x.redMul(beta)) === 0) { 51 | lambda = lambdas[0]; 52 | } else { 53 | lambda = lambdas[1]; 54 | assert(this.g.mul(lambda).x.cmp(this.g.x.redMul(beta)) === 0); 55 | } 56 | } 57 | 58 | // Get basis vectors, used for balanced length-two representation 59 | var basis; 60 | if (conf.basis) { 61 | basis = conf.basis.map(function(vec) { 62 | return { 63 | a: new BN(vec.a, 16), 64 | b: new BN(vec.b, 16) 65 | }; 66 | }); 67 | } else { 68 | basis = this._getEndoBasis(lambda); 69 | } 70 | 71 | return { 72 | beta: beta, 73 | lambda: lambda, 74 | basis: basis 75 | }; 76 | }; 77 | 78 | ShortCurve.prototype._getEndoRoots = function _getEndoRoots(num) { 79 | // Find roots of for x^2 + x + 1 in F 80 | // Root = (-1 +- Sqrt(-3)) / 2 81 | // 82 | var red = num === this.p ? this.red : BN.mont(num); 83 | var tinv = new BN(2).toRed(red).redInvm(); 84 | var ntinv = tinv.redNeg(); 85 | 86 | var s = new BN(3).toRed(red).redNeg().redSqrt().redMul(tinv); 87 | 88 | var l1 = ntinv.redAdd(s).fromRed(); 89 | var l2 = ntinv.redSub(s).fromRed(); 90 | return [ l1, l2 ]; 91 | }; 92 | 93 | ShortCurve.prototype._getEndoBasis = function _getEndoBasis(lambda) { 94 | // aprxSqrt >= sqrt(this.n) 95 | var aprxSqrt = this.n.ushrn(Math.floor(this.n.bitLength() / 2)); 96 | 97 | // 3.74 98 | // Run EGCD, until r(L + 1) < aprxSqrt 99 | var u = lambda; 100 | var v = this.n.clone(); 101 | var x1 = new BN(1); 102 | var y1 = new BN(0); 103 | var x2 = new BN(0); 104 | var y2 = new BN(1); 105 | 106 | // NOTE: all vectors are roots of: a + b * lambda = 0 (mod n) 107 | var a0; 108 | var b0; 109 | // First vector 110 | var a1; 111 | var b1; 112 | // Second vector 113 | var a2; 114 | var b2; 115 | 116 | var prevR; 117 | var i = 0; 118 | var r; 119 | var x; 120 | while (u.cmpn(0) !== 0) { 121 | var q = v.div(u); 122 | r = v.sub(q.mul(u)); 123 | x = x2.sub(q.mul(x1)); 124 | var y = y2.sub(q.mul(y1)); 125 | 126 | if (!a1 && r.cmp(aprxSqrt) < 0) { 127 | a0 = prevR.neg(); 128 | b0 = x1; 129 | a1 = r.neg(); 130 | b1 = x; 131 | } else if (a1 && ++i === 2) { 132 | break; 133 | } 134 | prevR = r; 135 | 136 | v = u; 137 | u = r; 138 | x2 = x1; 139 | x1 = x; 140 | y2 = y1; 141 | y1 = y; 142 | } 143 | a2 = r.neg(); 144 | b2 = x; 145 | 146 | var len1 = a1.sqr().add(b1.sqr()); 147 | var len2 = a2.sqr().add(b2.sqr()); 148 | if (len2.cmp(len1) >= 0) { 149 | a2 = a0; 150 | b2 = b0; 151 | } 152 | 153 | // Normalize signs 154 | if (a1.negative) { 155 | a1 = a1.neg(); 156 | b1 = b1.neg(); 157 | } 158 | if (a2.negative) { 159 | a2 = a2.neg(); 160 | b2 = b2.neg(); 161 | } 162 | 163 | return [ 164 | { a: a1, b: b1 }, 165 | { a: a2, b: b2 } 166 | ]; 167 | }; 168 | 169 | ShortCurve.prototype._endoSplit = function _endoSplit(k) { 170 | var basis = this.endo.basis; 171 | var v1 = basis[0]; 172 | var v2 = basis[1]; 173 | 174 | var c1 = v2.b.mul(k).divRound(this.n); 175 | var c2 = v1.b.neg().mul(k).divRound(this.n); 176 | 177 | var p1 = c1.mul(v1.a); 178 | var p2 = c2.mul(v2.a); 179 | var q1 = c1.mul(v1.b); 180 | var q2 = c2.mul(v2.b); 181 | 182 | // Calculate answer 183 | var k1 = k.sub(p1).sub(p2); 184 | var k2 = q1.add(q2).neg(); 185 | return { k1: k1, k2: k2 }; 186 | }; 187 | 188 | ShortCurve.prototype.pointFromX = function pointFromX(x, odd) { 189 | x = new BN(x, 16); 190 | if (!x.red) 191 | x = x.toRed(this.red); 192 | 193 | var y2 = x.redSqr().redMul(x).redIAdd(x.redMul(this.a)).redIAdd(this.b); 194 | var y = y2.redSqrt(); 195 | if (y.redSqr().redSub(y2).cmp(this.zero) !== 0) 196 | throw new Error('invalid point'); 197 | 198 | // XXX Is there any way to tell if the number is odd without converting it 199 | // to non-red form? 200 | var isOdd = y.fromRed().isOdd(); 201 | if (odd && !isOdd || !odd && isOdd) 202 | y = y.redNeg(); 203 | 204 | return this.point(x, y); 205 | }; 206 | 207 | ShortCurve.prototype.validate = function validate(point) { 208 | if (point.inf) 209 | return true; 210 | 211 | var x = point.x; 212 | var y = point.y; 213 | 214 | var ax = this.a.redMul(x); 215 | var rhs = x.redSqr().redMul(x).redIAdd(ax).redIAdd(this.b); 216 | return y.redSqr().redISub(rhs).cmpn(0) === 0; 217 | }; 218 | 219 | ShortCurve.prototype._endoWnafMulAdd = 220 | function _endoWnafMulAdd(points, coeffs, jacobianResult) { 221 | var npoints = this._endoWnafT1; 222 | var ncoeffs = this._endoWnafT2; 223 | for (var i = 0; i < points.length; i++) { 224 | var split = this._endoSplit(coeffs[i]); 225 | var p = points[i]; 226 | var beta = p._getBeta(); 227 | 228 | if (split.k1.negative) { 229 | split.k1.ineg(); 230 | p = p.neg(true); 231 | } 232 | if (split.k2.negative) { 233 | split.k2.ineg(); 234 | beta = beta.neg(true); 235 | } 236 | 237 | npoints[i * 2] = p; 238 | npoints[i * 2 + 1] = beta; 239 | ncoeffs[i * 2] = split.k1; 240 | ncoeffs[i * 2 + 1] = split.k2; 241 | } 242 | var res = this._wnafMulAdd(1, npoints, ncoeffs, i * 2, jacobianResult); 243 | 244 | // Clean-up references to points and coefficients 245 | for (var j = 0; j < i * 2; j++) { 246 | npoints[j] = null; 247 | ncoeffs[j] = null; 248 | } 249 | return res; 250 | }; 251 | 252 | function Point(curve, x, y, isRed) { 253 | Base.BasePoint.call(this, curve, 'affine'); 254 | if (x === null && y === null) { 255 | this.x = null; 256 | this.y = null; 257 | this.inf = true; 258 | } else { 259 | this.x = new BN(x, 16); 260 | this.y = new BN(y, 16); 261 | // Force redgomery representation when loading from JSON 262 | if (isRed) { 263 | this.x.forceRed(this.curve.red); 264 | this.y.forceRed(this.curve.red); 265 | } 266 | if (!this.x.red) 267 | this.x = this.x.toRed(this.curve.red); 268 | if (!this.y.red) 269 | this.y = this.y.toRed(this.curve.red); 270 | this.inf = false; 271 | } 272 | } 273 | inherits(Point, Base.BasePoint); 274 | 275 | ShortCurve.prototype.point = function point(x, y, isRed) { 276 | return new Point(this, x, y, isRed); 277 | }; 278 | 279 | ShortCurve.prototype.pointFromJSON = function pointFromJSON(obj, red) { 280 | return Point.fromJSON(this, obj, red); 281 | }; 282 | 283 | Point.prototype._getBeta = function _getBeta() { 284 | if (!this.curve.endo) 285 | return; 286 | 287 | var pre = this.precomputed; 288 | if (pre && pre.beta) 289 | return pre.beta; 290 | 291 | var beta = this.curve.point(this.x.redMul(this.curve.endo.beta), this.y); 292 | if (pre) { 293 | var curve = this.curve; 294 | var endoMul = function(p) { 295 | return curve.point(p.x.redMul(curve.endo.beta), p.y); 296 | }; 297 | pre.beta = beta; 298 | beta.precomputed = { 299 | beta: null, 300 | naf: pre.naf && { 301 | wnd: pre.naf.wnd, 302 | points: pre.naf.points.map(endoMul) 303 | }, 304 | doubles: pre.doubles && { 305 | step: pre.doubles.step, 306 | points: pre.doubles.points.map(endoMul) 307 | } 308 | }; 309 | } 310 | return beta; 311 | }; 312 | 313 | Point.prototype.toJSON = function toJSON() { 314 | if (!this.precomputed) 315 | return [ this.x, this.y ]; 316 | 317 | return [ this.x, this.y, this.precomputed && { 318 | doubles: this.precomputed.doubles && { 319 | step: this.precomputed.doubles.step, 320 | points: this.precomputed.doubles.points.slice(1) 321 | }, 322 | naf: this.precomputed.naf && { 323 | wnd: this.precomputed.naf.wnd, 324 | points: this.precomputed.naf.points.slice(1) 325 | } 326 | } ]; 327 | }; 328 | 329 | Point.fromJSON = function fromJSON(curve, obj, red) { 330 | if (typeof obj === 'string') 331 | obj = JSON.parse(obj); 332 | var res = curve.point(obj[0], obj[1], red); 333 | if (!obj[2]) 334 | return res; 335 | 336 | function obj2point(obj) { 337 | return curve.point(obj[0], obj[1], red); 338 | } 339 | 340 | var pre = obj[2]; 341 | res.precomputed = { 342 | beta: null, 343 | doubles: pre.doubles && { 344 | step: pre.doubles.step, 345 | points: [ res ].concat(pre.doubles.points.map(obj2point)) 346 | }, 347 | naf: pre.naf && { 348 | wnd: pre.naf.wnd, 349 | points: [ res ].concat(pre.naf.points.map(obj2point)) 350 | } 351 | }; 352 | return res; 353 | }; 354 | 355 | Point.prototype.inspect = function inspect() { 356 | if (this.isInfinity()) 357 | return ''; 358 | return ''; 360 | }; 361 | 362 | Point.prototype.isInfinity = function isInfinity() { 363 | return this.inf; 364 | }; 365 | 366 | Point.prototype.add = function add(p) { 367 | // O + P = P 368 | if (this.inf) 369 | return p; 370 | 371 | // P + O = P 372 | if (p.inf) 373 | return this; 374 | 375 | // P + P = 2P 376 | if (this.eq(p)) 377 | return this.dbl(); 378 | 379 | // P + (-P) = O 380 | if (this.neg().eq(p)) 381 | return this.curve.point(null, null); 382 | 383 | // P + Q = O 384 | if (this.x.cmp(p.x) === 0) 385 | return this.curve.point(null, null); 386 | 387 | var c = this.y.redSub(p.y); 388 | if (c.cmpn(0) !== 0) 389 | c = c.redMul(this.x.redSub(p.x).redInvm()); 390 | var nx = c.redSqr().redISub(this.x).redISub(p.x); 391 | var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); 392 | return this.curve.point(nx, ny); 393 | }; 394 | 395 | Point.prototype.dbl = function dbl() { 396 | if (this.inf) 397 | return this; 398 | 399 | // 2P = O 400 | var ys1 = this.y.redAdd(this.y); 401 | if (ys1.cmpn(0) === 0) 402 | return this.curve.point(null, null); 403 | 404 | var a = this.curve.a; 405 | 406 | var x2 = this.x.redSqr(); 407 | var dyinv = ys1.redInvm(); 408 | var c = x2.redAdd(x2).redIAdd(x2).redIAdd(a).redMul(dyinv); 409 | 410 | var nx = c.redSqr().redISub(this.x.redAdd(this.x)); 411 | var ny = c.redMul(this.x.redSub(nx)).redISub(this.y); 412 | return this.curve.point(nx, ny); 413 | }; 414 | 415 | Point.prototype.getX = function getX() { 416 | return this.x.fromRed(); 417 | }; 418 | 419 | Point.prototype.getY = function getY() { 420 | return this.y.fromRed(); 421 | }; 422 | 423 | Point.prototype.mul = function mul(k) { 424 | k = new BN(k, 16); 425 | 426 | if (this._hasDoubles(k)) 427 | return this.curve._fixedNafMul(this, k); 428 | else if (this.curve.endo) 429 | return this.curve._endoWnafMulAdd([ this ], [ k ]); 430 | else 431 | return this.curve._wnafMul(this, k); 432 | }; 433 | 434 | Point.prototype.mulAdd = function mulAdd(k1, p2, k2) { 435 | var points = [ this, p2 ]; 436 | var coeffs = [ k1, k2 ]; 437 | if (this.curve.endo) 438 | return this.curve._endoWnafMulAdd(points, coeffs); 439 | else 440 | return this.curve._wnafMulAdd(1, points, coeffs, 2); 441 | }; 442 | 443 | Point.prototype.jmulAdd = function jmulAdd(k1, p2, k2) { 444 | var points = [ this, p2 ]; 445 | var coeffs = [ k1, k2 ]; 446 | if (this.curve.endo) 447 | return this.curve._endoWnafMulAdd(points, coeffs, true); 448 | else 449 | return this.curve._wnafMulAdd(1, points, coeffs, 2, true); 450 | }; 451 | 452 | Point.prototype.eq = function eq(p) { 453 | return this === p || 454 | this.inf === p.inf && 455 | (this.inf || this.x.cmp(p.x) === 0 && this.y.cmp(p.y) === 0); 456 | }; 457 | 458 | Point.prototype.neg = function neg(_precompute) { 459 | if (this.inf) 460 | return this; 461 | 462 | var res = this.curve.point(this.x, this.y.redNeg()); 463 | if (_precompute && this.precomputed) { 464 | var pre = this.precomputed; 465 | var negate = function(p) { 466 | return p.neg(); 467 | }; 468 | res.precomputed = { 469 | naf: pre.naf && { 470 | wnd: pre.naf.wnd, 471 | points: pre.naf.points.map(negate) 472 | }, 473 | doubles: pre.doubles && { 474 | step: pre.doubles.step, 475 | points: pre.doubles.points.map(negate) 476 | } 477 | }; 478 | } 479 | return res; 480 | }; 481 | 482 | Point.prototype.toJ = function toJ() { 483 | if (this.inf) 484 | return this.curve.jpoint(null, null, null); 485 | 486 | var res = this.curve.jpoint(this.x, this.y, this.curve.one); 487 | return res; 488 | }; 489 | 490 | function JPoint(curve, x, y, z) { 491 | Base.BasePoint.call(this, curve, 'jacobian'); 492 | if (x === null && y === null && z === null) { 493 | this.x = this.curve.one; 494 | this.y = this.curve.one; 495 | this.z = new BN(0); 496 | } else { 497 | this.x = new BN(x, 16); 498 | this.y = new BN(y, 16); 499 | this.z = new BN(z, 16); 500 | } 501 | if (!this.x.red) 502 | this.x = this.x.toRed(this.curve.red); 503 | if (!this.y.red) 504 | this.y = this.y.toRed(this.curve.red); 505 | if (!this.z.red) 506 | this.z = this.z.toRed(this.curve.red); 507 | 508 | this.zOne = this.z === this.curve.one; 509 | } 510 | inherits(JPoint, Base.BasePoint); 511 | 512 | ShortCurve.prototype.jpoint = function jpoint(x, y, z) { 513 | return new JPoint(this, x, y, z); 514 | }; 515 | 516 | JPoint.prototype.toP = function toP() { 517 | if (this.isInfinity()) 518 | return this.curve.point(null, null); 519 | 520 | var zinv = this.z.redInvm(); 521 | var zinv2 = zinv.redSqr(); 522 | var ax = this.x.redMul(zinv2); 523 | var ay = this.y.redMul(zinv2).redMul(zinv); 524 | 525 | return this.curve.point(ax, ay); 526 | }; 527 | 528 | JPoint.prototype.neg = function neg() { 529 | return this.curve.jpoint(this.x, this.y.redNeg(), this.z); 530 | }; 531 | 532 | JPoint.prototype.add = function add(p) { 533 | // O + P = P 534 | if (this.isInfinity()) 535 | return p; 536 | 537 | // P + O = P 538 | if (p.isInfinity()) 539 | return this; 540 | 541 | // 12M + 4S + 7A 542 | var pz2 = p.z.redSqr(); 543 | var z2 = this.z.redSqr(); 544 | var u1 = this.x.redMul(pz2); 545 | var u2 = p.x.redMul(z2); 546 | var s1 = this.y.redMul(pz2.redMul(p.z)); 547 | var s2 = p.y.redMul(z2.redMul(this.z)); 548 | 549 | var h = u1.redSub(u2); 550 | var r = s1.redSub(s2); 551 | if (h.cmpn(0) === 0) { 552 | if (r.cmpn(0) !== 0) 553 | return this.curve.jpoint(null, null, null); 554 | else 555 | return this.dbl(); 556 | } 557 | 558 | var h2 = h.redSqr(); 559 | var h3 = h2.redMul(h); 560 | var v = u1.redMul(h2); 561 | 562 | var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); 563 | var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); 564 | var nz = this.z.redMul(p.z).redMul(h); 565 | 566 | return this.curve.jpoint(nx, ny, nz); 567 | }; 568 | 569 | JPoint.prototype.mixedAdd = function mixedAdd(p) { 570 | // O + P = P 571 | if (this.isInfinity()) 572 | return p.toJ(); 573 | 574 | // P + O = P 575 | if (p.isInfinity()) 576 | return this; 577 | 578 | // 8M + 3S + 7A 579 | var z2 = this.z.redSqr(); 580 | var u1 = this.x; 581 | var u2 = p.x.redMul(z2); 582 | var s1 = this.y; 583 | var s2 = p.y.redMul(z2).redMul(this.z); 584 | 585 | var h = u1.redSub(u2); 586 | var r = s1.redSub(s2); 587 | if (h.cmpn(0) === 0) { 588 | if (r.cmpn(0) !== 0) 589 | return this.curve.jpoint(null, null, null); 590 | else 591 | return this.dbl(); 592 | } 593 | 594 | var h2 = h.redSqr(); 595 | var h3 = h2.redMul(h); 596 | var v = u1.redMul(h2); 597 | 598 | var nx = r.redSqr().redIAdd(h3).redISub(v).redISub(v); 599 | var ny = r.redMul(v.redISub(nx)).redISub(s1.redMul(h3)); 600 | var nz = this.z.redMul(h); 601 | 602 | return this.curve.jpoint(nx, ny, nz); 603 | }; 604 | 605 | JPoint.prototype.dblp = function dblp(pow) { 606 | if (pow === 0) 607 | return this; 608 | if (this.isInfinity()) 609 | return this; 610 | if (!pow) 611 | return this.dbl(); 612 | 613 | if (this.curve.zeroA || this.curve.threeA) { 614 | var r = this; 615 | for (var i = 0; i < pow; i++) 616 | r = r.dbl(); 617 | return r; 618 | } 619 | 620 | // 1M + 2S + 1A + N * (4S + 5M + 8A) 621 | // N = 1 => 6M + 6S + 9A 622 | var a = this.curve.a; 623 | var tinv = this.curve.tinv; 624 | 625 | var jx = this.x; 626 | var jy = this.y; 627 | var jz = this.z; 628 | var jz4 = jz.redSqr().redSqr(); 629 | 630 | // Reuse results 631 | var jyd = jy.redAdd(jy); 632 | for (var i = 0; i < pow; i++) { 633 | var jx2 = jx.redSqr(); 634 | var jyd2 = jyd.redSqr(); 635 | var jyd4 = jyd2.redSqr(); 636 | var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); 637 | 638 | var t1 = jx.redMul(jyd2); 639 | var nx = c.redSqr().redISub(t1.redAdd(t1)); 640 | var t2 = t1.redISub(nx); 641 | var dny = c.redMul(t2); 642 | dny = dny.redIAdd(dny).redISub(jyd4); 643 | var nz = jyd.redMul(jz); 644 | if (i + 1 < pow) 645 | jz4 = jz4.redMul(jyd4); 646 | 647 | jx = nx; 648 | jz = nz; 649 | jyd = dny; 650 | } 651 | 652 | return this.curve.jpoint(jx, jyd.redMul(tinv), jz); 653 | }; 654 | 655 | JPoint.prototype.dbl = function dbl() { 656 | if (this.isInfinity()) 657 | return this; 658 | 659 | if (this.curve.zeroA) 660 | return this._zeroDbl(); 661 | else if (this.curve.threeA) 662 | return this._threeDbl(); 663 | else 664 | return this._dbl(); 665 | }; 666 | 667 | JPoint.prototype._zeroDbl = function _zeroDbl() { 668 | var nx; 669 | var ny; 670 | var nz; 671 | // Z = 1 672 | if (this.zOne) { 673 | // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html 674 | // #doubling-mdbl-2007-bl 675 | // 1M + 5S + 14A 676 | 677 | // XX = X1^2 678 | var xx = this.x.redSqr(); 679 | // YY = Y1^2 680 | var yy = this.y.redSqr(); 681 | // YYYY = YY^2 682 | var yyyy = yy.redSqr(); 683 | // S = 2 * ((X1 + YY)^2 - XX - YYYY) 684 | var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); 685 | s = s.redIAdd(s); 686 | // M = 3 * XX + a; a = 0 687 | var m = xx.redAdd(xx).redIAdd(xx); 688 | // T = M ^ 2 - 2*S 689 | var t = m.redSqr().redISub(s).redISub(s); 690 | 691 | // 8 * YYYY 692 | var yyyy8 = yyyy.redIAdd(yyyy); 693 | yyyy8 = yyyy8.redIAdd(yyyy8); 694 | yyyy8 = yyyy8.redIAdd(yyyy8); 695 | 696 | // X3 = T 697 | nx = t; 698 | // Y3 = M * (S - T) - 8 * YYYY 699 | ny = m.redMul(s.redISub(t)).redISub(yyyy8); 700 | // Z3 = 2*Y1 701 | nz = this.y.redAdd(this.y); 702 | } else { 703 | // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html 704 | // #doubling-dbl-2009-l 705 | // 2M + 5S + 13A 706 | 707 | // A = X1^2 708 | var a = this.x.redSqr(); 709 | // B = Y1^2 710 | var b = this.y.redSqr(); 711 | // C = B^2 712 | var c = b.redSqr(); 713 | // D = 2 * ((X1 + B)^2 - A - C) 714 | var d = this.x.redAdd(b).redSqr().redISub(a).redISub(c); 715 | d = d.redIAdd(d); 716 | // E = 3 * A 717 | var e = a.redAdd(a).redIAdd(a); 718 | // F = E^2 719 | var f = e.redSqr(); 720 | 721 | // 8 * C 722 | var c8 = c.redIAdd(c); 723 | c8 = c8.redIAdd(c8); 724 | c8 = c8.redIAdd(c8); 725 | 726 | // X3 = F - 2 * D 727 | nx = f.redISub(d).redISub(d); 728 | // Y3 = E * (D - X3) - 8 * C 729 | ny = e.redMul(d.redISub(nx)).redISub(c8); 730 | // Z3 = 2 * Y1 * Z1 731 | nz = this.y.redMul(this.z); 732 | nz = nz.redIAdd(nz); 733 | } 734 | 735 | return this.curve.jpoint(nx, ny, nz); 736 | }; 737 | 738 | JPoint.prototype._threeDbl = function _threeDbl() { 739 | var nx; 740 | var ny; 741 | var nz; 742 | // Z = 1 743 | if (this.zOne) { 744 | // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html 745 | // #doubling-mdbl-2007-bl 746 | // 1M + 5S + 15A 747 | 748 | // XX = X1^2 749 | var xx = this.x.redSqr(); 750 | // YY = Y1^2 751 | var yy = this.y.redSqr(); 752 | // YYYY = YY^2 753 | var yyyy = yy.redSqr(); 754 | // S = 2 * ((X1 + YY)^2 - XX - YYYY) 755 | var s = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); 756 | s = s.redIAdd(s); 757 | // M = 3 * XX + a 758 | var m = xx.redAdd(xx).redIAdd(xx).redIAdd(this.curve.a); 759 | // T = M^2 - 2 * S 760 | var t = m.redSqr().redISub(s).redISub(s); 761 | // X3 = T 762 | nx = t; 763 | // Y3 = M * (S - T) - 8 * YYYY 764 | var yyyy8 = yyyy.redIAdd(yyyy); 765 | yyyy8 = yyyy8.redIAdd(yyyy8); 766 | yyyy8 = yyyy8.redIAdd(yyyy8); 767 | ny = m.redMul(s.redISub(t)).redISub(yyyy8); 768 | // Z3 = 2 * Y1 769 | nz = this.y.redAdd(this.y); 770 | } else { 771 | // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b 772 | // 3M + 5S 773 | 774 | // delta = Z1^2 775 | var delta = this.z.redSqr(); 776 | // gamma = Y1^2 777 | var gamma = this.y.redSqr(); 778 | // beta = X1 * gamma 779 | var beta = this.x.redMul(gamma); 780 | // alpha = 3 * (X1 - delta) * (X1 + delta) 781 | var alpha = this.x.redSub(delta).redMul(this.x.redAdd(delta)); 782 | alpha = alpha.redAdd(alpha).redIAdd(alpha); 783 | // X3 = alpha^2 - 8 * beta 784 | var beta4 = beta.redIAdd(beta); 785 | beta4 = beta4.redIAdd(beta4); 786 | var beta8 = beta4.redAdd(beta4); 787 | nx = alpha.redSqr().redISub(beta8); 788 | // Z3 = (Y1 + Z1)^2 - gamma - delta 789 | nz = this.y.redAdd(this.z).redSqr().redISub(gamma).redISub(delta); 790 | // Y3 = alpha * (4 * beta - X3) - 8 * gamma^2 791 | var ggamma8 = gamma.redSqr(); 792 | ggamma8 = ggamma8.redIAdd(ggamma8); 793 | ggamma8 = ggamma8.redIAdd(ggamma8); 794 | ggamma8 = ggamma8.redIAdd(ggamma8); 795 | ny = alpha.redMul(beta4.redISub(nx)).redISub(ggamma8); 796 | } 797 | 798 | return this.curve.jpoint(nx, ny, nz); 799 | }; 800 | 801 | JPoint.prototype._dbl = function _dbl() { 802 | var a = this.curve.a; 803 | 804 | // 4M + 6S + 10A 805 | var jx = this.x; 806 | var jy = this.y; 807 | var jz = this.z; 808 | var jz4 = jz.redSqr().redSqr(); 809 | 810 | var jx2 = jx.redSqr(); 811 | var jy2 = jy.redSqr(); 812 | 813 | var c = jx2.redAdd(jx2).redIAdd(jx2).redIAdd(a.redMul(jz4)); 814 | 815 | var jxd4 = jx.redAdd(jx); 816 | jxd4 = jxd4.redIAdd(jxd4); 817 | var t1 = jxd4.redMul(jy2); 818 | var nx = c.redSqr().redISub(t1.redAdd(t1)); 819 | var t2 = t1.redISub(nx); 820 | 821 | var jyd8 = jy2.redSqr(); 822 | jyd8 = jyd8.redIAdd(jyd8); 823 | jyd8 = jyd8.redIAdd(jyd8); 824 | jyd8 = jyd8.redIAdd(jyd8); 825 | var ny = c.redMul(t2).redISub(jyd8); 826 | var nz = jy.redAdd(jy).redMul(jz); 827 | 828 | return this.curve.jpoint(nx, ny, nz); 829 | }; 830 | 831 | JPoint.prototype.trpl = function trpl() { 832 | if (!this.curve.zeroA) 833 | return this.dbl().add(this); 834 | 835 | // hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#tripling-tpl-2007-bl 836 | // 5M + 10S + ... 837 | 838 | // XX = X1^2 839 | var xx = this.x.redSqr(); 840 | // YY = Y1^2 841 | var yy = this.y.redSqr(); 842 | // ZZ = Z1^2 843 | var zz = this.z.redSqr(); 844 | // YYYY = YY^2 845 | var yyyy = yy.redSqr(); 846 | // M = 3 * XX + a * ZZ2; a = 0 847 | var m = xx.redAdd(xx).redIAdd(xx); 848 | // MM = M^2 849 | var mm = m.redSqr(); 850 | // E = 6 * ((X1 + YY)^2 - XX - YYYY) - MM 851 | var e = this.x.redAdd(yy).redSqr().redISub(xx).redISub(yyyy); 852 | e = e.redIAdd(e); 853 | e = e.redAdd(e).redIAdd(e); 854 | e = e.redISub(mm); 855 | // EE = E^2 856 | var ee = e.redSqr(); 857 | // T = 16*YYYY 858 | var t = yyyy.redIAdd(yyyy); 859 | t = t.redIAdd(t); 860 | t = t.redIAdd(t); 861 | t = t.redIAdd(t); 862 | // U = (M + E)^2 - MM - EE - T 863 | var u = m.redIAdd(e).redSqr().redISub(mm).redISub(ee).redISub(t); 864 | // X3 = 4 * (X1 * EE - 4 * YY * U) 865 | var yyu4 = yy.redMul(u); 866 | yyu4 = yyu4.redIAdd(yyu4); 867 | yyu4 = yyu4.redIAdd(yyu4); 868 | var nx = this.x.redMul(ee).redISub(yyu4); 869 | nx = nx.redIAdd(nx); 870 | nx = nx.redIAdd(nx); 871 | // Y3 = 8 * Y1 * (U * (T - U) - E * EE) 872 | var ny = this.y.redMul(u.redMul(t.redISub(u)).redISub(e.redMul(ee))); 873 | ny = ny.redIAdd(ny); 874 | ny = ny.redIAdd(ny); 875 | ny = ny.redIAdd(ny); 876 | // Z3 = (Z1 + E)^2 - ZZ - EE 877 | var nz = this.z.redAdd(e).redSqr().redISub(zz).redISub(ee); 878 | 879 | return this.curve.jpoint(nx, ny, nz); 880 | }; 881 | 882 | JPoint.prototype.mul = function mul(k, kbase) { 883 | k = new BN(k, kbase); 884 | 885 | return this.curve._wnafMul(this, k); 886 | }; 887 | 888 | JPoint.prototype.eq = function eq(p) { 889 | if (p.type === 'affine') 890 | return this.eq(p.toJ()); 891 | 892 | if (this === p) 893 | return true; 894 | 895 | // x1 * z2^2 == x2 * z1^2 896 | var z2 = this.z.redSqr(); 897 | var pz2 = p.z.redSqr(); 898 | if (this.x.redMul(pz2).redISub(p.x.redMul(z2)).cmpn(0) !== 0) 899 | return false; 900 | 901 | // y1 * z2^3 == y2 * z1^3 902 | var z3 = z2.redMul(this.z); 903 | var pz3 = pz2.redMul(p.z); 904 | return this.y.redMul(pz3).redISub(p.y.redMul(z3)).cmpn(0) === 0; 905 | }; 906 | 907 | JPoint.prototype.eqXToP = function eqXToP(x) { 908 | var zs = this.z.redSqr(); 909 | var rx = x.toRed(this.curve.red).redMul(zs); 910 | if (this.x.cmp(rx) === 0) 911 | return true; 912 | 913 | var xc = x.clone(); 914 | var t = this.curve.redN.redMul(zs); 915 | for (;;) { 916 | xc.iadd(this.curve.n); 917 | if (xc.cmp(this.curve.p) >= 0) 918 | return false; 919 | 920 | rx.redIAdd(t); 921 | if (this.x.cmp(rx) === 0) 922 | return true; 923 | } 924 | }; 925 | 926 | JPoint.prototype.inspect = function inspect() { 927 | if (this.isInfinity()) 928 | return ''; 929 | return ''; 932 | }; 933 | 934 | JPoint.prototype.isInfinity = function isInfinity() { 935 | // XXX This code assumes that zero is always zero in red 936 | return this.z.cmpn(0) === 0; 937 | }; 938 | -------------------------------------------------------------------------------- /lib/elliptic/precomputed/secp256k1.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | doubles: { 3 | step: 4, 4 | points: [ 5 | [ 6 | 'e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a', 7 | 'f7e3507399e595929db99f34f57937101296891e44d23f0be1f32cce69616821' 8 | ], 9 | [ 10 | '8282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508', 11 | '11f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf' 12 | ], 13 | [ 14 | '175e159f728b865a72f99cc6c6fc846de0b93833fd2222ed73fce5b551e5b739', 15 | 'd3506e0d9e3c79eba4ef97a51ff71f5eacb5955add24345c6efa6ffee9fed695' 16 | ], 17 | [ 18 | '363d90d447b00c9c99ceac05b6262ee053441c7e55552ffe526bad8f83ff4640', 19 | '4e273adfc732221953b445397f3363145b9a89008199ecb62003c7f3bee9de9' 20 | ], 21 | [ 22 | '8b4b5f165df3c2be8c6244b5b745638843e4a781a15bcd1b69f79a55dffdf80c', 23 | '4aad0a6f68d308b4b3fbd7813ab0da04f9e336546162ee56b3eff0c65fd4fd36' 24 | ], 25 | [ 26 | '723cbaa6e5db996d6bf771c00bd548c7b700dbffa6c0e77bcb6115925232fcda', 27 | '96e867b5595cc498a921137488824d6e2660a0653779494801dc069d9eb39f5f' 28 | ], 29 | [ 30 | 'eebfa4d493bebf98ba5feec812c2d3b50947961237a919839a533eca0e7dd7fa', 31 | '5d9a8ca3970ef0f269ee7edaf178089d9ae4cdc3a711f712ddfd4fdae1de8999' 32 | ], 33 | [ 34 | '100f44da696e71672791d0a09b7bde459f1215a29b3c03bfefd7835b39a48db0', 35 | 'cdd9e13192a00b772ec8f3300c090666b7ff4a18ff5195ac0fbd5cd62bc65a09' 36 | ], 37 | [ 38 | 'e1031be262c7ed1b1dc9227a4a04c017a77f8d4464f3b3852c8acde6e534fd2d', 39 | '9d7061928940405e6bb6a4176597535af292dd419e1ced79a44f18f29456a00d' 40 | ], 41 | [ 42 | 'feea6cae46d55b530ac2839f143bd7ec5cf8b266a41d6af52d5e688d9094696d', 43 | 'e57c6b6c97dce1bab06e4e12bf3ecd5c981c8957cc41442d3155debf18090088' 44 | ], 45 | [ 46 | 'da67a91d91049cdcb367be4be6ffca3cfeed657d808583de33fa978bc1ec6cb1', 47 | '9bacaa35481642bc41f463f7ec9780e5dec7adc508f740a17e9ea8e27a68be1d' 48 | ], 49 | [ 50 | '53904faa0b334cdda6e000935ef22151ec08d0f7bb11069f57545ccc1a37b7c0', 51 | '5bc087d0bc80106d88c9eccac20d3c1c13999981e14434699dcb096b022771c8' 52 | ], 53 | [ 54 | '8e7bcd0bd35983a7719cca7764ca906779b53a043a9b8bcaeff959f43ad86047', 55 | '10b7770b2a3da4b3940310420ca9514579e88e2e47fd68b3ea10047e8460372a' 56 | ], 57 | [ 58 | '385eed34c1cdff21e6d0818689b81bde71a7f4f18397e6690a841e1599c43862', 59 | '283bebc3e8ea23f56701de19e9ebf4576b304eec2086dc8cc0458fe5542e5453' 60 | ], 61 | [ 62 | '6f9d9b803ecf191637c73a4413dfa180fddf84a5947fbc9c606ed86c3fac3a7', 63 | '7c80c68e603059ba69b8e2a30e45c4d47ea4dd2f5c281002d86890603a842160' 64 | ], 65 | [ 66 | '3322d401243c4e2582a2147c104d6ecbf774d163db0f5e5313b7e0e742d0e6bd', 67 | '56e70797e9664ef5bfb019bc4ddaf9b72805f63ea2873af624f3a2e96c28b2a0' 68 | ], 69 | [ 70 | '85672c7d2de0b7da2bd1770d89665868741b3f9af7643397721d74d28134ab83', 71 | '7c481b9b5b43b2eb6374049bfa62c2e5e77f17fcc5298f44c8e3094f790313a6' 72 | ], 73 | [ 74 | '948bf809b1988a46b06c9f1919413b10f9226c60f668832ffd959af60c82a0a', 75 | '53a562856dcb6646dc6b74c5d1c3418c6d4dff08c97cd2bed4cb7f88d8c8e589' 76 | ], 77 | [ 78 | '6260ce7f461801c34f067ce0f02873a8f1b0e44dfc69752accecd819f38fd8e8', 79 | 'bc2da82b6fa5b571a7f09049776a1ef7ecd292238051c198c1a84e95b2b4ae17' 80 | ], 81 | [ 82 | 'e5037de0afc1d8d43d8348414bbf4103043ec8f575bfdc432953cc8d2037fa2d', 83 | '4571534baa94d3b5f9f98d09fb990bddbd5f5b03ec481f10e0e5dc841d755bda' 84 | ], 85 | [ 86 | 'e06372b0f4a207adf5ea905e8f1771b4e7e8dbd1c6a6c5b725866a0ae4fce725', 87 | '7a908974bce18cfe12a27bb2ad5a488cd7484a7787104870b27034f94eee31dd' 88 | ], 89 | [ 90 | '213c7a715cd5d45358d0bbf9dc0ce02204b10bdde2a3f58540ad6908d0559754', 91 | '4b6dad0b5ae462507013ad06245ba190bb4850f5f36a7eeddff2c27534b458f2' 92 | ], 93 | [ 94 | '4e7c272a7af4b34e8dbb9352a5419a87e2838c70adc62cddf0cc3a3b08fbd53c', 95 | '17749c766c9d0b18e16fd09f6def681b530b9614bff7dd33e0b3941817dcaae6' 96 | ], 97 | [ 98 | 'fea74e3dbe778b1b10f238ad61686aa5c76e3db2be43057632427e2840fb27b6', 99 | '6e0568db9b0b13297cf674deccb6af93126b596b973f7b77701d3db7f23cb96f' 100 | ], 101 | [ 102 | '76e64113f677cf0e10a2570d599968d31544e179b760432952c02a4417bdde39', 103 | 'c90ddf8dee4e95cf577066d70681f0d35e2a33d2b56d2032b4b1752d1901ac01' 104 | ], 105 | [ 106 | 'c738c56b03b2abe1e8281baa743f8f9a8f7cc643df26cbee3ab150242bcbb891', 107 | '893fb578951ad2537f718f2eacbfbbbb82314eef7880cfe917e735d9699a84c3' 108 | ], 109 | [ 110 | 'd895626548b65b81e264c7637c972877d1d72e5f3a925014372e9f6588f6c14b', 111 | 'febfaa38f2bc7eae728ec60818c340eb03428d632bb067e179363ed75d7d991f' 112 | ], 113 | [ 114 | 'b8da94032a957518eb0f6433571e8761ceffc73693e84edd49150a564f676e03', 115 | '2804dfa44805a1e4d7c99cc9762808b092cc584d95ff3b511488e4e74efdf6e7' 116 | ], 117 | [ 118 | 'e80fea14441fb33a7d8adab9475d7fab2019effb5156a792f1a11778e3c0df5d', 119 | 'eed1de7f638e00771e89768ca3ca94472d155e80af322ea9fcb4291b6ac9ec78' 120 | ], 121 | [ 122 | 'a301697bdfcd704313ba48e51d567543f2a182031efd6915ddc07bbcc4e16070', 123 | '7370f91cfb67e4f5081809fa25d40f9b1735dbf7c0a11a130c0d1a041e177ea1' 124 | ], 125 | [ 126 | '90ad85b389d6b936463f9d0512678de208cc330b11307fffab7ac63e3fb04ed4', 127 | 'e507a3620a38261affdcbd9427222b839aefabe1582894d991d4d48cb6ef150' 128 | ], 129 | [ 130 | '8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da', 131 | '662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82' 132 | ], 133 | [ 134 | 'e4f3fb0176af85d65ff99ff9198c36091f48e86503681e3e6686fd5053231e11', 135 | '1e63633ad0ef4f1c1661a6d0ea02b7286cc7e74ec951d1c9822c38576feb73bc' 136 | ], 137 | [ 138 | '8c00fa9b18ebf331eb961537a45a4266c7034f2f0d4e1d0716fb6eae20eae29e', 139 | 'efa47267fea521a1a9dc343a3736c974c2fadafa81e36c54e7d2a4c66702414b' 140 | ], 141 | [ 142 | 'e7a26ce69dd4829f3e10cec0a9e98ed3143d084f308b92c0997fddfc60cb3e41', 143 | '2a758e300fa7984b471b006a1aafbb18d0a6b2c0420e83e20e8a9421cf2cfd51' 144 | ], 145 | [ 146 | 'b6459e0ee3662ec8d23540c223bcbdc571cbcb967d79424f3cf29eb3de6b80ef', 147 | '67c876d06f3e06de1dadf16e5661db3c4b3ae6d48e35b2ff30bf0b61a71ba45' 148 | ], 149 | [ 150 | 'd68a80c8280bb840793234aa118f06231d6f1fc67e73c5a5deda0f5b496943e8', 151 | 'db8ba9fff4b586d00c4b1f9177b0e28b5b0e7b8f7845295a294c84266b133120' 152 | ], 153 | [ 154 | '324aed7df65c804252dc0270907a30b09612aeb973449cea4095980fc28d3d5d', 155 | '648a365774b61f2ff130c0c35aec1f4f19213b0c7e332843967224af96ab7c84' 156 | ], 157 | [ 158 | '4df9c14919cde61f6d51dfdbe5fee5dceec4143ba8d1ca888e8bd373fd054c96', 159 | '35ec51092d8728050974c23a1d85d4b5d506cdc288490192ebac06cad10d5d' 160 | ], 161 | [ 162 | '9c3919a84a474870faed8a9c1cc66021523489054d7f0308cbfc99c8ac1f98cd', 163 | 'ddb84f0f4a4ddd57584f044bf260e641905326f76c64c8e6be7e5e03d4fc599d' 164 | ], 165 | [ 166 | '6057170b1dd12fdf8de05f281d8e06bb91e1493a8b91d4cc5a21382120a959e5', 167 | '9a1af0b26a6a4807add9a2daf71df262465152bc3ee24c65e899be932385a2a8' 168 | ], 169 | [ 170 | 'a576df8e23a08411421439a4518da31880cef0fba7d4df12b1a6973eecb94266', 171 | '40a6bf20e76640b2c92b97afe58cd82c432e10a7f514d9f3ee8be11ae1b28ec8' 172 | ], 173 | [ 174 | '7778a78c28dec3e30a05fe9629de8c38bb30d1f5cf9a3a208f763889be58ad71', 175 | '34626d9ab5a5b22ff7098e12f2ff580087b38411ff24ac563b513fc1fd9f43ac' 176 | ], 177 | [ 178 | '928955ee637a84463729fd30e7afd2ed5f96274e5ad7e5cb09eda9c06d903ac', 179 | 'c25621003d3f42a827b78a13093a95eeac3d26efa8a8d83fc5180e935bcd091f' 180 | ], 181 | [ 182 | '85d0fef3ec6db109399064f3a0e3b2855645b4a907ad354527aae75163d82751', 183 | '1f03648413a38c0be29d496e582cf5663e8751e96877331582c237a24eb1f962' 184 | ], 185 | [ 186 | 'ff2b0dce97eece97c1c9b6041798b85dfdfb6d8882da20308f5404824526087e', 187 | '493d13fef524ba188af4c4dc54d07936c7b7ed6fb90e2ceb2c951e01f0c29907' 188 | ], 189 | [ 190 | '827fbbe4b1e880ea9ed2b2e6301b212b57f1ee148cd6dd28780e5e2cf856e241', 191 | 'c60f9c923c727b0b71bef2c67d1d12687ff7a63186903166d605b68baec293ec' 192 | ], 193 | [ 194 | 'eaa649f21f51bdbae7be4ae34ce6e5217a58fdce7f47f9aa7f3b58fa2120e2b3', 195 | 'be3279ed5bbbb03ac69a80f89879aa5a01a6b965f13f7e59d47a5305ba5ad93d' 196 | ], 197 | [ 198 | 'e4a42d43c5cf169d9391df6decf42ee541b6d8f0c9a137401e23632dda34d24f', 199 | '4d9f92e716d1c73526fc99ccfb8ad34ce886eedfa8d8e4f13a7f7131deba9414' 200 | ], 201 | [ 202 | '1ec80fef360cbdd954160fadab352b6b92b53576a88fea4947173b9d4300bf19', 203 | 'aeefe93756b5340d2f3a4958a7abbf5e0146e77f6295a07b671cdc1cc107cefd' 204 | ], 205 | [ 206 | '146a778c04670c2f91b00af4680dfa8bce3490717d58ba889ddb5928366642be', 207 | 'b318e0ec3354028add669827f9d4b2870aaa971d2f7e5ed1d0b297483d83efd0' 208 | ], 209 | [ 210 | 'fa50c0f61d22e5f07e3acebb1aa07b128d0012209a28b9776d76a8793180eef9', 211 | '6b84c6922397eba9b72cd2872281a68a5e683293a57a213b38cd8d7d3f4f2811' 212 | ], 213 | [ 214 | 'da1d61d0ca721a11b1a5bf6b7d88e8421a288ab5d5bba5220e53d32b5f067ec2', 215 | '8157f55a7c99306c79c0766161c91e2966a73899d279b48a655fba0f1ad836f1' 216 | ], 217 | [ 218 | 'a8e282ff0c9706907215ff98e8fd416615311de0446f1e062a73b0610d064e13', 219 | '7f97355b8db81c09abfb7f3c5b2515888b679a3e50dd6bd6cef7c73111f4cc0c' 220 | ], 221 | [ 222 | '174a53b9c9a285872d39e56e6913cab15d59b1fa512508c022f382de8319497c', 223 | 'ccc9dc37abfc9c1657b4155f2c47f9e6646b3a1d8cb9854383da13ac079afa73' 224 | ], 225 | [ 226 | '959396981943785c3d3e57edf5018cdbe039e730e4918b3d884fdff09475b7ba', 227 | '2e7e552888c331dd8ba0386a4b9cd6849c653f64c8709385e9b8abf87524f2fd' 228 | ], 229 | [ 230 | 'd2a63a50ae401e56d645a1153b109a8fcca0a43d561fba2dbb51340c9d82b151', 231 | 'e82d86fb6443fcb7565aee58b2948220a70f750af484ca52d4142174dcf89405' 232 | ], 233 | [ 234 | '64587e2335471eb890ee7896d7cfdc866bacbdbd3839317b3436f9b45617e073', 235 | 'd99fcdd5bf6902e2ae96dd6447c299a185b90a39133aeab358299e5e9faf6589' 236 | ], 237 | [ 238 | '8481bde0e4e4d885b3a546d3e549de042f0aa6cea250e7fd358d6c86dd45e458', 239 | '38ee7b8cba5404dd84a25bf39cecb2ca900a79c42b262e556d64b1b59779057e' 240 | ], 241 | [ 242 | '13464a57a78102aa62b6979ae817f4637ffcfed3c4b1ce30bcd6303f6caf666b', 243 | '69be159004614580ef7e433453ccb0ca48f300a81d0942e13f495a907f6ecc27' 244 | ], 245 | [ 246 | 'bc4a9df5b713fe2e9aef430bcc1dc97a0cd9ccede2f28588cada3a0d2d83f366', 247 | 'd3a81ca6e785c06383937adf4b798caa6e8a9fbfa547b16d758d666581f33c1' 248 | ], 249 | [ 250 | '8c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa', 251 | '40a30463a3305193378fedf31f7cc0eb7ae784f0451cb9459e71dc73cbef9482' 252 | ], 253 | [ 254 | '8ea9666139527a8c1dd94ce4f071fd23c8b350c5a4bb33748c4ba111faccae0', 255 | '620efabbc8ee2782e24e7c0cfb95c5d735b783be9cf0f8e955af34a30e62b945' 256 | ], 257 | [ 258 | 'dd3625faef5ba06074669716bbd3788d89bdde815959968092f76cc4eb9a9787', 259 | '7a188fa3520e30d461da2501045731ca941461982883395937f68d00c644a573' 260 | ], 261 | [ 262 | 'f710d79d9eb962297e4f6232b40e8f7feb2bc63814614d692c12de752408221e', 263 | 'ea98e67232d3b3295d3b535532115ccac8612c721851617526ae47a9c77bfc82' 264 | ] 265 | ] 266 | }, 267 | naf: { 268 | wnd: 7, 269 | points: [ 270 | [ 271 | 'f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9', 272 | '388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672' 273 | ], 274 | [ 275 | '2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4', 276 | 'd8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6' 277 | ], 278 | [ 279 | '5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc', 280 | '6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da' 281 | ], 282 | [ 283 | 'acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe', 284 | 'cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37' 285 | ], 286 | [ 287 | '774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb', 288 | 'd984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b' 289 | ], 290 | [ 291 | 'f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8', 292 | 'ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81' 293 | ], 294 | [ 295 | 'd7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e', 296 | '581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58' 297 | ], 298 | [ 299 | 'defdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34', 300 | '4211ab0694635168e997b0ead2a93daeced1f4a04a95c0f6cfb199f69e56eb77' 301 | ], 302 | [ 303 | '2b4ea0a797a443d293ef5cff444f4979f06acfebd7e86d277475656138385b6c', 304 | '85e89bc037945d93b343083b5a1c86131a01f60c50269763b570c854e5c09b7a' 305 | ], 306 | [ 307 | '352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5', 308 | '321eb4075348f534d59c18259dda3e1f4a1b3b2e71b1039c67bd3d8bcf81998c' 309 | ], 310 | [ 311 | '2fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f', 312 | '2de1068295dd865b64569335bd5dd80181d70ecfc882648423ba76b532b7d67' 313 | ], 314 | [ 315 | '9248279b09b4d68dab21a9b066edda83263c3d84e09572e269ca0cd7f5453714', 316 | '73016f7bf234aade5d1aa71bdea2b1ff3fc0de2a887912ffe54a32ce97cb3402' 317 | ], 318 | [ 319 | 'daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729', 320 | 'a69dce4a7d6c98e8d4a1aca87ef8d7003f83c230f3afa726ab40e52290be1c55' 321 | ], 322 | [ 323 | 'c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db', 324 | '2119a460ce326cdc76c45926c982fdac0e106e861edf61c5a039063f0e0e6482' 325 | ], 326 | [ 327 | '6a245bf6dc698504c89a20cfded60853152b695336c28063b61c65cbd269e6b4', 328 | 'e022cf42c2bd4a708b3f5126f16a24ad8b33ba48d0423b6efd5e6348100d8a82' 329 | ], 330 | [ 331 | '1697ffa6fd9de627c077e3d2fe541084ce13300b0bec1146f95ae57f0d0bd6a5', 332 | 'b9c398f186806f5d27561506e4557433a2cf15009e498ae7adee9d63d01b2396' 333 | ], 334 | [ 335 | '605bdb019981718b986d0f07e834cb0d9deb8360ffb7f61df982345ef27a7479', 336 | '2972d2de4f8d20681a78d93ec96fe23c26bfae84fb14db43b01e1e9056b8c49' 337 | ], 338 | [ 339 | '62d14dab4150bf497402fdc45a215e10dcb01c354959b10cfe31c7e9d87ff33d', 340 | '80fc06bd8cc5b01098088a1950eed0db01aa132967ab472235f5642483b25eaf' 341 | ], 342 | [ 343 | '80c60ad0040f27dade5b4b06c408e56b2c50e9f56b9b8b425e555c2f86308b6f', 344 | '1c38303f1cc5c30f26e66bad7fe72f70a65eed4cbe7024eb1aa01f56430bd57a' 345 | ], 346 | [ 347 | '7a9375ad6167ad54aa74c6348cc54d344cc5dc9487d847049d5eabb0fa03c8fb', 348 | 'd0e3fa9eca8726909559e0d79269046bdc59ea10c70ce2b02d499ec224dc7f7' 349 | ], 350 | [ 351 | 'd528ecd9b696b54c907a9ed045447a79bb408ec39b68df504bb51f459bc3ffc9', 352 | 'eecf41253136e5f99966f21881fd656ebc4345405c520dbc063465b521409933' 353 | ], 354 | [ 355 | '49370a4b5f43412ea25f514e8ecdad05266115e4a7ecb1387231808f8b45963', 356 | '758f3f41afd6ed428b3081b0512fd62a54c3f3afbb5b6764b653052a12949c9a' 357 | ], 358 | [ 359 | '77f230936ee88cbbd73df930d64702ef881d811e0e1498e2f1c13eb1fc345d74', 360 | '958ef42a7886b6400a08266e9ba1b37896c95330d97077cbbe8eb3c7671c60d6' 361 | ], 362 | [ 363 | 'f2dac991cc4ce4b9ea44887e5c7c0bce58c80074ab9d4dbaeb28531b7739f530', 364 | 'e0dedc9b3b2f8dad4da1f32dec2531df9eb5fbeb0598e4fd1a117dba703a3c37' 365 | ], 366 | [ 367 | '463b3d9f662621fb1b4be8fbbe2520125a216cdfc9dae3debcba4850c690d45b', 368 | '5ed430d78c296c3543114306dd8622d7c622e27c970a1de31cb377b01af7307e' 369 | ], 370 | [ 371 | 'f16f804244e46e2a09232d4aff3b59976b98fac14328a2d1a32496b49998f247', 372 | 'cedabd9b82203f7e13d206fcdf4e33d92a6c53c26e5cce26d6579962c4e31df6' 373 | ], 374 | [ 375 | 'caf754272dc84563b0352b7a14311af55d245315ace27c65369e15f7151d41d1', 376 | 'cb474660ef35f5f2a41b643fa5e460575f4fa9b7962232a5c32f908318a04476' 377 | ], 378 | [ 379 | '2600ca4b282cb986f85d0f1709979d8b44a09c07cb86d7c124497bc86f082120', 380 | '4119b88753c15bd6a693b03fcddbb45d5ac6be74ab5f0ef44b0be9475a7e4b40' 381 | ], 382 | [ 383 | '7635ca72d7e8432c338ec53cd12220bc01c48685e24f7dc8c602a7746998e435', 384 | '91b649609489d613d1d5e590f78e6d74ecfc061d57048bad9e76f302c5b9c61' 385 | ], 386 | [ 387 | '754e3239f325570cdbbf4a87deee8a66b7f2b33479d468fbc1a50743bf56cc18', 388 | '673fb86e5bda30fb3cd0ed304ea49a023ee33d0197a695d0c5d98093c536683' 389 | ], 390 | [ 391 | 'e3e6bd1071a1e96aff57859c82d570f0330800661d1c952f9fe2694691d9b9e8', 392 | '59c9e0bba394e76f40c0aa58379a3cb6a5a2283993e90c4167002af4920e37f5' 393 | ], 394 | [ 395 | '186b483d056a033826ae73d88f732985c4ccb1f32ba35f4b4cc47fdcf04aa6eb', 396 | '3b952d32c67cf77e2e17446e204180ab21fb8090895138b4a4a797f86e80888b' 397 | ], 398 | [ 399 | 'df9d70a6b9876ce544c98561f4be4f725442e6d2b737d9c91a8321724ce0963f', 400 | '55eb2dafd84d6ccd5f862b785dc39d4ab157222720ef9da217b8c45cf2ba2417' 401 | ], 402 | [ 403 | '5edd5cc23c51e87a497ca815d5dce0f8ab52554f849ed8995de64c5f34ce7143', 404 | 'efae9c8dbc14130661e8cec030c89ad0c13c66c0d17a2905cdc706ab7399a868' 405 | ], 406 | [ 407 | '290798c2b6476830da12fe02287e9e777aa3fba1c355b17a722d362f84614fba', 408 | 'e38da76dcd440621988d00bcf79af25d5b29c094db2a23146d003afd41943e7a' 409 | ], 410 | [ 411 | 'af3c423a95d9f5b3054754efa150ac39cd29552fe360257362dfdecef4053b45', 412 | 'f98a3fd831eb2b749a93b0e6f35cfb40c8cd5aa667a15581bc2feded498fd9c6' 413 | ], 414 | [ 415 | '766dbb24d134e745cccaa28c99bf274906bb66b26dcf98df8d2fed50d884249a', 416 | '744b1152eacbe5e38dcc887980da38b897584a65fa06cedd2c924f97cbac5996' 417 | ], 418 | [ 419 | '59dbf46f8c94759ba21277c33784f41645f7b44f6c596a58ce92e666191abe3e', 420 | 'c534ad44175fbc300f4ea6ce648309a042ce739a7919798cd85e216c4a307f6e' 421 | ], 422 | [ 423 | 'f13ada95103c4537305e691e74e9a4a8dd647e711a95e73cb62dc6018cfd87b8', 424 | 'e13817b44ee14de663bf4bc808341f326949e21a6a75c2570778419bdaf5733d' 425 | ], 426 | [ 427 | '7754b4fa0e8aced06d4167a2c59cca4cda1869c06ebadfb6488550015a88522c', 428 | '30e93e864e669d82224b967c3020b8fa8d1e4e350b6cbcc537a48b57841163a2' 429 | ], 430 | [ 431 | '948dcadf5990e048aa3874d46abef9d701858f95de8041d2a6828c99e2262519', 432 | 'e491a42537f6e597d5d28a3224b1bc25df9154efbd2ef1d2cbba2cae5347d57e' 433 | ], 434 | [ 435 | '7962414450c76c1689c7b48f8202ec37fb224cf5ac0bfa1570328a8a3d7c77ab', 436 | '100b610ec4ffb4760d5c1fc133ef6f6b12507a051f04ac5760afa5b29db83437' 437 | ], 438 | [ 439 | '3514087834964b54b15b160644d915485a16977225b8847bb0dd085137ec47ca', 440 | 'ef0afbb2056205448e1652c48e8127fc6039e77c15c2378b7e7d15a0de293311' 441 | ], 442 | [ 443 | 'd3cc30ad6b483e4bc79ce2c9dd8bc54993e947eb8df787b442943d3f7b527eaf', 444 | '8b378a22d827278d89c5e9be8f9508ae3c2ad46290358630afb34db04eede0a4' 445 | ], 446 | [ 447 | '1624d84780732860ce1c78fcbfefe08b2b29823db913f6493975ba0ff4847610', 448 | '68651cf9b6da903e0914448c6cd9d4ca896878f5282be4c8cc06e2a404078575' 449 | ], 450 | [ 451 | '733ce80da955a8a26902c95633e62a985192474b5af207da6df7b4fd5fc61cd4', 452 | 'f5435a2bd2badf7d485a4d8b8db9fcce3e1ef8e0201e4578c54673bc1dc5ea1d' 453 | ], 454 | [ 455 | '15d9441254945064cf1a1c33bbd3b49f8966c5092171e699ef258dfab81c045c', 456 | 'd56eb30b69463e7234f5137b73b84177434800bacebfc685fc37bbe9efe4070d' 457 | ], 458 | [ 459 | 'a1d0fcf2ec9de675b612136e5ce70d271c21417c9d2b8aaaac138599d0717940', 460 | 'edd77f50bcb5a3cab2e90737309667f2641462a54070f3d519212d39c197a629' 461 | ], 462 | [ 463 | 'e22fbe15c0af8ccc5780c0735f84dbe9a790badee8245c06c7ca37331cb36980', 464 | 'a855babad5cd60c88b430a69f53a1a7a38289154964799be43d06d77d31da06' 465 | ], 466 | [ 467 | '311091dd9860e8e20ee13473c1155f5f69635e394704eaa74009452246cfa9b3', 468 | '66db656f87d1f04fffd1f04788c06830871ec5a64feee685bd80f0b1286d8374' 469 | ], 470 | [ 471 | '34c1fd04d301be89b31c0442d3e6ac24883928b45a9340781867d4232ec2dbdf', 472 | '9414685e97b1b5954bd46f730174136d57f1ceeb487443dc5321857ba73abee' 473 | ], 474 | [ 475 | 'f219ea5d6b54701c1c14de5b557eb42a8d13f3abbcd08affcc2a5e6b049b8d63', 476 | '4cb95957e83d40b0f73af4544cccf6b1f4b08d3c07b27fb8d8c2962a400766d1' 477 | ], 478 | [ 479 | 'd7b8740f74a8fbaab1f683db8f45de26543a5490bca627087236912469a0b448', 480 | 'fa77968128d9c92ee1010f337ad4717eff15db5ed3c049b3411e0315eaa4593b' 481 | ], 482 | [ 483 | '32d31c222f8f6f0ef86f7c98d3a3335ead5bcd32abdd94289fe4d3091aa824bf', 484 | '5f3032f5892156e39ccd3d7915b9e1da2e6dac9e6f26e961118d14b8462e1661' 485 | ], 486 | [ 487 | '7461f371914ab32671045a155d9831ea8793d77cd59592c4340f86cbc18347b5', 488 | '8ec0ba238b96bec0cbdddcae0aa442542eee1ff50c986ea6b39847b3cc092ff6' 489 | ], 490 | [ 491 | 'ee079adb1df1860074356a25aa38206a6d716b2c3e67453d287698bad7b2b2d6', 492 | '8dc2412aafe3be5c4c5f37e0ecc5f9f6a446989af04c4e25ebaac479ec1c8c1e' 493 | ], 494 | [ 495 | '16ec93e447ec83f0467b18302ee620f7e65de331874c9dc72bfd8616ba9da6b5', 496 | '5e4631150e62fb40d0e8c2a7ca5804a39d58186a50e497139626778e25b0674d' 497 | ], 498 | [ 499 | 'eaa5f980c245f6f038978290afa70b6bd8855897f98b6aa485b96065d537bd99', 500 | 'f65f5d3e292c2e0819a528391c994624d784869d7e6ea67fb18041024edc07dc' 501 | ], 502 | [ 503 | '78c9407544ac132692ee1910a02439958ae04877151342ea96c4b6b35a49f51', 504 | 'f3e0319169eb9b85d5404795539a5e68fa1fbd583c064d2462b675f194a3ddb4' 505 | ], 506 | [ 507 | '494f4be219a1a77016dcd838431aea0001cdc8ae7a6fc688726578d9702857a5', 508 | '42242a969283a5f339ba7f075e36ba2af925ce30d767ed6e55f4b031880d562c' 509 | ], 510 | [ 511 | 'a598a8030da6d86c6bc7f2f5144ea549d28211ea58faa70ebf4c1e665c1fe9b5', 512 | '204b5d6f84822c307e4b4a7140737aec23fc63b65b35f86a10026dbd2d864e6b' 513 | ], 514 | [ 515 | 'c41916365abb2b5d09192f5f2dbeafec208f020f12570a184dbadc3e58595997', 516 | '4f14351d0087efa49d245b328984989d5caf9450f34bfc0ed16e96b58fa9913' 517 | ], 518 | [ 519 | '841d6063a586fa475a724604da03bc5b92a2e0d2e0a36acfe4c73a5514742881', 520 | '73867f59c0659e81904f9a1c7543698e62562d6744c169ce7a36de01a8d6154' 521 | ], 522 | [ 523 | '5e95bb399a6971d376026947f89bde2f282b33810928be4ded112ac4d70e20d5', 524 | '39f23f366809085beebfc71181313775a99c9aed7d8ba38b161384c746012865' 525 | ], 526 | [ 527 | '36e4641a53948fd476c39f8a99fd974e5ec07564b5315d8bf99471bca0ef2f66', 528 | 'd2424b1b1abe4eb8164227b085c9aa9456ea13493fd563e06fd51cf5694c78fc' 529 | ], 530 | [ 531 | '336581ea7bfbbb290c191a2f507a41cf5643842170e914faeab27c2c579f726', 532 | 'ead12168595fe1be99252129b6e56b3391f7ab1410cd1e0ef3dcdcabd2fda224' 533 | ], 534 | [ 535 | '8ab89816dadfd6b6a1f2634fcf00ec8403781025ed6890c4849742706bd43ede', 536 | '6fdcef09f2f6d0a044e654aef624136f503d459c3e89845858a47a9129cdd24e' 537 | ], 538 | [ 539 | '1e33f1a746c9c5778133344d9299fcaa20b0938e8acff2544bb40284b8c5fb94', 540 | '60660257dd11b3aa9c8ed618d24edff2306d320f1d03010e33a7d2057f3b3b6' 541 | ], 542 | [ 543 | '85b7c1dcb3cec1b7ee7f30ded79dd20a0ed1f4cc18cbcfcfa410361fd8f08f31', 544 | '3d98a9cdd026dd43f39048f25a8847f4fcafad1895d7a633c6fed3c35e999511' 545 | ], 546 | [ 547 | '29df9fbd8d9e46509275f4b125d6d45d7fbe9a3b878a7af872a2800661ac5f51', 548 | 'b4c4fe99c775a606e2d8862179139ffda61dc861c019e55cd2876eb2a27d84b' 549 | ], 550 | [ 551 | 'a0b1cae06b0a847a3fea6e671aaf8adfdfe58ca2f768105c8082b2e449fce252', 552 | 'ae434102edde0958ec4b19d917a6a28e6b72da1834aff0e650f049503a296cf2' 553 | ], 554 | [ 555 | '4e8ceafb9b3e9a136dc7ff67e840295b499dfb3b2133e4ba113f2e4c0e121e5', 556 | 'cf2174118c8b6d7a4b48f6d534ce5c79422c086a63460502b827ce62a326683c' 557 | ], 558 | [ 559 | 'd24a44e047e19b6f5afb81c7ca2f69080a5076689a010919f42725c2b789a33b', 560 | '6fb8d5591b466f8fc63db50f1c0f1c69013f996887b8244d2cdec417afea8fa3' 561 | ], 562 | [ 563 | 'ea01606a7a6c9cdd249fdfcfacb99584001edd28abbab77b5104e98e8e3b35d4', 564 | '322af4908c7312b0cfbfe369f7a7b3cdb7d4494bc2823700cfd652188a3ea98d' 565 | ], 566 | [ 567 | 'af8addbf2b661c8a6c6328655eb96651252007d8c5ea31be4ad196de8ce2131f', 568 | '6749e67c029b85f52a034eafd096836b2520818680e26ac8f3dfbcdb71749700' 569 | ], 570 | [ 571 | 'e3ae1974566ca06cc516d47e0fb165a674a3dabcfca15e722f0e3450f45889', 572 | '2aeabe7e4531510116217f07bf4d07300de97e4874f81f533420a72eeb0bd6a4' 573 | ], 574 | [ 575 | '591ee355313d99721cf6993ffed1e3e301993ff3ed258802075ea8ced397e246', 576 | 'b0ea558a113c30bea60fc4775460c7901ff0b053d25ca2bdeee98f1a4be5d196' 577 | ], 578 | [ 579 | '11396d55fda54c49f19aa97318d8da61fa8584e47b084945077cf03255b52984', 580 | '998c74a8cd45ac01289d5833a7beb4744ff536b01b257be4c5767bea93ea57a4' 581 | ], 582 | [ 583 | '3c5d2a1ba39c5a1790000738c9e0c40b8dcdfd5468754b6405540157e017aa7a', 584 | 'b2284279995a34e2f9d4de7396fc18b80f9b8b9fdd270f6661f79ca4c81bd257' 585 | ], 586 | [ 587 | 'cc8704b8a60a0defa3a99a7299f2e9c3fbc395afb04ac078425ef8a1793cc030', 588 | 'bdd46039feed17881d1e0862db347f8cf395b74fc4bcdc4e940b74e3ac1f1b13' 589 | ], 590 | [ 591 | 'c533e4f7ea8555aacd9777ac5cad29b97dd4defccc53ee7ea204119b2889b197', 592 | '6f0a256bc5efdf429a2fb6242f1a43a2d9b925bb4a4b3a26bb8e0f45eb596096' 593 | ], 594 | [ 595 | 'c14f8f2ccb27d6f109f6d08d03cc96a69ba8c34eec07bbcf566d48e33da6593', 596 | 'c359d6923bb398f7fd4473e16fe1c28475b740dd098075e6c0e8649113dc3a38' 597 | ], 598 | [ 599 | 'a6cbc3046bc6a450bac24789fa17115a4c9739ed75f8f21ce441f72e0b90e6ef', 600 | '21ae7f4680e889bb130619e2c0f95a360ceb573c70603139862afd617fa9b9f' 601 | ], 602 | [ 603 | '347d6d9a02c48927ebfb86c1359b1caf130a3c0267d11ce6344b39f99d43cc38', 604 | '60ea7f61a353524d1c987f6ecec92f086d565ab687870cb12689ff1e31c74448' 605 | ], 606 | [ 607 | 'da6545d2181db8d983f7dcb375ef5866d47c67b1bf31c8cf855ef7437b72656a', 608 | '49b96715ab6878a79e78f07ce5680c5d6673051b4935bd897fea824b77dc208a' 609 | ], 610 | [ 611 | 'c40747cc9d012cb1a13b8148309c6de7ec25d6945d657146b9d5994b8feb1111', 612 | '5ca560753be2a12fc6de6caf2cb489565db936156b9514e1bb5e83037e0fa2d4' 613 | ], 614 | [ 615 | '4e42c8ec82c99798ccf3a610be870e78338c7f713348bd34c8203ef4037f3502', 616 | '7571d74ee5e0fb92a7a8b33a07783341a5492144cc54bcc40a94473693606437' 617 | ], 618 | [ 619 | '3775ab7089bc6af823aba2e1af70b236d251cadb0c86743287522a1b3b0dedea', 620 | 'be52d107bcfa09d8bcb9736a828cfa7fac8db17bf7a76a2c42ad961409018cf7' 621 | ], 622 | [ 623 | 'cee31cbf7e34ec379d94fb814d3d775ad954595d1314ba8846959e3e82f74e26', 624 | '8fd64a14c06b589c26b947ae2bcf6bfa0149ef0be14ed4d80f448a01c43b1c6d' 625 | ], 626 | [ 627 | 'b4f9eaea09b6917619f6ea6a4eb5464efddb58fd45b1ebefcdc1a01d08b47986', 628 | '39e5c9925b5a54b07433a4f18c61726f8bb131c012ca542eb24a8ac07200682a' 629 | ], 630 | [ 631 | 'd4263dfc3d2df923a0179a48966d30ce84e2515afc3dccc1b77907792ebcc60e', 632 | '62dfaf07a0f78feb30e30d6295853ce189e127760ad6cf7fae164e122a208d54' 633 | ], 634 | [ 635 | '48457524820fa65a4f8d35eb6930857c0032acc0a4a2de422233eeda897612c4', 636 | '25a748ab367979d98733c38a1fa1c2e7dc6cc07db2d60a9ae7a76aaa49bd0f77' 637 | ], 638 | [ 639 | 'dfeeef1881101f2cb11644f3a2afdfc2045e19919152923f367a1767c11cceda', 640 | 'ecfb7056cf1de042f9420bab396793c0c390bde74b4bbdff16a83ae09a9a7517' 641 | ], 642 | [ 643 | '6d7ef6b17543f8373c573f44e1f389835d89bcbc6062ced36c82df83b8fae859', 644 | 'cd450ec335438986dfefa10c57fea9bcc521a0959b2d80bbf74b190dca712d10' 645 | ], 646 | [ 647 | 'e75605d59102a5a2684500d3b991f2e3f3c88b93225547035af25af66e04541f', 648 | 'f5c54754a8f71ee540b9b48728473e314f729ac5308b06938360990e2bfad125' 649 | ], 650 | [ 651 | 'eb98660f4c4dfaa06a2be453d5020bc99a0c2e60abe388457dd43fefb1ed620c', 652 | '6cb9a8876d9cb8520609af3add26cd20a0a7cd8a9411131ce85f44100099223e' 653 | ], 654 | [ 655 | '13e87b027d8514d35939f2e6892b19922154596941888336dc3563e3b8dba942', 656 | 'fef5a3c68059a6dec5d624114bf1e91aac2b9da568d6abeb2570d55646b8adf1' 657 | ], 658 | [ 659 | 'ee163026e9fd6fe017c38f06a5be6fc125424b371ce2708e7bf4491691e5764a', 660 | '1acb250f255dd61c43d94ccc670d0f58f49ae3fa15b96623e5430da0ad6c62b2' 661 | ], 662 | [ 663 | 'b268f5ef9ad51e4d78de3a750c2dc89b1e626d43505867999932e5db33af3d80', 664 | '5f310d4b3c99b9ebb19f77d41c1dee018cf0d34fd4191614003e945a1216e423' 665 | ], 666 | [ 667 | 'ff07f3118a9df035e9fad85eb6c7bfe42b02f01ca99ceea3bf7ffdba93c4750d', 668 | '438136d603e858a3a5c440c38eccbaddc1d2942114e2eddd4740d098ced1f0d8' 669 | ], 670 | [ 671 | '8d8b9855c7c052a34146fd20ffb658bea4b9f69e0d825ebec16e8c3ce2b526a1', 672 | 'cdb559eedc2d79f926baf44fb84ea4d44bcf50fee51d7ceb30e2e7f463036758' 673 | ], 674 | [ 675 | '52db0b5384dfbf05bfa9d472d7ae26dfe4b851ceca91b1eba54263180da32b63', 676 | 'c3b997d050ee5d423ebaf66a6db9f57b3180c902875679de924b69d84a7b375' 677 | ], 678 | [ 679 | 'e62f9490d3d51da6395efd24e80919cc7d0f29c3f3fa48c6fff543becbd43352', 680 | '6d89ad7ba4876b0b22c2ca280c682862f342c8591f1daf5170e07bfd9ccafa7d' 681 | ], 682 | [ 683 | '7f30ea2476b399b4957509c88f77d0191afa2ff5cb7b14fd6d8e7d65aaab1193', 684 | 'ca5ef7d4b231c94c3b15389a5f6311e9daff7bb67b103e9880ef4bff637acaec' 685 | ], 686 | [ 687 | '5098ff1e1d9f14fb46a210fada6c903fef0fb7b4a1dd1d9ac60a0361800b7a00', 688 | '9731141d81fc8f8084d37c6e7542006b3ee1b40d60dfe5362a5b132fd17ddc0' 689 | ], 690 | [ 691 | '32b78c7de9ee512a72895be6b9cbefa6e2f3c4ccce445c96b9f2c81e2778ad58', 692 | 'ee1849f513df71e32efc3896ee28260c73bb80547ae2275ba497237794c8753c' 693 | ], 694 | [ 695 | 'e2cb74fddc8e9fbcd076eef2a7c72b0ce37d50f08269dfc074b581550547a4f7', 696 | 'd3aa2ed71c9dd2247a62df062736eb0baddea9e36122d2be8641abcb005cc4a4' 697 | ], 698 | [ 699 | '8438447566d4d7bedadc299496ab357426009a35f235cb141be0d99cd10ae3a8', 700 | 'c4e1020916980a4da5d01ac5e6ad330734ef0d7906631c4f2390426b2edd791f' 701 | ], 702 | [ 703 | '4162d488b89402039b584c6fc6c308870587d9c46f660b878ab65c82c711d67e', 704 | '67163e903236289f776f22c25fb8a3afc1732f2b84b4e95dbda47ae5a0852649' 705 | ], 706 | [ 707 | '3fad3fa84caf0f34f0f89bfd2dcf54fc175d767aec3e50684f3ba4a4bf5f683d', 708 | 'cd1bc7cb6cc407bb2f0ca647c718a730cf71872e7d0d2a53fa20efcdfe61826' 709 | ], 710 | [ 711 | '674f2600a3007a00568c1a7ce05d0816c1fb84bf1370798f1c69532faeb1a86b', 712 | '299d21f9413f33b3edf43b257004580b70db57da0b182259e09eecc69e0d38a5' 713 | ], 714 | [ 715 | 'd32f4da54ade74abb81b815ad1fb3b263d82d6c692714bcff87d29bd5ee9f08f', 716 | 'f9429e738b8e53b968e99016c059707782e14f4535359d582fc416910b3eea87' 717 | ], 718 | [ 719 | '30e4e670435385556e593657135845d36fbb6931f72b08cb1ed954f1e3ce3ff6', 720 | '462f9bce619898638499350113bbc9b10a878d35da70740dc695a559eb88db7b' 721 | ], 722 | [ 723 | 'be2062003c51cc3004682904330e4dee7f3dcd10b01e580bf1971b04d4cad297', 724 | '62188bc49d61e5428573d48a74e1c655b1c61090905682a0d5558ed72dccb9bc' 725 | ], 726 | [ 727 | '93144423ace3451ed29e0fb9ac2af211cb6e84a601df5993c419859fff5df04a', 728 | '7c10dfb164c3425f5c71a3f9d7992038f1065224f72bb9d1d902a6d13037b47c' 729 | ], 730 | [ 731 | 'b015f8044f5fcbdcf21ca26d6c34fb8197829205c7b7d2a7cb66418c157b112c', 732 | 'ab8c1e086d04e813744a655b2df8d5f83b3cdc6faa3088c1d3aea1454e3a1d5f' 733 | ], 734 | [ 735 | 'd5e9e1da649d97d89e4868117a465a3a4f8a18de57a140d36b3f2af341a21b52', 736 | '4cb04437f391ed73111a13cc1d4dd0db1693465c2240480d8955e8592f27447a' 737 | ], 738 | [ 739 | 'd3ae41047dd7ca065dbf8ed77b992439983005cd72e16d6f996a5316d36966bb', 740 | 'bd1aeb21ad22ebb22a10f0303417c6d964f8cdd7df0aca614b10dc14d125ac46' 741 | ], 742 | [ 743 | '463e2763d885f958fc66cdd22800f0a487197d0a82e377b49f80af87c897b065', 744 | 'bfefacdb0e5d0fd7df3a311a94de062b26b80c61fbc97508b79992671ef7ca7f' 745 | ], 746 | [ 747 | '7985fdfd127c0567c6f53ec1bb63ec3158e597c40bfe747c83cddfc910641917', 748 | '603c12daf3d9862ef2b25fe1de289aed24ed291e0ec6708703a5bd567f32ed03' 749 | ], 750 | [ 751 | '74a1ad6b5f76e39db2dd249410eac7f99e74c59cb83d2d0ed5ff1543da7703e9', 752 | 'cc6157ef18c9c63cd6193d83631bbea0093e0968942e8c33d5737fd790e0db08' 753 | ], 754 | [ 755 | '30682a50703375f602d416664ba19b7fc9bab42c72747463a71d0896b22f6da3', 756 | '553e04f6b018b4fa6c8f39e7f311d3176290d0e0f19ca73f17714d9977a22ff8' 757 | ], 758 | [ 759 | '9e2158f0d7c0d5f26c3791efefa79597654e7a2b2464f52b1ee6c1347769ef57', 760 | '712fcdd1b9053f09003a3481fa7762e9ffd7c8ef35a38509e2fbf2629008373' 761 | ], 762 | [ 763 | '176e26989a43c9cfeba4029c202538c28172e566e3c4fce7322857f3be327d66', 764 | 'ed8cc9d04b29eb877d270b4878dc43c19aefd31f4eee09ee7b47834c1fa4b1c3' 765 | ], 766 | [ 767 | '75d46efea3771e6e68abb89a13ad747ecf1892393dfc4f1b7004788c50374da8', 768 | '9852390a99507679fd0b86fd2b39a868d7efc22151346e1a3ca4726586a6bed8' 769 | ], 770 | [ 771 | '809a20c67d64900ffb698c4c825f6d5f2310fb0451c869345b7319f645605721', 772 | '9e994980d9917e22b76b061927fa04143d096ccc54963e6a5ebfa5f3f8e286c1' 773 | ], 774 | [ 775 | '1b38903a43f7f114ed4500b4eac7083fdefece1cf29c63528d563446f972c180', 776 | '4036edc931a60ae889353f77fd53de4a2708b26b6f5da72ad3394119daf408f9' 777 | ] 778 | ] 779 | } 780 | }; 781 | --------------------------------------------------------------------------------