You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
3.7 KiB
146 lines
3.7 KiB
/** |
|
* 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. |
|
*/ |
|
var forge = require('./forge'); |
|
require('./md'); |
|
require('./util'); |
|
|
|
/* HMAC API */ |
|
var hmac = module.exports = 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; |
|
};
|
|
|