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.
		
		
		
		
		
			
		
			
				
					116 lines
				
				3.1 KiB
			
		
		
			
		
	
	
					116 lines
				
				3.1 KiB
			| 
											3 years ago
										 | // Copyright 2015 Joyent, Inc.
 | ||
|  | 
 | ||
|  | module.exports = { | ||
|  | 	read: read, | ||
|  | 	write: write | ||
|  | }; | ||
|  | 
 | ||
|  | var assert = require('assert-plus'); | ||
|  | var Buffer = require('safer-buffer').Buffer; | ||
|  | var rfc4253 = require('./rfc4253'); | ||
|  | var utils = require('../utils'); | ||
|  | var Key = require('../key'); | ||
|  | var PrivateKey = require('../private-key'); | ||
|  | 
 | ||
|  | var sshpriv = require('./ssh-private'); | ||
|  | 
 | ||
|  | /*JSSTYLED*/ | ||
|  | var SSHKEY_RE = /^([a-z0-9-]+)[ \t]+([a-zA-Z0-9+\/]+[=]*)([ \t]+([^ \t][^\n]*[\n]*)?)?$/; | ||
|  | /*JSSTYLED*/ | ||
|  | var SSHKEY_RE2 = /^([a-z0-9-]+)[ \t\n]+([a-zA-Z0-9+\/][a-zA-Z0-9+\/ \t\n=]*)([^a-zA-Z0-9+\/ \t\n=].*)?$/; | ||
|  | 
 | ||
|  | function read(buf, options) { | ||
|  | 	if (typeof (buf) !== 'string') { | ||
|  | 		assert.buffer(buf, 'buf'); | ||
|  | 		buf = buf.toString('ascii'); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var trimmed = buf.trim().replace(/[\\\r]/g, ''); | ||
|  | 	var m = trimmed.match(SSHKEY_RE); | ||
|  | 	if (!m) | ||
|  | 		m = trimmed.match(SSHKEY_RE2); | ||
|  | 	assert.ok(m, 'key must match regex'); | ||
|  | 
 | ||
|  | 	var type = rfc4253.algToKeyType(m[1]); | ||
|  | 	var kbuf = Buffer.from(m[2], 'base64'); | ||
|  | 
 | ||
|  | 	/* | ||
|  | 	 * This is a bit tricky. If we managed to parse the key and locate the | ||
|  | 	 * key comment with the regex, then do a non-partial read and assert | ||
|  | 	 * that we have consumed all bytes. If we couldn't locate the key | ||
|  | 	 * comment, though, there may be whitespace shenanigans going on that | ||
|  | 	 * have conjoined the comment to the rest of the key. We do a partial | ||
|  | 	 * read in this case to try to make the best out of a sorry situation. | ||
|  | 	 */ | ||
|  | 	var key; | ||
|  | 	var ret = {}; | ||
|  | 	if (m[4]) { | ||
|  | 		try { | ||
|  | 			key = rfc4253.read(kbuf); | ||
|  | 
 | ||
|  | 		} catch (e) { | ||
|  | 			m = trimmed.match(SSHKEY_RE2); | ||
|  | 			assert.ok(m, 'key must match regex'); | ||
|  | 			kbuf = Buffer.from(m[2], 'base64'); | ||
|  | 			key = rfc4253.readInternal(ret, 'public', kbuf); | ||
|  | 		} | ||
|  | 	} else { | ||
|  | 		key = rfc4253.readInternal(ret, 'public', kbuf); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	assert.strictEqual(type, key.type); | ||
|  | 
 | ||
|  | 	if (m[4] && m[4].length > 0) { | ||
|  | 		key.comment = m[4]; | ||
|  | 
 | ||
|  | 	} else if (ret.consumed) { | ||
|  | 		/* | ||
|  | 		 * Now the magic: trying to recover the key comment when it's | ||
|  | 		 * gotten conjoined to the key or otherwise shenanigan'd. | ||
|  | 		 * | ||
|  | 		 * Work out how much base64 we used, then drop all non-base64 | ||
|  | 		 * chars from the beginning up to this point in the the string. | ||
|  | 		 * Then offset in this and try to make up for missing = chars. | ||
|  | 		 */ | ||
|  | 		var data = m[2] + (m[3] ? m[3] : ''); | ||
|  | 		var realOffset = Math.ceil(ret.consumed / 3) * 4; | ||
|  | 		data = data.slice(0, realOffset - 2). /*JSSTYLED*/ | ||
|  | 		    replace(/[^a-zA-Z0-9+\/=]/g, '') + | ||
|  | 		    data.slice(realOffset - 2); | ||
|  | 
 | ||
|  | 		var padding = ret.consumed % 3; | ||
|  | 		if (padding > 0 && | ||
|  | 		    data.slice(realOffset - 1, realOffset) !== '=') | ||
|  | 			realOffset--; | ||
|  | 		while (data.slice(realOffset, realOffset + 1) === '=') | ||
|  | 			realOffset++; | ||
|  | 
 | ||
|  | 		/* Finally, grab what we think is the comment & clean it up. */ | ||
|  | 		var trailer = data.slice(realOffset); | ||
|  | 		trailer = trailer.replace(/[\r\n]/g, ' '). | ||
|  | 		    replace(/^\s+/, ''); | ||
|  | 		if (trailer.match(/^[a-zA-Z0-9]/)) | ||
|  | 			key.comment = trailer; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return (key); | ||
|  | } | ||
|  | 
 | ||
|  | function write(key, options) { | ||
|  | 	assert.object(key); | ||
|  | 	if (!Key.isKey(key)) | ||
|  | 		throw (new Error('Must be a public key')); | ||
|  | 
 | ||
|  | 	var parts = []; | ||
|  | 	var alg = rfc4253.keyTypeToAlg(key); | ||
|  | 	parts.push(alg); | ||
|  | 
 | ||
|  | 	var buf = rfc4253.write(key); | ||
|  | 	parts.push(buf.toString('base64')); | ||
|  | 
 | ||
|  | 	if (key.comment) | ||
|  | 		parts.push(key.comment); | ||
|  | 
 | ||
|  | 	return (Buffer.from(parts.join(' '))); | ||
|  | } |