org.bouncycastle.tls.AbstractTlsContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of impersonator Show documentation
Show all versions of impersonator Show documentation
Spoof TLS/JA3/JA4 and HTTP/2 fingerprints in Java
package org.bouncycastle.tls;
import java.io.IOException;
import org.bouncycastle.tls.crypto.TlsCrypto;
import org.bouncycastle.tls.crypto.TlsCryptoUtils;
import org.bouncycastle.tls.crypto.TlsHash;
import org.bouncycastle.tls.crypto.TlsNonceGenerator;
import org.bouncycastle.tls.crypto.TlsSecret;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.Pack;
import org.bouncycastle.util.Times;
abstract class AbstractTlsContext
implements TlsContext
{
private static long counter = Times.nanoTime();
private synchronized static long nextCounterValue()
{
return ++counter;
}
private static TlsNonceGenerator createNonceGenerator(TlsCrypto crypto, int connectionEnd)
{
byte[] additionalSeedMaterial = new byte[16];
Pack.longToBigEndian(nextCounterValue(), additionalSeedMaterial, 0);
Pack.longToBigEndian(Times.nanoTime(), additionalSeedMaterial, 8);
additionalSeedMaterial[0] &= 0x7F;
additionalSeedMaterial[0] |= (byte)(connectionEnd << 7);
return crypto.createNonceGenerator(additionalSeedMaterial);
}
private TlsCrypto crypto;
private int connectionEnd;
private TlsNonceGenerator nonceGenerator;
private SecurityParameters securityParametersHandshake = null;
private SecurityParameters securityParametersConnection = null;
private ProtocolVersion[] clientSupportedVersions = null;
private ProtocolVersion clientVersion = null;
private ProtocolVersion rsaPreMasterSecretVersion = null;
private TlsSession session = null;
private Object userObject = null;
AbstractTlsContext(TlsCrypto crypto, int connectionEnd)
{
this.crypto = crypto;
this.connectionEnd = connectionEnd;
this.nonceGenerator = createNonceGenerator(crypto, connectionEnd);
}
void handshakeBeginning(TlsPeer peer) throws IOException
{
synchronized (this)
{
if (null != securityParametersHandshake)
{
throw new TlsFatalAlert(AlertDescription.internal_error, "Handshake already started");
}
this.securityParametersHandshake = new SecurityParameters();
this.securityParametersHandshake.entity = connectionEnd;
if (null != securityParametersConnection)
{
securityParametersHandshake.renegotiating = true;
securityParametersHandshake.secureRenegotiation = securityParametersConnection.isSecureRenegotiation();
securityParametersHandshake.negotiatedVersion = securityParametersConnection.getNegotiatedVersion();
}
}
peer.notifyHandshakeBeginning();
}
void handshakeComplete(TlsPeer peer, TlsSession session) throws IOException
{
synchronized (this)
{
if (null == securityParametersHandshake)
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
this.session = session;
this.securityParametersConnection = securityParametersHandshake;
this.securityParametersHandshake = null;
}
peer.notifyHandshakeComplete();
}
synchronized boolean isConnected()
{
return null != securityParametersConnection;
}
synchronized boolean isHandshaking()
{
return null != securityParametersHandshake;
}
public TlsCrypto getCrypto()
{
return crypto;
}
public TlsNonceGenerator getNonceGenerator()
{
return nonceGenerator;
}
public synchronized SecurityParameters getSecurityParameters()
{
return null != securityParametersHandshake
? securityParametersHandshake
: securityParametersConnection;
}
public synchronized SecurityParameters getSecurityParametersConnection()
{
return securityParametersConnection;
}
public synchronized SecurityParameters getSecurityParametersHandshake()
{
return securityParametersHandshake;
}
public ProtocolVersion[] getClientSupportedVersions()
{
return clientSupportedVersions;
}
void setClientSupportedVersions(ProtocolVersion[] clientSupportedVersions)
{
this.clientSupportedVersions = clientSupportedVersions;
}
public ProtocolVersion getClientVersion()
{
return clientVersion;
}
void setClientVersion(ProtocolVersion clientVersion)
{
this.clientVersion = clientVersion;
}
public ProtocolVersion getRSAPreMasterSecretVersion()
{
return rsaPreMasterSecretVersion;
}
void setRSAPreMasterSecretVersion(ProtocolVersion rsaPreMasterSecretVersion)
{
this.rsaPreMasterSecretVersion = rsaPreMasterSecretVersion;
}
public ProtocolVersion getServerVersion()
{
return getSecurityParameters().getNegotiatedVersion();
}
public TlsSession getResumableSession()
{
TlsSession session = getSession();
if (session == null || !session.isResumable())
{
return null;
}
return session;
}
public TlsSession getSession()
{
return session;
}
public Object getUserObject()
{
return userObject;
}
public void setUserObject(Object userObject)
{
this.userObject = userObject;
}
public byte[] exportChannelBinding(int channelBinding)
{
SecurityParameters securityParameters = getSecurityParametersConnection();
if (null == securityParameters)
{
throw new IllegalStateException("Export of channel bindings unavailable before handshake completion");
}
if (ChannelBinding.tls_exporter == channelBinding)
{
return exportKeyingMaterial("EXPORTER-Channel-Binding", TlsUtils.EMPTY_BYTES, 32);
}
if (TlsUtils.isTLSv13(securityParameters.getNegotiatedVersion()))
{
return null;
}
switch (channelBinding)
{
case ChannelBinding.tls_server_end_point:
{
byte[] tlsServerEndPoint = securityParameters.getTLSServerEndPoint();
return TlsUtils.isNullOrEmpty(tlsServerEndPoint) ? null : Arrays.clone(tlsServerEndPoint);
}
case ChannelBinding.tls_unique:
{
return Arrays.clone(securityParameters.getTLSUnique());
}
case ChannelBinding.tls_unique_for_telnet:
default:
throw new UnsupportedOperationException();
}
}
public byte[] exportEarlyKeyingMaterial(String asciiLabel, byte[] context, int length)
{
// TODO[tls13] Ensure early_exporter_master_secret is available suitably early!
SecurityParameters sp = getSecurityParametersHandshake();
if (null == sp)
{
throw new IllegalStateException("Export of early key material only available during handshake");
}
return exportKeyingMaterial13(checkEarlyExportSecret(sp.getEarlyExporterMasterSecret()),
sp.getPRFCryptoHashAlgorithm(), asciiLabel, context, length);
}
public byte[] exportKeyingMaterial(String asciiLabel, byte[] context, int length)
{
/*
* TODO[tls13] Introduce a TlsExporter interface? Avoid calculating (early) exporter
* secret(s) unless the peer actually uses it.
*/
SecurityParameters sp = getSecurityParametersConnection();
if (null == sp)
{
throw new IllegalStateException("Export of key material unavailable before handshake completion");
}
if (!sp.isExtendedMasterSecret())
{
/*
* RFC 7627 5.4. If a client or server chooses to continue with a full handshake without
* the extended master secret extension, [..] the client or server MUST NOT export any
* key material based on the new master secret for any subsequent application-level
* authentication. In particular, it MUST disable [RFC5705] [..].
*/
throw new IllegalStateException("Export of key material requires extended_master_secret");
}
if (TlsUtils.isTLSv13(sp.getNegotiatedVersion()))
{
return exportKeyingMaterial13(checkExportSecret(sp.getExporterMasterSecret()),
sp.getPRFCryptoHashAlgorithm(), asciiLabel, context, length);
}
byte[] seed = TlsUtils.calculateExporterSeed(sp, context);
return TlsUtils.PRF(sp, checkExportSecret(sp.getMasterSecret()), asciiLabel, seed, length).extract();
}
protected byte[] exportKeyingMaterial13(TlsSecret secret, int cryptoHashAlgorithm, String asciiLabel,
byte[] context, int length)
{
if (null == context)
{
context = TlsUtils.EMPTY_BYTES;
}
else if (!TlsUtils.isValidUint16(context.length))
{
throw new IllegalArgumentException("'context' must have length less than 2^16 (or be null)");
}
try
{
TlsHash exporterHash = getCrypto().createHash(cryptoHashAlgorithm);
byte[] emptyTranscriptHash = exporterHash.calculateHash();
TlsSecret exporterSecret = TlsUtils.deriveSecret(getSecurityParametersConnection(), secret, asciiLabel,
emptyTranscriptHash);
byte[] exporterContext = emptyTranscriptHash;
if (context.length > 0)
{
exporterHash.update(context, 0, context.length);
exporterContext = exporterHash.calculateHash();
}
return TlsCryptoUtils
.hkdfExpandLabel(exporterSecret, cryptoHashAlgorithm, "exporter", exporterContext, length).extract();
}
catch (IOException e)
{
// Should never happen
throw new RuntimeException(e);
}
}
protected TlsSecret checkEarlyExportSecret(TlsSecret secret)
{
if (null == secret)
{
// TODO[tls13] For symmetry with normal export, ideally available for notifyHandshakeBeginning() only
// throw new IllegalStateException("Export of early key material only available from notifyHandshakeBeginning()");
throw new IllegalStateException("Export of early key material not available for this handshake");
}
return secret;
}
protected TlsSecret checkExportSecret(TlsSecret secret)
{
if (null == secret)
{
throw new IllegalStateException("Export of key material only available from notifyHandshakeComplete()");
}
return secret;
}
}