package org.bouncycastle.tls.crypto.impl.jcajce;
import java.io.IOException;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import javax.crypto.Cipher;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.jcajce.util.JcaJceHelper;
import org.bouncycastle.tls.AlertDescription;
import org.bouncycastle.tls.CombinedHash;
import org.bouncycastle.tls.EncryptionAlgorithm;
import org.bouncycastle.tls.HashAlgorithm;
import org.bouncycastle.tls.MACAlgorithm;
import org.bouncycastle.tls.NamedCurve;
import org.bouncycastle.tls.ProtocolVersion;
import org.bouncycastle.tls.SignatureAndHashAlgorithm;
import org.bouncycastle.tls.TlsFatalAlert;
import org.bouncycastle.tls.TlsUtils;
import org.bouncycastle.tls.crypto.SRP6Group;
import org.bouncycastle.tls.crypto.TlsCertificate;
import org.bouncycastle.tls.crypto.TlsCipher;
import org.bouncycastle.tls.crypto.TlsCryptoException;
import org.bouncycastle.tls.crypto.TlsCryptoParameters;
import org.bouncycastle.tls.crypto.TlsDHConfig;
import org.bouncycastle.tls.crypto.TlsDHDomain;
import org.bouncycastle.tls.crypto.TlsECConfig;
import org.bouncycastle.tls.crypto.TlsECDomain;
import org.bouncycastle.tls.crypto.TlsHMAC;
import org.bouncycastle.tls.crypto.TlsHash;
import org.bouncycastle.tls.crypto.TlsMAC;
import org.bouncycastle.tls.crypto.TlsSRP6Client;
import org.bouncycastle.tls.crypto.TlsSRP6Server;
import org.bouncycastle.tls.crypto.TlsSRP6VerifierGenerator;
import org.bouncycastle.tls.crypto.TlsSRPConfig;
import org.bouncycastle.tls.crypto.TlsSecret;
import org.bouncycastle.tls.crypto.impl.AbstractTlsCrypto;
import org.bouncycastle.tls.crypto.impl.ChaCha20Poly1305Cipher;
import org.bouncycastle.tls.crypto.impl.TlsAEADCipher;
import org.bouncycastle.tls.crypto.impl.TlsAEADCipherImpl;
import org.bouncycastle.tls.crypto.impl.TlsBlockCipher;
import org.bouncycastle.tls.crypto.impl.TlsBlockCipherImpl;
import org.bouncycastle.tls.crypto.impl.TlsEncryptor;
import org.bouncycastle.tls.crypto.impl.TlsImplUtils;
import org.bouncycastle.tls.crypto.impl.TlsNullCipher;
import org.bouncycastle.tls.crypto.impl.TlsStreamCipher;
import org.bouncycastle.tls.crypto.impl.TlsStreamCipherImpl;
import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6Client;
import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6Server;
import org.bouncycastle.tls.crypto.impl.jcajce.srp.SRP6VerifierGenerator;
import org.bouncycastle.util.Arrays;
/**
* Class for providing cryptographic services for TLS based on implementations in the JCA/JCE.
* <p>
* This class provides default implementations for everything. If you need to customise it, extend the class
* and override the appropriate methods.
* </p>
*/
public class JcaTlsCrypto
extends AbstractTlsCrypto
{
private final JcaJceHelper helper;
private final SecureRandom entropySource;
private final SecureRandom nonceEntropySource;
private final Boolean[] supportedCurveIDs = new Boolean[28 + 1]; // current max curveID + 1
/**
* Base constructor.
*
* @param helper a JCA/JCE helper configured for the class's default provider.
* @param entropySource primary entropy source, used for key generation.
* @param nonceEntropySource secondary entropy source, used for nonce and IV generation.
*/
protected JcaTlsCrypto(JcaJceHelper helper, SecureRandom entropySource, SecureRandom nonceEntropySource)
{
this.helper = helper;
this.entropySource = entropySource;
this.nonceEntropySource = nonceEntropySource;
}
private boolean checkCurve(int namedCurve)
{
String curveName = NamedCurve.getNameOfSpecificCurve(namedCurve);
if (curveName == null)
{
return false;
}
if (namedCurve < supportedCurveIDs.length && supportedCurveIDs[namedCurve] != null)
{
return supportedCurveIDs[namedCurve].booleanValue();
}
try
{
AlgorithmParameters params = this.getHelper().createAlgorithmParameters("EC");
params.init(new ECGenParameterSpec(curveName));
boolean supported = params.getParameterSpec(ECParameterSpec.class) != null;
if (namedCurve < supportedCurveIDs.length)
{
supportedCurveIDs[namedCurve] = Boolean.valueOf(supported);
}
return supported;
}
catch (Exception e)
{
supportedCurveIDs[namedCurve] = Boolean.valueOf(false);
return false;
}
}
JceTlsSecret adoptLocalSecret(byte[] data)
{
return new JceTlsSecret(this, data);
}
Cipher createRSAEncryptionCipher() throws GeneralSecurityException
{
try
{
return getHelper().createCipher("RSA/NONE/PKCS1Padding");
}
catch (GeneralSecurityException e)
{
return getHelper().createCipher("RSA/ECB/PKCS1Padding"); // try old style
}
}
public byte[] createNonce(int size)
{
byte[] nonce = new byte[size];
nonceEntropySource.nextBytes(nonce);
return nonce;
}
public SecureRandom getSecureRandom()
{
return entropySource;
}
public TlsCertificate createCertificate(byte[] encoding)
throws IOException
{
return new JcaTlsCertificate(encoding, helper);
}
protected TlsCipher createCipher(TlsCryptoParameters cryptoParams, int encryptionAlgorithm, int macAlgorithm)
throws IOException
{
try
{
switch (encryptionAlgorithm)
{
case EncryptionAlgorithm._3DES_EDE_CBC:
return createDESedeCipher(cryptoParams, macAlgorithm);
case EncryptionAlgorithm.AES_128_CBC:
return createAESCipher(cryptoParams, 16, macAlgorithm);
case EncryptionAlgorithm.AES_128_CCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_CCM(cryptoParams, 16, 16);
case EncryptionAlgorithm.AES_128_CCM_8:
// NOTE: Ignores macAlgorithm
return createCipher_AES_CCM(cryptoParams, 16, 8);
case EncryptionAlgorithm.AES_128_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_GCM(cryptoParams, 16, 16);
case EncryptionAlgorithm.AES_128_OCB_TAGLEN96:
// NOTE: Ignores macAlgorithm
return createCipher_AES_OCB(cryptoParams, 16, 12);
case EncryptionAlgorithm.AES_256_CBC:
return createAESCipher(cryptoParams, 32, macAlgorithm);
case EncryptionAlgorithm.AES_256_CCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_CCM(cryptoParams, 32, 16);
case EncryptionAlgorithm.AES_256_CCM_8:
// NOTE: Ignores macAlgorithm
return createCipher_AES_CCM(cryptoParams, 32, 8);
case EncryptionAlgorithm.AES_256_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_AES_GCM(cryptoParams, 32, 16);
case EncryptionAlgorithm.AES_256_OCB_TAGLEN96:
// NOTE: Ignores macAlgorithm
return createCipher_AES_OCB(cryptoParams, 32, 12);
case EncryptionAlgorithm.ARIA_128_CBC:
return createARIACipher(cryptoParams, 16, macAlgorithm);
case EncryptionAlgorithm.ARIA_128_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_ARIA_GCM(cryptoParams, 16, 16);
case EncryptionAlgorithm.ARIA_256_CBC:
return createARIACipher(cryptoParams, 32, macAlgorithm);
case EncryptionAlgorithm.ARIA_256_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_ARIA_GCM(cryptoParams, 32, 16);
case EncryptionAlgorithm.CAMELLIA_128_CBC:
return createCamelliaCipher(cryptoParams, 16, macAlgorithm);
case EncryptionAlgorithm.CAMELLIA_128_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_Camellia_GCM(cryptoParams, 16, 16);
case EncryptionAlgorithm.CAMELLIA_256_CBC:
return createCamelliaCipher(cryptoParams, 32, macAlgorithm);
case EncryptionAlgorithm.CAMELLIA_256_GCM:
// NOTE: Ignores macAlgorithm
return createCipher_Camellia_GCM(cryptoParams, 32, 16);
case EncryptionAlgorithm.CHACHA20_POLY1305:
// NOTE: Ignores macAlgorithm
return createChaCha20Poly1305(cryptoParams);
case EncryptionAlgorithm.NULL:
return createNullCipher(cryptoParams, macAlgorithm);
case EncryptionAlgorithm.RC4_128:
return createRC4Cipher(cryptoParams, 16, macAlgorithm);
case EncryptionAlgorithm.SEED_CBC:
return createSEEDCipher(cryptoParams, macAlgorithm);
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
catch (GeneralSecurityException e)
{
throw new TlsCryptoException("cannot create cipher: " + e.getMessage(), e);
}
}
public final TlsHMAC createHMAC(int macAlgorithm)
throws IOException
{
try
{
switch (macAlgorithm)
{
case MACAlgorithm._null:
return null;
case MACAlgorithm.hmac_md5:
return createHMAC("HmacMD5");
case MACAlgorithm.hmac_sha1:
return createHMAC("HmacSHA1");
case MACAlgorithm.hmac_sha256:
return createHMAC("HmacSHA256");
case MACAlgorithm.hmac_sha384:
return createHMAC("HmacSHA384");
case MACAlgorithm.hmac_sha512:
return createHMAC("HmacSHA512");
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
catch (GeneralSecurityException e)
{
throw new TlsCryptoException("cannot create HMAC: " + e.getMessage(), e);
}
}
public TlsSRP6Client createSRP6Client(TlsSRPConfig srpConfig)
{
final SRP6Client srpClient = new SRP6Client();
BigInteger[] ng = srpConfig.getExplicitNG();
SRP6Group srpGroup= new SRP6Group(ng[0], ng[1]);
srpClient.init(srpGroup, createHash(HashAlgorithm.sha1), this.getSecureRandom());
return new TlsSRP6Client()
{
public BigInteger calculateSecret(BigInteger serverB)
throws TlsFatalAlert
{
try
{
return srpClient.calculateSecret(serverB);
}
catch (IllegalArgumentException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
public BigInteger generateClientCredentials(byte[] srpSalt, byte[] identity, byte[] password)
{
return srpClient.generateClientCredentials(srpSalt, identity, password);
}
};
}
public TlsSRP6Server createSRP6Server(TlsSRPConfig srpConfig, BigInteger srpVerifier)
{
final SRP6Server srpServer = new SRP6Server();
BigInteger[] ng = srpConfig.getExplicitNG();
SRP6Group srpGroup= new SRP6Group(ng[0], ng[1]);
srpServer.init(srpGroup, srpVerifier, createHash(HashAlgorithm.sha1), this.getSecureRandom());
return new TlsSRP6Server()
{
public BigInteger generateServerCredentials()
{
return srpServer.generateServerCredentials();
}
public BigInteger calculateSecret(BigInteger clientA)
throws IOException
{
try
{
return srpServer.calculateSecret(clientA);
}
catch (IllegalArgumentException e)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
}
}
};
}
public TlsSRP6VerifierGenerator createSRP6VerifierGenerator(TlsSRPConfig srpConfig)
{
BigInteger[] ng = srpConfig.getExplicitNG();
final SRP6VerifierGenerator verifierGenerator = new SRP6VerifierGenerator();
verifierGenerator.init(ng[0], ng[1], createHash(HashAlgorithm.sha1));
return new TlsSRP6VerifierGenerator()
{
public BigInteger generateVerifier(byte[] salt, byte[] identity, byte[] password)
{
return verifierGenerator.generateVerifier(salt, identity, password);
}
};
}
public boolean hasAllRawSignatureAlgorithms()
{
return !JcaUtils.isSunMSCAPIProviderActive();
}
public boolean hasDHAgreement()
{
return true;
}
public boolean hasECDHAgreement()
{
return true;
}
public boolean hasEncryptionAlgorithm(int encryptionAlgorithm)
{
try
{
switch (encryptionAlgorithm)
{
case EncryptionAlgorithm.CHACHA20_POLY1305:
{
helper.createCipher("ChaCha7539");
helper.createMac("Poly1305");
break;
}
case EncryptionAlgorithm.AES_128_CCM:
case EncryptionAlgorithm.AES_128_CCM_8:
case EncryptionAlgorithm.AES_256_CCM:
case EncryptionAlgorithm.AES_256_CCM_8:
{
helper.createCipher("AES/CCM/NoPadding");
break;
}
case EncryptionAlgorithm.AES_128_GCM:
case EncryptionAlgorithm.AES_256_GCM:
{
helper.createCipher("AES/GCM/NoPadding");
break;
}
case EncryptionAlgorithm.ARIA_128_GCM:
case EncryptionAlgorithm.ARIA_256_GCM:
{
helper.createCipher("ARIA/GCM/NoPadding");
break;
}
case EncryptionAlgorithm.CAMELLIA_128_GCM:
case EncryptionAlgorithm.CAMELLIA_256_GCM:
{
helper.createCipher("CAMELLIA/GCM/NoPadding");
break;
}
}
}
catch (GeneralSecurityException e)
{
return false;
}
return true;
}
public boolean hasHashAlgorithm(short hashAlgorithm)
{
// TODO: expand
return true;
}
public boolean hasMacAlgorithm(int macAlgorithm)
{
// TODO: expand
return true;
}
public boolean hasNamedCurve(int curveID)
{
return checkCurve(curveID);
}
public boolean hasRSAEncryption()
{
try
{
createRSAEncryptionCipher();
return true;
}
catch (GeneralSecurityException e)
{
return false;
}
}
public boolean hasSignatureAlgorithm(int signatureAlgorithm)
{
// TODO: expand
return true;
}
public boolean hasSignatureAndHashAlgorithm(SignatureAndHashAlgorithm sigAndHashAlgorithm)
{
/*
* This is somewhat overkill, but much simpler for now. It's also consistent with SunJSSE behaviour.
*/
if (sigAndHashAlgorithm.getHash() == HashAlgorithm.sha224 && JcaUtils.isSunMSCAPIProviderActive())
{
return false;
}
// TODO: expand
return true;
}
public boolean hasSRPAuthentication()
{
return true;
}
public TlsSecret createSecret(byte[] data)
{
try
{
return adoptLocalSecret(Arrays.clone(data));
}
finally
{
// TODO[tls-ops] Add this after checking all callers
// if (data != null)
// {
// Arrays.fill(data, (byte)0);
// }
}
}
public TlsSecret generateRSAPreMasterSecret(ProtocolVersion version)
{
byte[] data = new byte[48];
getSecureRandom().nextBytes(data);
TlsUtils.writeVersion(version, data, 0);
return adoptLocalSecret(data);
}
public TlsHash createHash(short algorithm)
{
try
{
return createHash(getDigestName(algorithm));
}
catch (GeneralSecurityException e)
{
throw new IllegalArgumentException("unable to create message digest:" + e.getMessage(), e);
}
}
public TlsHash createHash(final SignatureAndHashAlgorithm signatureAndHashAlgorithm)
{
if (signatureAndHashAlgorithm == null)
{
return new CombinedHash(this);
}
return createHash(signatureAndHashAlgorithm.getHash());
}
public TlsDHDomain createDHDomain(TlsDHConfig dhConfig)
{
return new JceTlsDHDomain(this, dhConfig);
}
public TlsECDomain createECDomain(TlsECConfig ecConfig)
{
return new JceTlsECDomain(this, ecConfig);
}
public TlsEncryptor createEncryptor(TlsCertificate certificate)
throws IOException
{
JcaTlsCertificate jcaCert = JcaTlsCertificate.convert(certificate, this.getHelper());
jcaCert.validateKeyUsage(KeyUsage.keyEncipherment);
final RSAPublicKey pubKeyRSA = jcaCert.getPubKeyRSA();
return new TlsEncryptor()
{
public byte[] encrypt(byte[] input, int inOff, int length)
throws IOException
{
try
{
Cipher c = createRSAEncryptionCipher();
c.init(Cipher.ENCRYPT_MODE, pubKeyRSA, getSecureRandom());
return c.doFinal(input, inOff, length);
}
catch (GeneralSecurityException e)
{
/*
* This should never happen, only during decryption.
*/
throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
}
};
}
/**
* If you want to create your own versions of the AEAD ciphers required, override this method.
*
* @param cipherName the full name of the cipher (algorithm/mode/padding)
* @param algorithm the base algorithm name
* @param keySize keySize (in bytes) for the cipher key.
* @param isEncrypting true if the cipher is for encryption, false otherwise.
* @return an AEAD cipher.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsAEADCipherImpl createAEADCipher(String cipherName, String algorithm, int keySize, boolean isEncrypting)
throws GeneralSecurityException
{
return new JceAEADCipherImpl(helper.createCipher(cipherName), algorithm, isEncrypting);
}
/**
* If you want to create your own versions of the block ciphers required, override this method.
*
* @param cipherName the full name of the cipher (algorithm/mode/padding)
* @param algorithm the base algorithm name
* @param keySize keySize (in bytes) for the cipher key.
* @param isEncrypting true if the cipher is for encryption, false otherwise.
* @return a block cipher.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsBlockCipherImpl createBlockCipher(String cipherName, String algorithm, int keySize, boolean isEncrypting)
throws GeneralSecurityException
{
return new JceBlockCipherImpl(helper.createCipher(cipherName), algorithm, isEncrypting);
}
/**
* If you want to create your own versions of the block ciphers for < TLS 1.1, override this method.
*
* @param cipherName the full name of the cipher (algorithm/mode/padding)
* @param algorithm the base algorithm name
* @param keySize keySize (in bytes) for the cipher key.
* @param isEncrypting true if the cipher is for encryption, false otherwise.
* @return a block cipher.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsBlockCipherImpl createBlockCipherWithCBCImplicitIV(String cipherName, String algorithm, int keySize, boolean isEncrypting)
throws GeneralSecurityException
{
return new JceBlockCipherWithCBCImplicitIVImpl(helper.createCipher(cipherName), algorithm, isEncrypting);
}
/**
* If you want to create your own versions of stream ciphers, override this method.
*
* @param cipherName the full name of the cipher (algorithm/mode/padding)
* @param algorithm the base algorithm name
* @param keySize keySize (in bytes) for the cipher key.
* @param isEncrypting true if the cipher is for encryption, false otherwise.
* @return a block cipher.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsStreamCipherImpl createStreamCipher(String cipherName, String algorithm, int keySize, boolean isEncrypting)
throws GeneralSecurityException
{
return new JceStreamCipherImpl(helper.createCipher(cipherName), algorithm, isEncrypting);
}
/**
* If you want to create your own versions of HMACs, override this method.
*
* @param hmacName the name of the HMAC required.
* @return a HMAC calculator.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsHMAC createHMAC(String hmacName)
throws GeneralSecurityException
{
return new JceTlsHMAC(helper.createMac(hmacName), hmacName);
}
/**
* If you want to create your own versions of MACs, override this method.
*
* @param macName the name of the MAC required.
* @return a MAC calculator.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsMAC createMAC(String macName)
throws GeneralSecurityException
{
return new JceTlsMAC(helper.createMac(macName), macName);
}
/**
* If you want to create your own versions of Hash functions, override this method.
*
* @param digestName the name of the Hash function required.
* @return a hash calculator.
* @throws GeneralSecurityException in case of failure.
*/
protected TlsHash createHash(String digestName)
throws GeneralSecurityException
{
return new JcaTlsHash(helper.createDigest(digestName));
}
/**
* To disable the null cipher suite, override this method with one that throws an IOException.
*
* @param macAlgorithm the name of the algorithm supporting the MAC.
* @return a null cipher suite implementation.
* @throws IOException in case of failure.
* @throws GeneralSecurityException in case of a specific failure in the JCA/JCE layer.
*/
protected TlsNullCipher createNullCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsNullCipher(cryptoParams, createMAC(cryptoParams, macAlgorithm), createMAC(cryptoParams, macAlgorithm));
}
JcaJceHelper getHelper()
{
return helper;
}
private TlsBlockCipher createAESCipher(TlsCryptoParameters cryptoParams, int cipherKeySize, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "AES", true, cipherKeySize), createCBCBlockOperator(cryptoParams, "AES", false, cipherKeySize),
createMAC(cryptoParams, macAlgorithm), createMAC(cryptoParams, macAlgorithm), cipherKeySize);
}
private TlsBlockCipher createARIACipher(TlsCryptoParameters cryptoParams, int cipherKeySize, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "ARIA", true, cipherKeySize), createCBCBlockOperator(cryptoParams, "ARIA", false, cipherKeySize),
createMAC(cryptoParams, macAlgorithm), createMAC(cryptoParams, macAlgorithm), cipherKeySize);
}
private TlsBlockCipher createCamelliaCipher(TlsCryptoParameters cryptoParams, int cipherKeySize, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "Camellia", true, cipherKeySize), createCBCBlockOperator(cryptoParams, "Camellia", false, cipherKeySize),
createMAC(cryptoParams, macAlgorithm), createMAC(cryptoParams, macAlgorithm), cipherKeySize);
}
private TlsBlockCipher createDESedeCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "DESede", true, 24), createCBCBlockOperator(cryptoParams, "DESede", false, 24),
createMAC(cryptoParams, macAlgorithm), createMAC(cryptoParams, macAlgorithm), 24);
}
private TlsBlockCipher createSEEDCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsBlockCipher(this, cryptoParams, createCBCBlockOperator(cryptoParams, "SEED", true, 16), createCBCBlockOperator(cryptoParams, "SEED", false, 16),
createMAC(cryptoParams, macAlgorithm), createMAC(cryptoParams, macAlgorithm), 16);
}
private TlsBlockCipherImpl createCBCBlockOperator(TlsCryptoParameters cryptoParams, String algorithm, boolean forEncryption, int keySize)
throws GeneralSecurityException
{
String cipherName = algorithm + "/CBC/NoPadding";
if (TlsImplUtils.isTLSv11(cryptoParams))
{
return createBlockCipher(cipherName, algorithm, keySize, forEncryption);
}
else
{
return createBlockCipherWithCBCImplicitIV(cipherName, algorithm, keySize, forEncryption);
}
}
private TlsHMAC createMAC(TlsCryptoParameters cryptoParams, int macAlgorithm)
throws GeneralSecurityException, IOException
{
if (TlsImplUtils.isSSL(cryptoParams))
{
return createSSL3HMAC(macAlgorithm);
}
else
{
return createHMAC(macAlgorithm);
}
}
private TlsCipher createChaCha20Poly1305(TlsCryptoParameters cryptoParams)
throws IOException, GeneralSecurityException
{
return new ChaCha20Poly1305Cipher(cryptoParams,
createStreamCipher("ChaCha7539", "ChaCha7539", 32, true), createStreamCipher("ChaCha7539", "ChaCha7539", 32, false),
createMAC("Poly1305"), createMAC("Poly1305"));
}
private TlsAEADCipher createCipher_AES_CCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("AES/CCM/NoPadding", "AES", cipherKeySize, true), createAEADCipher("AES/CCM/NoPadding", "AES", cipherKeySize, false),
cipherKeySize, macSize);
}
private TlsAEADCipher createCipher_AES_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("AES/GCM/NoPadding", "AES", cipherKeySize, true), createAEADCipher("AES/GCM/NoPadding", "AES", cipherKeySize, false),
cipherKeySize, macSize);
}
private TlsAEADCipher createCipher_AES_OCB(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("AES/OCB/NoPadding", "AES", cipherKeySize, true), createAEADCipher("AES/OCB/NoPadding", "AES", cipherKeySize, false),
cipherKeySize, macSize, TlsAEADCipher.NONCE_RFC7905);
}
private TlsAEADCipher createCipher_ARIA_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("ARIA/GCM/NoPadding", "ARIA", cipherKeySize, true), createAEADCipher("ARIA/GCM/NoPadding", "ARIA", cipherKeySize, false),
cipherKeySize, macSize);
}
private TlsAEADCipher createCipher_Camellia_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize)
throws IOException, GeneralSecurityException
{
return new TlsAEADCipher(cryptoParams, createAEADCipher("Camellia/GCM/NoPadding", "Camellia", cipherKeySize, true), createAEADCipher("Camellia/GCM/NoPadding", "Camellia", cipherKeySize, false),
cipherKeySize, macSize);
}
private TlsStreamCipher createRC4Cipher(TlsCryptoParameters cryptoParams, int cipherKeySize, int macAlgorithm)
throws IOException, GeneralSecurityException
{
return new TlsStreamCipher(cryptoParams, createStreamCipher("RC4", "RC4", 128, true), createStreamCipher("RC4", "RC4", 128, false),
createMAC(cryptoParams, macAlgorithm), createMAC(cryptoParams, macAlgorithm), cipherKeySize, false);
}
private TlsHMAC createSSL3HMAC(int macAlgorithm)
throws IOException, GeneralSecurityException
{
switch (macAlgorithm)
{
case MACAlgorithm._null:
return null;
case MACAlgorithm.hmac_md5:
return new SSL3Mac(createHash(getDigestName(HashAlgorithm.md5)), 16, 64);
case MACAlgorithm.hmac_sha1:
return new SSL3Mac(createHash(getDigestName(HashAlgorithm.sha1)), 20, 64);
case MACAlgorithm.hmac_sha256:
return new SSL3Mac(createHash(getDigestName(HashAlgorithm.sha256)), 32, 64);
case MACAlgorithm.hmac_sha384:
return new SSL3Mac(createHash(getDigestName(HashAlgorithm.sha384)), 48, 128);
case MACAlgorithm.hmac_sha512:
return new SSL3Mac(createHash(getDigestName(HashAlgorithm.sha512)), 64, 128);
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
String getDigestName(short algorithm)
{
String digestName;
switch (algorithm)
{
case HashAlgorithm.md5:
digestName = "MD5";
break;
case HashAlgorithm.sha1:
digestName = "SHA-1";
break;
case HashAlgorithm.sha224:
digestName = "SHA-224";
break;
case HashAlgorithm.sha256:
digestName = "SHA-256";
break;
case HashAlgorithm.sha384:
digestName = "SHA-384";
break;
case HashAlgorithm.sha512:
digestName = "SHA-512";
break;
default:
throw new IllegalArgumentException("unknown HashAlgorithm");
}
return digestName;
}
/**
* HMAC implementation based on original internet draft for HMAC (RFC 2104)
* <p/>
* The difference is that padding is concatenated versus XORed with the key
* <p/>
* H(K + opad, H(K + ipad, text))
*/
private static class SSL3Mac
implements TlsHMAC
{
private static final byte IPAD_BYTE = (byte)0x36;
private static final byte OPAD_BYTE = (byte)0x5C;
private static final byte[] IPAD = genPad(IPAD_BYTE, 48);
private static final byte[] OPAD = genPad(OPAD_BYTE, 48);
private TlsHash digest;
private final int digestSize;
private final int internalBlockSize;
private int padLength;
private byte[] secret;
/**
* Base constructor for one of the standard digest algorithms that the byteLength of
* the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1.
*
* @param digest the digest.
* @param digestSize the digest size.
* @param internalBlockSize the digest internal block size.
*/
public SSL3Mac(TlsHash digest, int digestSize, int internalBlockSize)
{
this.digest = digest;
this.digestSize = digestSize;
this.internalBlockSize = internalBlockSize;
if (digestSize == 20)
{
this.padLength = 40;
}
else
{
this.padLength = 48;
}
}
public void setKey(byte[] key)
{
this.secret = key;
reset();
}
public void update(byte[] in, int inOff, int len)
{
digest.update(in, inOff, len);
}
public byte[] calculateMAC()
{
byte[] tmp = digest.calculateHash();
digest.update(secret, 0, secret.length);
digest.update(OPAD, 0, padLength);
digest.update(tmp, 0, tmp.length);
byte[] rv = digest.calculateHash();
reset();
return rv;
}
public int getInternalBlockSize()
{
return internalBlockSize;
}
public int getMacLength()
{
return digestSize;
}
/**
* Reset the mac generator.
*/
public void reset()
{
digest.reset();
digest.update(secret, 0, secret.length);
digest.update(IPAD, 0, padLength);
}
private static byte[] genPad(byte b, int count)
{
byte[] padding = new byte[count];
Arrays.fill(padding, b);
return padding;
}
}
}