© 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