package org.bouncycastle.tls.crypto.impl.jcajce;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.tls.PRFAlgorithm;
import org.bouncycastle.tls.TlsUtils;
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;
/**
* JCE support class for handling TLS secrets and deriving key material and other secrets from them.
*/
public class JceTlsSecret
extends AbstractTlsSecret
{
private final JcaTlsCrypto crypto;
public JceTlsSecret(JcaTlsCrypto crypto, byte[] data)
{
super(data);
this.crypto = crypto;
}
public synchronized TlsSecret deriveSSLKeyBlock(byte[] seed, int length)
{
checkAlive();
try
{
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;
}
catch (GeneralSecurityException e)
{
throw new RuntimeException(); // TODO:
}
}
public synchronized TlsSecret deriveSSLMasterSecret(byte[] seed)
{
checkAlive();
try
{
return crypto.adoptLocalSecret(prf_SSL(seed, 3));
}
catch (GeneralSecurityException e)
{
throw new RuntimeException(); // TODO:
}
}
public synchronized TlsSecret deriveUsingPRF(int prfAlgorithm, byte[] labelSeed, int length)
{
checkAlive();
try
{
byte[] result = (prfAlgorithm == PRFAlgorithm.tls_prf_legacy)
? prf_1_0(data, labelSeed, length)
: prf_1_2(crypto.getDigestName(TlsUtils.getHashAlgorithmForPRFAlgorithm(prfAlgorithm)), data, labelSeed, length);
return crypto.adoptLocalSecret(result);
}
catch (GeneralSecurityException e)
{
throw new IllegalStateException(); // TODO
}
}
protected AbstractTlsCrypto getCrypto()
{
return crypto;
}
protected void hmacHash(String digestName, byte[] secret, byte[] seed, byte[] output)
throws GeneralSecurityException
{
String macName = "Hmac" + digestName;
Mac mac = crypto.getHelper().createMac(macName);
mac.init(new SecretKeySpec(secret, macName));
byte[] a = seed;
int size = mac.getMacLength();
int iterations = (output.length + size - 1) / size;
byte[] b1 = new byte[mac.getMacLength()];
byte[] b2 = new byte[mac.getMacLength()];
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)
throws GeneralSecurityException
{
MessageDigest md5 = crypto.getHelper().createDigest("MD5");
MessageDigest sha1 = crypto.getHelper().createDigest("SHA-1");
int md5Size = md5.getDigestLength();
byte[] md5Buf = new byte[md5Size * md5Count];
int md5Pos = 0;
byte[] sha1Buf = new byte[sha1.getDigestLength()];
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.digest(sha1Buf, 0, sha1Buf.length);
md5.update(data, 0, data.length);
md5.update(sha1Buf, 0, sha1Buf.length);
md5.digest(md5Buf, md5Pos, md5Size);
md5Pos += md5Size;
}
return md5Buf;
}
protected byte[] prf_1_0(byte[] secret, byte[] labelSeed, int length)
throws GeneralSecurityException
{
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("MD5", s1, labelSeed, b1);
hmacHash("SHA1", s2, labelSeed, b2);
for (int i = 0; i < length; i++)
{
b1[i] ^= b2[i];
}
return b1;
}
protected byte[] prf_1_2(String prfDigest, byte[] secret, byte[] labelSeed, int length)
throws GeneralSecurityException
{
byte[] result = new byte[length];
hmacHash(prfDigest.replace("-", ""), secret, labelSeed, result);
return result;
}
}