static byte[] doTLS12PRF( byte[] secret, byte[] labelBytes, byte[] seed, int outputLength, MessageDigest mdPRF, int mdPRFLen, int mdPRFBlockSize) throws DigestException { if (secret == null) { secret = B0; } // If we have a long secret, digest it first. if (secret.length > mdPRFBlockSize) { secret = mdPRF.digest(secret); } byte[] output = new byte[outputLength]; byte[] ipad; byte[] opad; switch (mdPRFBlockSize) { case 64: ipad = HMAC_ipad64.clone(); opad = HMAC_opad64.clone(); break; case 128: ipad = HMAC_ipad128.clone(); opad = HMAC_opad128.clone(); break; default: throw new DigestException("Unexpected block size."); } // P_HASH(Secret, label + seed) expand(mdPRF, mdPRFLen, secret, 0, secret.length, labelBytes, seed, output, ipad, opad); return output; }
/* * @param digest the MessageDigest to produce the HMAC * @param hmacSize the HMAC size * @param secret the secret * @param secOff the offset into the secret * @param secLen the secret length * @param label the label * @param seed the seed * @param output the output array */ private static void expand( MessageDigest digest, int hmacSize, byte[] secret, int secOff, int secLen, byte[] label, byte[] seed, byte[] output, byte[] pad1, byte[] pad2) throws DigestException { /* * modify the padding used, by XORing the key into our copy of that * padding. That's to avoid doing that for each HMAC computation. */ for (int i = 0; i < secLen; i++) { pad1[i] ^= secret[i + secOff]; pad2[i] ^= secret[i + secOff]; } byte[] tmp = new byte[hmacSize]; byte[] aBytes = null; /* * compute: * * 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)) */ int remaining = output.length; int ofs = 0; while (remaining > 0) { /* * compute A() ... */ // inner digest digest.update(pad1); if (aBytes == null) { digest.update(label); digest.update(seed); } else { digest.update(aBytes); } digest.digest(tmp, 0, hmacSize); // outer digest digest.update(pad2); digest.update(tmp); if (aBytes == null) { aBytes = new byte[hmacSize]; } digest.digest(aBytes, 0, hmacSize); /* * compute HMAC_hash() ... */ // inner digest digest.update(pad1); digest.update(aBytes); digest.update(label); digest.update(seed); digest.digest(tmp, 0, hmacSize); // outer digest digest.update(pad2); digest.update(tmp); digest.digest(tmp, 0, hmacSize); int k = Math.min(hmacSize, remaining); for (int i = 0; i < k; i++) { output[ofs++] ^= tmp[i]; } remaining -= k; } }
static byte[] doTLS10PRF( byte[] secret, byte[] labelBytes, byte[] seed, int outputLength, MessageDigest md5, MessageDigest sha) throws DigestException { /* * Split the secret into two halves S1 and S2 of same length. * S1 is taken from the first half of the secret, S2 from the * second half. * Their length is created by rounding up the length of the * overall secret divided by two; thus, if the original secret * is an odd number of bytes long, the last byte of S1 will be * the same as the first byte of S2. * * Note: Instead of creating S1 and S2, we determine the offset into * the overall secret where S2 starts. */ if (secret == null) { secret = B0; } int off = secret.length >> 1; int seclen = off + (secret.length & 1); byte[] secKey = secret; int keyLen = seclen; byte[] output = new byte[outputLength]; // P_MD5(S1, label + seed) // If we have a long secret, digest it first. if (seclen > 64) { // 64: block size of HMAC-MD5 md5.update(secret, 0, seclen); secKey = md5.digest(); keyLen = secKey.length; } expand( md5, 16, secKey, 0, keyLen, labelBytes, seed, output, HMAC_ipad64.clone(), HMAC_opad64.clone()); // P_SHA-1(S2, label + seed) // If we have a long secret, digest it first. if (seclen > 64) { // 64: block size of HMAC-SHA1 sha.update(secret, off, seclen); secKey = sha.digest(); keyLen = secKey.length; off = 0; } expand( sha, 20, secKey, off, keyLen, labelBytes, seed, output, HMAC_ipad64.clone(), HMAC_opad64.clone()); return output; }