├── .npmignore ├── .travis.yml ├── LICENSE ├── browser.js ├── index.js ├── package.json ├── readme.md └── test.js /.npmignore: -------------------------------------------------------------------------------- 1 | test.js 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - 6 5 | - 8 6 | - 9 7 | - 10 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2017 createECDH contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /browser.js: -------------------------------------------------------------------------------- 1 | var elliptic = require('elliptic') 2 | var BN = require('bn.js') 3 | 4 | module.exports = function createECDH (curve) { 5 | return new ECDH(curve) 6 | } 7 | 8 | var aliases = { 9 | secp256k1: { 10 | name: 'secp256k1', 11 | byteLength: 32 12 | }, 13 | secp224r1: { 14 | name: 'p224', 15 | byteLength: 28 16 | }, 17 | prime256v1: { 18 | name: 'p256', 19 | byteLength: 32 20 | }, 21 | prime192v1: { 22 | name: 'p192', 23 | byteLength: 24 24 | }, 25 | ed25519: { 26 | name: 'ed25519', 27 | byteLength: 32 28 | }, 29 | secp384r1: { 30 | name: 'p384', 31 | byteLength: 48 32 | }, 33 | secp521r1: { 34 | name: 'p521', 35 | byteLength: 66 36 | } 37 | } 38 | 39 | aliases.p224 = aliases.secp224r1 40 | aliases.p256 = aliases.secp256r1 = aliases.prime256v1 41 | aliases.p192 = aliases.secp192r1 = aliases.prime192v1 42 | aliases.p384 = aliases.secp384r1 43 | aliases.p521 = aliases.secp521r1 44 | 45 | function ECDH (curve) { 46 | this.curveType = aliases[curve] 47 | if (!this.curveType) { 48 | this.curveType = { 49 | name: curve 50 | } 51 | } 52 | this.curve = new elliptic.ec(this.curveType.name) // eslint-disable-line new-cap 53 | this.keys = void 0 54 | } 55 | 56 | ECDH.prototype.generateKeys = function (enc, format) { 57 | this.keys = this.curve.genKeyPair() 58 | return this.getPublicKey(enc, format) 59 | } 60 | 61 | ECDH.prototype.computeSecret = function (other, inenc, enc) { 62 | inenc = inenc || 'utf8' 63 | if (!Buffer.isBuffer(other)) { 64 | other = new Buffer(other, inenc) 65 | } 66 | var otherPub = this.curve.keyFromPublic(other).getPublic() 67 | var out = otherPub.mul(this.keys.getPrivate()).getX() 68 | return formatReturnValue(out, enc, this.curveType.byteLength) 69 | } 70 | 71 | ECDH.prototype.getPublicKey = function (enc, format) { 72 | var key = this.keys.getPublic(format === 'compressed', true) 73 | if (format === 'hybrid') { 74 | if (key[key.length - 1] % 2) { 75 | key[0] = 7 76 | } else { 77 | key[0] = 6 78 | } 79 | } 80 | return formatReturnValue(key, enc) 81 | } 82 | 83 | ECDH.prototype.getPrivateKey = function (enc) { 84 | return formatReturnValue(this.keys.getPrivate(), enc) 85 | } 86 | 87 | ECDH.prototype.setPublicKey = function (pub, enc) { 88 | enc = enc || 'utf8' 89 | if (!Buffer.isBuffer(pub)) { 90 | pub = new Buffer(pub, enc) 91 | } 92 | this.keys._importPublic(pub) 93 | return this 94 | } 95 | 96 | ECDH.prototype.setPrivateKey = function (priv, enc) { 97 | enc = enc || 'utf8' 98 | if (!Buffer.isBuffer(priv)) { 99 | priv = new Buffer(priv, enc) 100 | } 101 | 102 | var _priv = new BN(priv) 103 | _priv = _priv.toString(16) 104 | this.keys = this.curve.genKeyPair() 105 | this.keys._importPrivate(_priv) 106 | return this 107 | } 108 | 109 | function formatReturnValue (bn, enc, len) { 110 | if (!Array.isArray(bn)) { 111 | bn = bn.toArray() 112 | } 113 | var buf = new Buffer(bn) 114 | if (len && buf.length < len) { 115 | var zeros = new Buffer(len - buf.length) 116 | zeros.fill(0) 117 | buf = Buffer.concat([zeros, buf]) 118 | } 119 | if (!enc) { 120 | return buf 121 | } else { 122 | return buf.toString(enc) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var createECDH = require('crypto').createECDH 2 | 3 | module.exports = createECDH || require('./browser') 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-ecdh", 3 | "version": "4.0.4", 4 | "description": "createECDH but browserifiable", 5 | "main": "index.js", 6 | "browser": "browser.js", 7 | "scripts": { 8 | "test": "standard && node test.js | tspec" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/crypto-browserify/createECDH.git" 13 | }, 14 | "keywords": [ 15 | "diffie", 16 | "hellman", 17 | "diffiehellman", 18 | "ECDH" 19 | ], 20 | "author": "Calvin Metcalf", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/crypto-browserify/createECDH/issues" 24 | }, 25 | "homepage": "https://github.com/crypto-browserify/createECDH", 26 | "dependencies": { 27 | "bn.js": "^4.1.0", 28 | "elliptic": "^6.5.3" 29 | }, 30 | "devDependencies": { 31 | "tap-spec": "^1.0.1", 32 | "tape": "^3.0.1", 33 | "standard": "^5.4.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | createECDH [![Build Status](https://travis-ci.org/crypto-browserify/createECDH.svg)](https://travis-ci.org/crypto-browserify/createECDH) 2 | ==== 3 | 4 | In io.js or node >= 0.11 this module is just a shortcut to crypto.createECDH. In node <= 0.11 or the browser this is a pure JavaScript implimentation, more specifically a wrapper around [elliptic](https://github.com/indutny/elliptic), to give it the same API as node. `secp256k1`, `secp224r1` (aka p224), `prime256v1` (aka p256, secp256r1), `prime192v1` (aka p192, secp192r1), `secp384r1` (aka p384), `secp521r1` (aka p521) curves all work in both this library and node (though only the highlighted name will work in node). 5 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | var nodeCrypto = require('./') 3 | var myCrypto = require('./browser') 4 | 5 | var mods = [ 6 | 'secp256k1', 7 | 'secp224r1', 8 | 'prime256v1', 9 | 'prime192v1', 10 | 'secp384r1', 11 | 'secp521r1' 12 | ] 13 | 14 | function run (i) { 15 | mods.forEach(function (mod) { 16 | test(mod + ' run ' + i + ' uncompressed', function (t) { 17 | t.plan(2) 18 | var dh1 = nodeCrypto(mod) 19 | dh1.generateKeys() 20 | var dh2 = myCrypto(mod) 21 | dh2.generateKeys() 22 | var pubk1 = dh1.getPublicKey() 23 | var pubk2 = dh2.getPublicKey() 24 | t.notEquals(pubk1.toString('hex'), pubk2.toString('hex'), 'diff public keys') 25 | var pub1 = dh1.computeSecret(pubk2).toString('hex') 26 | var pub2 = dh2.computeSecret(pubk1).toString('hex') 27 | t.equals(pub1, pub2, 'equal secrets') 28 | }) 29 | test(mod + ' run ' + i + ' compressed', function (t) { 30 | t.plan(2) 31 | var dh1 = nodeCrypto(mod) 32 | dh1.generateKeys() 33 | var dh2 = myCrypto(mod) 34 | dh2.generateKeys() 35 | var pubk1 = dh1.getPublicKey(null, 'compressed') 36 | var pubk2 = dh2.getPublicKey(null, 'compressed') 37 | t.notEquals(pubk1.toString('hex'), pubk2.toString('hex'), 'diff public keys') 38 | var pub1 = dh1.computeSecret(pubk2).toString('hex') 39 | var pub2 = dh2.computeSecret(pubk1).toString('hex') 40 | t.equals(pub1, pub2, 'equal secrets') 41 | }) 42 | test(mod + ' run ' + i + ' set stuff', function (t) { 43 | t.plan(5) 44 | var dh1 = nodeCrypto(mod) 45 | var dh2 = myCrypto(mod) 46 | dh1.generateKeys() 47 | dh2.generateKeys() 48 | dh1.setPrivateKey(dh2.getPrivateKey()) 49 | dh1.setPublicKey(dh2.getPublicKey()) 50 | var priv1 = dh1.getPrivateKey('hex') 51 | var priv2 = dh2.getPrivateKey('hex') 52 | t.equals(priv1, priv2, 'same private key') 53 | var pubk1 = dh1.getPublicKey() 54 | var pubk2 = dh2.getPublicKey() 55 | t.equals(pubk1.toString('hex'), pubk2.toString('hex'), 'same public keys, uncompressed') 56 | t.equals(dh1.getPublicKey('hex', 'compressed'), dh2.getPublicKey('hex', 'compressed'), 'same public keys compressed') 57 | t.equals(dh1.getPublicKey('hex', 'hybrid'), dh2.getPublicKey('hex', 'hybrid'), 'same public keys hybrid') 58 | var pub1 = dh1.computeSecret(pubk2).toString('hex') 59 | var pub2 = dh2.computeSecret(pubk1).toString('hex') 60 | t.equals(pub1, pub2, 'equal secrets') 61 | }) 62 | test(mod + ' run ' + i + ' new way to set stuff', function (t) { 63 | t.plan(5) 64 | var dh1 = myCrypto(mod) 65 | var dh2 = nodeCrypto(mod) 66 | dh2.generateKeys() 67 | dh1.setPrivateKey(dh2.getPrivateKey()) 68 | var priv1 = dh1.getPrivateKey('hex') 69 | var priv2 = dh2.getPrivateKey('hex') 70 | t.equals(priv1, priv2, 'same private key') 71 | var pubk1 = dh1.getPublicKey() 72 | var pubk2 = dh2.getPublicKey() 73 | t.equals(pubk1.toString('hex'), pubk2.toString('hex'), 'same public keys, uncompressed') 74 | t.equals(dh1.getPublicKey('hex', 'compressed'), dh2.getPublicKey('hex', 'compressed'), 'same public keys compressed') 75 | t.equals(dh1.getPublicKey('hex', 'hybrid'), dh2.getPublicKey('hex', 'hybrid'), 'same public keys hybrid') 76 | var pub1 = dh1.computeSecret(pubk2).toString('hex') 77 | var pub2 = dh2.computeSecret(pubk1).toString('hex') 78 | t.equals(pub1, pub2, 'equal secrets') 79 | }) 80 | }) 81 | } 82 | apitests('api tests for my crypto', myCrypto) 83 | apitests('api tests for node crypto', nodeCrypto) 84 | function apitests (name, crypto) { 85 | test(name, function (t) { 86 | t.test('check about regenerating keys', function (t) { 87 | t.plan(2) 88 | var dh1 = crypto('secp256k1') 89 | var dh2 = crypto('secp256k1') 90 | dh1.generateKeys() 91 | dh2.generateKeys() 92 | var pub = dh1.getPublicKey('hex') 93 | var priv = dh1.getPrivateKey('hex') 94 | dh1.setPrivateKey(dh2.getPrivateKey()) 95 | t.notEquals(dh1.getPrivateKey('hex'), priv, 'private keys not equal') 96 | t.notEquals(dh1.getPublicKey('hex'), pub, 'public keys not equal') 97 | }) 98 | t.test('set private keys without generating them', function (t) { 99 | t.plan(1) 100 | var dh1 = crypto('secp256k1') 101 | var dh2 = crypto('secp256k1') 102 | dh2.generateKeys() 103 | dh1.setPrivateKey(dh2.getPrivateKey()) 104 | t.equals(dh1.getPublicKey('hex'), dh1.getPublicKey('hex'), 'equal public keys') 105 | }) 106 | }) 107 | } 108 | var i = 0 109 | while (++i < 20) { 110 | run(i) 111 | } 112 | --------------------------------------------------------------------------------