© 2005 Goetz Heller
Table of Contents Description Examples Copyright Note back /// JavaScript generic HMAC implementation
///
/// This implementation is based on RFC 2104. Since it is not expected that this
/// implementation will handle large amounts of data efficiency was not considered
/// a primary goal.
///
/// Goetz Heller
/// Dr.Heller Information Management
/// 08.02.2005
/// Dependencies
/// genhash.js
// class HMAC
//
try {
if (system.security.encryption.hash.HMAC == null) {
throw new Error('');
};
} catch(e) { // don't install twice
// dependencies
try {
eval('core');
} catch(e) {
throw new Error('HMAC: class \'core\' not installed');
}
var hashClass = 'system.security.encryption.hash.hashAlgorithm';
try {
eval(hashClass);
} catch(e) {
throw new Error('HMAC: class \'+hashClass+\' not installed');
}
// constructor - throws exception if alg does not exist
var HMAC = function() {};
// provide for class information
HMAC.classID = function() {
return 'system.security.encryption.hash.HMAC';
};
HMAC.prototype.hashClass = hashClass;
//disallow subclassing
HMAC.isFinal = function() {
return true;
};
// override toString() method
HMAC.prototype.toString = function() { return 'HMAC-'+this.alg.toString(); };
HMAC.prototype.initialize = function(alg) {
this.setAlgorithm(alg);
this.innerKeyPad = new Array(this.alg.iInputBlockSize);
this.outerKeyPad = new Array(this.alg.iInputBlockSize);
this.reset();
};
// reset HMAC structures
HMAC.prototype.reset = function() {
for (var i = 0; i < this.innerKeyPad.length; i++) {
this.innerKeyPad[i] = 0x00;
}
for (var i = 0; i < this.outerKeyPad.length; i++) {
this.outerKeyPad[i] = 0x00;
}
};
// "re-instantiate"
HMAC.prototype.setAlgorithm = function(alg) {
if (alg == null) throw new Error('HMAC.setAlgorithm(): missing parameter');
if (typeof alg == 'string') {
var classID = '';
var cp = new core.classPath(alg);
if (cp.nameSpaceString() == '') {
classID = (new core.classPath(this.classID())).nameSpaceString() + '.' + alg;
} else {
classID = alg;
}
try {
alg = core.createInstance(classID);
} catch(e) {
throw new Error('HMAC.setAlgorithm(): \''+classID+'\' cannot be instantiated');
}
}
if (alg.constructor.sup == null || alg.constructor.sup.classID() != this.hashClass) throw new Error('HMAC.setAlgorithm(): \''+alg.classID()+'\' is not a hash algorithm');
this.alg = alg;
// initialize
try {
this.reset();
} catch(e) {}
};
// computes the keyed hash and returns it as string.
HMAC.prototype.computeHash = function(inp, key) {
// if key is longer than algorithm's input block size reset it to this length;
var redKey = null; // reduced key
if (key.length > this.alg.iInputBlockSize) {
this.alg.reset();
this.alg.hashCore(key, 0, key.length);
redKey = this.alg.hashFinal();
} else {
redKey = key;
}
this.reset();
// start out by storing key in pads
for (var i = 0; i < redKey.length; i++) {
this.innerKeyPad[i] = (redKey.charCodeAt(i) ^ 0x36);
this.outerKeyPad[i] = (redKey.charCodeAt(i) ^ 0x5c);
}
for (var i = redKey.length; i < this.alg.iInputBlockSize; i++) {
this.innerKeyPad[i] = 0x36;
this.outerKeyPad[i] = 0x5c;
}
// perform inner algorithm
this.alg.reset();
this.alg.hashCore(this.innerKeyPad, 0, this.innerKeyPad.length);
this.alg.hashCore(inp, 0, inp.length);
var digest = this.alg.hashFinal();
// perform outer algorithm
this.alg.reset();
this.alg.hashCore(this.outerKeyPad, 0, this.outerKeyPad.length);
this.alg.hashCore(digest, 0, digest.length);
digest = this.alg.hashFinal();
return digest;
};
// returns the hash value as a byte array. Assumes computeHash() has been called before.
HMAC.prototype.getHashValue = function() {
return this.alg.getHashValue();
};
core.installClass(HMAC);
} // end catch