356 lines
14 KiB
C#
356 lines
14 KiB
C#
#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
|
|
#pragma warning disable
|
|
using System;
|
|
using System.Collections;
|
|
using System.IO;
|
|
|
|
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
|
|
|
|
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Tls
|
|
{
|
|
public abstract class AbstractTlsServer
|
|
: AbstractTlsPeer, TlsServer
|
|
{
|
|
protected TlsCipherFactory mCipherFactory;
|
|
|
|
protected TlsServerContext mContext;
|
|
|
|
protected ProtocolVersion mClientVersion;
|
|
protected int[] mOfferedCipherSuites;
|
|
protected byte[] mOfferedCompressionMethods;
|
|
protected IDictionary mClientExtensions;
|
|
|
|
protected bool mEncryptThenMacOffered;
|
|
protected short mMaxFragmentLengthOffered;
|
|
protected bool mTruncatedHMacOffered;
|
|
protected IList mSupportedSignatureAlgorithms;
|
|
protected bool mEccCipherSuitesOffered;
|
|
protected int[] mNamedCurves;
|
|
protected byte[] mClientECPointFormats, mServerECPointFormats;
|
|
|
|
protected ProtocolVersion mServerVersion;
|
|
protected int mSelectedCipherSuite;
|
|
protected byte mSelectedCompressionMethod;
|
|
protected IDictionary mServerExtensions;
|
|
|
|
public AbstractTlsServer()
|
|
: this(new DefaultTlsCipherFactory())
|
|
{
|
|
}
|
|
|
|
public AbstractTlsServer(TlsCipherFactory cipherFactory)
|
|
{
|
|
this.mCipherFactory = cipherFactory;
|
|
}
|
|
|
|
protected virtual bool AllowEncryptThenMac
|
|
{
|
|
get { return true; }
|
|
}
|
|
|
|
protected virtual bool AllowTruncatedHMac
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
protected virtual IDictionary CheckServerExtensions()
|
|
{
|
|
return this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mServerExtensions);
|
|
}
|
|
|
|
protected abstract int[] GetCipherSuites();
|
|
|
|
protected byte[] GetCompressionMethods()
|
|
{
|
|
return new byte[] { CompressionMethod.cls_null };
|
|
}
|
|
|
|
protected virtual ProtocolVersion MaximumVersion
|
|
{
|
|
get { return ProtocolVersion.TLSv11; }
|
|
}
|
|
|
|
protected virtual ProtocolVersion MinimumVersion
|
|
{
|
|
get { return ProtocolVersion.TLSv10; }
|
|
}
|
|
|
|
protected virtual bool SupportsClientEccCapabilities(int[] namedCurves, byte[] ecPointFormats)
|
|
{
|
|
// NOTE: BC supports all the current set of point formats so we don't check them here
|
|
|
|
if (namedCurves == null)
|
|
{
|
|
/*
|
|
* RFC 4492 4. A client that proposes ECC cipher suites may choose not to include these
|
|
* extensions. In this case, the server is free to choose any one of the elliptic curves
|
|
* or point formats [...].
|
|
*/
|
|
return TlsEccUtilities.HasAnySupportedNamedCurves();
|
|
}
|
|
|
|
for (int i = 0; i < namedCurves.Length; ++i)
|
|
{
|
|
int namedCurve = namedCurves[i];
|
|
if (NamedCurve.IsValid(namedCurve)
|
|
&& (!NamedCurve.RefersToASpecificNamedCurve(namedCurve) || TlsEccUtilities.IsSupportedNamedCurve(namedCurve)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public virtual void Init(TlsServerContext context)
|
|
{
|
|
this.mContext = context;
|
|
}
|
|
|
|
public virtual void NotifyClientVersion(ProtocolVersion clientVersion)
|
|
{
|
|
this.mClientVersion = clientVersion;
|
|
}
|
|
|
|
public virtual void NotifyFallback(bool isFallback)
|
|
{
|
|
/*
|
|
* RFC 7507 3. If TLS_FALLBACK_SCSV appears in ClientHello.cipher_suites and the highest
|
|
* protocol version supported by the server is higher than the version indicated in
|
|
* ClientHello.client_version, the server MUST respond with a fatal inappropriate_fallback
|
|
* alert [..].
|
|
*/
|
|
if (isFallback && MaximumVersion.IsLaterVersionOf(mClientVersion))
|
|
throw new TlsFatalAlert(AlertDescription.inappropriate_fallback);
|
|
}
|
|
|
|
public virtual void NotifyOfferedCipherSuites(int[] offeredCipherSuites)
|
|
{
|
|
this.mOfferedCipherSuites = offeredCipherSuites;
|
|
this.mEccCipherSuitesOffered = TlsEccUtilities.ContainsEccCipherSuites(this.mOfferedCipherSuites);
|
|
}
|
|
|
|
public virtual void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods)
|
|
{
|
|
this.mOfferedCompressionMethods = offeredCompressionMethods;
|
|
}
|
|
|
|
public virtual void ProcessClientExtensions(IDictionary clientExtensions)
|
|
{
|
|
this.mClientExtensions = clientExtensions;
|
|
|
|
if (clientExtensions != null)
|
|
{
|
|
this.mEncryptThenMacOffered = TlsExtensionsUtilities.HasEncryptThenMacExtension(clientExtensions);
|
|
|
|
this.mMaxFragmentLengthOffered = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions);
|
|
if (mMaxFragmentLengthOffered >= 0 && !MaxFragmentLength.IsValid((byte)mMaxFragmentLengthOffered))
|
|
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
|
|
|
|
this.mTruncatedHMacOffered = TlsExtensionsUtilities.HasTruncatedHMacExtension(clientExtensions);
|
|
|
|
this.mSupportedSignatureAlgorithms = TlsUtilities.GetSignatureAlgorithmsExtension(clientExtensions);
|
|
if (this.mSupportedSignatureAlgorithms != null)
|
|
{
|
|
/*
|
|
* RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior
|
|
* to 1.2. Clients MUST NOT offer it if they are offering prior versions.
|
|
*/
|
|
if (!TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mClientVersion))
|
|
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
|
|
}
|
|
|
|
this.mNamedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(clientExtensions);
|
|
this.mClientECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(clientExtensions);
|
|
}
|
|
|
|
/*
|
|
* RFC 4429 4. The client MUST NOT include these extensions in the ClientHello message if it
|
|
* does not propose any ECC cipher suites.
|
|
*
|
|
* NOTE: This was overly strict as there may be ECC cipher suites that we don't recognize.
|
|
* Also, draft-ietf-tls-negotiated-ff-dhe will be overloading the 'elliptic_curves'
|
|
* extension to explicitly allow FFDHE (i.e. non-ECC) groups.
|
|
*/
|
|
//if (!this.mEccCipherSuitesOffered && (this.mNamedCurves != null || this.mClientECPointFormats != null))
|
|
// throw new TlsFatalAlert(AlertDescription.illegal_parameter);
|
|
}
|
|
|
|
public virtual ProtocolVersion GetServerVersion()
|
|
{
|
|
if (MinimumVersion.IsEqualOrEarlierVersionOf(mClientVersion))
|
|
{
|
|
ProtocolVersion maximumVersion = MaximumVersion;
|
|
if (mClientVersion.IsEqualOrEarlierVersionOf(maximumVersion))
|
|
{
|
|
return mServerVersion = mClientVersion;
|
|
}
|
|
if (mClientVersion.IsLaterVersionOf(maximumVersion))
|
|
{
|
|
return mServerVersion = maximumVersion;
|
|
}
|
|
}
|
|
throw new TlsFatalAlert(AlertDescription.protocol_version);
|
|
}
|
|
|
|
public virtual int GetSelectedCipherSuite()
|
|
{
|
|
/*
|
|
* RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate
|
|
* cipher suites against the "signature_algorithms" extension before selecting them. This is
|
|
* somewhat inelegant but is a compromise designed to minimize changes to the original
|
|
* cipher suite design.
|
|
*/
|
|
IList sigAlgs = TlsUtilities.GetUsableSignatureAlgorithms(this.mSupportedSignatureAlgorithms);
|
|
|
|
/*
|
|
* RFC 4429 5.1. A server that receives a ClientHello containing one or both of these
|
|
* extensions MUST use the client's enumerated capabilities to guide its selection of an
|
|
* appropriate cipher suite. One of the proposed ECC cipher suites must be negotiated only
|
|
* if the server can successfully complete the handshake while using the curves and point
|
|
* formats supported by the client [...].
|
|
*/
|
|
bool eccCipherSuitesEnabled = SupportsClientEccCapabilities(this.mNamedCurves, this.mClientECPointFormats);
|
|
|
|
int[] cipherSuites = GetCipherSuites();
|
|
for (int i = 0; i < cipherSuites.Length; ++i)
|
|
{
|
|
int cipherSuite = cipherSuites[i];
|
|
|
|
if (Arrays.Contains(this.mOfferedCipherSuites, cipherSuite)
|
|
&& (eccCipherSuitesEnabled || !TlsEccUtilities.IsEccCipherSuite(cipherSuite))
|
|
&& TlsUtilities.IsValidCipherSuiteForVersion(cipherSuite, mServerVersion)
|
|
&& TlsUtilities.IsValidCipherSuiteForSignatureAlgorithms(cipherSuite, sigAlgs))
|
|
{
|
|
return this.mSelectedCipherSuite = cipherSuite;
|
|
}
|
|
}
|
|
throw new TlsFatalAlert(AlertDescription.handshake_failure);
|
|
}
|
|
|
|
public virtual byte GetSelectedCompressionMethod()
|
|
{
|
|
byte[] compressionMethods = GetCompressionMethods();
|
|
for (int i = 0; i < compressionMethods.Length; ++i)
|
|
{
|
|
if (Arrays.Contains(mOfferedCompressionMethods, compressionMethods[i]))
|
|
{
|
|
return this.mSelectedCompressionMethod = compressionMethods[i];
|
|
}
|
|
}
|
|
throw new TlsFatalAlert(AlertDescription.handshake_failure);
|
|
}
|
|
|
|
// IDictionary is (Int32 -> byte[])
|
|
public virtual IDictionary GetServerExtensions()
|
|
{
|
|
if (this.mEncryptThenMacOffered && AllowEncryptThenMac)
|
|
{
|
|
/*
|
|
* RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
|
|
* and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
|
|
* ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
|
|
* client.
|
|
*/
|
|
if (TlsUtilities.IsBlockCipherSuite(this.mSelectedCipherSuite))
|
|
{
|
|
TlsExtensionsUtilities.AddEncryptThenMacExtension(CheckServerExtensions());
|
|
}
|
|
}
|
|
|
|
if (this.mMaxFragmentLengthOffered >= 0
|
|
&& TlsUtilities.IsValidUint8(mMaxFragmentLengthOffered)
|
|
&& MaxFragmentLength.IsValid((byte)mMaxFragmentLengthOffered))
|
|
{
|
|
TlsExtensionsUtilities.AddMaxFragmentLengthExtension(CheckServerExtensions(), (byte)mMaxFragmentLengthOffered);
|
|
}
|
|
|
|
if (this.mTruncatedHMacOffered && AllowTruncatedHMac)
|
|
{
|
|
TlsExtensionsUtilities.AddTruncatedHMacExtension(CheckServerExtensions());
|
|
}
|
|
|
|
if (this.mClientECPointFormats != null && TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite))
|
|
{
|
|
/*
|
|
* RFC 4492 5.2. A server that selects an ECC cipher suite in response to a ClientHello
|
|
* message including a Supported Point Formats Extension appends this extension (along
|
|
* with others) to its ServerHello message, enumerating the point formats it can parse.
|
|
*/
|
|
this.mServerECPointFormats = new byte[]{ ECPointFormat.uncompressed,
|
|
ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
|
|
|
|
TlsEccUtilities.AddSupportedPointFormatsExtension(CheckServerExtensions(), mServerECPointFormats);
|
|
}
|
|
|
|
return mServerExtensions;
|
|
}
|
|
|
|
public virtual IList GetServerSupplementalData()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public abstract TlsCredentials GetCredentials();
|
|
|
|
public virtual CertificateStatus GetCertificateStatus()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public abstract TlsKeyExchange GetKeyExchange();
|
|
|
|
public virtual CertificateRequest GetCertificateRequest()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public virtual void ProcessClientSupplementalData(IList clientSupplementalData)
|
|
{
|
|
if (clientSupplementalData != null)
|
|
throw new TlsFatalAlert(AlertDescription.unexpected_message);
|
|
}
|
|
|
|
public virtual void NotifyClientCertificate(Certificate clientCertificate)
|
|
{
|
|
throw new TlsFatalAlert(AlertDescription.internal_error);
|
|
}
|
|
|
|
public override TlsCompression GetCompression()
|
|
{
|
|
switch (mSelectedCompressionMethod)
|
|
{
|
|
case CompressionMethod.cls_null:
|
|
return new TlsNullCompression();
|
|
|
|
default:
|
|
/*
|
|
* Note: internal error here; we selected the compression method, so if we now can't
|
|
* produce an implementation, we shouldn't have chosen it!
|
|
*/
|
|
throw new TlsFatalAlert(AlertDescription.internal_error);
|
|
}
|
|
}
|
|
|
|
public override TlsCipher GetCipher()
|
|
{
|
|
int encryptionAlgorithm = TlsUtilities.GetEncryptionAlgorithm(mSelectedCipherSuite);
|
|
int macAlgorithm = TlsUtilities.GetMacAlgorithm(mSelectedCipherSuite);
|
|
|
|
return mCipherFactory.CreateCipher(mContext, encryptionAlgorithm, macAlgorithm);
|
|
}
|
|
|
|
public virtual NewSessionTicket GetNewSessionTicket()
|
|
{
|
|
/*
|
|
* RFC 5077 3.3. If the server determines that it does not want to include a ticket after it
|
|
* has included the SessionTicket extension in the ServerHello, then it sends a zero-length
|
|
* ticket in the NewSessionTicket handshake message.
|
|
*/
|
|
return new NewSessionTicket(0L, TlsUtilities.EmptyBytes);
|
|
}
|
|
}
|
|
}
|
|
#pragma warning restore
|
|
#endif
|