Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

http-signature

Package Overview
Dependencies
Maintainers
3
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

http-signature - npm Package Compare versions

Comparing version 0.11.0 to 1.0.0

CHANGES.md

33

http_signing.md

@@ -123,6 +123,6 @@ # Abstract

Host: example.org
Date: Tue, 07 Jun 2011 20:51:35 GMT
Date: Tue, 07 Jun 2014 20:51:35 GMT
Content-Type: application/json
Content-MD5: h0auK8hnYJKmHTLhKtMTkQ==
Content-Length: 123
Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
Content-Length: 18

@@ -141,3 +141,3 @@ The "rsa-key-1" keyId refers to a private key known to the client and a public

date: Tue, 07 Jun 2011 20:51:35 GMT
date: Tue, 07 Jun 2014 20:51:35 GMT

@@ -148,3 +148,3 @@ ## Header List

Authorization: Signature keyId="rsa-key-1",algorithm="rsa-sha256",headers="request-line date content-type content-md5",signature="Base64(RSA-SHA256(signing string))"
Authorization: Signature keyId="rsa-key-1",algorithm="rsa-sha256",headers="(request-target) date content-type digest",signature="Base64(RSA-SHA256(signing string))"

@@ -154,6 +154,6 @@ The client would compose the signing string as (`+ "\n"` inserted for

POST /foo HTTP/1.1 + "\n"
(request-target) post /foo + "\n"
date: Tue, 07 Jun 2011 20:51:35 GMT + "\n"
content-type: application/json + "\n"
content-md5: h0auK8hnYJKmHTLhKtMTkQ==
digest: SHA-256=Base64(SHA256(Body))

@@ -268,5 +268,5 @@ ## Algorithm

Host: example.com
Date: Thu, 05 Jan 2012 21:31:40 GMT
Date: Thu, 05 Jan 2014 21:31:40 GMT
Content-Type: application/json
Content-MD5: Sd/dVLAcvNLSq16eXua5uQ==
Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
Content-Length: 18

@@ -280,3 +280,3 @@

date: Thu, 05 Jan 2012 21:31:40 GMT
date: Thu, 05 Jan 2014 21:31:40 GMT

@@ -292,7 +292,7 @@ The Authorization header would be:

POST /foo?param=value&pet=dog HTTP/1.1 + "\n"
host: example.com + "\n"
date: Thu, 05 Jan 2012 21:31:40 GMT + "\n"
content-type: application/json + "\n"
content-md5: Sd/dVLAcvNLSq16eXua5uQ== + "\n"
(request-target): post /foo?param=value&pet=dog
host: example.com
date: Thu, 05 Jan 2014 21:31:40 GMT
content-type: application/json
digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
content-length: 18

@@ -302,3 +302,2 @@

Authorization: Signature keyId="Test",algorithm="rsa-sha256",headers="request-line host date content-type content-md5 content-length",signature="H/AaTDkJvLELy4i1RujnKlS6dm8QWiJvEpn9cKRMi49kKF+mohZ15z1r+mF+XiKS5kOOscyS83olfBtsVhYjPg2Ei3/D9D4Mvb7bFm9IaLJgYTFFuQCghrKQQFPiqJN320emjHxFowpIm1BkstnEU7lktH/XdXVBo8a6Uteiztw="
Authorization: Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="jgSqYK0yKclIHfF9zdApVEbDp5eqj8C4i4X76pE+XHoxugXv7qnVrGR+30bmBgtpR39I4utq17s9ghz/2QFVxlnToYAvbSVZJ9ulLd1HQBugO0jOyn9sXOtcN7uNHBjqNCqUsnt0sw/cJA6B6nJZpyNqNyAXKdxZZItOuhIs78w="

@@ -6,3 +6,3 @@ // Copyright 2015 Joyent, Inc.

var verify = require('./verify');
var util = require('./util');
var utils = require('./utils');

@@ -20,6 +20,8 @@

signRequest: signer.signRequest,
createSigner: signer.createSigner,
isSigner: signer.isSigner,
sshKeyToPEM: util.sshKeyToPEM,
sshKeyFingerprint: util.fingerprint,
pemToRsaSSHKey: util.pemToRsaSSHKey,
sshKeyToPEM: utils.sshKeyToPEM,
sshKeyFingerprint: utils.fingerprint,
pemToRsaSSHKey: utils.pemToRsaSSHKey,

@@ -26,0 +28,0 @@ verify: verify.verifySignature,

@@ -5,2 +5,3 @@ // Copyright 2012 Joyent, Inc. All rights reserved.

var util = require('util');
var utils = require('./utils');

@@ -11,11 +12,7 @@

var Algorithms = {
'rsa-sha1': true,
'rsa-sha256': true,
'rsa-sha512': true,
'dsa-sha1': true,
'hmac-sha1': true,
'hmac-sha256': true,
'hmac-sha512': true
};
var HASH_ALGOS = utils.HASH_ALGOS;
var PK_ALGOS = utils.PK_ALGOS;
var HttpSignatureError = utils.HttpSignatureError;
var InvalidAlgorithmError = utils.InvalidAlgorithmError;
var validateAlgorithm = utils.validateAlgorithm;

@@ -35,14 +32,5 @@ var State = {

///--- Specific Errors
function HttpSignatureError(message, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || HttpSignatureError);
this.message = message;
this.name = caller.name;
}
util.inherits(HttpSignatureError, Error);
function ExpiredRequestError(message) {

@@ -71,4 +59,7 @@ HttpSignatureError.call(this, message, ExpiredRequestError);

function StrictParsingError(message) {
HttpSignatureError.call(this, message, StrictParsingError);
}
util.inherits(StrictParsingError, HttpSignatureError);
///--- Exported API

@@ -95,3 +86,3 @@

* "date" or "x-date",
* "content-md5"
* "digest"
* ],

@@ -108,2 +99,4 @@ * "signature": "base64"

* - algorithms: algorithms to support (default: all).
* - strict: should enforce latest spec parsing
* (default: false).
* @return {Object} parsed out object (see above).

@@ -117,2 +110,4 @@ * @throws {TypeError} on invalid input.

* in options.
* @throws {StrictParsingError} if old attributes are used in strict parsing
* mode.
* @throws {ExpiredRequestError} if the value of date or x-date exceeds skew.

@@ -158,3 +153,2 @@ */

}
};

@@ -254,5 +248,11 @@

parsed.params.algorithm = parsed.params.algorithm.toLowerCase();
if (!Algorithms[parsed.params.algorithm])
throw new InvalidParamsError(parsed.params.algorithm +
' is not supported');
try {
validateAlgorithm(parsed.params.algorithm);
} catch (e) {
if (e instanceof InvalidAlgorithmError)
throw (new InvalidParamsError(parsed.params.algorithm + ' is not ' +
'supported'));
else
throw (e);
}

@@ -264,10 +264,24 @@ // Build the signingString

if (h !== 'request-line') {
if (h === 'request-line') {
if (!options.strict) {
/*
* We allow headers from the older spec drafts if strict parsing isn't
* specified in options.
*/
parsed.signingString +=
request.method + ' ' + request.url + ' HTTP/' + request.httpVersion;
} else {
/* Strict parsing doesn't allow older draft headers. */
throw (new StrictParsingError('request-line is not a valid header ' +
'with strict parsing enabled.'));
}
} else if (h === '(request-target)') {
parsed.signingString +=
'(request-target): ' + request.method.toLowerCase() + ' ' +
request.url;
} else {
var value = request.headers[h];
if (!value)
if (value === undefined)
throw new MissingHeaderError(h + ' was not in the request');
parsed.signingString += h + ': ' + value;
} else {
parsed.signingString +=
request.method + ' ' + request.url + ' HTTP/' + request.httpVersion;
}

@@ -274,0 +288,0 @@

@@ -6,86 +6,250 @@ // Copyright 2012 Joyent, Inc. All rights reserved.

var http = require('http');
var util = require('util');
var sshpk = require('sshpk');
var jsprim = require('jsprim');
var utils = require('./utils');
var sprintf = require('util').format;
var HASH_ALGOS = utils.HASH_ALGOS;
var PK_ALGOS = utils.PK_ALGOS;
var InvalidAlgorithmError = utils.InvalidAlgorithmError;
var HttpSignatureError = utils.HttpSignatureError;
var validateAlgorithm = utils.validateAlgorithm;
///--- Globals
var Algorithms = {
'rsa-sha1': true,
'rsa-sha256': true,
'rsa-sha512': true,
'dsa-sha1': true,
'hmac-sha1': true,
'hmac-sha256': true,
'hmac-sha512': true
};
var Authorization =
var AUTHZ_FMT =
'Signature keyId="%s",algorithm="%s",headers="%s",signature="%s"';
///--- Specific Errors
function MissingHeaderError(message) {
this.name = 'MissingHeaderError';
this.message = message;
this.stack = (new Error()).stack;
HttpSignatureError.call(this, message, MissingHeaderError);
}
MissingHeaderError.prototype = new Error();
util.inherits(MissingHeaderError, HttpSignatureError);
function InvalidAlgorithmError(message) {
this.name = 'InvalidAlgorithmError';
this.message = message;
this.stack = (new Error()).stack;
function StrictParsingError(message) {
HttpSignatureError.call(this, message, StrictParsingError);
}
InvalidAlgorithmError.prototype = new Error();
util.inherits(StrictParsingError, HttpSignatureError);
/* See createSigner() */
function RequestSigner(options) {
assert.object(options, 'options');
var alg = [];
if (options.algorithm !== undefined) {
assert.string(options.algorithm, 'options.algorithm');
alg = validateAlgorithm(options.algorithm);
}
this.rs_alg = alg;
///--- Internal Functions
/*
* RequestSigners come in two varieties: ones with an rs_signFunc, and ones
* with an rs_signer.
*
* rs_signFunc-based RequestSigners have to build up their entire signing
* string within the rs_lines array and give it to rs_signFunc as a single
* concat'd blob. rs_signer-based RequestSigners can add a line at a time to
* their signing state by using rs_signer.update(), thus only needing to
* buffer the hash function state and one line at a time.
*/
if (options.sign !== undefined) {
assert.func(options.sign, 'options.sign');
this.rs_signFunc = options.sign;
function _pad(val) {
if (parseInt(val, 10) < 10) {
val = '0' + val;
} else if (alg[0] === 'hmac' && options.key !== undefined) {
assert.string(options.keyId, 'options.keyId');
this.rs_keyId = options.keyId;
if (typeof (options.key) !== 'string' && !Buffer.isBuffer(options.key))
throw (new TypeError('options.key for HMAC must be a string or Buffer'));
/*
* Make an rs_signer for HMACs, not a rs_signFunc -- HMACs digest their
* data in chunks rather than requiring it all to be given in one go
* at the end, so they are more similar to signers than signFuncs.
*/
this.rs_signer = crypto.createHmac(alg[1].toUpperCase(), options.key);
this.rs_signer.sign = function () {
var digest = this.digest('base64');
return ({
hashAlgorithm: alg[1],
toString: function () { return (digest); }
});
};
} else if (options.key !== undefined) {
var key = options.key;
if (typeof (key) === 'string' || Buffer.isBuffer(key))
key = sshpk.parsePrivateKey(key);
assert.ok(key instanceof sshpk.PrivateKey,
'options.key must be a sshpk.PrivateKey');
this.rs_key = key;
assert.string(options.keyId, 'options.keyId');
this.rs_keyId = options.keyId;
if (!PK_ALGOS[key.type]) {
throw (new InvalidAlgorithmError(key.type.toUpperCase() + ' type ' +
'keys are not supported'));
}
if (alg[0] !== undefined && key.type !== alg[0]) {
throw (new InvalidAlgorithmError('options.key must be a ' +
alg[0].toUpperCase() + ' key, was given a ' +
key.type.toUpperCase() + ' key instead'));
}
this.rs_signer = key.createSign(alg[1]);
} else {
throw (new TypeError('options.sign (func) or options.key is required'));
}
return val;
this.rs_headers = [];
this.rs_lines = [];
}
/**
* Adds a header to be signed, with its value, into this signer.
*
* @param {String} header
* @param {String} value
* @return {String} value written
*/
RequestSigner.prototype.writeHeader = function (header, value) {
assert.string(header, 'header');
header = header.toLowerCase();
assert.string(value, 'value');
function _rfc1123() {
var date = new Date();
this.rs_headers.push(header);
var months = ['Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'];
var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
return days[date.getUTCDay()] + ', ' +
_pad(date.getUTCDate()) + ' ' +
months[date.getUTCMonth()] + ' ' +
date.getUTCFullYear() + ' ' +
_pad(date.getUTCHours()) + ':' +
_pad(date.getUTCMinutes()) + ':' +
_pad(date.getUTCSeconds()) +
' GMT';
}
if (this.rs_signFunc) {
this.rs_lines.push(header + ': ' + value);
} else {
var line = header + ': ' + value;
if (this.rs_headers.length > 0)
line = '\n' + line;
this.rs_signer.update(line);
}
return (value);
};
/**
* Adds a default Date header, returning its value.
*
* @return {String}
*/
RequestSigner.prototype.writeDateHeader = function () {
return (this.writeHeader('date', jsprim.rfc1123(new Date())));
};
/**
* Adds the request target line to be signed.
*
* @param {String} method, HTTP method (e.g. 'get', 'post', 'put')
* @param {String} path
*/
RequestSigner.prototype.writeTarget = function (method, path) {
assert.string(method, 'method');
assert.string(path, 'path');
method = method.toLowerCase();
this.writeHeader('(request-target)', method + ' ' + path);
};
/**
* Calculate the value for the Authorization header on this request
* asynchronously.
*
* @param {Func} callback (err, authz)
*/
RequestSigner.prototype.sign = function (cb) {
assert.func(cb, 'callback');
if (this.rs_headers.length < 1)
throw (new Error('At least one header must be signed'));
var alg, authz;
if (this.rs_signFunc) {
var data = this.rs_lines.join('\n');
var self = this;
this.rs_signFunc(data, function (err, sig) {
if (err) {
cb(err);
return;
}
try {
assert.object(sig, 'signature');
assert.string(sig.keyId, 'signature.keyId');
assert.string(sig.algorithm, 'signature.algorithm');
assert.string(sig.signature, 'signature.signature');
alg = validateAlgorithm(sig.algorithm);
authz = sprintf(AUTHZ_FMT,
sig.keyId,
sig.algorithm,
self.rs_headers.join(' '),
sig.signature);
} catch (e) {
cb(e);
return;
}
cb(null, authz);
});
} else {
try {
var sigObj = this.rs_signer.sign();
} catch (e) {
cb(e);
return;
}
alg = (this.rs_alg[0] || this.rs_key.type) + '-' + sigObj.hashAlgorithm;
var signature = sigObj.toString();
authz = sprintf(AUTHZ_FMT,
this.rs_keyId,
alg,
this.rs_headers.join(' '),
signature);
cb(null, authz);
}
};
///--- Exported API
module.exports = {
/**
* Identifies whether a given object is a request signer or not.
*
* @param {Object} object, the object to identify
* @returns {Boolean}
*/
isSigner: function (obj) {
if (typeof (obj) === 'object' && obj instanceof RequestSigner)
return (true);
return (false);
},
/**
* Creates a request signer, used to asynchronously build a signature
* for a request (does not have to be an http.ClientRequest).
*
* @param {Object} options, either:
* - {String} keyId
* - {String|Buffer} key
* - {String} algorithm (optional, required for HMAC)
* or:
* - {Func} sign (data, cb)
* @return {RequestSigner}
*/
createSigner: function createSigner(options) {
return (new RequestSigner(options));
},
/**
* Adds an 'Authorization' header to an http.ClientRequest object.

@@ -100,2 +264,7 @@ *

*
* The optional flag indicates whether parsing should use strict enforcement
* of the version draft-cavage-http-signatures-04 of the spec or beyond.
* The default is to be loose and support
* older versions for compatibility.
*
* @param {Object} request an instance of http.ClientRequest.

@@ -106,7 +275,12 @@ * @param {Object} options signing parameters object:

* - {Array} headers optional; defaults to ['date'].
* - {String} algorithm optional; defaults to 'rsa-sha256'.
* - {String} algorithm optional (unless key is HMAC);
* default is the same as the sshpk default
* signing algorithm for the type of key given
* - {String} httpVersion optional; defaults to '1.1'.
* - {Boolean} strict optional; defaults to 'false'.
* @return {Boolean} true if Authorization (and optionally Date) were added.
* @throws {TypeError} on bad parameter types (input).
* @throws {InvalidAlgorithmError} if algorithm was bad.
* @throws {InvalidAlgorithmError} if algorithm was bad or incompatible with
* the given key.
* @throws {sshpk.KeyParseError} if key was bad.
* @throws {MissingHeaderError} if a header to be signed was specified but

@@ -124,15 +298,14 @@ * was not present.

if (!request.getHeader('Date'))
request.setHeader('Date', _rfc1123());
request.setHeader('Date', jsprim.rfc1123(new Date()));
if (!options.headers)
options.headers = ['date'];
if (!options.algorithm)
options.algorithm = 'rsa-sha256';
if (!options.httpVersion)
options.httpVersion = '1.1';
options.algorithm = options.algorithm.toLowerCase();
var alg = [];
if (options.algorithm) {
options.algorithm = options.algorithm.toLowerCase();
alg = validateAlgorithm(options.algorithm);
}
if (!Algorithms[options.algorithm])
throw new InvalidAlgorithmError(options.algorithm + ' is not supported');
var i;

@@ -146,11 +319,26 @@ var stringToSign = '';

if (h !== 'request-line') {
if (h === 'request-line') {
if (!options.strict) {
/**
* We allow headers from the older spec drafts if strict parsing isn't
* specified in options.
*/
stringToSign +=
request.method + ' ' + request.path + ' HTTP/' +
options.httpVersion;
} else {
/* Strict parsing doesn't allow older draft headers. */
throw (new StrictParsingError('request-line is not a valid header ' +
'with strict parsing enabled.'));
}
} else if (h === '(request-target)') {
stringToSign +=
'(request-target): ' + request.method.toLowerCase() + ' ' +
request.path;
} else {
var value = request.getHeader(h);
if (!value) {
if (value === undefined || value === '') {
throw new MissingHeaderError(h + ' was not in the request');
}
stringToSign += h + ': ' + value;
} else {
stringToSign +=
request.method + ' ' + request.path + ' HTTP/' + options.httpVersion;
}

@@ -162,15 +350,43 @@

var alg = options.algorithm.match(/(hmac|rsa)-(\w+)/);
var signature;
if (alg[1] === 'hmac') {
var hmac = crypto.createHmac(alg[2].toUpperCase(), options.key);
if (alg[0] === 'hmac') {
if (typeof (options.key) !== 'string' && !Buffer.isBuffer(options.key))
throw (new TypeError('options.key must be a string or Buffer'));
var hmac = crypto.createHmac(alg[1].toUpperCase(), options.key);
hmac.update(stringToSign);
signature = hmac.digest('base64');
} else {
var signer = crypto.createSign(options.algorithm.toUpperCase());
var key = options.key;
if (typeof (key) === 'string' || Buffer.isBuffer(key))
key = sshpk.parsePrivateKey(options.key);
assert.ok(key instanceof sshpk.PrivateKey,
'options.key must be a sshpk.PrivateKey');
if (!PK_ALGOS[key.type]) {
throw (new InvalidAlgorithmError(key.type.toUpperCase() + ' type ' +
'keys are not supported'));
}
if (alg[0] !== undefined && key.type !== alg[0]) {
throw (new InvalidAlgorithmError('options.key must be a ' +
alg[0].toUpperCase() + ' key, was given a ' +
key.type.toUpperCase() + ' key instead'));
}
var signer = key.createSign(alg[1]);
signer.update(stringToSign);
signature = signer.sign(options.key, 'base64');
var sigObj = signer.sign();
if (!HASH_ALGOS[sigObj.hashAlgorithm]) {
throw (new InvalidAlgorithmError(sigObj.hashAlgorithm.toUpperCase() +
' is not a supported hash algorithm'));
}
options.algorithm = key.type + '-' + sigObj.hashAlgorithm;
signature = sigObj.toString();
assert.notStrictEqual(signature, '', 'empty signature produced');
}
request.setHeader('Authorization', sprintf(Authorization,
request.setHeader('Authorization', sprintf(AUTHZ_FMT,
options.keyId,

@@ -177,0 +393,0 @@ options.algorithm,

@@ -5,5 +5,11 @@ // Copyright 2015 Joyent, Inc.

var crypto = require('crypto');
var sshpk = require('sshpk');
var utils = require('./utils');
var HASH_ALGOS = utils.HASH_ALGOS;
var PK_ALGOS = utils.PK_ALGOS;
var InvalidAlgorithmError = utils.InvalidAlgorithmError;
var HttpSignatureError = utils.HttpSignatureError;
var validateAlgorithm = utils.validateAlgorithm;
///--- Exported API

@@ -20,15 +26,17 @@

* @throws {TypeError} if you pass in bad arguments.
* @throws {InvalidAlgorithmError}
*/
verifySignature: function verifySignature(parsedSignature, pubkey) {
assert.object(parsedSignature, 'parsedSignature');
assert.string(pubkey, 'pubkey');
if (typeof (pubkey) === 'string' || Buffer.isBuffer(pubkey))
pubkey = sshpk.parseKey(pubkey);
assert.ok(pubkey instanceof sshpk.Key, 'pubkey must be a sshpk.Key');
var alg = parsedSignature.algorithm.match(/^(RSA|DSA)-(\w+)/);
if (!alg || alg.length !== 3)
throw new TypeError('parsedSignature: unsupported algorithm ' +
parsedSignature.algorithm);
var alg = validateAlgorithm(parsedSignature.algorithm);
if (alg[0] === 'hmac' || alg[0] !== pubkey.type)
return (false);
var verify = crypto.createVerify(alg[0]);
verify.update(parsedSignature.signingString);
return verify.verify(pubkey, parsedSignature.params.signature, 'base64');
var v = pubkey.createVerify(alg[1]);
v.update(parsedSignature.signingString);
return (v.verify(parsedSignature.params.signature, 'base64'));
},

@@ -44,2 +52,3 @@

* @throws {TypeError} if you pass in bad arguments.
* @throws {InvalidAlgorithmError}
*/

@@ -50,11 +59,34 @@ verifyHMAC: function verifyHMAC(parsedSignature, secret) {

var alg = parsedSignature.algorithm.match(/^HMAC-(\w+)/);
if (!alg || alg.length !== 2)
throw new TypeError('parsedSignature: unsupported algorithm ' +
parsedSignature.algorithm);
var alg = validateAlgorithm(parsedSignature.algorithm);
if (alg[0] !== 'hmac')
return (false);
var hmac = crypto.createHmac(alg[1].toUpperCase(), secret);
var hashAlg = alg[1].toUpperCase();
var hmac = crypto.createHmac(hashAlg, secret);
hmac.update(parsedSignature.signingString);
return (hmac.digest('base64') === parsedSignature.params.signature);
/*
* Now double-hash to avoid leaking timing information - there's
* no easy constant-time compare in JS, so we use this approach
* instead. See for more info:
* https://www.isecpartners.com/blog/2011/february/double-hmac-
* verification.aspx
*/
var h1 = crypto.createHmac(hashAlg, secret);
h1.update(hmac.digest());
h1 = h1.digest();
var h2 = crypto.createHmac(hashAlg, secret);
h2.update(new Buffer(parsedSignature.params.signature, 'base64'));
h2 = h2.digest();
/* Node 0.8 returns strings from .digest(). */
if (typeof (h1) === 'string')
return (h1 === h2);
/* And node 0.10 lacks the .equals() method on Buffers. */
if (Buffer.isBuffer(h1) && !h1.equals)
return (h1.toString('binary') === h2.toString('binary'));
return (h1.equals(h2));
}
};
{
"name": "http-signature",
"description": "Reference implementation of Joyent's HTTP Signature scheme.",
"version": "0.11.0",
"version": "1.0.0",
"license": "MIT",

@@ -23,3 +23,4 @@ "author": "Joyent, Inc",

"engines": {
"node": ">=0.8"
"node": ">=0.8",
"npm": ">=1.3.7"
},

@@ -32,5 +33,8 @@ "main": "lib/index.js",

"assert-plus": "^0.1.5",
"asn1": "0.1.11",
"ctype": "0.5.3"
"jsprim": "^1.2.0",
"sshpk": "^1.4.0"
},
"peerDependencies": {
"sshpk": "^1.4.0"
},
"devDependencies": {

@@ -37,0 +41,0 @@ "node-uuid": "^1.4.1",

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc