sshpk
Advanced tools
Comparing version 1.11.0 to 1.13.0
103
lib/dhe.js
@@ -1,4 +0,8 @@ | ||
// Copyright 2015 Joyent, Inc. | ||
// Copyright 2017 Joyent, Inc. | ||
module.exports = DiffieHellman; | ||
module.exports = { | ||
DiffieHellman: DiffieHellman, | ||
generateECDSA: generateECDSA, | ||
generateED25519: generateED25519 | ||
}; | ||
@@ -10,2 +14,3 @@ var assert = require('assert-plus'); | ||
var ed; | ||
var nacl; | ||
@@ -313,1 +318,95 @@ var Key = require('./key'); | ||
}; | ||
function generateED25519() { | ||
if (nacl === undefined) | ||
nacl = require('tweetnacl'); | ||
var pair = nacl.sign.keyPair(); | ||
var priv = new Buffer(pair.secretKey); | ||
var pub = new Buffer(pair.publicKey); | ||
assert.strictEqual(priv.length, 64); | ||
assert.strictEqual(pub.length, 32); | ||
var parts = []; | ||
parts.push({name: 'R', data: pub}); | ||
parts.push({name: 'r', data: priv}); | ||
var key = new PrivateKey({ | ||
type: 'ed25519', | ||
parts: parts | ||
}); | ||
return (key); | ||
} | ||
/* Generates a new ECDSA private key on a given curve. */ | ||
function generateECDSA(curve) { | ||
var parts = []; | ||
var key; | ||
if (CRYPTO_HAVE_ECDH) { | ||
/* | ||
* Node crypto doesn't expose key generation directly, but the | ||
* ECDH instances can generate keys. It turns out this just | ||
* calls into the OpenSSL generic key generator, and we can | ||
* read its output happily without doing an actual DH. So we | ||
* use that here. | ||
*/ | ||
var osCurve = { | ||
'nistp256': 'prime256v1', | ||
'nistp384': 'secp384r1', | ||
'nistp521': 'secp521r1' | ||
}[curve]; | ||
var dh = crypto.createECDH(osCurve); | ||
dh.generateKeys(); | ||
parts.push({name: 'curve', | ||
data: new Buffer(curve)}); | ||
parts.push({name: 'Q', data: dh.getPublicKey()}); | ||
parts.push({name: 'd', data: dh.getPrivateKey()}); | ||
key = new PrivateKey({ | ||
type: 'ecdsa', | ||
curve: curve, | ||
parts: parts | ||
}); | ||
return (key); | ||
} else { | ||
if (ecdh === undefined) | ||
ecdh = require('ecc-jsbn'); | ||
if (ec === undefined) | ||
ec = require('ecc-jsbn/lib/ec'); | ||
if (jsbn === undefined) | ||
jsbn = require('jsbn').BigInteger; | ||
var ecParams = new X9ECParameters(curve); | ||
/* This algorithm taken from FIPS PUB 186-4 (section B.4.1) */ | ||
var n = ecParams.getN(); | ||
/* | ||
* The crypto.randomBytes() function can only give us whole | ||
* bytes, so taking a nod from X9.62, we round up. | ||
*/ | ||
var cByteLen = Math.ceil((n.bitLength() + 64) / 8); | ||
var c = new jsbn(crypto.randomBytes(cByteLen)); | ||
var n1 = n.subtract(jsbn.ONE); | ||
var priv = c.mod(n1).add(jsbn.ONE); | ||
var pub = ecParams.getG().multiply(priv); | ||
priv = new Buffer(priv.toByteArray()); | ||
pub = new Buffer(ecParams.getCurve(). | ||
encodePointHex(pub), 'hex'); | ||
parts.push({name: 'curve', data: new Buffer(curve)}); | ||
parts.push({name: 'Q', data: pub}); | ||
parts.push({name: 'd', data: priv}); | ||
key = new PrivateKey({ | ||
type: 'ecdsa', | ||
curve: curve, | ||
parts: parts | ||
}); | ||
return (key); | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
// Copyright 2016 Joyent, Inc. | ||
// Copyright 2017 Joyent, Inc. | ||
@@ -7,2 +7,3 @@ module.exports = { | ||
sign: sign, | ||
signAsync: signAsync, | ||
write: write, | ||
@@ -192,2 +193,34 @@ | ||
function signAsync(cert, signer, done) { | ||
if (cert.signatures.openssh === undefined) | ||
cert.signatures.openssh = {}; | ||
try { | ||
var blob = toBuffer(cert, true); | ||
} catch (e) { | ||
delete (cert.signatures.openssh); | ||
done(e); | ||
return; | ||
} | ||
var sig = cert.signatures.openssh; | ||
signer(blob, function (err, signature) { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
try { | ||
/* | ||
* This will throw if the signature isn't of a | ||
* type/algo that can be used for SSH. | ||
*/ | ||
signature.toBuffer('ssh'); | ||
} catch (e) { | ||
done(e); | ||
return; | ||
} | ||
sig.signature = signature; | ||
done(); | ||
}); | ||
} | ||
function write(cert, options) { | ||
@@ -194,0 +227,0 @@ if (options === undefined) |
@@ -1,2 +0,2 @@ | ||
// Copyright 2016 Joyent, Inc. | ||
// Copyright 2017 Joyent, Inc. | ||
@@ -7,2 +7,3 @@ module.exports = { | ||
sign: sign, | ||
signAsync: signAsync, | ||
write: write | ||
@@ -455,2 +456,28 @@ }; | ||
function signAsync(cert, signer, done) { | ||
if (cert.signatures.x509 === undefined) | ||
cert.signatures.x509 = {}; | ||
var sig = cert.signatures.x509; | ||
var der = new asn1.BerWriter(); | ||
writeTBSCert(cert, der); | ||
var blob = der.buffer; | ||
sig.cache = blob; | ||
signer(blob, function (err, signature) { | ||
if (err) { | ||
done(err); | ||
return; | ||
} | ||
sig.algo = signature.type + '-' + signature.hashAlgorithm; | ||
if (SIGN_ALGS[sig.algo] === undefined) { | ||
done(new Error('Invalid signing algorithm "' + | ||
sig.algo + '"')); | ||
return; | ||
} | ||
sig.signature = signature; | ||
done(); | ||
}); | ||
} | ||
function write(cert, options) { | ||
@@ -457,0 +484,0 @@ var sig = cert.signatures.x509; |
@@ -1,2 +0,2 @@ | ||
// Copyright 2016 Joyent, Inc. | ||
// Copyright 2017 Joyent, Inc. | ||
@@ -3,0 +3,0 @@ module.exports = Identity; |
@@ -21,2 +21,3 @@ // Copyright 2015 Joyent, Inc. | ||
parsePrivateKey: PrivateKey.parse, | ||
generatePrivateKey: PrivateKey.generate, | ||
Certificate: Certificate, | ||
@@ -23,0 +24,0 @@ parseCertificate: Certificate.parse, |
@@ -1,2 +0,2 @@ | ||
// Copyright 2015 Joyent, Inc. | ||
// Copyright 2017 Joyent, Inc. | ||
@@ -10,3 +10,3 @@ module.exports = Key; | ||
var Signature = require('./signature'); | ||
var DiffieHellman = require('./dhe'); | ||
var DiffieHellman = require('./dhe').DiffieHellman; | ||
var errs = require('./errors'); | ||
@@ -175,2 +175,3 @@ var utils = require('./utils'); | ||
var key = this.toBuffer('pkcs8'); | ||
var curve = this.curve; | ||
var self = this; | ||
@@ -184,2 +185,5 @@ v.verify = function (signature, fmt) { | ||
return (false); | ||
if (signature.curve && self.type === 'ecdsa' && | ||
signature.curve !== curve) | ||
return (false); | ||
return (oldVerify(key, signature.toBuffer('asn1'))); | ||
@@ -186,0 +190,0 @@ |
@@ -1,2 +0,2 @@ | ||
// Copyright 2015 Joyent, Inc. | ||
// Copyright 2017 Joyent, Inc. | ||
@@ -13,2 +13,5 @@ module.exports = PrivateKey; | ||
var utils = require('./utils'); | ||
var dhe = require('./dhe'); | ||
var generateECDSA = dhe.generateECDSA; | ||
var generateED25519 = dhe.generateED25519; | ||
var edCompat; | ||
@@ -167,2 +170,3 @@ var ed; | ||
var type = this.type; | ||
var curve = this.curve; | ||
v.sign = function () { | ||
@@ -174,2 +178,3 @@ var sig = oldSign(key); | ||
sig.hashAlgorithm = hashAlgo; | ||
sig.curve = curve; | ||
return (sig); | ||
@@ -214,2 +219,21 @@ }; | ||
PrivateKey.generate = function (type, options) { | ||
if (options === undefined) | ||
options = {}; | ||
assert.object(options, 'options'); | ||
switch (type) { | ||
case 'ecdsa': | ||
if (options.curve === undefined) | ||
options.curve = 'nistp256'; | ||
assert.string(options.curve, 'options.curve'); | ||
return (generateECDSA(options.curve)); | ||
case 'ed25519': | ||
return (generateED25519()); | ||
default: | ||
throw (new Error('Key generation not supported with key ' + | ||
'type "' + type + '"')); | ||
} | ||
}; | ||
/* | ||
@@ -216,0 +240,0 @@ * API versions for PrivateKey: |
@@ -29,2 +29,3 @@ // Copyright 2015 Joyent, Inc. | ||
this.hashAlgorithm = opts.hashAlgo; | ||
this.curve = opts.curve; | ||
this.parts = opts.parts; | ||
@@ -40,9 +41,35 @@ this.part = partLookup; | ||
var buf; | ||
var stype = 'ssh-' + this.type; | ||
switch (this.type) { | ||
case 'rsa': | ||
switch (this.hashAlgorithm) { | ||
case 'sha256': | ||
stype = 'rsa-sha2-256'; | ||
break; | ||
case 'sha512': | ||
stype = 'rsa-sha2-512'; | ||
break; | ||
case 'sha1': | ||
case undefined: | ||
break; | ||
default: | ||
throw (new Error('SSH signature ' + | ||
'format does not support hash ' + | ||
'algorithm ' + this.hashAlgorithm)); | ||
} | ||
if (format === 'ssh') { | ||
buf = new SSHBuffer({}); | ||
buf.writeString(stype); | ||
buf.writePart(this.part.sig); | ||
return (buf.toBuffer()); | ||
} else { | ||
return (this.part.sig.data); | ||
} | ||
break; | ||
case 'ed25519': | ||
if (format === 'ssh') { | ||
buf = new SSHBuffer({}); | ||
buf.writeString('ssh-' + this.type); | ||
buf.writeString(stype); | ||
buf.writePart(this.part.sig); | ||
@@ -53,2 +80,3 @@ return (buf.toBuffer()); | ||
} | ||
break; | ||
@@ -132,7 +160,5 @@ case 'dsa': | ||
case 'rsa': | ||
return (parseOneNum(data, type, format, opts, | ||
'ssh-rsa')); | ||
return (parseOneNum(data, type, format, opts)); | ||
case 'ed25519': | ||
return (parseOneNum(data, type, format, opts, | ||
'ssh-ed25519')); | ||
return (parseOneNum(data, type, format, opts)); | ||
@@ -159,3 +185,3 @@ case 'dsa': | ||
function parseOneNum(data, type, format, opts, headType) { | ||
function parseOneNum(data, type, format, opts) { | ||
if (format === 'ssh') { | ||
@@ -168,3 +194,26 @@ try { | ||
} | ||
if (head === headType) { | ||
if (buf !== undefined) { | ||
var msg = 'SSH signature does not match expected ' + | ||
'type (expected ' + type + ', got ' + head + ')'; | ||
switch (head) { | ||
case 'ssh-rsa': | ||
assert.strictEqual(type, 'rsa', msg); | ||
opts.hashAlgo = 'sha1'; | ||
break; | ||
case 'rsa-sha2-256': | ||
assert.strictEqual(type, 'rsa', msg); | ||
opts.hashAlgo = 'sha256'; | ||
break; | ||
case 'rsa-sha2-512': | ||
assert.strictEqual(type, 'rsa', msg); | ||
opts.hashAlgo = 'sha512'; | ||
break; | ||
case 'ssh-ed25519': | ||
assert.strictEqual(type, 'ed25519', msg); | ||
opts.hashAlgo = 'sha512'; | ||
break; | ||
default: | ||
throw (new Error('Unknown SSH signature ' + | ||
'type: ' + head)); | ||
} | ||
var sig = buf.readPart(); | ||
@@ -213,3 +262,22 @@ assert.ok(buf.atEnd(), 'extra trailing bytes'); | ||
var inner = buf.readBuffer(); | ||
if (inner.toString('ascii').match(/^ecdsa-/)) { | ||
var stype = inner.toString('ascii'); | ||
if (stype.slice(0, 6) === 'ecdsa-') { | ||
var parts = stype.split('-'); | ||
assert.strictEqual(parts[0], 'ecdsa'); | ||
assert.strictEqual(parts[1], 'sha2'); | ||
opts.curve = parts[2]; | ||
switch (opts.curve) { | ||
case 'nistp256': | ||
opts.hashAlgo = 'sha256'; | ||
break; | ||
case 'nistp384': | ||
opts.hashAlgo = 'sha384'; | ||
break; | ||
case 'nistp521': | ||
opts.hashAlgo = 'sha512'; | ||
break; | ||
default: | ||
throw (new Error('Unsupported ECDSA curve: ' + | ||
opts.curve)); | ||
} | ||
inner = buf.readBuffer(); | ||
@@ -216,0 +284,0 @@ assert.ok(buf.atEnd(), 'extra trailing bytes on outer'); |
{ | ||
"name": "sshpk", | ||
"version": "1.11.0", | ||
"version": "1.13.0", | ||
"description": "A library for finding and using SSH public keys", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -230,3 +230,3 @@ sshpk | ||
- `pem`: supports both PKCS#1 and PKCS#8 | ||
- `ssh`, `openssh`: new post-OpenSSH 6.5 internal format, produced by | ||
- `ssh`, `openssh`: new post-OpenSSH 6.5 internal format, produced by | ||
`ssh-keygen -o` | ||
@@ -236,3 +236,3 @@ - `pkcs1`, `pkcs8`: variants of `pem` | ||
- `options` -- Optional Object, extra options, with keys: | ||
- `filename` -- Optional String, name for the key being parsed | ||
- `filename` -- Optional String, name for the key being parsed | ||
(eg. the filename that was opened). Used to generate | ||
@@ -243,2 +243,15 @@ Error messages | ||
### `generatePrivateKey(type[, options])` | ||
Generates a new private key of a certain key type, from random data. | ||
Parameters | ||
- `type` -- String, type of key to generate. Currently supported are `'ecdsa'` | ||
and `'ed25519'` | ||
- `options` -- optional Object, with keys: | ||
- `curve` -- optional String, for `'ecdsa'` keys, specifies the curve to use. | ||
If ECDSA is specified and this option is not given, defaults to | ||
using `'nistp256'`. | ||
### `PrivateKey.isPrivateKey(obj)` | ||
@@ -245,0 +258,0 @@ |
190380
4865
699