package org.bouncycastle.tls.crypto.impl.bc;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.tls.HashAlgorithm;
import org.bouncycastle.tls.PRFAlgorithm;
import org.bouncycastle.tls.crypto.TlsSecret;
import org.bouncycastle.tls.crypto.impl.AbstractTlsCrypto;
import org.bouncycastle.tls.crypto.impl.AbstractTlsSecret;
import org.bouncycastle.util.Arrays;
/**
* BC light-weight support class for handling TLS secrets and deriving key material and other secrets from them.
*/
public class BcTlsSecret
extends AbstractTlsSecret
{
protected final BcTlsCrypto crypto;
public BcTlsSecret(BcTlsCrypto crypto, byte[] data)
{
super(data);
this.crypto = crypto;
}
protected AbstractTlsCrypto getCrypto()
{
return crypto;
}
public synchronized TlsSecret deriveSSLKeyBlock(byte[] seed, int length)
{
checkAlive();
int md5Count = (length + MD5_SIZE - 1) / MD5_SIZE;
byte[] md5Buf = prf_SSL(seed, md5Count);
TlsSecret result = crypto.adoptLocalSecret(Arrays.copyOfRange(md5Buf, 0, length));
Arrays.fill(md5Buf, (byte)0);
return result;
}
public synchronized TlsSecret deriveSSLMasterSecret(byte[] seed)
{
checkAlive();
return crypto.adoptLocalSecret(prf_SSL(seed, 3));
}
public synchronized TlsSecret deriveUsingPRF(int prfAlgorithm, byte[] labelSeed, int length)
{
checkAlive();
byte[] result = (prfAlgorithm == PRFAlgorithm.tls_prf_legacy)
? prf_1_0(data, labelSeed, length)
: prf_1_2(crypto.createPRFHash(prfAlgorithm), data, labelSeed, length);
return crypto.adoptLocalSecret(result);
}
protected void hmacHash(Digest digest, byte[] secret, byte[] seed, byte[] output)
{
HMac mac = new HMac(digest);
mac.init(new KeyParameter(secret));
byte[] a = seed;
int size = digest.getDigestSize();
int iterations = (output.length + size - 1) / size;
byte[] b1 = new byte[mac.getMacSize()];
byte[] b2 = new byte[mac.getMacSize()];
for (int i = 0; i < iterations; i++)
{
mac.update(a, 0, a.length);
mac.doFinal(b1, 0);
a = b1;
mac.update(a, 0, a.length);
mac.update(seed, 0, seed.length);
mac.doFinal(b2, 0);
System.arraycopy(b2, 0, output, (size * i), Math.min(size, output.length - (size * i)));
}
}
protected byte[] prf_SSL(byte[] seed, int md5Count)
{
Digest md5 = crypto.createDigest(HashAlgorithm.md5);
Digest sha1 = crypto.createDigest(HashAlgorithm.sha1);
int md5Size = md5.getDigestSize();
byte[] md5Buf = new byte[md5Size * md5Count];
int md5Pos = 0;
byte[] sha1Buf = new byte[sha1.getDigestSize()];
for (int i = 0; i < md5Count; ++i)
{
byte[] ssl3Const = SSL3_CONST[i];
sha1.update(ssl3Const, 0, ssl3Const.length);
sha1.update(data, 0, data.length);
sha1.update(seed, 0, seed.length);
sha1.doFinal(sha1Buf, 0);
md5.update(data, 0, data.length);
md5.update(sha1Buf, 0, sha1Buf.length);
md5.doFinal(md5Buf, md5Pos);
md5Pos += md5Size;
}
return md5Buf;
}
protected byte[] prf_1_0(byte[] secret, byte[] labelSeed, int length)
{
int s_half = (secret.length + 1) / 2;
byte[] s1 = new byte[s_half];
byte[] s2 = new byte[s_half];
System.arraycopy(secret, 0, s1, 0, s_half);
System.arraycopy(secret, secret.length - s_half, s2, 0, s_half);
byte[] b1 = new byte[length];
byte[] b2 = new byte[length];
hmacHash(crypto.createDigest(HashAlgorithm.md5), s1, labelSeed, b1);
hmacHash(crypto.createDigest(HashAlgorithm.sha1), s2, labelSeed, b2);
for (int i = 0; i < length; i++)
{
b1[i] ^= b2[i];
}
return b1;
}
protected byte[] prf_1_2(Digest prfDigest, byte[] secret, byte[] labelSeed, int length)
{
byte[] result = new byte[length];
hmacHash(prfDigest, secret, labelSeed, result);
return result;
}
}