| /** |
| * Hash-based Message Authentication Code implementation. Requires a message |
| * digest object that can be obtained, for example, from forge.md.sha1 or |
| * forge.md.md5. |
| * |
| * @author Dave Longley |
| * |
| * Copyright (c) 2010-2012 Digital Bazaar, Inc. All rights reserved. |
| */ |
| (function() { |
| /* ########## Begin module implementation ########## */ |
| function initModule(forge) { |
| |
| /* HMAC API */ |
| var hmac = forge.hmac = forge.hmac || {}; |
| |
| /** |
| * Creates an HMAC object that uses the given message digest object. |
| * |
| * @return an HMAC object. |
| */ |
| hmac.create = function() { |
| // the hmac key to use |
| var _key = null; |
| |
| // the message digest to use |
| var _md = null; |
| |
| // the inner padding |
| var _ipadding = null; |
| |
| // the outer padding |
| var _opadding = null; |
| |
| // hmac context |
| var ctx = {}; |
| |
| /** |
| * Starts or restarts the HMAC with the given key and message digest. |
| * |
| * @param md the message digest to use, null to reuse the previous one, |
| * a string to use builtin 'sha1', 'md5', 'sha256'. |
| * @param key the key to use as a string, array of bytes, byte buffer, |
| * or null to reuse the previous key. |
| */ |
| ctx.start = function(md, key) { |
| if(md !== null) { |
| if(typeof md === 'string') { |
| // create builtin message digest |
| md = md.toLowerCase(); |
| if(md in forge.md.algorithms) { |
| _md = forge.md.algorithms[md].create(); |
| } else { |
| throw new Error('Unknown hash algorithm "' + md + '"'); |
| } |
| } else { |
| // store message digest |
| _md = md; |
| } |
| } |
| |
| if(key === null) { |
| // reuse previous key |
| key = _key; |
| } else { |
| if(typeof key === 'string') { |
| // convert string into byte buffer |
| key = forge.util.createBuffer(key); |
| } else if(forge.util.isArray(key)) { |
| // convert byte array into byte buffer |
| var tmp = key; |
| key = forge.util.createBuffer(); |
| for(var i = 0; i < tmp.length; ++i) { |
| key.putByte(tmp[i]); |
| } |
| } |
| |
| // if key is longer than blocksize, hash it |
| var keylen = key.length(); |
| if(keylen > _md.blockLength) { |
| _md.start(); |
| _md.update(key.bytes()); |
| key = _md.digest(); |
| } |
| |
| // mix key into inner and outer padding |
| // ipadding = [0x36 * blocksize] ^ key |
| // opadding = [0x5C * blocksize] ^ key |
| _ipadding = forge.util.createBuffer(); |
| _opadding = forge.util.createBuffer(); |
| keylen = key.length(); |
| for(var i = 0; i < keylen; ++i) { |
| var tmp = key.at(i); |
| _ipadding.putByte(0x36 ^ tmp); |
| _opadding.putByte(0x5C ^ tmp); |
| } |
| |
| // if key is shorter than blocksize, add additional padding |
| if(keylen < _md.blockLength) { |
| var tmp = _md.blockLength - keylen; |
| for(var i = 0; i < tmp; ++i) { |
| _ipadding.putByte(0x36); |
| _opadding.putByte(0x5C); |
| } |
| } |
| _key = key; |
| _ipadding = _ipadding.bytes(); |
| _opadding = _opadding.bytes(); |
| } |
| |
| // digest is done like so: hash(opadding | hash(ipadding | message)) |
| |
| // prepare to do inner hash |
| // hash(ipadding | message) |
| _md.start(); |
| _md.update(_ipadding); |
| }; |
| |
| /** |
| * Updates the HMAC with the given message bytes. |
| * |
| * @param bytes the bytes to update with. |
| */ |
| ctx.update = function(bytes) { |
| _md.update(bytes); |
| }; |
| |
| /** |
| * Produces the Message Authentication Code (MAC). |
| * |
| * @return a byte buffer containing the digest value. |
| */ |
| ctx.getMac = function() { |
| // digest is done like so: hash(opadding | hash(ipadding | message)) |
| // here we do the outer hashing |
| var inner = _md.digest().bytes(); |
| _md.start(); |
| _md.update(_opadding); |
| _md.update(inner); |
| return _md.digest(); |
| }; |
| // alias for getMac |
| ctx.digest = ctx.getMac; |
| |
| return ctx; |
| }; |
| |
| } // end module implementation |
| |
| /* ########## Begin module wrapper ########## */ |
| var name = 'hmac'; |
| if(typeof define !== 'function') { |
| // NodeJS -> AMD |
| if(typeof module === 'object' && module.exports) { |
| var nodeJS = true; |
| define = function(ids, factory) { |
| factory(require, module); |
| }; |
| } else { |
| // <script> |
| if(typeof forge === 'undefined') { |
| forge = {}; |
| } |
| return initModule(forge); |
| } |
| } |
| // AMD |
| var deps; |
| var defineFunc = function(require, module) { |
| module.exports = function(forge) { |
| var mods = deps.map(function(dep) { |
| return require(dep); |
| }).concat(initModule); |
| // handle circular dependencies |
| forge = forge || {}; |
| forge.defined = forge.defined || {}; |
| if(forge.defined[name]) { |
| return forge[name]; |
| } |
| forge.defined[name] = true; |
| for(var i = 0; i < mods.length; ++i) { |
| mods[i](forge); |
| } |
| return forge[name]; |
| }; |
| }; |
| var tmpDefine = define; |
| define = function(ids, factory) { |
| deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2); |
| if(nodeJS) { |
| delete define; |
| return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0)); |
| } |
| define = tmpDefine; |
| return define.apply(null, Array.prototype.slice.call(arguments, 0)); |
| }; |
| define(['require', 'module', './md', './util'], function() { |
| defineFunc.apply(null, Array.prototype.slice.call(arguments, 0)); |
| }); |
| })(); |