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.
		
		
		
		
		
			
		
			
				
					
					
						
							221 lines
						
					
					
						
							5.4 KiB
						
					
					
				
			
		
		
	
	
							221 lines
						
					
					
						
							5.4 KiB
						
					
					
				// Copyright 2018 Joyent, Inc.
 | 
						|
 | 
						|
module.exports = Fingerprint;
 | 
						|
 | 
						|
var assert = require('assert-plus');
 | 
						|
var Buffer = require('safer-buffer').Buffer;
 | 
						|
var algs = require('./algs');
 | 
						|
var crypto = require('crypto');
 | 
						|
var errs = require('./errors');
 | 
						|
var Key = require('./key');
 | 
						|
var PrivateKey = require('./private-key');
 | 
						|
var Certificate = require('./certificate');
 | 
						|
var utils = require('./utils');
 | 
						|
 | 
						|
var FingerprintFormatError = errs.FingerprintFormatError;
 | 
						|
var InvalidAlgorithmError = errs.InvalidAlgorithmError;
 | 
						|
 | 
						|
function Fingerprint(opts) {
 | 
						|
	assert.object(opts, 'options');
 | 
						|
	assert.string(opts.type, 'options.type');
 | 
						|
	assert.buffer(opts.hash, 'options.hash');
 | 
						|
	assert.string(opts.algorithm, 'options.algorithm');
 | 
						|
 | 
						|
	this.algorithm = opts.algorithm.toLowerCase();
 | 
						|
	if (algs.hashAlgs[this.algorithm] !== true)
 | 
						|
		throw (new InvalidAlgorithmError(this.algorithm));
 | 
						|
 | 
						|
	this.hash = opts.hash;
 | 
						|
	this.type = opts.type;
 | 
						|
	this.hashType = opts.hashType;
 | 
						|
}
 | 
						|
 | 
						|
Fingerprint.prototype.toString = function (format) {
 | 
						|
	if (format === undefined) {
 | 
						|
		if (this.algorithm === 'md5' || this.hashType === 'spki')
 | 
						|
			format = 'hex';
 | 
						|
		else
 | 
						|
			format = 'base64';
 | 
						|
	}
 | 
						|
	assert.string(format);
 | 
						|
 | 
						|
	switch (format) {
 | 
						|
	case 'hex':
 | 
						|
		if (this.hashType === 'spki')
 | 
						|
			return (this.hash.toString('hex'));
 | 
						|
		return (addColons(this.hash.toString('hex')));
 | 
						|
	case 'base64':
 | 
						|
		if (this.hashType === 'spki')
 | 
						|
			return (this.hash.toString('base64'));
 | 
						|
		return (sshBase64Format(this.algorithm,
 | 
						|
		    this.hash.toString('base64')));
 | 
						|
	default:
 | 
						|
		throw (new FingerprintFormatError(undefined, format));
 | 
						|
	}
 | 
						|
};
 | 
						|
 | 
						|
Fingerprint.prototype.matches = function (other) {
 | 
						|
	assert.object(other, 'key or certificate');
 | 
						|
	if (this.type === 'key' && this.hashType !== 'ssh') {
 | 
						|
		utils.assertCompatible(other, Key, [1, 7], 'key with spki');
 | 
						|
		if (PrivateKey.isPrivateKey(other)) {
 | 
						|
			utils.assertCompatible(other, PrivateKey, [1, 6],
 | 
						|
			    'privatekey with spki support');
 | 
						|
		}
 | 
						|
	} else if (this.type === 'key') {
 | 
						|
		utils.assertCompatible(other, Key, [1, 0], 'key');
 | 
						|
	} else {
 | 
						|
		utils.assertCompatible(other, Certificate, [1, 0],
 | 
						|
		    'certificate');
 | 
						|
	}
 | 
						|
 | 
						|
	var theirHash = other.hash(this.algorithm, this.hashType);
 | 
						|
	var theirHash2 = crypto.createHash(this.algorithm).
 | 
						|
	    update(theirHash).digest('base64');
 | 
						|
 | 
						|
	if (this.hash2 === undefined)
 | 
						|
		this.hash2 = crypto.createHash(this.algorithm).
 | 
						|
		    update(this.hash).digest('base64');
 | 
						|
 | 
						|
	return (this.hash2 === theirHash2);
 | 
						|
};
 | 
						|
 | 
						|
/*JSSTYLED*/
 | 
						|
var base64RE = /^[A-Za-z0-9+\/=]+$/;
 | 
						|
/*JSSTYLED*/
 | 
						|
var hexRE = /^[a-fA-F0-9]+$/;
 | 
						|
 | 
						|
Fingerprint.parse = function (fp, options) {
 | 
						|
	assert.string(fp, 'fingerprint');
 | 
						|
 | 
						|
	var alg, hash, enAlgs;
 | 
						|
	if (Array.isArray(options)) {
 | 
						|
		enAlgs = options;
 | 
						|
		options = {};
 | 
						|
	}
 | 
						|
	assert.optionalObject(options, 'options');
 | 
						|
	if (options === undefined)
 | 
						|
		options = {};
 | 
						|
	if (options.enAlgs !== undefined)
 | 
						|
		enAlgs = options.enAlgs;
 | 
						|
	if (options.algorithms !== undefined)
 | 
						|
		enAlgs = options.algorithms;
 | 
						|
	assert.optionalArrayOfString(enAlgs, 'algorithms');
 | 
						|
 | 
						|
	var hashType = 'ssh';
 | 
						|
	if (options.hashType !== undefined)
 | 
						|
		hashType = options.hashType;
 | 
						|
	assert.string(hashType, 'options.hashType');
 | 
						|
 | 
						|
	var parts = fp.split(':');
 | 
						|
	if (parts.length == 2) {
 | 
						|
		alg = parts[0].toLowerCase();
 | 
						|
		if (!base64RE.test(parts[1]))
 | 
						|
			throw (new FingerprintFormatError(fp));
 | 
						|
		try {
 | 
						|
			hash = Buffer.from(parts[1], 'base64');
 | 
						|
		} catch (e) {
 | 
						|
			throw (new FingerprintFormatError(fp));
 | 
						|
		}
 | 
						|
	} else if (parts.length > 2) {
 | 
						|
		alg = 'md5';
 | 
						|
		if (parts[0].toLowerCase() === 'md5')
 | 
						|
			parts = parts.slice(1);
 | 
						|
		parts = parts.map(function (p) {
 | 
						|
			while (p.length < 2)
 | 
						|
				p = '0' + p;
 | 
						|
			if (p.length > 2)
 | 
						|
				throw (new FingerprintFormatError(fp));
 | 
						|
			return (p);
 | 
						|
		});
 | 
						|
		parts = parts.join('');
 | 
						|
		if (!hexRE.test(parts) || parts.length % 2 !== 0)
 | 
						|
			throw (new FingerprintFormatError(fp));
 | 
						|
		try {
 | 
						|
			hash = Buffer.from(parts, 'hex');
 | 
						|
		} catch (e) {
 | 
						|
			throw (new FingerprintFormatError(fp));
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if (hexRE.test(fp)) {
 | 
						|
			hash = Buffer.from(fp, 'hex');
 | 
						|
		} else if (base64RE.test(fp)) {
 | 
						|
			hash = Buffer.from(fp, 'base64');
 | 
						|
		} else {
 | 
						|
			throw (new FingerprintFormatError(fp));
 | 
						|
		}
 | 
						|
 | 
						|
		switch (hash.length) {
 | 
						|
		case 32:
 | 
						|
			alg = 'sha256';
 | 
						|
			break;
 | 
						|
		case 16:
 | 
						|
			alg = 'md5';
 | 
						|
			break;
 | 
						|
		case 20:
 | 
						|
			alg = 'sha1';
 | 
						|
			break;
 | 
						|
		case 64:
 | 
						|
			alg = 'sha512';
 | 
						|
			break;
 | 
						|
		default:
 | 
						|
			throw (new FingerprintFormatError(fp));
 | 
						|
		}
 | 
						|
 | 
						|
		/* Plain hex/base64: guess it's probably SPKI unless told. */
 | 
						|
		if (options.hashType === undefined)
 | 
						|
			hashType = 'spki';
 | 
						|
	}
 | 
						|
 | 
						|
	if (alg === undefined)
 | 
						|
		throw (new FingerprintFormatError(fp));
 | 
						|
 | 
						|
	if (algs.hashAlgs[alg] === undefined)
 | 
						|
		throw (new InvalidAlgorithmError(alg));
 | 
						|
 | 
						|
	if (enAlgs !== undefined) {
 | 
						|
		enAlgs = enAlgs.map(function (a) { return a.toLowerCase(); });
 | 
						|
		if (enAlgs.indexOf(alg) === -1)
 | 
						|
			throw (new InvalidAlgorithmError(alg));
 | 
						|
	}
 | 
						|
 | 
						|
	return (new Fingerprint({
 | 
						|
		algorithm: alg,
 | 
						|
		hash: hash,
 | 
						|
		type: options.type || 'key',
 | 
						|
		hashType: hashType
 | 
						|
	}));
 | 
						|
};
 | 
						|
 | 
						|
function addColons(s) {
 | 
						|
	/*JSSTYLED*/
 | 
						|
	return (s.replace(/(.{2})(?=.)/g, '$1:'));
 | 
						|
}
 | 
						|
 | 
						|
function base64Strip(s) {
 | 
						|
	/*JSSTYLED*/
 | 
						|
	return (s.replace(/=*$/, ''));
 | 
						|
}
 | 
						|
 | 
						|
function sshBase64Format(alg, h) {
 | 
						|
	return (alg.toUpperCase() + ':' + base64Strip(h));
 | 
						|
}
 | 
						|
 | 
						|
Fingerprint.isFingerprint = function (obj, ver) {
 | 
						|
	return (utils.isCompatible(obj, Fingerprint, ver));
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * API versions for Fingerprint:
 | 
						|
 * [1,0] -- initial ver
 | 
						|
 * [1,1] -- first tagged ver
 | 
						|
 * [1,2] -- hashType and spki support
 | 
						|
 */
 | 
						|
Fingerprint.prototype._sshpkApiVersion = [1, 2];
 | 
						|
 | 
						|
Fingerprint._oldVersionDetect = function (obj) {
 | 
						|
	assert.func(obj.toString);
 | 
						|
	assert.func(obj.matches);
 | 
						|
	return ([1, 0]);
 | 
						|
};
 |