twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

rfc4253.js (4240B)


      1 // Copyright 2015 Joyent, Inc.
      2 
      3 module.exports = {
      4 	read: read.bind(undefined, false, undefined),
      5 	readType: read.bind(undefined, false),
      6 	write: write,
      7 	/* semi-private api, used by sshpk-agent */
      8 	readPartial: read.bind(undefined, true),
      9 
     10 	/* shared with ssh format */
     11 	readInternal: read,
     12 	keyTypeToAlg: keyTypeToAlg,
     13 	algToKeyType: algToKeyType
     14 };
     15 
     16 var assert = require('assert-plus');
     17 var Buffer = require('safer-buffer').Buffer;
     18 var algs = require('../algs');
     19 var utils = require('../utils');
     20 var Key = require('../key');
     21 var PrivateKey = require('../private-key');
     22 var SSHBuffer = require('../ssh-buffer');
     23 
     24 function algToKeyType(alg) {
     25 	assert.string(alg);
     26 	if (alg === 'ssh-dss')
     27 		return ('dsa');
     28 	else if (alg === 'ssh-rsa')
     29 		return ('rsa');
     30 	else if (alg === 'ssh-ed25519')
     31 		return ('ed25519');
     32 	else if (alg === 'ssh-curve25519')
     33 		return ('curve25519');
     34 	else if (alg.match(/^ecdsa-sha2-/))
     35 		return ('ecdsa');
     36 	else
     37 		throw (new Error('Unknown algorithm ' + alg));
     38 }
     39 
     40 function keyTypeToAlg(key) {
     41 	assert.object(key);
     42 	if (key.type === 'dsa')
     43 		return ('ssh-dss');
     44 	else if (key.type === 'rsa')
     45 		return ('ssh-rsa');
     46 	else if (key.type === 'ed25519')
     47 		return ('ssh-ed25519');
     48 	else if (key.type === 'curve25519')
     49 		return ('ssh-curve25519');
     50 	else if (key.type === 'ecdsa')
     51 		return ('ecdsa-sha2-' + key.part.curve.data.toString());
     52 	else
     53 		throw (new Error('Unknown key type ' + key.type));
     54 }
     55 
     56 function read(partial, type, buf, options) {
     57 	if (typeof (buf) === 'string')
     58 		buf = Buffer.from(buf);
     59 	assert.buffer(buf, 'buf');
     60 
     61 	var key = {};
     62 
     63 	var parts = key.parts = [];
     64 	var sshbuf = new SSHBuffer({buffer: buf});
     65 
     66 	var alg = sshbuf.readString();
     67 	assert.ok(!sshbuf.atEnd(), 'key must have at least one part');
     68 
     69 	key.type = algToKeyType(alg);
     70 
     71 	var partCount = algs.info[key.type].parts.length;
     72 	if (type && type === 'private')
     73 		partCount = algs.privInfo[key.type].parts.length;
     74 
     75 	while (!sshbuf.atEnd() && parts.length < partCount)
     76 		parts.push(sshbuf.readPart());
     77 	while (!partial && !sshbuf.atEnd())
     78 		parts.push(sshbuf.readPart());
     79 
     80 	assert.ok(parts.length >= 1,
     81 	    'key must have at least one part');
     82 	assert.ok(partial || sshbuf.atEnd(),
     83 	    'leftover bytes at end of key');
     84 
     85 	var Constructor = Key;
     86 	var algInfo = algs.info[key.type];
     87 	if (type === 'private' || algInfo.parts.length !== parts.length) {
     88 		algInfo = algs.privInfo[key.type];
     89 		Constructor = PrivateKey;
     90 	}
     91 	assert.strictEqual(algInfo.parts.length, parts.length);
     92 
     93 	if (key.type === 'ecdsa') {
     94 		var res = /^ecdsa-sha2-(.+)$/.exec(alg);
     95 		assert.ok(res !== null);
     96 		assert.strictEqual(res[1], parts[0].data.toString());
     97 	}
     98 
     99 	var normalized = true;
    100 	for (var i = 0; i < algInfo.parts.length; ++i) {
    101 		var p = parts[i];
    102 		p.name = algInfo.parts[i];
    103 		/*
    104 		 * OpenSSH stores ed25519 "private" keys as seed + public key
    105 		 * concat'd together (k followed by A). We want to keep them
    106 		 * separate for other formats that don't do this.
    107 		 */
    108 		if (key.type === 'ed25519' && p.name === 'k')
    109 			p.data = p.data.slice(0, 32);
    110 
    111 		if (p.name !== 'curve' && algInfo.normalize !== false) {
    112 			var nd;
    113 			if (key.type === 'ed25519') {
    114 				nd = utils.zeroPadToLength(p.data, 32);
    115 			} else {
    116 				nd = utils.mpNormalize(p.data);
    117 			}
    118 			if (nd.toString('binary') !==
    119 			    p.data.toString('binary')) {
    120 				p.data = nd;
    121 				normalized = false;
    122 			}
    123 		}
    124 	}
    125 
    126 	if (normalized)
    127 		key._rfc4253Cache = sshbuf.toBuffer();
    128 
    129 	if (partial && typeof (partial) === 'object') {
    130 		partial.remainder = sshbuf.remainder();
    131 		partial.consumed = sshbuf._offset;
    132 	}
    133 
    134 	return (new Constructor(key));
    135 }
    136 
    137 function write(key, options) {
    138 	assert.object(key);
    139 
    140 	var alg = keyTypeToAlg(key);
    141 	var i;
    142 
    143 	var algInfo = algs.info[key.type];
    144 	if (PrivateKey.isPrivateKey(key))
    145 		algInfo = algs.privInfo[key.type];
    146 	var parts = algInfo.parts;
    147 
    148 	var buf = new SSHBuffer({});
    149 
    150 	buf.writeString(alg);
    151 
    152 	for (i = 0; i < parts.length; ++i) {
    153 		var data = key.part[parts[i]].data;
    154 		if (algInfo.normalize !== false) {
    155 			if (key.type === 'ed25519')
    156 				data = utils.zeroPadToLength(data, 32);
    157 			else
    158 				data = utils.mpNormalize(data);
    159 		}
    160 		if (key.type === 'ed25519' && parts[i] === 'k')
    161 			data = Buffer.concat([data, key.part.A.data]);
    162 		buf.writeBuffer(data);
    163 	}
    164 
    165 	return (buf.toBuffer());
    166 }