| // Copyright 2017 Joyent, Inc. | 
|   | 
| module.exports = PrivateKey; | 
|   | 
| var assert = require('assert-plus'); | 
| var Buffer = require('safer-buffer').Buffer; | 
| var algs = require('./algs'); | 
| var crypto = require('crypto'); | 
| var Fingerprint = require('./fingerprint'); | 
| var Signature = require('./signature'); | 
| var errs = require('./errors'); | 
| var util = require('util'); | 
| var utils = require('./utils'); | 
| var dhe = require('./dhe'); | 
| var generateECDSA = dhe.generateECDSA; | 
| var generateED25519 = dhe.generateED25519; | 
| var edCompat = require('./ed-compat'); | 
| var nacl = require('tweetnacl'); | 
|   | 
| var Key = require('./key'); | 
|   | 
| var InvalidAlgorithmError = errs.InvalidAlgorithmError; | 
| var KeyParseError = errs.KeyParseError; | 
| var KeyEncryptedError = errs.KeyEncryptedError; | 
|   | 
| var formats = {}; | 
| formats['auto'] = require('./formats/auto'); | 
| formats['pem'] = require('./formats/pem'); | 
| formats['pkcs1'] = require('./formats/pkcs1'); | 
| formats['pkcs8'] = require('./formats/pkcs8'); | 
| formats['rfc4253'] = require('./formats/rfc4253'); | 
| formats['ssh-private'] = require('./formats/ssh-private'); | 
| formats['openssh'] = formats['ssh-private']; | 
| formats['ssh'] = formats['ssh-private']; | 
| formats['dnssec'] = require('./formats/dnssec'); | 
| formats['putty'] = require('./formats/putty'); | 
|   | 
| function PrivateKey(opts) { | 
|     assert.object(opts, 'options'); | 
|     Key.call(this, opts); | 
|   | 
|     this._pubCache = undefined; | 
| } | 
| util.inherits(PrivateKey, Key); | 
|   | 
| PrivateKey.formats = formats; | 
|   | 
| PrivateKey.prototype.toBuffer = function (format, options) { | 
|     if (format === undefined) | 
|         format = 'pkcs1'; | 
|     assert.string(format, 'format'); | 
|     assert.object(formats[format], 'formats[format]'); | 
|     assert.optionalObject(options, 'options'); | 
|   | 
|     return (formats[format].write(this, options)); | 
| }; | 
|   | 
| PrivateKey.prototype.hash = function (algo, type) { | 
|     return (this.toPublic().hash(algo, type)); | 
| }; | 
|   | 
| PrivateKey.prototype.fingerprint = function (algo, type) { | 
|     return (this.toPublic().fingerprint(algo, type)); | 
| }; | 
|   | 
| PrivateKey.prototype.toPublic = function () { | 
|     if (this._pubCache) | 
|         return (this._pubCache); | 
|   | 
|     var algInfo = algs.info[this.type]; | 
|     var pubParts = []; | 
|     for (var i = 0; i < algInfo.parts.length; ++i) { | 
|         var p = algInfo.parts[i]; | 
|         pubParts.push(this.part[p]); | 
|     } | 
|   | 
|     this._pubCache = new Key({ | 
|         type: this.type, | 
|         source: this, | 
|         parts: pubParts | 
|     }); | 
|     if (this.comment) | 
|         this._pubCache.comment = this.comment; | 
|     return (this._pubCache); | 
| }; | 
|   | 
| PrivateKey.prototype.derive = function (newType) { | 
|     assert.string(newType, 'type'); | 
|     var priv, pub, pair; | 
|   | 
|     if (this.type === 'ed25519' && newType === 'curve25519') { | 
|         priv = this.part.k.data; | 
|         if (priv[0] === 0x00) | 
|             priv = priv.slice(1); | 
|   | 
|         pair = nacl.box.keyPair.fromSecretKey(new Uint8Array(priv)); | 
|         pub = Buffer.from(pair.publicKey); | 
|   | 
|         return (new PrivateKey({ | 
|             type: 'curve25519', | 
|             parts: [ | 
|                 { name: 'A', data: utils.mpNormalize(pub) }, | 
|                 { name: 'k', data: utils.mpNormalize(priv) } | 
|             ] | 
|         })); | 
|     } else if (this.type === 'curve25519' && newType === 'ed25519') { | 
|         priv = this.part.k.data; | 
|         if (priv[0] === 0x00) | 
|             priv = priv.slice(1); | 
|   | 
|         pair = nacl.sign.keyPair.fromSeed(new Uint8Array(priv)); | 
|         pub = Buffer.from(pair.publicKey); | 
|   | 
|         return (new PrivateKey({ | 
|             type: 'ed25519', | 
|             parts: [ | 
|                 { name: 'A', data: utils.mpNormalize(pub) }, | 
|                 { name: 'k', data: utils.mpNormalize(priv) } | 
|             ] | 
|         })); | 
|     } | 
|     throw (new Error('Key derivation not supported from ' + this.type + | 
|         ' to ' + newType)); | 
| }; | 
|   | 
| PrivateKey.prototype.createVerify = function (hashAlgo) { | 
|     return (this.toPublic().createVerify(hashAlgo)); | 
| }; | 
|   | 
| PrivateKey.prototype.createSign = function (hashAlgo) { | 
|     if (hashAlgo === undefined) | 
|         hashAlgo = this.defaultHashAlgorithm(); | 
|     assert.string(hashAlgo, 'hash algorithm'); | 
|   | 
|     /* ED25519 is not supported by OpenSSL, use a javascript impl. */ | 
|     if (this.type === 'ed25519' && edCompat !== undefined) | 
|         return (new edCompat.Signer(this, hashAlgo)); | 
|     if (this.type === 'curve25519') | 
|         throw (new Error('Curve25519 keys are not suitable for ' + | 
|             'signing or verification')); | 
|   | 
|     var v, nm, err; | 
|     try { | 
|         nm = hashAlgo.toUpperCase(); | 
|         v = crypto.createSign(nm); | 
|     } catch (e) { | 
|         err = e; | 
|     } | 
|     if (v === undefined || (err instanceof Error && | 
|         err.message.match(/Unknown message digest/))) { | 
|         nm = 'RSA-'; | 
|         nm += hashAlgo.toUpperCase(); | 
|         v = crypto.createSign(nm); | 
|     } | 
|     assert.ok(v, 'failed to create verifier'); | 
|     var oldSign = v.sign.bind(v); | 
|     var key = this.toBuffer('pkcs1'); | 
|     var type = this.type; | 
|     var curve = this.curve; | 
|     v.sign = function () { | 
|         var sig = oldSign(key); | 
|         if (typeof (sig) === 'string') | 
|             sig = Buffer.from(sig, 'binary'); | 
|         sig = Signature.parse(sig, type, 'asn1'); | 
|         sig.hashAlgorithm = hashAlgo; | 
|         sig.curve = curve; | 
|         return (sig); | 
|     }; | 
|     return (v); | 
| }; | 
|   | 
| PrivateKey.parse = function (data, format, options) { | 
|     if (typeof (data) !== 'string') | 
|         assert.buffer(data, 'data'); | 
|     if (format === undefined) | 
|         format = 'auto'; | 
|     assert.string(format, 'format'); | 
|     if (typeof (options) === 'string') | 
|         options = { filename: options }; | 
|     assert.optionalObject(options, 'options'); | 
|     if (options === undefined) | 
|         options = {}; | 
|     assert.optionalString(options.filename, 'options.filename'); | 
|     if (options.filename === undefined) | 
|         options.filename = '(unnamed)'; | 
|   | 
|     assert.object(formats[format], 'formats[format]'); | 
|   | 
|     try { | 
|         var k = formats[format].read(data, options); | 
|         assert.ok(k instanceof PrivateKey, 'key is not a private key'); | 
|         if (!k.comment) | 
|             k.comment = options.filename; | 
|         return (k); | 
|     } catch (e) { | 
|         if (e.name === 'KeyEncryptedError') | 
|             throw (e); | 
|         throw (new KeyParseError(options.filename, format, e)); | 
|     } | 
| }; | 
|   | 
| PrivateKey.isPrivateKey = function (obj, ver) { | 
|     return (utils.isCompatible(obj, PrivateKey, ver)); | 
| }; | 
|   | 
| 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 + '"')); | 
|     } | 
| }; | 
|   | 
| /* | 
|  * API versions for PrivateKey: | 
|  * [1,0] -- initial ver | 
|  * [1,1] -- added auto, pkcs[18], openssh/ssh-private formats | 
|  * [1,2] -- added defaultHashAlgorithm | 
|  * [1,3] -- added derive, ed, createDH | 
|  * [1,4] -- first tagged version | 
|  * [1,5] -- changed ed25519 part names and format | 
|  * [1,6] -- type arguments for hash() and fingerprint() | 
|  */ | 
| PrivateKey.prototype._sshpkApiVersion = [1, 6]; | 
|   | 
| PrivateKey._oldVersionDetect = function (obj) { | 
|     assert.func(obj.toPublic); | 
|     assert.func(obj.createSign); | 
|     if (obj.derive) | 
|         return ([1, 3]); | 
|     if (obj.defaultHashAlgorithm) | 
|         return ([1, 2]); | 
|     if (obj.formats['auto']) | 
|         return ([1, 1]); | 
|     return ([1, 0]); | 
| }; |