1- // Copyright 2015 Joyent, Inc.
1+ // Copyright 2018 Joyent, Inc.
22
33module . exports = Fingerprint ;
44
@@ -8,6 +8,7 @@ var algs = require('./algs');
88var crypto = require ( 'crypto' ) ;
99var errs = require ( './errors' ) ;
1010var Key = require ( './key' ) ;
11+ var PrivateKey = require ( './private-key' ) ;
1112var Certificate = require ( './certificate' ) ;
1213var utils = require ( './utils' ) ;
1314
@@ -26,11 +27,12 @@ function Fingerprint(opts) {
2627
2728 this . hash = opts . hash ;
2829 this . type = opts . type ;
30+ this . hashType = opts . hashType ;
2931}
3032
3133Fingerprint . prototype . toString = function ( format ) {
3234 if ( format === undefined ) {
33- if ( this . algorithm === 'md5' )
35+ if ( this . algorithm === 'md5' || this . hashType === 'spki' )
3436 format = 'hex' ;
3537 else
3638 format = 'base64' ;
@@ -39,8 +41,12 @@ Fingerprint.prototype.toString = function (format) {
3941
4042 switch ( format ) {
4143 case 'hex' :
44+ if ( this . hashType === 'spki' )
45+ return ( this . hash . toString ( 'hex' ) ) ;
4246 return ( addColons ( this . hash . toString ( 'hex' ) ) ) ;
4347 case 'base64' :
48+ if ( this . hashType === 'spki' )
49+ return ( this . hash . toString ( 'base64' ) ) ;
4450 return ( sshBase64Format ( this . algorithm ,
4551 this . hash . toString ( 'base64' ) ) ) ;
4652 default :
@@ -50,14 +56,20 @@ Fingerprint.prototype.toString = function (format) {
5056
5157Fingerprint . prototype . matches = function ( other ) {
5258 assert . object ( other , 'key or certificate' ) ;
53- if ( this . type === 'key' ) {
59+ if ( this . type === 'key' && this . hashType !== 'ssh' ) {
60+ utils . assertCompatible ( other , Key , [ 1 , 7 ] , 'key with spki' ) ;
61+ if ( PrivateKey . isPrivateKey ( other ) ) {
62+ utils . assertCompatible ( other , PrivateKey , [ 1 , 6 ] ,
63+ 'privatekey with spki support' ) ;
64+ }
65+ } else if ( this . type === 'key' ) {
5466 utils . assertCompatible ( other , Key , [ 1 , 0 ] , 'key' ) ;
5567 } else {
5668 utils . assertCompatible ( other , Certificate , [ 1 , 0 ] ,
5769 'certificate' ) ;
5870 }
5971
60- var theirHash = other . hash ( this . algorithm ) ;
72+ var theirHash = other . hash ( this . algorithm , this . hashType ) ;
6173 var theirHash2 = crypto . createHash ( this . algorithm ) .
6274 update ( theirHash ) . digest ( 'base64' ) ;
6375
@@ -68,6 +80,11 @@ Fingerprint.prototype.matches = function (other) {
6880 return ( this . hash2 === theirHash2 ) ;
6981} ;
7082
83+ /*JSSTYLED*/
84+ var base64RE = / ^ [ A - Z a - z 0 - 9 + \/ = ] + $ / ;
85+ /*JSSTYLED*/
86+ var hexRE = / ^ [ a - f A - F 0 - 9 ] + $ / ;
87+
7188Fingerprint . parse = function ( fp , options ) {
7289 assert . string ( fp , 'fingerprint' ) ;
7390
@@ -81,13 +98,18 @@ Fingerprint.parse = function (fp, options) {
8198 options = { } ;
8299 if ( options . enAlgs !== undefined )
83100 enAlgs = options . enAlgs ;
101+ if ( options . algorithms !== undefined )
102+ enAlgs = options . algorithms ;
84103 assert . optionalArrayOfString ( enAlgs , 'algorithms' ) ;
85104
105+ var hashType = 'ssh' ;
106+ if ( options . hashType !== undefined )
107+ hashType = options . hashType ;
108+ assert . string ( hashType , 'options.hashType' ) ;
109+
86110 var parts = fp . split ( ':' ) ;
87111 if ( parts . length == 2 ) {
88112 alg = parts [ 0 ] . toLowerCase ( ) ;
89- /*JSSTYLED*/
90- var base64RE = / ^ [ A - Z a - z 0 - 9 + \/ = ] + $ / ;
91113 if ( ! base64RE . test ( parts [ 1 ] ) )
92114 throw ( new FingerprintFormatError ( fp ) ) ;
93115 try {
@@ -107,15 +129,42 @@ Fingerprint.parse = function (fp, options) {
107129 return ( p ) ;
108130 } ) ;
109131 parts = parts . join ( '' ) ;
110- /*JSSTYLED*/
111- var md5RE = / ^ [ a - f A - F 0 - 9 ] + $ / ;
112- if ( ! md5RE . test ( parts ) || parts . length % 2 !== 0 )
132+ if ( ! hexRE . test ( parts ) || parts . length % 2 !== 0 )
113133 throw ( new FingerprintFormatError ( fp ) ) ;
114134 try {
115135 hash = Buffer . from ( parts , 'hex' ) ;
116136 } catch ( e ) {
117137 throw ( new FingerprintFormatError ( fp ) ) ;
118138 }
139+ } else {
140+ if ( hexRE . test ( fp ) ) {
141+ hash = Buffer . from ( fp , 'hex' ) ;
142+ } else if ( base64RE . test ( fp ) ) {
143+ hash = Buffer . from ( fp , 'base64' ) ;
144+ } else {
145+ throw ( new FingerprintFormatError ( fp ) ) ;
146+ }
147+
148+ switch ( hash . length ) {
149+ case 32 :
150+ alg = 'sha256' ;
151+ break ;
152+ case 16 :
153+ alg = 'md5' ;
154+ break ;
155+ case 20 :
156+ alg = 'sha1' ;
157+ break ;
158+ case 64 :
159+ alg = 'sha512' ;
160+ break ;
161+ default :
162+ throw ( new FingerprintFormatError ( fp ) ) ;
163+ }
164+
165+ /* Plain hex/base64: guess it's probably SPKI unless told. */
166+ if ( options . hashType === undefined )
167+ hashType = 'spki' ;
119168 }
120169
121170 if ( alg === undefined )
@@ -133,7 +182,8 @@ Fingerprint.parse = function (fp, options) {
133182 return ( new Fingerprint ( {
134183 algorithm : alg ,
135184 hash : hash ,
136- type : options . type || 'key'
185+ type : options . type || 'key' ,
186+ hashType : hashType
137187 } ) ) ;
138188} ;
139189
@@ -159,8 +209,9 @@ Fingerprint.isFingerprint = function (obj, ver) {
159209 * API versions for Fingerprint:
160210 * [1,0] -- initial ver
161211 * [1,1] -- first tagged ver
212+ * [1,2] -- hashType and spki support
162213 */
163- Fingerprint . prototype . _sshpkApiVersion = [ 1 , 1 ] ;
214+ Fingerprint . prototype . _sshpkApiVersion = [ 1 , 2 ] ;
164215
165216Fingerprint . _oldVersionDetect = function ( obj ) {
166217 assert . func ( obj . toString ) ;
0 commit comments