You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
305 lines
10 KiB
305 lines
10 KiB
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) |
|
#pragma warning disable |
|
using System; |
|
using System.IO; |
|
using System.Threading; |
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto; |
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; |
|
|
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Tls |
|
{ |
|
internal abstract class AbstractTlsContext |
|
: TlsContext |
|
{ |
|
private static long counter = Times.NanoTime(); |
|
|
|
#if NETCF_1_0 |
|
private static object counterLock = new object(); |
|
private static long NextCounterValue() |
|
{ |
|
lock (counterLock) |
|
{ |
|
return ++counter; |
|
} |
|
} |
|
#else |
|
private static long NextCounterValue() |
|
{ |
|
return Interlocked.Increment(ref counter); |
|
} |
|
#endif |
|
|
|
private static TlsNonceGenerator CreateNonceGenerator(TlsCrypto crypto, int connectionEnd) |
|
{ |
|
byte[] additionalSeedMaterial = new byte[16]; |
|
Pack.UInt64_To_BE((ulong)NextCounterValue(), additionalSeedMaterial, 0); |
|
Pack.UInt64_To_BE((ulong)Times.NanoTime(), additionalSeedMaterial, 8); |
|
additionalSeedMaterial[0] &= 0x7F; |
|
additionalSeedMaterial[0] |= (byte)(connectionEnd << 7); |
|
|
|
return crypto.CreateNonceGenerator(additionalSeedMaterial); |
|
} |
|
|
|
private readonly TlsCrypto m_crypto; |
|
private readonly int m_connectionEnd; |
|
private readonly TlsNonceGenerator m_nonceGenerator; |
|
|
|
private SecurityParameters m_securityParameters = null; |
|
private ProtocolVersion[] m_clientSupportedVersions = null; |
|
private ProtocolVersion m_clientVersion = null; |
|
private ProtocolVersion m_rsaPreMasterSecretVersion = null; |
|
private TlsSession m_session = null; |
|
private object m_userObject = null; |
|
private bool m_connected = false; |
|
|
|
internal AbstractTlsContext(TlsCrypto crypto, int connectionEnd) |
|
{ |
|
this.m_crypto = crypto; |
|
this.m_connectionEnd = connectionEnd; |
|
this.m_nonceGenerator = CreateNonceGenerator(crypto, connectionEnd); |
|
} |
|
|
|
/// <exception cref="IOException"/> |
|
internal void HandshakeBeginning(TlsPeer peer) |
|
{ |
|
lock (this) |
|
{ |
|
//if (null != m_securityParameters) |
|
// throw new TlsFatalAlert(AlertDescription.internal_error, "Handshake already started"); |
|
|
|
var tmp = this.m_securityParameters; |
|
|
|
m_securityParameters = new SecurityParameters(); |
|
m_securityParameters.m_entity = m_connectionEnd; |
|
|
|
if (tmp != null) |
|
{ |
|
this.m_securityParameters.IsRenegotiating = true; |
|
this.m_securityParameters.m_secureRenegotiation = tmp.m_secureRenegotiation; |
|
this.m_securityParameters.m_negotiatedVersion = tmp.m_negotiatedVersion; |
|
|
|
this.m_securityParameters.m_localVerifyData = tmp.m_localVerifyData; |
|
this.m_securityParameters.m_peerVerifyData = tmp.m_peerVerifyData; |
|
this.m_securityParameters.PreRenegotiatingServerCert = tmp.m_peerCertificate; |
|
} |
|
} |
|
|
|
peer.NotifyHandshakeBeginning(); |
|
} |
|
|
|
/// <exception cref="IOException"/> |
|
internal void HandshakeComplete(TlsPeer peer, TlsSession session) |
|
{ |
|
lock (this) |
|
{ |
|
if (null == m_securityParameters) |
|
throw new TlsFatalAlert(AlertDescription.internal_error); |
|
|
|
this.m_session = session; |
|
this.m_connected = true; |
|
} |
|
|
|
peer.NotifyHandshakeComplete(); |
|
} |
|
|
|
internal bool IsConnected |
|
{ |
|
get { lock (this) return m_connected; } |
|
} |
|
|
|
internal bool IsHandshaking |
|
{ |
|
get { lock (this) return !m_connected && null != m_securityParameters; } |
|
} |
|
|
|
public TlsCrypto Crypto |
|
{ |
|
get { return m_crypto; } |
|
} |
|
|
|
public virtual TlsNonceGenerator NonceGenerator |
|
{ |
|
get { return m_nonceGenerator; } |
|
} |
|
|
|
public SecurityParameters SecurityParameters |
|
{ |
|
get { lock (this) return m_securityParameters; } |
|
} |
|
|
|
public abstract bool IsServer { get; } |
|
|
|
public virtual ProtocolVersion[] ClientSupportedVersions |
|
{ |
|
get { return m_clientSupportedVersions; } |
|
} |
|
|
|
internal void SetClientSupportedVersions(ProtocolVersion[] clientSupportedVersions) |
|
{ |
|
this.m_clientSupportedVersions = clientSupportedVersions; |
|
} |
|
|
|
public virtual ProtocolVersion ClientVersion |
|
{ |
|
get { return m_clientVersion; } |
|
} |
|
|
|
internal void SetClientVersion(ProtocolVersion clientVersion) |
|
{ |
|
this.m_clientVersion = clientVersion; |
|
} |
|
|
|
public virtual ProtocolVersion RsaPreMasterSecretVersion |
|
{ |
|
get { return m_rsaPreMasterSecretVersion; } |
|
} |
|
|
|
internal void SetRsaPreMasterSecretVersion(ProtocolVersion rsaPreMasterSecretVersion) |
|
{ |
|
this.m_rsaPreMasterSecretVersion = rsaPreMasterSecretVersion; |
|
} |
|
|
|
public virtual ProtocolVersion ServerVersion |
|
{ |
|
get { return SecurityParameters.NegotiatedVersion; } |
|
} |
|
|
|
public virtual TlsSession ResumableSession |
|
{ |
|
get |
|
{ |
|
TlsSession session = Session; |
|
if (session == null || !session.IsResumable) |
|
return null; |
|
|
|
return session; |
|
} |
|
} |
|
|
|
public virtual TlsSession Session |
|
{ |
|
get { return m_session; } |
|
} |
|
|
|
public virtual object UserObject |
|
{ |
|
get { return m_userObject; } |
|
set { this.m_userObject = value; } |
|
} |
|
|
|
public virtual byte[] ExportChannelBinding(int channelBinding) |
|
{ |
|
if (!IsConnected) |
|
throw new InvalidOperationException("Export of channel bindings unavailable before handshake completion"); |
|
|
|
SecurityParameters securityParameters = SecurityParameters; |
|
|
|
if (TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion)) |
|
return null; |
|
|
|
switch (channelBinding) |
|
{ |
|
case ChannelBinding.tls_server_end_point: |
|
{ |
|
byte[] tlsServerEndPoint = securityParameters.TlsServerEndPoint; |
|
|
|
return TlsUtilities.IsNullOrEmpty(tlsServerEndPoint) ? null : Arrays.Clone(tlsServerEndPoint); |
|
} |
|
|
|
case ChannelBinding.tls_unique: |
|
{ |
|
return Arrays.Clone(securityParameters.TlsUnique); |
|
} |
|
|
|
case ChannelBinding.tls_unique_for_telnet: |
|
default: |
|
throw new NotSupportedException(); |
|
} |
|
} |
|
|
|
public virtual byte[] ExportEarlyKeyingMaterial(string asciiLabel, byte[] context, int length) |
|
{ |
|
// TODO[tls13] Ensure early_exporter_master_secret is available suitably early! |
|
if (!IsConnected) |
|
throw new InvalidOperationException("Export of early key material only available during handshake"); |
|
|
|
SecurityParameters sp = SecurityParameters; |
|
|
|
return ExportKeyingMaterial13(CheckEarlyExportSecret(sp.EarlyExporterMasterSecret), |
|
sp.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"); |
|
|
|
/* |
|
* TODO[tls13] Introduce a TlsExporter interface? Avoid calculating (early) exporter |
|
* secret(s) unless the peer actually uses it. |
|
*/ |
|
SecurityParameters sp = SecurityParameters; |
|
|
|
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 InvalidOperationException("Export of key material requires extended_master_secret"); |
|
} |
|
|
|
if (TlsUtilities.IsTlsV13(sp.NegotiatedVersion)) |
|
{ |
|
return ExportKeyingMaterial13(CheckExportSecret(sp.ExporterMasterSecret), sp.PrfCryptoHashAlgorithm, |
|
asciiLabel, context, length); |
|
} |
|
|
|
byte[] seed = TlsUtilities.CalculateExporterSeed(sp, context); |
|
|
|
return TlsUtilities.Prf(sp, CheckExportSecret(sp.MasterSecret), asciiLabel, seed, length).Extract(); |
|
} |
|
|
|
protected virtual byte[] ExportKeyingMaterial13(TlsSecret secret, int cryptoHashAlgorithm, string asciiLabel, |
|
byte[] context, int length) |
|
{ |
|
if (null == context) |
|
{ |
|
context = TlsUtilities.EmptyBytes; |
|
} |
|
else if (!TlsUtilities.IsValidUint16(context.Length)) |
|
{ |
|
throw new ArgumentException("must have length less than 2^16 (or be null)", "context"); |
|
} |
|
|
|
return TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, asciiLabel, context, length) |
|
.Extract(); |
|
} |
|
|
|
protected virtual TlsSecret CheckEarlyExportSecret(TlsSecret secret) |
|
{ |
|
if (null == secret) |
|
{ |
|
// TODO[tls13] For symmetry with normal export, ideally available for NotifyHandshakeBeginning() only |
|
//throw new InvalidOperationException("Export of early key material only available from NotifyHandshakeBeginning()"); |
|
throw new InvalidOperationException("Export of early key material not available for this handshake"); |
|
} |
|
return secret; |
|
} |
|
|
|
protected virtual TlsSecret CheckExportSecret(TlsSecret secret) |
|
{ |
|
if (null == secret) |
|
throw new InvalidOperationException( |
|
"Export of key material only available from NotifyHandshakeComplete()"); |
|
|
|
return secret; |
|
} |
|
} |
|
} |
|
#pragma warning restore |
|
#endif
|
|
|