| // Copyright 2015 Joyent, Inc. | 
|   | 
| module.exports = { | 
|     read: read.bind(undefined, false, undefined), | 
|     readType: read.bind(undefined, false), | 
|     write: write, | 
|     /* semi-private api, used by sshpk-agent */ | 
|     readPartial: read.bind(undefined, true), | 
|   | 
|     /* shared with ssh format */ | 
|     readInternal: read, | 
|     keyTypeToAlg: keyTypeToAlg, | 
|     algToKeyType: algToKeyType | 
| }; | 
|   | 
| var assert = require('assert-plus'); | 
| var Buffer = require('safer-buffer').Buffer; | 
| var algs = require('../algs'); | 
| var utils = require('../utils'); | 
| var Key = require('../key'); | 
| var PrivateKey = require('../private-key'); | 
| var SSHBuffer = require('../ssh-buffer'); | 
|   | 
| function algToKeyType(alg) { | 
|     assert.string(alg); | 
|     if (alg === 'ssh-dss') | 
|         return ('dsa'); | 
|     else if (alg === 'ssh-rsa') | 
|         return ('rsa'); | 
|     else if (alg === 'ssh-ed25519') | 
|         return ('ed25519'); | 
|     else if (alg === 'ssh-curve25519') | 
|         return ('curve25519'); | 
|     else if (alg.match(/^ecdsa-sha2-/)) | 
|         return ('ecdsa'); | 
|     else | 
|         throw (new Error('Unknown algorithm ' + alg)); | 
| } | 
|   | 
| function keyTypeToAlg(key) { | 
|     assert.object(key); | 
|     if (key.type === 'dsa') | 
|         return ('ssh-dss'); | 
|     else if (key.type === 'rsa') | 
|         return ('ssh-rsa'); | 
|     else if (key.type === 'ed25519') | 
|         return ('ssh-ed25519'); | 
|     else if (key.type === 'curve25519') | 
|         return ('ssh-curve25519'); | 
|     else if (key.type === 'ecdsa') | 
|         return ('ecdsa-sha2-' + key.part.curve.data.toString()); | 
|     else | 
|         throw (new Error('Unknown key type ' + key.type)); | 
| } | 
|   | 
| function read(partial, type, buf, options) { | 
|     if (typeof (buf) === 'string') | 
|         buf = Buffer.from(buf); | 
|     assert.buffer(buf, 'buf'); | 
|   | 
|     var key = {}; | 
|   | 
|     var parts = key.parts = []; | 
|     var sshbuf = new SSHBuffer({buffer: buf}); | 
|   | 
|     var alg = sshbuf.readString(); | 
|     assert.ok(!sshbuf.atEnd(), 'key must have at least one part'); | 
|   | 
|     key.type = algToKeyType(alg); | 
|   | 
|     var partCount = algs.info[key.type].parts.length; | 
|     if (type && type === 'private') | 
|         partCount = algs.privInfo[key.type].parts.length; | 
|   | 
|     while (!sshbuf.atEnd() && parts.length < partCount) | 
|         parts.push(sshbuf.readPart()); | 
|     while (!partial && !sshbuf.atEnd()) | 
|         parts.push(sshbuf.readPart()); | 
|   | 
|     assert.ok(parts.length >= 1, | 
|         'key must have at least one part'); | 
|     assert.ok(partial || sshbuf.atEnd(), | 
|         'leftover bytes at end of key'); | 
|   | 
|     var Constructor = Key; | 
|     var algInfo = algs.info[key.type]; | 
|     if (type === 'private' || algInfo.parts.length !== parts.length) { | 
|         algInfo = algs.privInfo[key.type]; | 
|         Constructor = PrivateKey; | 
|     } | 
|     assert.strictEqual(algInfo.parts.length, parts.length); | 
|   | 
|     if (key.type === 'ecdsa') { | 
|         var res = /^ecdsa-sha2-(.+)$/.exec(alg); | 
|         assert.ok(res !== null); | 
|         assert.strictEqual(res[1], parts[0].data.toString()); | 
|     } | 
|   | 
|     var normalized = true; | 
|     for (var i = 0; i < algInfo.parts.length; ++i) { | 
|         var p = parts[i]; | 
|         p.name = algInfo.parts[i]; | 
|         /* | 
|          * OpenSSH stores ed25519 "private" keys as seed + public key | 
|          * concat'd together (k followed by A). We want to keep them | 
|          * separate for other formats that don't do this. | 
|          */ | 
|         if (key.type === 'ed25519' && p.name === 'k') | 
|             p.data = p.data.slice(0, 32); | 
|   | 
|         if (p.name !== 'curve' && algInfo.normalize !== false) { | 
|             var nd; | 
|             if (key.type === 'ed25519') { | 
|                 nd = utils.zeroPadToLength(p.data, 32); | 
|             } else { | 
|                 nd = utils.mpNormalize(p.data); | 
|             } | 
|             if (nd.toString('binary') !== | 
|                 p.data.toString('binary')) { | 
|                 p.data = nd; | 
|                 normalized = false; | 
|             } | 
|         } | 
|     } | 
|   | 
|     if (normalized) | 
|         key._rfc4253Cache = sshbuf.toBuffer(); | 
|   | 
|     if (partial && typeof (partial) === 'object') { | 
|         partial.remainder = sshbuf.remainder(); | 
|         partial.consumed = sshbuf._offset; | 
|     } | 
|   | 
|     return (new Constructor(key)); | 
| } | 
|   | 
| function write(key, options) { | 
|     assert.object(key); | 
|   | 
|     var alg = keyTypeToAlg(key); | 
|     var i; | 
|   | 
|     var algInfo = algs.info[key.type]; | 
|     if (PrivateKey.isPrivateKey(key)) | 
|         algInfo = algs.privInfo[key.type]; | 
|     var parts = algInfo.parts; | 
|   | 
|     var buf = new SSHBuffer({}); | 
|   | 
|     buf.writeString(alg); | 
|   | 
|     for (i = 0; i < parts.length; ++i) { | 
|         var data = key.part[parts[i]].data; | 
|         if (algInfo.normalize !== false) { | 
|             if (key.type === 'ed25519') | 
|                 data = utils.zeroPadToLength(data, 32); | 
|             else | 
|                 data = utils.mpNormalize(data); | 
|         } | 
|         if (key.type === 'ed25519' && parts[i] === 'k') | 
|             data = Buffer.concat([data, key.part.A.data]); | 
|         buf.writeBuffer(data); | 
|     } | 
|   | 
|     return (buf.toBuffer()); | 
| } |