HTML Data Port

© 2005 Goetz Heller

Table of Contents Description Examples Copyright Note back

HMAC: Keyed-Hashing for Message Authentication - JavaScript Source Code

   /// 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