From a17ec8861242649038dcdba8f8d7df5edf2ddb8c Mon Sep 17 00:00:00 2001 From: Alex Wilson Date: Fri, 5 May 2017 12:14:31 -0700 Subject: [PATCH] joyent/node-sshpk#28 remove remaining usage of jodid25519 joyent/node-sshpk#30 curve25519 keys should always have size 256 Reviewed by: Cody Peter Mello --- lib/dhe.js | 33 +++++++++++++++++---------------- lib/key.js | 2 +- lib/private-key.js | 27 +++++++++++++-------------- package.json | 3 +-- test/assets/ed25519-negative | 7 +++++++ test/dhe.js | 14 ++++++++++++++ 6 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 test/assets/ed25519-negative diff --git a/lib/dhe.js b/lib/dhe.js index 74f5e04..b4d3662 100644 --- a/lib/dhe.js +++ b/lib/dhe.js @@ -10,7 +10,6 @@ var assert = require('assert-plus'); var crypto = require('crypto'); var algs = require('./algs'); var utils = require('./utils'); -var ed; var nacl; var Key = require('./key'); @@ -76,14 +75,11 @@ function DiffieHellman(key) { this._dh.setPublicKey(key.part.Q.data); } else if (key.type === 'curve25519') { - if (ed === undefined) - ed = require('jodid25519'); + if (nacl === undefined) + nacl = require('tweetnacl'); if (this._isPriv) { this._priv = key.part.r.data; - if (this._priv[0] === 0x00) - this._priv = this._priv.slice(1); - this._priv = this._priv.slice(0, 32); } } else { @@ -180,14 +176,17 @@ DiffieHellman.prototype.computeSecret = function (otherpk) { } else if (this._algo === 'curve25519') { pub = otherpk.part.R.data; - if (pub[0] === 0x00) + while (pub[0] === 0x00 && pub.length > 32) pub = pub.slice(1); + assert.strictEqual(pub.length, 32); + assert.strictEqual(this._priv.length, 64); - var secret = ed.dh.computeKey( - this._priv.toString('binary'), - pub.toString('binary')); + var priv = this._priv.slice(0, 32); - return (new Buffer(secret, 'binary')); + var secret = nacl.box.before(new Uint8Array(pub), + new Uint8Array(priv)); + + return (new Buffer(secret)); } throw (new Error('Invalid algorithm: ' + this._algo)); @@ -255,13 +254,15 @@ DiffieHellman.prototype.generateKey = function () { } } else if (this._algo === 'curve25519') { - priv = ed.dh.generateKey(); - pub = ed.dh.publicKey(priv); - this._priv = priv = new Buffer(priv, 'binary'); - pub = new Buffer(pub, 'binary'); + var pair = nacl.box.keyPair(); + priv = new Buffer(pair.secretKey); + pub = new Buffer(pair.publicKey); + priv = Buffer.concat([priv, pub]); + assert.strictEqual(priv.length, 64); + assert.strictEqual(pub.length, 32); parts.push({name: 'R', data: pub}); - parts.push({name: 'r', data: Buffer.concat([priv, pub])}); + parts.push({name: 'r', data: priv}); this._key = new PrivateKey({ type: 'curve25519', parts: parts diff --git a/lib/key.js b/lib/key.js index 56b8cb4..64e24b4 100644 --- a/lib/key.js +++ b/lib/key.js @@ -64,7 +64,7 @@ function Key(opts) { var curve = this.part.curve.data.toString(); this.curve = curve; sz = algs.curves[curve].size; - } else if (this.type === 'ed25519') { + } else if (this.type === 'ed25519' || this.type === 'curve25519') { sz = 256; this.curve = 'curve25519'; } else { diff --git a/lib/private-key.js b/lib/private-key.js index b56201a..36b6f8c 100644 --- a/lib/private-key.js +++ b/lib/private-key.js @@ -14,7 +14,7 @@ var dhe = require('./dhe'); var generateECDSA = dhe.generateECDSA; var generateED25519 = dhe.generateED25519; var edCompat; -var ed; +var nacl; try { edCompat = require('./ed-compat'); @@ -83,22 +83,22 @@ PrivateKey.prototype.toPublic = function () { return (this._pubCache); }; -PrivateKey.prototype.derive = function (newType, newSize) { +PrivateKey.prototype.derive = function (newType) { assert.string(newType, 'type'); - assert.optionalNumber(newSize, 'size'); - var priv, pub; + var priv, pub, pair; if (this.type === 'ed25519' && newType === 'curve25519') { - if (ed === undefined) - ed = require('jodid25519'); + if (nacl === undefined) + nacl = require('tweetnacl'); priv = this.part.r.data; if (priv[0] === 0x00) priv = priv.slice(1); priv = priv.slice(0, 32); - pub = ed.dh.publicKey(priv); - priv = utils.mpNormalize(Buffer.concat([priv, pub])); + pair = nacl.box.keyPair.fromSecretKey(new Uint8Array(priv)); + pub = new Buffer(pair.publicKey); + priv = Buffer.concat([priv, pub]); return (new PrivateKey({ type: 'curve25519', @@ -108,18 +108,17 @@ PrivateKey.prototype.derive = function (newType, newSize) { ] })); } else if (this.type === 'curve25519' && newType === 'ed25519') { - if (ed === undefined) - ed = require('jodid25519'); + if (nacl === undefined) + nacl = require('tweetnacl'); priv = this.part.r.data; if (priv[0] === 0x00) priv = priv.slice(1); priv = priv.slice(0, 32); - pub = ed.eddsa.publicKey(priv.toString('binary')); - pub = new Buffer(pub, 'binary'); - - priv = utils.mpNormalize(Buffer.concat([priv, pub])); + pair = nacl.sign.keyPair.fromSeed(new Uint8Array(priv)); + pub = new Buffer(pair.publicKey); + priv = Buffer.concat([priv, pub]); return (new PrivateKey({ type: 'ed25519', diff --git a/package.json b/package.json index a20ca57..a553c66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sshpk", - "version": "1.13.0", + "version": "1.13.1", "description": "A library for finding and using SSH public keys", "main": "lib/index.js", "scripts": { @@ -47,7 +47,6 @@ "optionalDependencies": { "jsbn": "~0.1.0", "tweetnacl": "~0.14.0", - "jodid25519": "^1.0.0", "ecc-jsbn": "~0.1.1", "bcrypt-pbkdf": "^1.0.0" }, diff --git a/test/assets/ed25519-negative b/test/assets/ed25519-negative new file mode 100644 index 0000000..3c5c4d1 --- /dev/null +++ b/test/assets/ed25519-negative @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACAvKwD3bKfjHMID69k5Sd/4RQvU1eOG170KVjJK11JCmwAAAIjZfvZs2X72 +bAAAAAtzc2gtZWQyNTUxOQAAACAvKwD3bKfjHMID69k5Sd/4RQvU1eOG170KVjJK11JCmw +AAAECwk2CUV9fFK55Q5A8H4vCx07qS3FOSAOFwZ9Yid8gvqC8rAPdsp+McwgPr2TlJ3/hF +C9TV44bXvQpWMkrXUkKbAAAAAAECAwQF +-----END OPENSSH PRIVATE KEY----- diff --git a/test/dhe.js b/test/dhe.js index 44f81fd..511bebb 100644 --- a/test/dhe.js +++ b/test/dhe.js @@ -10,6 +10,7 @@ var sinon = require('sinon'); var ED_KEY, ED2_KEY, EC_KEY, EC2_KEY, ECOUT_KEY, DS_KEY, DS2_KEY, DSOUT_KEY; var C_KEY, C2_KEY; +var NG_KEY; var C_SSH; var testDir = path.join(__dirname, 'assets'); @@ -31,6 +32,8 @@ test('setup', function (t) { DS2_KEY = sshpk.parsePrivateKey(k); k = fs.readFileSync(path.join(testDir, 'id_dsa')); DSOUT_KEY = sshpk.parsePrivateKey(k); + k = fs.readFileSync(path.join(testDir, 'ed25519-negative')); + NG_KEY = sshpk.parsePrivateKey(k); t.end(); }); @@ -43,6 +46,17 @@ test('derive ed25519 -> curve25519', function (t) { t.end(); }); +test('derive ed25519 -> curve25519 -> back (negative seed)', function (t) { + var key = NG_KEY.derive('curve25519'); + t.strictEqual(key.type, 'curve25519'); + t.strictEqual(key.size, 256); + var key2 = key.derive('ed25519'); + t.ok(key2.fingerprint().matches(NG_KEY)); + t.strictEqual(key2.part.r.toString('base64'), + key.part.r.toString('base64')); + t.end(); +}); + test('derive curve25519 -> ed25519', function (t) { var k = sshpk.parsePrivateKey(C_SSH); t.strictEqual(k.type, 'curve25519');