Last active
January 4, 2016 10:49
-
-
Save adoc/8611494 to your computer and use it in GitHub Desktop.
hmac.js: Using CryptoJS, implements HMAC algorithm while also hashing and appending timestamp information.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
hmac.js | |
Using CryptoJS, implements HMAC algorithm while also hashing and appending | |
timestamp information. | |
Author: github.com/adoc | |
Location: https://gist.github.com/adoc/8611494 | |
*/ | |
(function(window) { | |
// Just a simple time provider that can be offset to another clock. | |
// Second-only accuracy. | |
function TimeProvider() { | |
var _timeProvider = { | |
offset: 0, | |
reset: function(time) { | |
if (!time) { | |
this.offset = 0; | |
} else { | |
var now = new Date().getTime() / 1000; | |
this.offset = time - parseInt(now); | |
} | |
}, | |
get: function() { | |
var now = new Date().getTime() / 1000; | |
return parseInt(now + this.offset); | |
} | |
} | |
return _timeProvider; | |
} | |
var Hmac = function(opts) { | |
opts = opts || {}; | |
var defaults = { | |
secret: null, | |
passes: 1, | |
hmacAlg: CryptoJS.algo.HMAC, | |
hashAlg: CryptoJS.algo.SHA256, | |
Encoder: CryptoJS.enc.Base64, | |
expiry: 1, | |
timeProvider: new TimeProvider(), | |
debug: false | |
}; | |
var _Hmac = { | |
// Basic signing method. | |
_sign: function () { | |
if (this.debug) { | |
for(var arg=0; arg<arguments.length; arg++) { | |
console.log(arguments[arg]); | |
} | |
} | |
// Init HMAC alg. | |
var hmac = this.hmacAlg.create(this.hashAlg, this.secret); | |
// Do hashing. | |
for (pass=0; pass<this.passes; pass++) { | |
for(var arg=0; arg<arguments.length; arg++) { | |
hmac.update(arguments[arg].toString()); | |
} | |
} | |
return hmac.finalize(); | |
}, | |
// Sign with timestamp. | |
sign: function() { | |
var args = [].slice.apply(arguments); | |
// Get current timestamp. | |
var ts = this.timeProvider.get().toString(); | |
// Set up our own args. | |
args.splice(0, 0, ts); | |
// Sign and apply timestamp. | |
var digest = this._sign.apply(this, args); | |
// Convert so we can add some bytes. | |
digest = wordsToBytes(digest.words); | |
// Add the timestamp. | |
digest = stringToBytes(ts, digest); | |
// Back to WordArray then encoded. | |
digest = new CryptoJS.lib.WordArray.init(bytesToWords(digest)) | |
return digest.toString(this.Encoder); | |
}, | |
// Verify a challenge and payload. | |
verify: function(challenge) { | |
var args = [].slice.apply(arguments); | |
args.splice(0, 1); // Remove `challenge` arg. | |
challenge = this.Encoder.parse(challenge); | |
var challenge_bytes = stripArray(wordsToBytes(challenge.words)); | |
var sig_bytes = challenge_bytes.slice(0,-10); | |
var ts_bytes = challenge_bytes.slice(-10); | |
// Get the timestamp from the bytes. | |
var ts = bytesToString(ts_bytes); | |
// Check the timestamp against current time. | |
var now = parseInt(this.timeProvider.get()); | |
var delta = now - parseInt(ts); | |
if (Math.abs(delta) > this.expiry) { | |
throw "Hmac: Signature is too old."; | |
} | |
// Sign and check against challenge. | |
args.splice(0, 0, ts); | |
sig_bytes = new CryptoJS.lib.WordArray.init(bytesToWords(sig_bytes)); | |
var chal = sig_bytes.toString(this.Encoder); | |
var sig = this._sign.apply(this, args).toString(this.Encoder); | |
if (sig == chal) { | |
return true; | |
} else { | |
if (this.debug) { | |
console.log("Sig: " + sig + " Challenge: " + chal) | |
} | |
throw "Hmac.verify: Incorrect HMAC challenge."; | |
} | |
} | |
}; | |
return _.extend(_Hmac, defaults, opts); | |
} | |
// Set global object. | |
window.TimeProvider = TimeProvider; | |
window.Hmac = Hmac; | |
// AMD Hook | |
if (typeof define === "function" && define.amd) { | |
define( "mypackage", ['underscore'], function () { | |
return {Hmac: Hmac, | |
TimeProvider: TimeProvider}; | |
}); | |
} | |
}).call(window, this); | |
function Hmac_test() { | |
// sign | |
var h = new Hmac({secret: '12345'}); | |
digest = h.sign('this is my sensitive data.'); | |
// verify | |
var h = new Hmac({secret: '12345'}); | |
h.verify(digest, 'this is my sensitive data.'); | |
// sign | |
var h = new Hmac({secret: '12345'}); | |
digest = h.sign('this is my sensitive data.', 'oh I forgot, this too.'); | |
// verify | |
var h = new Hmac({secret: '12345'}); | |
h.verify(digest, 'this is my sensitive data.', 'oh I forgot, this too.'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment