build/authorify.js
function(secret, label, seed, length) {
  var rval = forge.util.createBuffer();

  /* For TLS 1.0, the secret is split in half, into two secrets of equal
    length. If the secret has an odd length then the last byte of the first
    half will be the same as the first byte of the second. The length of the
    two secrets is half of the secret rounded up. */
  var idx = (secret.length >> 1);
  var slen = idx + (secret.length & 1);
  var s1 = secret.substr(0, slen);
  var s2 = secret.substr(idx, slen);
  var ai = forge.util.createBuffer();
  var hmac = forge.hmac.create();
  seed = label + seed;

  // determine the number of iterations that must be performed to generate
  // enough output bytes, md5 creates 16 byte hashes, sha1 creates 20
  var md5itr = Math.ceil(length / 16);
  var sha1itr = Math.ceil(length / 20);

  // do md5 iterations
  hmac.start('MD5', s1);
  var md5bytes = forge.util.createBuffer();
  ai.putBytes(seed);
  for(var i = 0; i < md5itr; ++i) {
    // HMAC_hash(secret, A(i-1))
    hmac.start(null, null);
    hmac.update(ai.getBytes());
    ai.putBuffer(hmac.digest());

    // HMAC_hash(secret, A(i) + seed)
    hmac.start(null, null);
    hmac.update(ai.bytes() + seed);
    md5bytes.putBuffer(hmac.digest());
  }

  // do sha1 iterations
  hmac.start('SHA1', s2);
  var sha1bytes = forge.util.createBuffer();
  ai.clear();
  ai.putBytes(seed);
  for(var i = 0; i < sha1itr; ++i) {
    // HMAC_hash(secret, A(i-1))
    hmac.start(null, null);
    hmac.update(ai.getBytes());
    ai.putBuffer(hmac.digest());

    // HMAC_hash(secret, A(i) + seed)
    hmac.start(null, null);
    hmac.update(ai.bytes() + seed);
    sha1bytes.putBuffer(hmac.digest());
  }

  // XOR the md5 bytes with the sha1 bytes
  rval.putBytes(forge.util.xorBytes(
    md5bytes.getBytes(), sha1bytes.getBytes(), length));

  return rval;
}
function(secret, label, seed, length) {
  var rval = forge.util.createBuffer();

  /* For TLS 1.0, the secret is split in half, into two secrets of equal
    length. If the secret has an odd length then the last byte of the first
    half will be the same as the first byte of the second. The length of the
    two secrets is half of the secret rounded up. */
  var idx = (secret.length >> 1);
  var slen = idx + (secret.length & 1);
  var s1 = secret.substr(0, slen);
  var s2 = secret.substr(idx, slen);
  var ai = forge.util.createBuffer();
  var hmac = forge.hmac.create();
  seed = label + seed;

  // determine the number of iterations that must be performed to generate
  // enough output bytes, md5 creates 16 byte hashes, sha1 creates 20
  var md5itr = Math.ceil(length / 16);
  var sha1itr = Math.ceil(length / 20);

  // do md5 iterations
  hmac.start('MD5', s1);
  var md5bytes = forge.util.createBuffer();
  ai.putBytes(seed);
  for(var i = 0; i < md5itr; ++i) {
    // HMAC_hash(secret, A(i-1))
    hmac.start(null, null);
    hmac.update(ai.getBytes());
    ai.putBuffer(hmac.digest());

    // HMAC_hash(secret, A(i) + seed)
    hmac.start(null, null);
    hmac.update(ai.bytes() + seed);
    md5bytes.putBuffer(hmac.digest());
  }

  // do sha1 iterations
  hmac.start('SHA1', s2);
  var sha1bytes = forge.util.createBuffer();
  ai.clear();
  ai.putBytes(seed);
  for(var i = 0; i < sha1itr; ++i) {
    // HMAC_hash(secret, A(i-1))
    hmac.start(null, null);
    hmac.update(ai.getBytes());
    ai.putBuffer(hmac.digest());

    // HMAC_hash(secret, A(i) + seed)
    hmac.start(null, null);
    hmac.update(ai.bytes() + seed);
    sha1bytes.putBuffer(hmac.digest());
  }

  // XOR the md5 bytes with the sha1 bytes
  rval.putBytes(forge.util.xorBytes(
    md5bytes.getBytes(), sha1bytes.getBytes(), length));

  return rval;
}
function ( secret , label , seed , length ) { var rval = forge . util . createBuffer ( ) ; /* For TLS 1.0, the secret is split in half, into two secrets of equal
    length. If the secret has an odd length then the last byte of the first
    half will be the same as the first byte of the second. The length of the
    two secrets is half of the secret rounded up. */ var idx = ( secret . length >> 1 ) ; var slen = idx + ( secret . length & 1 ) ; var s1 = secret . substr ( 0 , slen ) ; var s2 = secret . substr ( idx , slen ) ; var ai = forge . util . createBuffer ( ) ; var hmac = forge . hmac . create ( ) ; seed = label + seed ; // determine the number of iterations that must be performed to generate // enough output bytes, md5 creates 16 byte hashes, sha1 creates 20 var md5itr = Math . ceil ( length / 16 ) ; var sha1itr = Math . ceil ( length / 20 ) ; // do md5 iterations hmac . start ( 'MD5' , s1 ) ; var md5bytes = forge . util . createBuffer ( ) ; ai . putBytes ( seed ) ; for ( var i = 0 ; i < md5itr ; ++ i ) { // HMAC_hash(secret, A(i-1)) hmac . start ( null , null ) ; hmac . update ( ai . getBytes ( ) ) ; ai . putBuffer ( hmac . digest ( ) ) ; // HMAC_hash(secret, A(i) + seed) hmac . start ( null , null ) ; hmac . update ( ai . bytes ( ) + seed ) ; md5bytes . putBuffer ( hmac . digest ( ) ) ; } // do sha1 iterations hmac . start ( 'SHA1' , s2 ) ; var sha1bytes = forge . util . createBuffer ( ) ; ai . clear ( ) ; ai . putBytes ( seed ) ; for ( var i = 0 ; i < sha1itr ; ++ i ) { // HMAC_hash(secret, A(i-1)) hmac . start ( null , null ) ; hmac . update ( ai . getBytes ( ) ) ; ai . putBuffer ( hmac . digest ( ) ) ; // HMAC_hash(secret, A(i) + seed) hmac . start ( null , null ) ; hmac . update ( ai . bytes ( ) + seed ) ; sha1bytes . putBuffer ( hmac . digest ( ) ) ; } // XOR the md5 bytes with the sha1 bytes rval . putBytes ( forge . util . xorBytes ( md5bytes . getBytes ( ) , sha1bytes . getBytes ( ) , length ) ) ; return rval ; }
Generates pseudo random bytes by mixing the result of two hash functions,
MD5 and SHA-1.

prf_TLS1(secret, label, seed) =
P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed);

Each P_hash function functions as follows:

P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
HMAC_hash(secret, A(2) + seed) +
HMAC_hash(secret, A(3) + seed) + ...
A() is defined as:
A(0) = seed
A(i) = HMAC_hash(secret, A(i-1))

The '+' operator denotes concatenation.

As many iterations A(N) as are needed are performed to generate enough
pseudo random byte output. If an iteration creates more data than is
necessary, then it is truncated.

Therefore:
A(1) = HMAC_hash(secret, A(0))
= HMAC_hash(secret, seed)
A(2) = HMAC_hash(secret, A(1))
= HMAC_hash(secret, HMAC_hash(secret, seed))

Therefore:
P_hash(secret, seed) =
HMAC_hash(secret, HMAC_hash(secret, A(0)) + seed) +
HMAC_hash(secret, HMAC_hash(secret, A(1)) + seed) +
...

Therefore:
P_hash(secret, seed) =
HMAC_hash(secret, HMAC_hash(secret, seed) + seed) +
HMAC_hash(secret, HMAC_hash(secret, HMAC_hash(secret, seed)) + seed) +
...

@param secret the secret to use.
@param label the label to use.
@param seed the seed value to use.
@param length the number of bytes to generate.

@return the pseudo random bytes in a byte buffer.
Generates pseudo random bytes by mixing the result of two hash functions MD5 and SHA - 1 .