203 lines
5.2 KiB
JavaScript
203 lines
5.2 KiB
JavaScript
/**
|
|
* 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) {
|
|
console.log('forge key start', typeof key, Object.prototype.toString.apply(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));
|
|
});
|
|
})();
|