"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/sshpk/lib/formats/ssh-private.js" (8 Mar 2017, 6881 Bytes) of package /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2015 Joyent, Inc.
    2 
    3 module.exports = {
    4     read: read,
    5     readSSHPrivate: readSSHPrivate,
    6     write: write
    7 };
    8 
    9 var assert = require('assert-plus');
   10 var asn1 = require('asn1');
   11 var algs = require('../algs');
   12 var utils = require('../utils');
   13 var crypto = require('crypto');
   14 
   15 var Key = require('../key');
   16 var PrivateKey = require('../private-key');
   17 var pem = require('./pem');
   18 var rfc4253 = require('./rfc4253');
   19 var SSHBuffer = require('../ssh-buffer');
   20 var errors = require('../errors');
   21 
   22 var bcrypt;
   23 
   24 function read(buf, options) {
   25     return (pem.read(buf, options));
   26 }
   27 
   28 var MAGIC = 'openssh-key-v1';
   29 
   30 function readSSHPrivate(type, buf, options) {
   31     buf = new SSHBuffer({buffer: buf});
   32 
   33     var magic = buf.readCString();
   34     assert.strictEqual(magic, MAGIC, 'bad magic string');
   35 
   36     var cipher = buf.readString();
   37     var kdf = buf.readString();
   38     var kdfOpts = buf.readBuffer();
   39 
   40     var nkeys = buf.readInt();
   41     if (nkeys !== 1) {
   42         throw (new Error('OpenSSH-format key file contains ' +
   43             'multiple keys: this is unsupported.'));
   44     }
   45 
   46     var pubKey = buf.readBuffer();
   47 
   48     if (type === 'public') {
   49         assert.ok(buf.atEnd(), 'excess bytes left after key');
   50         return (rfc4253.read(pubKey));
   51     }
   52 
   53     var privKeyBlob = buf.readBuffer();
   54     assert.ok(buf.atEnd(), 'excess bytes left after key');
   55 
   56     var kdfOptsBuf = new SSHBuffer({ buffer: kdfOpts });
   57     switch (kdf) {
   58     case 'none':
   59         if (cipher !== 'none') {
   60             throw (new Error('OpenSSH-format key uses KDF "none" ' +
   61                  'but specifies a cipher other than "none"'));
   62         }
   63         break;
   64     case 'bcrypt':
   65         var salt = kdfOptsBuf.readBuffer();
   66         var rounds = kdfOptsBuf.readInt();
   67         var cinf = utils.opensshCipherInfo(cipher);
   68         if (bcrypt === undefined) {
   69             bcrypt = require('bcrypt-pbkdf');
   70         }
   71 
   72         if (typeof (options.passphrase) === 'string') {
   73             options.passphrase = new Buffer(options.passphrase,
   74                 'utf-8');
   75         }
   76         if (!Buffer.isBuffer(options.passphrase)) {
   77             throw (new errors.KeyEncryptedError(
   78                 options.filename, 'OpenSSH'));
   79         }
   80 
   81         var pass = new Uint8Array(options.passphrase);
   82         var salti = new Uint8Array(salt);
   83         /* Use the pbkdf to derive both the key and the IV. */
   84         var out = new Uint8Array(cinf.keySize + cinf.blockSize);
   85         var res = bcrypt.pbkdf(pass, pass.length, salti, salti.length,
   86             out, out.length, rounds);
   87         if (res !== 0) {
   88             throw (new Error('bcrypt_pbkdf function returned ' +
   89                 'failure, parameters invalid'));
   90         }
   91         out = new Buffer(out);
   92         var ckey = out.slice(0, cinf.keySize);
   93         var iv = out.slice(cinf.keySize, cinf.keySize + cinf.blockSize);
   94         var cipherStream = crypto.createDecipheriv(cinf.opensslName,
   95             ckey, iv);
   96         cipherStream.setAutoPadding(false);
   97         var chunk, chunks = [];
   98         cipherStream.once('error', function (e) {
   99             if (e.toString().indexOf('bad decrypt') !== -1) {
  100                 throw (new Error('Incorrect passphrase ' +
  101                     'supplied, could not decrypt key'));
  102             }
  103             throw (e);
  104         });
  105         cipherStream.write(privKeyBlob);
  106         cipherStream.end();
  107         while ((chunk = cipherStream.read()) !== null)
  108             chunks.push(chunk);
  109         privKeyBlob = Buffer.concat(chunks);
  110         break;
  111     default:
  112         throw (new Error(
  113             'OpenSSH-format key uses unknown KDF "' + kdf + '"'));
  114     }
  115 
  116     buf = new SSHBuffer({buffer: privKeyBlob});
  117 
  118     var checkInt1 = buf.readInt();
  119     var checkInt2 = buf.readInt();
  120     if (checkInt1 !== checkInt2) {
  121         throw (new Error('Incorrect passphrase supplied, could not ' +
  122             'decrypt key'));
  123     }
  124 
  125     var ret = {};
  126     var key = rfc4253.readInternal(ret, 'private', buf.remainder());
  127 
  128     buf.skip(ret.consumed);
  129 
  130     var comment = buf.readString();
  131     key.comment = comment;
  132 
  133     return (key);
  134 }
  135 
  136 function write(key, options) {
  137     var pubKey;
  138     if (PrivateKey.isPrivateKey(key))
  139         pubKey = key.toPublic();
  140     else
  141         pubKey = key;
  142 
  143     var cipher = 'none';
  144     var kdf = 'none';
  145     var kdfopts = new Buffer(0);
  146     var cinf = { blockSize: 8 };
  147     var passphrase;
  148     if (options !== undefined) {
  149         passphrase = options.passphrase;
  150         if (typeof (passphrase) === 'string')
  151             passphrase = new Buffer(passphrase, 'utf-8');
  152         if (passphrase !== undefined) {
  153             assert.buffer(passphrase, 'options.passphrase');
  154             assert.optionalString(options.cipher, 'options.cipher');
  155             cipher = options.cipher;
  156             if (cipher === undefined)
  157                 cipher = 'aes128-ctr';
  158             cinf = utils.opensshCipherInfo(cipher);
  159             kdf = 'bcrypt';
  160         }
  161     }
  162 
  163     var privBuf;
  164     if (PrivateKey.isPrivateKey(key)) {
  165         privBuf = new SSHBuffer({});
  166         var checkInt = crypto.randomBytes(4).readUInt32BE(0);
  167         privBuf.writeInt(checkInt);
  168         privBuf.writeInt(checkInt);
  169         privBuf.write(key.toBuffer('rfc4253'));
  170         privBuf.writeString(key.comment || '');
  171 
  172         var n = 1;
  173         while (privBuf._offset % cinf.blockSize !== 0)
  174             privBuf.writeChar(n++);
  175         privBuf = privBuf.toBuffer();
  176     }
  177 
  178     switch (kdf) {
  179     case 'none':
  180         break;
  181     case 'bcrypt':
  182         var salt = crypto.randomBytes(16);
  183         var rounds = 16;
  184         var kdfssh = new SSHBuffer({});
  185         kdfssh.writeBuffer(salt);
  186         kdfssh.writeInt(rounds);
  187         kdfopts = kdfssh.toBuffer();
  188 
  189         if (bcrypt === undefined) {
  190             bcrypt = require('bcrypt-pbkdf');
  191         }
  192         var pass = new Uint8Array(passphrase);
  193         var salti = new Uint8Array(salt);
  194         /* Use the pbkdf to derive both the key and the IV. */
  195         var out = new Uint8Array(cinf.keySize + cinf.blockSize);
  196         var res = bcrypt.pbkdf(pass, pass.length, salti, salti.length,
  197             out, out.length, rounds);
  198         if (res !== 0) {
  199             throw (new Error('bcrypt_pbkdf function returned ' +
  200                 'failure, parameters invalid'));
  201         }
  202         out = new Buffer(out);
  203         var ckey = out.slice(0, cinf.keySize);
  204         var iv = out.slice(cinf.keySize, cinf.keySize + cinf.blockSize);
  205 
  206         var cipherStream = crypto.createCipheriv(cinf.opensslName,
  207             ckey, iv);
  208         cipherStream.setAutoPadding(false);
  209         var chunk, chunks = [];
  210         cipherStream.once('error', function (e) {
  211             throw (e);
  212         });
  213         cipherStream.write(privBuf);
  214         cipherStream.end();
  215         while ((chunk = cipherStream.read()) !== null)
  216             chunks.push(chunk);
  217         privBuf = Buffer.concat(chunks);
  218         break;
  219     default:
  220         throw (new Error('Unsupported kdf ' + kdf));
  221     }
  222 
  223     var buf = new SSHBuffer({});
  224 
  225     buf.writeCString(MAGIC);
  226     buf.writeString(cipher);    /* cipher */
  227     buf.writeString(kdf);       /* kdf */
  228     buf.writeBuffer(kdfopts);   /* kdfoptions */
  229 
  230     buf.writeInt(1);        /* nkeys */
  231     buf.writeBuffer(pubKey.toBuffer('rfc4253'));
  232 
  233     if (privBuf)
  234         buf.writeBuffer(privBuf);
  235 
  236     buf = buf.toBuffer();
  237 
  238     var header;
  239     if (PrivateKey.isPrivateKey(key))
  240         header = 'OPENSSH PRIVATE KEY';
  241     else
  242         header = 'OPENSSH PUBLIC KEY';
  243 
  244     var tmp = buf.toString('base64');
  245     var len = tmp.length + (tmp.length / 70) +
  246         18 + 16 + header.length*2 + 10;
  247     buf = new Buffer(len);
  248     var o = 0;
  249     o += buf.write('-----BEGIN ' + header + '-----\n', o);
  250     for (var i = 0; i < tmp.length; ) {
  251         var limit = i + 70;
  252         if (limit > tmp.length)
  253             limit = tmp.length;
  254         o += buf.write(tmp.slice(i, limit), o);
  255         buf[o++] = 10;
  256         i = limit;
  257     }
  258     o += buf.write('-----END ' + header + '-----\n', o);
  259 
  260     return (buf.slice(0, o));
  261 }