You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					167 lines
				
				4.1 KiB
			
		
		
			
		
	
	
					167 lines
				
				4.1 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								// 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());
							 | 
						||
| 
								 | 
							
								}
							 |