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

yub

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

yub - npm Package Compare versions

Comparing version 0.10.14 to 0.11.0

.eslintrc

12

example/test.js

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

'use strict';
/* eslint-disable no-console */
var yub = require('../index.js');

@@ -7,4 +8,7 @@

var clientId = process.env.CLIENT_ID || process.env.USER;
var secretKey = process.env.SECRET_KEY || process.env.PASSWORD;
// initialise the yub library
yub.init(process.env.USERNAME, process.env.PASSWORD);
yub.init(clientId, secretKey);

@@ -14,3 +18,3 @@ // attempt to verify the key

if(err) {
console.log("Error");
console.log('Error');
process.exit(-1);

@@ -23,5 +27,5 @@ }

} else {
console.log("Invalid OTP");
console.log('Invalid OTP');
process.exit(-2);
}
});

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

'use strict';
/* eslint-disable no-console */
var yub = require('../index.js');

@@ -7,4 +8,7 @@

var clientId = process.env.CLIENT_ID || process.env.USER;
var secretKey = process.env.SECRET_KEY || process.env.PASSWORD;
// initialise the yub library
yub.init(process.env.USERNAME, process.env.PASSWORD);
yub.init(clientId, secretKey);

@@ -14,3 +18,3 @@ // attempt to verify the key

if(err) {
console.log("Error");
console.log('Error');
process.exit(-1);

@@ -22,5 +26,5 @@ }

} else {
console.log("Invalid OTP");
console.log('Invalid OTP');
process.exit(-2);
}
});

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

'use strict';
module.exports = require('./lib/yub.js');

@@ -0,7 +1,8 @@

'use strict';
// lookup table for modhex codes
var trans = "cbdefghijklnrtuv";
var trans = 'cbdefghijklnrtuv';
// convert number to 2 digit hex = 255 --> ff
var toHex = function (n) {
return ("0" + n.toString(16)).substr(-2);
function toHex (n) {
return ('0' + n.toString(16)).substr(-2);
}

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

// see http://static.yubico.com/var/uploads/pdfs/YubiKey_manual-2.2.pdf
var decode = function (src) {
function decode (src) {
var b = 0;
var flag = false;
var dst = null;
var hex = "";
var hex = '';
var p1 = null;
// convert string to hexadecimal string
for (i=0; i < src.length; i++) {
for (var i=0; i < src.length; i++) {
p1 = trans.indexOf(src[i]);

@@ -33,3 +34,3 @@ if (p1 == -1) {

}
// return as hexadecimal number

@@ -41,6 +42,6 @@ return hex;

// see http://static.yubico.com/var/uploads/pdfs/YubiKey_manual-2.2.pdf
var decodeInt = function (src) {
function decodeInt (src) {
var d = decode(src);
return (d == null) ? null : parseInt(d, 16);
}
};

@@ -50,2 +51,2 @@ module.exports = {

decodeInt: decodeInt
}
};

@@ -0,28 +1,36 @@

'use strict';
var crypto = require('crypto');
var request = require('request');
var modhex = require('./modhex.js');
var https = require('https');
var qs = require('querystring');
var NODE_ENV = process.env.NODE_ENV;
// stored client credentials
var clientID = null;
var secretKey = null;
// List of valid servers. We go through them in round-robin fashion.
var servers = ['api.yubico.com', 'api2.yubico.com', 'api3.yubico.com', 'api4.yubico.com', 'api5.yubico.com'];
var currentServerIdx = Math.floor(Math.random() * servers.length);
// list of valid servers
var servers = ["api.yubico.com", "api2.yubico.com", "api3.yubico.com", "api4.yubico.com", "api5.yubico.com"];
var nonceLength = 40;
// Length of random nonce generated per-request
var DEFAULT_NONCE_LENGTH = 40;
// For automatic retry
var DEFAULT_MAX_TRIES = 3;
// store the client credentials
// Apply here https://upgrade.yubico.com/getapikey/
var init = function (client_id, secretkey) {
clientID = client_id;
secretKey = secretkey;
};
function Yub(clientID, secretKey, options) {
// store the client credentials
// Apply here https://upgrade.yubico.com/getapikey/
if (!clientID || !secretKey) throw new Error('Provide a client ID & secret key to yub.init()!');
if (!options) options = {};
this.clientID = clientID;
this.secretKey = secretKey;
this.maxTries = options.maxTries || DEFAULT_MAX_TRIES;
this.nonceLength = options.nonceLength || DEFAULT_NONCE_LENGTH;
}
// parse the returned date which is CR/LF delimited string with key/value pairs
// separated by '='
var parse = function (data) {
var obj = data.split("\r\n"),
function parse (data) {
var obj = data.split('\r\n'),
retval = {},
kv = [];
Object.keys(obj).map(function(key) {
kv = obj[key].split("=", 2);
kv = obj[key].split('=', 2);
if (kv[0].length > 0) {

@@ -37,3 +45,3 @@ retval[kv[0]] = kv[1];

// the string with last 32 characters removed
var calculateIdentity = function (otp) {
function calculateIdentity (otp) {
var len = otp.length;

@@ -44,4 +52,4 @@ return (len > 32) ? otp.substring(0, len - 32) : null;

// extract the encrypted portion of the Yubikey OTP i.e.
// the last 32 characters
var calculateEncrypted = function (otp) {
// the last 32 characters
function calculateEncrypted (otp) {
var len = otp.length;

@@ -52,5 +60,5 @@ return (len > 32) ? otp.substring(len - 32, len) : null;

// calculate the string that is required to be hashed i.e.
// keys in alphabetical order, separated from values by '='
// keys in alphabetical order, separated from values by '='
// and by each other by '&' (like querystrings, but without the escaping)
var calculateStringToHash = function (obj) {
function calculateStringToHash (obj) {
return Object

@@ -67,5 +75,5 @@ .keys(obj)

// according to instructions here: https://code.google.com/p/yubikey-val-server-php/wiki/ValidationProtocolV20
var calculateHmac = function (obj) {
function calculateHmac (obj, secretKey) {
var str = calculateStringToHash(obj);
var buf = new Buffer(secretKey, 'base64').toString('binary');
var buf = new Buffer(secretKey, 'base64');
var hmac = crypto.createHmac('sha1', buf);

@@ -75,49 +83,44 @@ return hmac.update(str).digest('base64');

// verify that the supplied one-time-password is valid or not
// calls back with (err,data). If err is not null, then you have
// an object in data to work with
var verify = function (otp, callback) {
// create 40 character random string
crypto.randomBytes(nonceLength, function (err, buf) {
// turn it to hex
var nonce = buf.toString('hex').slice(0, 40);
// create parameters to send to web service
var params = {
id: clientID,
nonce: nonce,
otp: otp
};
// calculate sha1 signature
// params.h = calculateHmac(params);
// calculate url
var server = servers[Math.floor(Math.random() * servers.length)];
var uri = 'https://' + server + '/wsapi/2.0/verify';
// Verify with a random Yubico server.
Yub.prototype.verifyWithYubico = function (params, callback, currentTry) {
// Automatic retry logic
if (!currentTry) currentTry = 1;
// to https request
request({ uri: uri, qs: params}, function (err, res, body) {
// error
if (err) {
return callback(err, null);
}
if (res.statusCode !== 200) {
return callback(true, null);
}
// parse the return value
body = parse(body);
// Choose a server in round-robin fashion. First offset is random.
var server = servers[currentServerIdx++];
if (NODE_ENV === 'test') server = servers[0]; // For mocking with nock
var uri = 'https://' + server + '/wsapi/2.0/verify';
var fullURI = uri + '?' + qs.stringify(params);
var me = this;
// check whether the signature of the reply
var bodyh = body.h;
// Send to Yubico.
https.get(fullURI, function(res) {
// Error handling
var shouldRetry = (currentTry < me.maxTries);
var badStatus = (res && res.statusCode !== 200);
var serverError = (res && res.statusCode >= 500);
if (shouldRetry && serverError) {
// Errored, but retry
return me.verifyWithYubico(params, callback, currentTry + 1);
} else if (badStatus) {
return callback(new Error('Bad status code: ' + res.statusCode));
}
// Parse body & go
var buffer = '';
res.on('data', function(chunk) {
buffer += chunk;
});
res.on('end', function() {
var body = parse(buffer);
// check whether the signature of the reply checks out
var bodyh = body.h;
delete body.h;
var h = calculateHmac(body);
body.signatureVerified = (bodyh === h.replace("=", ''));
var h = calculateHmac(body, me.secretKey);
body.signatureVerified = (bodyh === h.replace('=', ''));
// check whether the nonce is the same as the one we gave it
body.nonceVerified = (nonce === body.nonce);
body.nonceVerified = (params.nonce === body.nonce);
// calculate the key's identity

@@ -128,48 +131,94 @@ body.identity = null;

body.serial = null;
if (typeof body.status != "undefined" && body.status === "OK") {
body.identity = calculateIdentity(otp);
body.encrypted = calculateEncrypted(otp);
if (body.status === 'OK') {
body.identity = calculateIdentity(params.otp);
body.encrypted = calculateEncrypted(params.otp);
body.encryptedHex = modhex.decode(body.encrypted);
body.serial = modhex.decodeInt(body.identity);
body.valid = (body.signatureVerified && body.nonceVerified)
body.valid = (body.signatureVerified && body.nonceVerified);
} else {
body.valid = false;
}
callback(null, body);
});
})
.on('error', callback);
};
});
// Calculate the params to be sent to Yubico.
Yub.prototype.calculateParams = function (otp, callback) {
var me = this;
// create a nonceLength-character random string
crypto.randomBytes(me.nonceLength / 2, function (err, buf) {
if (err) return callback(err);
// turn it to hex
var nonce = buf.toString('hex');
// create parameters to send to web service
var params = {
id: me.clientID,
nonce: nonce,
otp: otp
};
// calculate sha1 signature
params.h = calculateHmac(params, me.secretKey);
callback(null, params);
});
};
// if we have no network connectivity, we still may wish to extract the
// identity from the OTP, but handy for offline applications
var verifyOffline = function (otp, callback) {
// Verify that the supplied one-time-password is valid or not
// calls back with (err,data). If err is not null, then you have
// an object in data to work with.
Yub.prototype.verify = function (otp, callback) {
var me = this;
this.calculateParams(otp, function (err, params) {
if (err) return callback(err);
me.verifyWithYubico(params, callback);
});
};
// If we have no network connectivity, we still may wish to extract the
// identity from the OTP, but handy for offline applications.
Yub.prototype.verifyOffline = function (otp, callback) {
var identity = calculateIdentity(otp);
var encrypted = calculateEncrypted(otp);
var body = {
t: null,
otp: otp,
nonce: null,
sl: '0',
status: null,
signatureVerified: false,
nonceVerified: false,
identity: identity,
encrypted: encrypted,
encryptedHex: modhex.decode(encrypted),
serial: modhex.decodeInt(identity),
valid: false
};
t: null,
otp: otp,
nonce: null,
sl: '0',
status: null,
signatureVerified: false,
nonceVerified: false,
identity: identity,
encrypted: encrypted,
encryptedHex: modhex.decode(encrypted),
serial: modhex.decodeInt(identity),
valid: false
};
callback(null, body);
};
// Export Yub. To maintain backcompat, we attach a few static properties.
module.exports = Yub;
module.exports = {
init: init,
verify: verify,
verifyOffline: verifyOffline
var legacyInstance = null;
// Don't break old methods!
Yub.init = function(clientID, secretKey) {
// As not to break backcompat, don't use retries with old API
legacyInstance = new Yub(clientID, secretKey, {maxTries: 0});
};
Yub.verify = function(otp, callback) {
if (!legacyInstance) throw new Error('init() before verifying!');
return legacyInstance.verify(otp, callback);
};
Yub.verifyOffline = Yub.prototype.verifyOffline; // No actual legacy instance required
Yub._calculateHmac = calculateHmac; // for tests
Yub._calculateStringToHash = calculateStringToHash; // for tests
{
"name": "yub",
"version": "0.10.14",
"version": "0.11.0",
"description": "Yubico Yubikey API Client for Node.js",

@@ -17,10 +17,17 @@ "main": "index.js",

"scripts": {
"test": "mocha"
"test": "mocha",
"lint": "eslint ."
},
"dependencies": {
"request": "2.73.0"
},
"devDependencies": {
"mocha": "^2.5.3"
}
"eslint": "^3.1.1",
"mocha": "^3.0.0",
"nock": "^8.0.0",
"pre-commit": "^1.1.3"
},
"pre-commit": [
"lint",
"test"
]
}

Sorry, the diff of this file is not supported yet

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