package org.bouncycastle.jcajce.provider.symmetric;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.Mac;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.util.DigestFactory;
import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory;
import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
import org.bouncycastle.jcajce.spec.TLSKeyMaterialSpec;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Strings;
public class TLSKDF
{
public static class TLSKeyMaterialFactory
extends BaseSecretKeyFactory
{
protected TLSKeyMaterialFactory(String algName)
{
super(algName, null);
}
}
public static final class TLS10
extends TLSKeyMaterialFactory
{
public TLS10()
{
super("TLS10KDF");
}
protected SecretKey engineGenerateSecret(
KeySpec keySpec)
throws InvalidKeySpecException
{
if (keySpec instanceof TLSKeyMaterialSpec)
{
return new SecretKeySpec(PRF_legacy((TLSKeyMaterialSpec)keySpec), algName);
}
throw new InvalidKeySpecException("Invalid KeySpec");
}
}
public static final class TLS11
extends TLSKeyMaterialFactory
{
public TLS11()
{
super("TLS11KDF");
}
protected SecretKey engineGenerateSecret(
KeySpec keySpec)
throws InvalidKeySpecException
{
if (keySpec instanceof TLSKeyMaterialSpec)
{
return new SecretKeySpec(PRF_legacy((TLSKeyMaterialSpec)keySpec), algName);
}
throw new InvalidKeySpecException("Invalid KeySpec");
}
}
private static byte[] PRF_legacy(TLSKeyMaterialSpec parameters)
{
Mac md5Hmac = new HMac(DigestFactory.createMD5());
Mac sha1HMac = new HMac(DigestFactory.createSHA1());
byte[] label = Strings.toByteArray(parameters.getLabel());
byte[] labelSeed = Arrays.concatenate(label, parameters.getSeed());
byte[] secret = parameters.getSecret();
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);
int size = parameters.getLength();
byte[] b1 = new byte[size];
byte[] b2 = new byte[size];
hmac_hash(md5Hmac, s1, labelSeed, b1);
hmac_hash(sha1HMac, s2, labelSeed, b2);
for (int i = 0; i < size; i++)
{
b1[i] ^= b2[i];
}
return b1;
}
public static class TLS12
extends TLSKeyMaterialFactory
{
private final Mac prf;
protected TLS12(String algName, Mac prf)
{
super(algName);
this.prf = prf;
}
protected SecretKey engineGenerateSecret(
KeySpec keySpec)
throws InvalidKeySpecException
{
if (keySpec instanceof TLSKeyMaterialSpec)
{
return new SecretKeySpec(PRF((TLSKeyMaterialSpec)keySpec, prf), algName);
}
throw new InvalidKeySpecException("Invalid KeySpec");
}
private byte[] PRF(TLSKeyMaterialSpec parameters, Mac prf)
{
byte[] label = Strings.toByteArray(parameters.getLabel());
byte[] labelSeed = Arrays.concatenate(label, parameters.getSeed());
byte[] secret = parameters.getSecret();
byte[] buf = new byte[parameters.getLength()];
hmac_hash(prf, secret, labelSeed, buf);
return buf;
}
}
public static final class TLS12withSHA256
extends TLS12
{
public TLS12withSHA256()
{
super("TLS12withSHA256KDF", new HMac(new SHA256Digest()));
}
}
public static final class TLS12withSHA384
extends TLS12
{
public TLS12withSHA384()
{
super("TLS12withSHA384KDF", new HMac(new SHA384Digest()));
}
}
public static final class TLS12withSHA512
extends TLS12
{
public TLS12withSHA512()
{
super("TLS12withSHA512KDF", new HMac(new SHA512Digest()));
}
}
private static void hmac_hash(Mac mac, byte[] secret, byte[] seed, byte[] out)
{
mac.init(new KeyParameter(secret));
byte[] a = seed;
int size = mac.getMacSize();
int iterations = (out.length + size - 1) / size;
byte[] buf = new byte[mac.getMacSize()];
byte[] buf2 = new byte[mac.getMacSize()];
for (int i = 0; i < iterations; i++)
{
mac.update(a, 0, a.length);
mac.doFinal(buf, 0);
a = buf;
mac.update(a, 0, a.length);
mac.update(seed, 0, seed.length);
mac.doFinal(buf2, 0);
System.arraycopy(buf2, 0, out, (size * i), Math.min(size, out.length - (size * i)));
}
}
public static class Mappings
extends AlgorithmProvider
{
private static final String PREFIX = TLSKDF.class.getName();
public Mappings()
{
}
public void configure(ConfigurableProvider provider)
{
provider.addAlgorithm("SecretKeyFactory.TLS10KDF", PREFIX + "$TLS10");
provider.addAlgorithm("SecretKeyFactory.TLS11KDF", PREFIX + "$TLS11");
provider.addAlgorithm("SecretKeyFactory.TLS12WITHSHA256KDF", PREFIX + "$TLS12withSHA256");
provider.addAlgorithm("SecretKeyFactory.TLS12WITHSHA384KDF", PREFIX + "$TLS12withSHA384");
provider.addAlgorithm("SecretKeyFactory.TLS12WITHSHA512KDF", PREFIX + "$TLS12withSHA512");
}
}
}