Initial commit
[yaffs-website] / node_modules / sshpk / lib / fingerprint.js
1 // Copyright 2015 Joyent, Inc.
2
3 module.exports = Fingerprint;
4
5 var assert = require('assert-plus');
6 var algs = require('./algs');
7 var crypto = require('crypto');
8 var errs = require('./errors');
9 var Key = require('./key');
10 var Certificate = require('./certificate');
11 var utils = require('./utils');
12
13 var FingerprintFormatError = errs.FingerprintFormatError;
14 var InvalidAlgorithmError = errs.InvalidAlgorithmError;
15
16 function Fingerprint(opts) {
17         assert.object(opts, 'options');
18         assert.string(opts.type, 'options.type');
19         assert.buffer(opts.hash, 'options.hash');
20         assert.string(opts.algorithm, 'options.algorithm');
21
22         this.algorithm = opts.algorithm.toLowerCase();
23         if (algs.hashAlgs[this.algorithm] !== true)
24                 throw (new InvalidAlgorithmError(this.algorithm));
25
26         this.hash = opts.hash;
27         this.type = opts.type;
28 }
29
30 Fingerprint.prototype.toString = function (format) {
31         if (format === undefined) {
32                 if (this.algorithm === 'md5')
33                         format = 'hex';
34                 else
35                         format = 'base64';
36         }
37         assert.string(format);
38
39         switch (format) {
40         case 'hex':
41                 return (addColons(this.hash.toString('hex')));
42         case 'base64':
43                 return (sshBase64Format(this.algorithm,
44                     this.hash.toString('base64')));
45         default:
46                 throw (new FingerprintFormatError(undefined, format));
47         }
48 };
49
50 Fingerprint.prototype.matches = function (other) {
51         assert.object(other, 'key or certificate');
52         if (this.type === 'key') {
53                 utils.assertCompatible(other, Key, [1, 0], 'key');
54         } else {
55                 utils.assertCompatible(other, Certificate, [1, 0],
56                     'certificate');
57         }
58
59         var theirHash = other.hash(this.algorithm);
60         var theirHash2 = crypto.createHash(this.algorithm).
61             update(theirHash).digest('base64');
62
63         if (this.hash2 === undefined)
64                 this.hash2 = crypto.createHash(this.algorithm).
65                     update(this.hash).digest('base64');
66
67         return (this.hash2 === theirHash2);
68 };
69
70 Fingerprint.parse = function (fp, options) {
71         assert.string(fp, 'fingerprint');
72
73         var alg, hash, enAlgs;
74         if (Array.isArray(options)) {
75                 enAlgs = options;
76                 options = {};
77         }
78         assert.optionalObject(options, 'options');
79         if (options === undefined)
80                 options = {};
81         if (options.enAlgs !== undefined)
82                 enAlgs = options.enAlgs;
83         assert.optionalArrayOfString(enAlgs, 'algorithms');
84
85         var parts = fp.split(':');
86         if (parts.length == 2) {
87                 alg = parts[0].toLowerCase();
88                 /*JSSTYLED*/
89                 var base64RE = /^[A-Za-z0-9+\/=]+$/;
90                 if (!base64RE.test(parts[1]))
91                         throw (new FingerprintFormatError(fp));
92                 try {
93                         hash = new Buffer(parts[1], 'base64');
94                 } catch (e) {
95                         throw (new FingerprintFormatError(fp));
96                 }
97         } else if (parts.length > 2) {
98                 alg = 'md5';
99                 if (parts[0].toLowerCase() === 'md5')
100                         parts = parts.slice(1);
101                 parts = parts.join('');
102                 /*JSSTYLED*/
103                 var md5RE = /^[a-fA-F0-9]+$/;
104                 if (!md5RE.test(parts))
105                         throw (new FingerprintFormatError(fp));
106                 try {
107                         hash = new Buffer(parts, 'hex');
108                 } catch (e) {
109                         throw (new FingerprintFormatError(fp));
110                 }
111         }
112
113         if (alg === undefined)
114                 throw (new FingerprintFormatError(fp));
115
116         if (algs.hashAlgs[alg] === undefined)
117                 throw (new InvalidAlgorithmError(alg));
118
119         if (enAlgs !== undefined) {
120                 enAlgs = enAlgs.map(function (a) { return a.toLowerCase(); });
121                 if (enAlgs.indexOf(alg) === -1)
122                         throw (new InvalidAlgorithmError(alg));
123         }
124
125         return (new Fingerprint({
126                 algorithm: alg,
127                 hash: hash,
128                 type: options.type || 'key'
129         }));
130 };
131
132 function addColons(s) {
133         /*JSSTYLED*/
134         return (s.replace(/(.{2})(?=.)/g, '$1:'));
135 }
136
137 function base64Strip(s) {
138         /*JSSTYLED*/
139         return (s.replace(/=*$/, ''));
140 }
141
142 function sshBase64Format(alg, h) {
143         return (alg.toUpperCase() + ':' + base64Strip(h));
144 }
145
146 Fingerprint.isFingerprint = function (obj, ver) {
147         return (utils.isCompatible(obj, Fingerprint, ver));
148 };
149
150 /*
151  * API versions for Fingerprint:
152  * [1,0] -- initial ver
153  * [1,1] -- first tagged ver
154  */
155 Fingerprint.prototype._sshpkApiVersion = [1, 1];
156
157 Fingerprint._oldVersionDetect = function (obj) {
158         assert.func(obj.toString);
159         assert.func(obj.matches);
160         return ([1, 0]);
161 };