360 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
 | |
| var crypto = require('crypto');
 | |
| var base32 = require('thirty-two');
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Export module as new Notp instance
 | |
|  */
 | |
| module.exports = new Notp();
 | |
| 
 | |
| function Notp() {
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Check a One Time Password based on a counter.
 | |
|  *
 | |
|  * First argument of callback is true if password check is successful,
 | |
|  * or false if check fails.
 | |
|  *
 | |
|  * Second argument is the time step difference between the client and
 | |
|  * the server.  This argument is only passed if the password check is
 | |
|  * successful.
 | |
|  *
 | |
|  * Arguments:
 | |
|  *
 | |
|  *  args
 | |
|  *     K - Key for the one time password.  This should be unique and secret for
 | |
|  *         every user as it is the seed used to calculate the HMAC
 | |
|  *
 | |
|  *     P - Passcode to validate.
 | |
|  *
 | |
|  *     W - The allowable margin for the counter.  The function will check
 | |
|  *         W codes in the future against the provided passcode.  Note,
 | |
|  *         it is the calling applications responsibility to keep track of
 | |
|  *         W and increment it for each password check, and also to adjust
 | |
|  *         it accordingly in the case where the client and server become
 | |
|  *         out of sync (second argument returns non zero).
 | |
|  *         E.g. if W = 100, and C = 5, this function will check the psscode
 | |
|  *         against all One Time Passcodes between 5 and 105.
 | |
|  *
 | |
|  *         Default - 50
 | |
|  *
 | |
|  *     C - Counter value.  This should be stored by the application, must
 | |
|  *         be user specific, and be incremented for each request.
 | |
|  *
 | |
|  */
 | |
| Notp.prototype.checkHOTP = function(args, err, cb) {
 | |
| 
 | |
| 	var hmac,
 | |
| 		digest,
 | |
| 		offset, h, v, p = 6, b,
 | |
| 		i,
 | |
| 		K = args.K || "",
 | |
| 		W = args.W || 50,
 | |
| 		C = args.C || 0,
 | |
| 		P = args.P || "";
 | |
| 
 | |
| 
 | |
| 	// Initiate the HMAC
 | |
| 	hmac = crypto.createHmac('SHA1', new Buffer(K))
 | |
| 
 | |
| 	// Now loop through from C to C + W to determine if there is
 | |
| 	// a correct code
 | |
| 	for(i = C; i <= C+W; i++) {
 | |
| 		if(this._calcHMAC(K,i) === P) {
 | |
| 			// We have found a matching code, trigger callback
 | |
| 			// and pass offset
 | |
| 			cb(true, i - C);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// If we get to here then no codes have matched, return false
 | |
| 	cb(false);
 | |
| 	return;
 | |
| 
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Check a One Time Password based on a timer.
 | |
|  *
 | |
|  * First argument of callback is true if password check is successful,
 | |
|  * or false if check fails.
 | |
|  *
 | |
|  * Second argument is the time step difference between the client and
 | |
|  * the server.  This argument is only passed if the password check is
 | |
|  * successful.
 | |
|  *
 | |
|  * Arguments:
 | |
|  *
 | |
|  *  args
 | |
|  *     K - Key for the one time password.  This should be unique and secret for
 | |
|  *         every user as it is the seed used to calculate the HMAC
 | |
|  *
 | |
|  *     P - Passcode to validate.
 | |
|  *
 | |
|  *     W - The allowable margin for the counter.  The function will check
 | |
|  *         W codes either side of the provided counter.  Note,
 | |
|  *         it is the calling applications responsibility to keep track of
 | |
|  *         W and increment it for each password check, and also to adjust
 | |
|  *         it accordingly in the case where the client and server become
 | |
|  *         out of sync (second argument returns non zero).
 | |
|  *         E.g. if W = 5, and C = 1000, this function will check the psscode
 | |
|  *         against all One Time Passcodes between 995 and 1005.
 | |
|  *
 | |
|  *         Default - 6
 | |
|  *
 | |
|  *     T - The time step of the counter.  This must be the same for
 | |
|  *         every request and is used to calculat C.
 | |
|  *
 | |
|  *         Default - 30
 | |
|  *
 | |
|  */
 | |
| Notp.prototype.checkTOTP = function(args, err, cb) {
 | |
| 
 | |
| 	var hmac,
 | |
| 		digest,
 | |
| 		offset, h, v, p = 6, b,
 | |
| 		C,i,
 | |
| 		K = args.K || "",
 | |
| 		W = args.W || 6,
 | |
| 		T = args.T || 30,
 | |
| 		P = args.P || "",
 | |
| 		_t;
 | |
| 
 | |
| 
 | |
| 	if(args._t) {
 | |
| 		// Time has been overwritten.
 | |
| 		console.log('#####################################');
 | |
| 		console.log('# NOTE: TOTP TIME VARIABLE HAS BEEN #');
 | |
| 		console.log('# OVERWRITTEN.  THIS SHOULD ONLY BE #');
 | |
| 		console.log('# USED FOR TEST PURPOSES.           #');
 | |
| 		console.log('#####################################');
 | |
| 		_t = args._t;
 | |
| 	} else {
 | |
| 		_t = new Date().getTime();
 | |
| 	}
 | |
| 
 | |
| 	// Initiate the HMAC
 | |
| 	hmac = crypto.createHmac('SHA1', new Buffer(K))
 | |
| 
 | |
| 	// Determine the value of the counter, C
 | |
| 	// This is the number of time steps in seconds since T0
 | |
| 	C = Math.floor((_t / 1000) / T);
 | |
| 
 | |
| 	// Now loop through from C - W to C + W and check to see
 | |
| 	// if we have a valid code in that time line
 | |
| 	for(i = C-W; i <= C+W; i++) {
 | |
| 
 | |
| 		if(this._calcHMAC(K,i) === P) {
 | |
| 			// We have found a matching code, trigger callback
 | |
| 			// and pass offset
 | |
| 			cb(true, i-C);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// If we get to here then no codes have matched, return false
 | |
| 	cb(false);
 | |
| 	return;
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Gennerate a counter based One Time Password
 | |
|  *
 | |
|  * First argument of callback is the value of the One Time Password
 | |
|  *
 | |
|  * Arguments:
 | |
|  *
 | |
|  *  args
 | |
|  *     K - Key for the one time password.  This should be unique and secret for
 | |
|  *         every user as it is the seed used to calculate the HMAC
 | |
|  *
 | |
|  *     C - Counter value.  This should be stored by the application, must
 | |
|  *         be user specific, and be incremented for each request.
 | |
|  *
 | |
|  */
 | |
| Notp.prototype.getHOTP = function(args, err, cb) {
 | |
| 
 | |
| 	var hmac,
 | |
| 		digest,
 | |
| 		offset, h, v, p = 6, b,
 | |
| 		i,
 | |
| 		K = args.K || "",
 | |
| 		C = args.C || 0,
 | |
| 
 | |
| 
 | |
| 	// Initiate the HMAC
 | |
| 	hmac = crypto.createHmac('SHA1', new Buffer(K))
 | |
| 
 | |
| 	cb(this._calcHMAC(K,C));
 | |
| 
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Gennerate a time based One Time Password
 | |
|  *
 | |
|  * First argument of callback is the value of the One Time Password
 | |
|  *
 | |
|  * Arguments:
 | |
|  *
 | |
|  *  args
 | |
|  *     K - Key for the one time password.  This should be unique and secret for
 | |
|  *         every user as it is the seed used to calculate the HMAC
 | |
|  *
 | |
|  *     T - The time step of the counter.  This must be the same for
 | |
|  *         every request and is used to calculat C.
 | |
|  *
 | |
|  *         Default - 30
 | |
|  *
 | |
|  */
 | |
| Notp.prototype.getTOTP = function(args, err, cb) {
 | |
| 	var hmac,
 | |
| 		digest,
 | |
| 		offset, h, v, p = 6, b,
 | |
| 		C,i,
 | |
| 		K = args.K || "",
 | |
| 		T = args.T || 30,
 | |
| 		_t;
 | |
| 
 | |
| 
 | |
| 	if(args._t) {
 | |
| 		// Time has been overwritten.
 | |
| 		console.log('#####################################');
 | |
| 		console.log('# NOTE: TOTP TIME VARIABLE HAS BEEN #');
 | |
| 		console.log('# OVERWRITTEN.  THIS SHOULD ONLY BE #');
 | |
| 		console.log('# USED FOR TEST PURPOSES.           #');
 | |
| 		console.log('#####################################');
 | |
| 		_t = args._t;
 | |
| 	} else {
 | |
| 		_t = new Date().getTime();
 | |
| 	}
 | |
| 
 | |
| 	// Initiate the HMAC
 | |
| 	hmac = crypto.createHmac('SHA1', new Buffer(K))
 | |
| 
 | |
| 	// Determine the value of the counter, C
 | |
| 	// This is the number of time steps in seconds since T0
 | |
| 	C = Math.floor((_t / 1000) / T);
 | |
| 
 | |
| 	cb(this._calcHMAC(K,C));
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Helper function to convert a string to a base32 encoded string
 | |
|  *
 | |
|  * Arguments:
 | |
|  *
 | |
|  * str - String to encode
 | |
|  *
 | |
|  * Returns: Base 32 encoded string
 | |
|  */
 | |
| Notp.prototype.encBase32 = function(str) {
 | |
| 	return base32.encode(str);
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Helper function to convert a base32 encoded string to an ascii string
 | |
|  *
 | |
|  * Arguments:
 | |
|  *
 | |
|  * b32 - String to decode
 | |
|  *
 | |
|  * Returns: ASCII string
 | |
|  */
 | |
| Notp.prototype.decBase32 = function(b32) {
 | |
| 	return base32.decode(b32);
 | |
| };
 | |
| 
 | |
| 
 | |
| /******************************************************************
 | |
|  * NOTE: Any functions below this line are private and therefore  *
 | |
|  * may change without providing backwards compatibility with      *
 | |
|  * previous versions.  You should not call the functions below    *
 | |
|  * directly.                                                      *
 | |
| *******************************************************************/
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Private functon to calculate an HMAC.
 | |
|  *
 | |
|  * Arguments
 | |
|  *
 | |
|  *  K - Key value
 | |
|  *  C - Counter value
 | |
|  *
 | |
|  *  Returns - truncated HMAC
 | |
|  */
 | |
| Notp.prototype._calcHMAC = function(K, C) {
 | |
| 
 | |
| 	var hmac = crypto.createHmac('SHA1', new Buffer(K)),
 | |
| 		digest,
 | |
| 		offset, h, v, p = 6, b;
 | |
| 
 | |
| 	// Create the byte array
 | |
| 	b = new Buffer(this._intToBytes(C)),
 | |
| 
 | |
| 	// Update the HMAC witht he byte array
 | |
| 	hmac.update(b);
 | |
| 
 | |
| 	// Diget the HMAC
 | |
| 	digest = hmac.digest('hex');
 | |
| 
 | |
| 	// Get byte array
 | |
| 	h = this._hexToBytes(digest);
 | |
| 
 | |
| 	// Truncate
 | |
| 	offset = h[19] & 0xf;
 | |
| 	v = (h[offset] & 0x7f) << 24 | (h[offset + 1] & 0xff) << 16 | (h[offset + 2] & 0xff) << 8 | (h[offset + 3] & 0xff);
 | |
| 	v = "" + v;
 | |
| 	v = v.substr(v.length - p, p);
 | |
| 
 | |
| 	return v;
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Private function to convert an integer to a byte array
 | |
|  *
 | |
|  * Arguments
 | |
|  *
 | |
|  * num - Integer
 | |
|  *
 | |
|  * Returns - byte array
 | |
|  */
 | |
| Notp.prototype._intToBytes = function(num) {
 | |
| 	var bytes = [],
 | |
| 		i;
 | |
| 
 | |
| 	for(i=7;i>=0;i--) {
 | |
| 		bytes[i] = num & (255);
 | |
| 		num = num >> 8;
 | |
| 	}
 | |
| 
 | |
| 	return bytes;
 | |
| };
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Private function to convert a hex value to a byte array
 | |
|  *
 | |
|  * Arguments
 | |
|  *
 | |
|  * hex - Hex value
 | |
|  *
 | |
|  * Returns - byte array
 | |
|  */
 | |
| Notp.prototype._hexToBytes = function(hex) {
 | |
| 	for(var bytes = [], c = 0; c < hex.length; c += 2)
 | |
| 			bytes.push(parseInt(hex.substr(c, 2), 16));
 | |
| 	return bytes;
 | |
| };
 | |
| 
 |