Socket
Socket
Sign inDemoInstall

sshpk

Package Overview
Dependencies
9
Maintainers
4
Versions
50
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.15.2 to 1.16.0

lib/formats/putty.js

73

lib/fingerprint.js

@@ -1,2 +0,2 @@

// Copyright 2015 Joyent, Inc.
// Copyright 2018 Joyent, Inc.

@@ -11,2 +11,3 @@ module.exports = Fingerprint;

var Key = require('./key');
var PrivateKey = require('./private-key');
var Certificate = require('./certificate');

@@ -30,2 +31,3 @@ var utils = require('./utils');

this.type = opts.type;
this.hashType = opts.hashType;
}

@@ -35,3 +37,3 @@

if (format === undefined) {
if (this.algorithm === 'md5')
if (this.algorithm === 'md5' || this.hashType === 'spki')
format = 'hex';

@@ -45,4 +47,8 @@ else

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,

@@ -57,3 +63,9 @@ this.hash.toString('base64')));

assert.object(other, 'key or certificate');
if (this.type === 'key') {
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');

@@ -65,3 +77,3 @@ } else {

var theirHash = other.hash(this.algorithm);
var theirHash = other.hash(this.algorithm, this.hashType);
var theirHash2 = crypto.createHash(this.algorithm).

@@ -77,2 +89,7 @@ update(theirHash).digest('base64');

/*JSSTYLED*/
var base64RE = /^[A-Za-z0-9+\/=]+$/;
/*JSSTYLED*/
var hexRE = /^[a-fA-F0-9]+$/;
Fingerprint.parse = function (fp, options) {

@@ -91,9 +108,14 @@ assert.string(fp, 'fingerprint');

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();
/*JSSTYLED*/
var base64RE = /^[A-Za-z0-9+\/=]+$/;
if (!base64RE.test(parts[1]))

@@ -118,5 +140,3 @@ throw (new FingerprintFormatError(fp));

parts = parts.join('');
/*JSSTYLED*/
var md5RE = /^[a-fA-F0-9]+$/;
if (!md5RE.test(parts) || parts.length % 2 !== 0)
if (!hexRE.test(parts) || parts.length % 2 !== 0)
throw (new FingerprintFormatError(fp));

@@ -128,2 +148,31 @@ try {

}
} 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';
}

@@ -146,3 +195,4 @@

hash: hash,
type: options.type || 'key'
type: options.type || 'key',
hashType: hashType
}));

@@ -173,4 +223,5 @@ };

* [1,1] -- first tagged ver
* [1,2] -- hashType and spki support
*/
Fingerprint.prototype._sshpkApiVersion = [1, 1];
Fingerprint.prototype._sshpkApiVersion = [1, 2];

@@ -177,0 +228,0 @@ Fingerprint._oldVersionDetect = function (obj) {

@@ -1,2 +0,2 @@

// Copyright 2015 Joyent, Inc.
// Copyright 2018 Joyent, Inc.

@@ -18,2 +18,3 @@ module.exports = {

var dnssec = require('./dnssec');
var putty = require('./putty');

@@ -30,2 +31,4 @@ var DNSSEC_PRIVKEY_HEADER_PREFIX = 'Private-key-format: v1';

return (ssh.read(buf, options));
if (buf.match(/^putty-user-key-file-2:/i))
return (putty.read(buf, options));
if (findDNSSECHeader(buf))

@@ -40,2 +43,4 @@ return (dnssec.read(buf, options));

return (ssh.read(buf, options));
if (findPuTTYHeader(buf))
return (putty.read(buf, options));
if (findDNSSECHeader(buf))

@@ -49,2 +54,14 @@ return (dnssec.read(buf, options));

function findPuTTYHeader(buf) {
var offset = 0;
while (offset < buf.length &&
(buf[offset] === 32 || buf[offset] === 10 || buf[offset] === 9))
++offset;
if (offset + 22 <= buf.length &&
buf.slice(offset, offset + 22).toString('ascii').toLowerCase() ===
'putty-user-key-file-2:')
return (true);
return (false);
}
function findSSHHeader(buf) {

@@ -51,0 +68,0 @@ var offset = 0;

@@ -1,2 +0,2 @@

// Copyright 2015 Joyent, Inc.
// Copyright 2018 Joyent, Inc.

@@ -24,2 +24,25 @@ module.exports = {

var OID_PBES2 = '1.2.840.113549.1.5.13';
var OID_PBKDF2 = '1.2.840.113549.1.5.12';
var OID_TO_CIPHER = {
'1.2.840.113549.3.7': '3des-cbc',
'2.16.840.1.101.3.4.1.2': 'aes128-cbc',
'2.16.840.1.101.3.4.1.42': 'aes256-cbc'
};
var CIPHER_TO_OID = {};
Object.keys(OID_TO_CIPHER).forEach(function (k) {
CIPHER_TO_OID[OID_TO_CIPHER[k]] = k;
});
var OID_TO_HASH = {
'1.2.840.113549.2.7': 'sha1',
'1.2.840.113549.2.9': 'sha256',
'1.2.840.113549.2.11': 'sha512'
};
var HASH_TO_OID = {};
Object.keys(OID_TO_HASH).forEach(function (k) {
HASH_TO_OID[OID_TO_HASH[k]] = k;
});
/*

@@ -36,10 +59,18 @@ * For reading we support both PKCS#1 and PKCS#8. If we find a private key,

var lines = buf.trim().split('\n');
var lines = buf.trim().split(/[\r\n]+/g);
var m = lines[0].match(/*JSSTYLED*/
/[-]+[ ]*BEGIN ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
var m;
var si = -1;
while (!m && si < lines.length) {
m = lines[++si].match(/*JSSTYLED*/
/[-]+[ ]*BEGIN ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
}
assert.ok(m, 'invalid PEM header');
var m2 = lines[lines.length - 1].match(/*JSSTYLED*/
/[-]+[ ]*END ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
var m2;
var ei = lines.length;
while (!m2 && ei > 0) {
m2 = lines[--ei].match(/*JSSTYLED*/
/[-]+[ ]*END ([A-Z0-9][A-Za-z0-9]+ )?(PUBLIC|PRIVATE) KEY[ ]*[-]+/);
}
assert.ok(m2, 'invalid PEM footer');

@@ -58,2 +89,4 @@

lines = lines.slice(si, ei + 1);
var headers = {};

@@ -69,2 +102,6 @@ while (true) {

/* Chop off the first and last lines */
lines = lines.slice(0, -1).join('');
buf = Buffer.from(lines, 'base64');
var cipher, key, iv;

@@ -92,6 +129,67 @@ if (headers['proc-type']) {

/* Chop off the first and last lines */
lines = lines.slice(0, -1).join('');
buf = Buffer.from(lines, 'base64');
if (alg && alg.toLowerCase() === 'encrypted') {
var eder = new asn1.BerReader(buf);
var pbesEnd;
eder.readSequence();
eder.readSequence();
pbesEnd = eder.offset + eder.length;
var method = eder.readOID();
if (method !== OID_PBES2) {
throw (new Error('Unsupported PEM/PKCS8 encryption ' +
'scheme: ' + method));
}
eder.readSequence(); /* PBES2-params */
eder.readSequence(); /* keyDerivationFunc */
var kdfEnd = eder.offset + eder.length;
var kdfOid = eder.readOID();
if (kdfOid !== OID_PBKDF2)
throw (new Error('Unsupported PBES2 KDF: ' + kdfOid));
eder.readSequence();
var salt = eder.readString(asn1.Ber.OctetString, true);
var iterations = eder.readInt();
var hashAlg = 'sha1';
if (eder.offset < kdfEnd) {
eder.readSequence();
var hashAlgOid = eder.readOID();
hashAlg = OID_TO_HASH[hashAlgOid];
if (hashAlg === undefined) {
throw (new Error('Unsupported PBKDF2 hash: ' +
hashAlgOid));
}
}
eder._offset = kdfEnd;
eder.readSequence(); /* encryptionScheme */
var cipherOid = eder.readOID();
cipher = OID_TO_CIPHER[cipherOid];
if (cipher === undefined) {
throw (new Error('Unsupported PBES2 cipher: ' +
cipherOid));
}
iv = eder.readString(asn1.Ber.OctetString, true);
eder._offset = pbesEnd;
buf = eder.readString(asn1.Ber.OctetString, true);
if (typeof (options.passphrase) === 'string') {
options.passphrase = Buffer.from(
options.passphrase, 'utf-8');
}
if (!Buffer.isBuffer(options.passphrase)) {
throw (new errors.KeyEncryptedError(
options.filename, 'PEM'));
}
var cinfo = utils.opensshCipherInfo(cipher);
cipher = cinfo.opensslName;
key = utils.pbkdf2(hashAlg, salt, iterations, cinfo.keySize,
options.passphrase);
alg = undefined;
}
if (cipher && key && iv) {

@@ -98,0 +196,0 @@ var cipherStream = crypto.createDecipheriv(cipher, key, iv);

@@ -1,2 +0,2 @@

// Copyright 2015 Joyent, Inc.
// Copyright 2018 Joyent, Inc.

@@ -8,2 +8,3 @@ module.exports = {

writePkcs8: writePkcs8,
pkcs8ToBuffer: pkcs8ToBuffer,

@@ -416,2 +417,8 @@ readECDSACurve: readECDSACurve,

function pkcs8ToBuffer(key) {
var der = new asn1.BerWriter();
writePkcs8(der, key);
return (der.buffer);
}
function writePkcs8(der, key) {

@@ -418,0 +425,0 @@ der.startSequence();

18

lib/formats/x509-pem.js

@@ -32,10 +32,20 @@ // Copyright 2016 Joyent, Inc.

var m = lines[0].match(/*JSSTYLED*/
/[-]+[ ]*BEGIN CERTIFICATE[ ]*[-]+/);
var m;
var si = -1;
while (!m && si < lines.length) {
m = lines[++si].match(/*JSSTYLED*/
/[-]+[ ]*BEGIN CERTIFICATE[ ]*[-]+/);
}
assert.ok(m, 'invalid PEM header');
var m2 = lines[lines.length - 1].match(/*JSSTYLED*/
/[-]+[ ]*END CERTIFICATE[ ]*[-]+/);
var m2;
var ei = lines.length;
while (!m2 && ei > 0) {
m2 = lines[--ei].match(/*JSSTYLED*/
/[-]+[ ]*END CERTIFICATE[ ]*[-]+/);
}
assert.ok(m2, 'invalid PEM footer');
lines = lines.slice(si, ei + 1);
var headers = {};

@@ -42,0 +52,0 @@ while (true) {

@@ -1,2 +0,2 @@

// Copyright 2017 Joyent, Inc.
// Copyright 2018 Joyent, Inc.

@@ -35,2 +35,4 @@ module.exports = Key;

formats['dnssec'] = require('./formats/dnssec');
formats['putty'] = require('./formats/putty');
formats['ppk'] = formats['putty'];

@@ -102,4 +104,7 @@ function Key(opts) {

Key.prototype.hash = function (algo) {
Key.prototype.hash = function (algo, type) {
assert.string(algo, 'algorithm');
assert.optionalString(type, 'type');
if (type === undefined)
type = 'ssh';
algo = algo.toLowerCase();

@@ -109,18 +114,31 @@ if (algs.hashAlgs[algo] === undefined)

if (this._hashCache[algo])
return (this._hashCache[algo]);
var hash = crypto.createHash(algo).
update(this.toBuffer('rfc4253')).digest();
this._hashCache[algo] = hash;
var cacheKey = algo + '||' + type;
if (this._hashCache[cacheKey])
return (this._hashCache[cacheKey]);
var buf;
if (type === 'ssh') {
buf = this.toBuffer('rfc4253');
} else if (type === 'spki') {
buf = formats.pkcs8.pkcs8ToBuffer(this);
} else {
throw (new Error('Hash type ' + type + ' not supported'));
}
var hash = crypto.createHash(algo).update(buf).digest();
this._hashCache[cacheKey] = hash;
return (hash);
};
Key.prototype.fingerprint = function (algo) {
Key.prototype.fingerprint = function (algo, type) {
if (algo === undefined)
algo = 'sha256';
if (type === undefined)
type = 'ssh';
assert.string(algo, 'algorithm');
assert.string(type, 'type');
var opts = {
type: 'key',
hash: this.hash(algo),
algorithm: algo
hash: this.hash(algo, type),
algorithm: algo,
hashType: type
};

@@ -263,4 +281,5 @@ return (new Fingerprint(opts));

* [1,6] -- changed ed25519 part names
* [1,7] -- spki hash types
*/
Key.prototype._sshpkApiVersion = [1, 6];
Key.prototype._sshpkApiVersion = [1, 7];

@@ -267,0 +286,0 @@ Key._oldVersionDetect = function (obj) {

@@ -57,6 +57,10 @@ // Copyright 2017 Joyent, Inc.

PrivateKey.prototype.hash = function (algo) {
return (this.toPublic().hash(algo));
PrivateKey.prototype.hash = function (algo, type) {
return (this.toPublic().hash(algo, type));
};
PrivateKey.prototype.fingerprint = function (algo, type) {
return (this.toPublic().fingerprint(algo, type));
};
PrivateKey.prototype.toPublic = function () {

@@ -229,4 +233,5 @@ if (this._pubCache)

* [1,5] -- changed ed25519 part names and format
* [1,6] -- type arguments for hash() and fingerprint()
*/
PrivateKey.prototype._sshpkApiVersion = [1, 5];
PrivateKey.prototype._sshpkApiVersion = [1, 6];

@@ -233,0 +238,0 @@ PrivateKey._oldVersionDetect = function (obj) {

@@ -20,3 +20,4 @@ // Copyright 2015 Joyent, Inc.

writeBitString: writeBitString,
readBitString: readBitString
readBitString: readBitString,
pbkdf2: pbkdf2
};

@@ -128,2 +129,36 @@

/* See: RFC2898 */
function pbkdf2(hashAlg, salt, iterations, size, passphrase) {
var hkey = Buffer.alloc(salt.length + 4);
salt.copy(hkey);
var gen = 0, ts = [];
var i = 1;
while (gen < size) {
var t = T(i++);
gen += t.length;
ts.push(t);
}
return (Buffer.concat(ts).slice(0, size));
function T(I) {
hkey.writeUInt32BE(I, hkey.length - 4);
var hmac = crypto.createHmac(hashAlg, passphrase);
hmac.update(hkey);
var Ti = hmac.digest();
var Uc = Ti;
var c = 1;
while (c++ < iterations) {
hmac = crypto.createHmac(hashAlg, passphrase);
hmac.update(Uc);
Uc = hmac.digest();
for (var x = 0; x < Ti.length; ++x)
Ti[x] ^= Uc[x];
}
return (Ti);
}
}
/* Count leading zero bits on a buffer */

@@ -130,0 +165,0 @@ function countZeros(buf) {

{
"name": "sshpk",
"version": "1.15.2",
"version": "1.16.0",
"description": "A library for finding and using SSH public keys",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

@@ -129,2 +129,5 @@ sshpk

`ssh-keygen -o`
- `dnssec`: `.key` file format output by `dnssec-keygen` etc
- `putty`: the PuTTY `.ppk` file format (supports truncated variant without
all the lines from `Private-Lines:` onwards)
- `options` -- Optional Object, extra options, with keys:

@@ -178,3 +181,3 @@ - `filename` -- Optional String, name for the key being parsed

### `Key#fingerprint([algorithm = 'sha256'])`
### `Key#fingerprint([algorithm = 'sha256'[, hashType = 'ssh']])`

@@ -187,2 +190,5 @@ Creates a new `Fingerprint` object representing this Key's fingerprint.

`sha1`, `sha256`, `sha384`, `sha512`
- `hashType` -- String name of fingerprint hash type to use, valid options are
`ssh` (the type of fingerprint used by OpenSSH, e.g. in
`ssh-keygen`), `spki` (used by HPKP, some OpenSSL applications)

@@ -237,2 +243,3 @@ ### `Key#createVerify([hashAlgorithm])`

- `rfc4253`: raw OpenSSH wire format
- `dnssec`: `.private` format output by `dnssec-keygen` etc.
- `options` -- Optional Object, extra options, with keys:

@@ -340,3 +347,3 @@ - `filename` -- Optional String, name for the key being parsed

### `parseFingerprint(fingerprint[, algorithms])`
### `parseFingerprint(fingerprint[, options])`

@@ -349,5 +356,11 @@ Pre-parses a fingerprint, creating a `Fingerprint` object that can be used to

- `fingerprint` -- String, the fingerprint value, in any supported format
- `algorithms` -- Optional list of strings, names of hash algorithms to limit
support to. If `fingerprint` uses a hash algorithm not on
this list, throws `InvalidAlgorithmError`.
- `options` -- Optional Object, with properties:
- `algorithms` -- Array of strings, names of hash algorithms to limit
support to. If `fingerprint` uses a hash algorithm not on
this list, throws `InvalidAlgorithmError`.
- `hashType` -- String, the type of hash the fingerprint uses, either `ssh`
or `spki` (normally auto-detected based on the format, but
can be overridden)
- `type` -- String, the entity this fingerprint identifies, either `key` or
`certificate`

@@ -373,10 +386,15 @@ ### `Fingerprint.isFingerprint(obj)`

### `Fingerprint#matches(key)`
### `Fingerprint#matches(keyOrCertificate)`
Verifies whether or not this `Fingerprint` matches a given `Key`. This function
uses double-hashing to avoid leaking timing information. Returns a boolean.
Verifies whether or not this `Fingerprint` matches a given `Key` or
`Certificate`. This function uses double-hashing to avoid leaking timing
information. Returns a boolean.
Note that a `Key`-type Fingerprint will always return `false` if asked to match
a `Certificate` and vice versa.
Parameters
- `key` -- a `Key` object, the key to match this fingerprint against
- `keyOrCertificate` -- a `Key` object or `Certificate` object, the entity to
match this fingerprint against

@@ -383,0 +401,0 @@ ## Signatures

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc