AbstractTlsContext
using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Tls.Crypto;
using Org.BouncyCastle.Utilities;
using System;
using System.Threading;
namespace Org.BouncyCastle.Tls
{
internal abstract class AbstractTlsContext : TlsContext
{
private static long counter = DateTime.UtcNow.Ticks;
private readonly TlsCrypto m_crypto;
private readonly int m_connectionEnd;
private readonly TlsNonceGenerator m_nonceGenerator;
private SecurityParameters m_securityParameters;
private ProtocolVersion[] m_clientSupportedVersions;
private ProtocolVersion m_clientVersion;
private ProtocolVersion m_rsaPreMasterSecretVersion;
private TlsSession m_session;
private object m_userObject;
private bool m_connected;
internal bool IsConnected {
get {
lock (this) {
return m_connected;
}
}
}
internal bool IsHandshaking {
get {
lock (this) {
return !m_connected && m_securityParameters != null;
}
}
}
public TlsCrypto Crypto => m_crypto;
public virtual TlsNonceGenerator NonceGenerator => m_nonceGenerator;
public SecurityParameters SecurityParameters {
get {
lock (this) {
return m_securityParameters;
}
}
}
public abstract bool IsServer { get; }
public virtual ProtocolVersion[] ClientSupportedVersions => m_clientSupportedVersions;
public virtual ProtocolVersion ClientVersion => m_clientVersion;
public virtual ProtocolVersion RsaPreMasterSecretVersion => m_rsaPreMasterSecretVersion;
public virtual ProtocolVersion ServerVersion => SecurityParameters.NegotiatedVersion;
public virtual TlsSession ResumableSession {
get {
TlsSession session = Session;
if (session == null || !session.IsResumable)
return null;
return session;
}
}
public virtual TlsSession Session => m_session;
public virtual object UserObject {
get {
return m_userObject;
}
set {
m_userObject = value;
}
}
private static long NextCounterValue()
{
return Interlocked.Increment(ref counter);
}
private unsafe static TlsNonceGenerator CreateNonceGenerator(TlsCrypto crypto, int connectionEnd)
{
Span<byte> span = new Span<byte>(stackalloc byte[16], 16);
Pack.UInt64_To_BE((ulong)NextCounterValue(), span);
Pack.UInt64_To_BE((ulong)DateTime.UtcNow.Ticks, span.Slice(8, span.Length - 8));
span[0] &= 127;
span[0] |= (byte)(connectionEnd << 7);
return crypto.CreateNonceGenerator(span);
}
internal AbstractTlsContext(TlsCrypto crypto, int connectionEnd)
{
m_crypto = crypto;
m_connectionEnd = connectionEnd;
m_nonceGenerator = CreateNonceGenerator(crypto, connectionEnd);
}
internal void HandshakeBeginning(TlsPeer peer)
{
lock (this) {
if (m_securityParameters != null)
throw new TlsFatalAlert(80, "Handshake already started");
m_securityParameters = new SecurityParameters();
m_securityParameters.m_entity = m_connectionEnd;
}
peer.NotifyHandshakeBeginning();
}
internal void HandshakeComplete(TlsPeer peer, TlsSession session)
{
lock (this) {
if (m_securityParameters == null)
throw new TlsFatalAlert(80);
m_session = session;
m_connected = true;
}
peer.NotifyHandshakeComplete();
}
internal void SetClientSupportedVersions(ProtocolVersion[] clientSupportedVersions)
{
m_clientSupportedVersions = clientSupportedVersions;
}
internal void SetClientVersion(ProtocolVersion clientVersion)
{
m_clientVersion = clientVersion;
}
internal void SetRsaPreMasterSecretVersion(ProtocolVersion rsaPreMasterSecretVersion)
{
m_rsaPreMasterSecretVersion = rsaPreMasterSecretVersion;
}
public virtual byte[] ExportChannelBinding(int channelBinding)
{
if (!IsConnected)
throw new InvalidOperationException("Export of channel bindings unavailable before handshake completion");
SecurityParameters securityParameters = SecurityParameters;
if (3 == channelBinding)
return ExportKeyingMaterial("EXPORTER-Channel-Binding", TlsUtilities.EmptyBytes, 32);
if (!TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion)) {
switch (channelBinding) {
case 0: {
byte[] tlsServerEndPoint = securityParameters.TlsServerEndPoint;
if (!TlsUtilities.IsNullOrEmpty(tlsServerEndPoint))
return Arrays.Clone(tlsServerEndPoint);
return null;
}
case 1:
return Arrays.Clone(securityParameters.TlsUnique);
default:
throw new NotSupportedException();
}
}
return null;
}
public virtual byte[] ExportEarlyKeyingMaterial(string asciiLabel, byte[] context, int length)
{
if (!IsConnected)
throw new InvalidOperationException("Export of early key material only available during handshake");
SecurityParameters securityParameters = SecurityParameters;
return ExportKeyingMaterial13(CheckEarlyExportSecret(securityParameters.EarlyExporterMasterSecret), securityParameters.PrfCryptoHashAlgorithm, asciiLabel, context, length);
}
public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context, int length)
{
if (!IsConnected)
throw new InvalidOperationException("Export of key material unavailable before handshake completion");
SecurityParameters securityParameters = SecurityParameters;
if (!securityParameters.IsExtendedMasterSecret)
throw new InvalidOperationException("Export of key material requires extended_master_secret");
if (TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
return ExportKeyingMaterial13(CheckExportSecret(securityParameters.ExporterMasterSecret), securityParameters.PrfCryptoHashAlgorithm, asciiLabel, context, length);
byte[] seed = TlsUtilities.CalculateExporterSeed(securityParameters, context);
return TlsUtilities.Prf(securityParameters, CheckExportSecret(securityParameters.MasterSecret), asciiLabel, seed, length).Extract();
}
protected virtual byte[] ExportKeyingMaterial13(TlsSecret secret, int cryptoHashAlgorithm, string asciiLabel, byte[] context, int length)
{
if (context == null)
context = TlsUtilities.EmptyBytes;
else if (!TlsUtilities.IsValidUint16(context.Length)) {
throw new ArgumentException("must have length less than 2^16 (or be null)", "context");
}
TlsHash tlsHash = Crypto.CreateHash(cryptoHashAlgorithm);
byte[] array = tlsHash.CalculateHash();
TlsSecret secret2 = TlsUtilities.DeriveSecret(SecurityParameters, secret, asciiLabel, array);
byte[] context2 = array;
if (context.Length != 0) {
tlsHash.Update(context, 0, context.Length);
context2 = tlsHash.CalculateHash();
}
return TlsCryptoUtilities.HkdfExpandLabel(secret2, cryptoHashAlgorithm, "exporter", context2, length).Extract();
}
protected virtual TlsSecret CheckEarlyExportSecret(TlsSecret secret)
{
if (secret == null)
throw new InvalidOperationException("Export of early key material not available for this handshake");
return secret;
}
protected virtual TlsSecret CheckExportSecret(TlsSecret secret)
{
if (secret == null)
throw new InvalidOperationException("Export of key material only available from NotifyHandshakeComplete()");
return secret;
}
}
}