diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index fc577c303..0e9227970 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4279,6 +4279,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\AbstractTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\AbstractTlsContext.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4294,6 +4299,16 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\AbstractTlsPeer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\crypto\tls\AbstractTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\AbstractTlsSigner.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4489,6 +4504,16 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\HeartbeatExtension.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\crypto\tls\HeartbeatMessage.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\HeartbeatMessageType.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4574,6 +4599,16 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\ServerName.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "src\crypto\tls\ServerNameList.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\SessionParameters.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4609,6 +4644,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\SupplementalDataEntry.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\SupplementalDataType.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4729,6 +4769,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\TlsExtensionsUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\TlsFatalAlert.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4799,6 +4844,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\TlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\TlsServerContext.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4814,6 +4864,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\TlsSessionImpl.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\TlsSigner.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4829,6 +4884,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\TlsSrpUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\TlsStream.cs"
SubType = "Code"
BuildAction = "Compile"
diff --git a/crypto/src/crypto/tls/AbstractTlsClient.cs b/crypto/src/crypto/tls/AbstractTlsClient.cs
new file mode 100644
index 000000000..3398d94df
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsClient.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsClient
+ : AbstractTlsPeer, TlsClient
+ {
+ protected TlsCipherFactory mCipherFactory;
+
+ protected TlsClientContext mContext;
+
+ protected IList mSupportedSignatureAlgorithms;
+ protected int[] mNamedCurves;
+ protected byte[] mClientECPointFormats, mServerECPointFormats;
+
+ protected int mSelectedCipherSuite;
+ protected short mSelectedCompressionMethod;
+
+ public AbstractTlsClient()
+ : this(new DefaultTlsCipherFactory())
+ {
+ }
+
+ public AbstractTlsClient(TlsCipherFactory cipherFactory)
+ {
+ this.mCipherFactory = cipherFactory;
+ }
+
+ public virtual void Init(TlsClientContext context)
+ {
+ this.mContext = context;
+ }
+
+ public virtual TlsSession GetSessionToResume()
+ {
+ return null;
+ }
+
+ /**
+ * RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value
+ * {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest
+ * version number supported by the client, and the value of ClientHello.client_version. No
+ * single value will guarantee interoperability with all old servers, but this is a complex
+ * topic beyond the scope of this document."
+ */
+ public virtual ProtocolVersion ClientHelloRecordLayerVersion
+ {
+ get
+ {
+ // "{03,00}"
+ // return ProtocolVersion.SSLv3;
+
+ // "the lowest version number supported by the client"
+ // return getMinimumVersion();
+
+ // "the value of ClientHello.client_version"
+ return ClientVersion;
+ }
+ }
+
+ public virtual ProtocolVersion ClientVersion
+ {
+ get { return ProtocolVersion.TLSv12; }
+ }
+
+ public virtual IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = null;
+
+ ProtocolVersion clientVersion = mContext.ClientVersion;
+
+ /*
+ * 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(clientVersion))
+ {
+ // TODO Provide a way for the user to specify the acceptable hash/signature algorithms.
+
+ byte[] hashAlgorithms = new byte[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
+ HashAlgorithm.sha224, HashAlgorithm.sha1 };
+
+ // TODO Sort out ECDSA signatures and add them as the preferred option here
+ byte[] signatureAlgorithms = new byte[]{ SignatureAlgorithm.rsa };
+
+ this.mSupportedSignatureAlgorithms = Platform.CreateArrayList();
+ for (int i = 0; i < hashAlgorithms.Length; ++i)
+ {
+ for (int j = 0; j < signatureAlgorithms.Length; ++j)
+ {
+ this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(hashAlgorithms[i],
+ signatureAlgorithms[j]));
+ }
+ }
+
+ /*
+ * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1.
+ */
+ this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
+ SignatureAlgorithm.dsa));
+
+ clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions);
+
+ TlsUtilities.AddSignatureAlgorithmsExtension(clientExtensions, mSupportedSignatureAlgorithms);
+ }
+
+ if (TlsEccUtilities.ContainsEccCipherSuites(GetCipherSuites()))
+ {
+ /*
+ * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message
+ * appends these extensions (along with any others), enumerating the curves it supports
+ * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic
+ * Curves Extension and the Supported Point Formats Extension.
+ */
+ /*
+ * TODO Could just add all the curves since we support them all, but users may not want
+ * to use unnecessarily large fields. Need configuration options.
+ */
+ this.mNamedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 };
+ this.mClientECPointFormats = new byte[]{ ECPointFormat.uncompressed,
+ ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
+
+ clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions);
+
+ TlsEccUtilities.AddSupportedEllipticCurvesExtension(clientExtensions, mNamedCurves);
+ TlsEccUtilities.AddSupportedPointFormatsExtension(clientExtensions, mClientECPointFormats);
+ }
+
+ return clientExtensions;
+ }
+
+ public virtual ProtocolVersion MinimumVersion
+ {
+ get { return ProtocolVersion.TLSv10; }
+ }
+
+ public virtual void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ if (!MinimumVersion.IsEqualOrEarlierVersionOf(serverVersion))
+ throw new TlsFatalAlert(AlertDescription.protocol_version);
+ }
+
+ public abstract int[] GetCipherSuites();
+
+ public virtual byte[] GetCompressionMethods()
+ {
+ return new byte[]{ CompressionMethod.NULL };
+ }
+
+ public virtual void NotifySessionID(byte[] sessionID)
+ {
+ // Currently ignored
+ }
+
+ public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
+ {
+ this.mSelectedCipherSuite = selectedCipherSuite;
+ }
+
+ public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
+ {
+ this.mSelectedCompressionMethod = selectedCompressionMethod;
+ }
+
+ public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+ {
+ /*
+ * TlsProtocol implementation validates that any server extensions received correspond to
+ * client extensions sent. By default, we don't send any, and this method is not called.
+ */
+ if (serverExtensions != null)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension.
+ */
+ if (serverExtensions.Contains(ExtensionType.signature_algorithms))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ int[] namedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(serverExtensions);
+ if (namedCurves != null)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mServerECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(serverExtensions);
+ if (this.mServerECPointFormats != null && !TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ public virtual void ProcessServerSupplementalData(IList serverSupplementalData)
+ {
+ if (serverSupplementalData != null)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ public abstract TlsKeyExchange GetKeyExchange();
+
+ public abstract TlsAuthentication GetAuthentication();
+
+ public virtual IList GetClientSupplementalData()
+ {
+ return null;
+ }
+
+ public override TlsCompression GetCompression()
+ {
+ switch (mSelectedCompressionMethod)
+ {
+ case CompressionMethod.NULL:
+ return new TlsNullCompression();
+
+ case CompressionMethod.DEFLATE:
+ return new TlsDeflateCompression();
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected compression method was in the list of client-offered compression
+ * methods, so if we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public virtual void NotifyNewSessionTicket(NewSessionTicket newSessionTicket)
+ {
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsPeer.cs b/crypto/src/crypto/tls/AbstractTlsPeer.cs
new file mode 100644
index 000000000..81a53386c
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsPeer.cs
@@ -0,0 +1,48 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class AbstractTlsPeer
+ : TlsPeer
+ {
+ public virtual bool ShouldUseGmtUnixTime()
+ {
+ /*
+ * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
+ * TLS implementors MUST by default set the entire value the ClientHello.Random and
+ * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
+ * sequence.
+ */
+ return false;
+ }
+
+ public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+ {
+ if (!secureRenegotiation)
+ {
+ /*
+ * RFC 5746 3.4/3.6. In this case, some clients/servers may want to terminate the handshake instead
+ * of continuing; see Section 4.1/4.3 for discussion.
+ */
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ public abstract TlsCompression GetCompression();
+
+ public abstract TlsCipher GetCipher();
+
+ public virtual void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+ {
+ }
+
+ public virtual void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+ {
+ }
+
+ public virtual void NotifyHandshakeComplete()
+ {
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsServer.cs b/crypto/src/crypto/tls/AbstractTlsServer.cs
new file mode 100644
index 000000000..036187c02
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsServer.cs
@@ -0,0 +1,318 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace 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.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 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);
+ 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.
+ */
+ 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()
+ {
+ /*
+ * TODO 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.
+ */
+
+ /*
+ * 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))
+ {
+ 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)
+ {
+ /*
+ * draft-ietf-tls-encrypt-then-mac-03 3. If a server receives an encrypt-then-MAC
+ * request extension from a client and then selects a stream or AEAD cipher suite, 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)
+ {
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(CheckServerExtensions(), (byte)this.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.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 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);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index 924e6ee2e..65106adef 100644
--- a/crypto/src/crypto/tls/DefaultTlsClient.cs
+++ b/crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -12,42 +12,19 @@ using Org.BouncyCastle.Crypto.Parameters;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class DefaultTlsClient
- : TlsClient
+ : AbstractTlsClient
{
- protected TlsCipherFactory cipherFactory;
-
- protected TlsClientContext context;
-
- protected byte selectedCompressionMethod;
- protected int selectedCipherSuite;
-
public DefaultTlsClient()
- : this(new DefaultTlsCipherFactory())
+ : base()
{
}
public DefaultTlsClient(TlsCipherFactory cipherFactory)
+ : base(cipherFactory)
{
- this.cipherFactory = cipherFactory;
- }
-
- public virtual void Init(TlsClientContext context)
- {
- this.context = context;
- }
-
- public virtual bool ShouldUseGmtUnixTime()
- {
- /*
- * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
- * TLS implementors MUST by default set the entire value the ClientHello.Random and
- * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
- * sequence.
- */
- return false;
}
- public virtual int[] GetCipherSuites()
+ public override int[] GetCipherSuites()
{
return new int[] {
CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
@@ -63,224 +40,150 @@ namespace Org.BouncyCastle.Crypto.Tls
};
}
- public virtual byte[] GetCompressionMethods()
- {
- /*
- * To offer DEFLATE compression, override this method:
- * return new byte[] { CompressionMethod.DEFLATE, CompressionMethod.NULL };
- */
-
- return new byte[] { CompressionMethod.NULL };
- }
-
- public virtual IDictionary GetClientExtensions()
- {
- return null;
- }
-
- public virtual void NotifySessionID(byte[] sessionID)
- {
- // Currently ignored
- }
-
- public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
- {
- this.selectedCipherSuite = selectedCipherSuite;
- }
-
- public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
- {
- this.selectedCompressionMethod = selectedCompressionMethod;
- }
-
- public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+ public override TlsKeyExchange GetKeyExchange()
{
- if (!secureRenegotiation)
+ switch (mSelectedCipherSuite)
{
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ return CreateRsaKeyExchange();
+
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+ default:
/*
- * RFC 5746 3.4.
- * If the extension is not present, the server does not support
- * secure renegotiation; set secure_renegotiation flag to FALSE.
- * In this case, some clients may want to terminate the handshake
- * instead of continuing; see Section 4.1 for discussion.
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
*/
-// throw new TlsFatalAlert(AlertDescription.handshake_failure);
- }
- }
-
- public virtual void ProcessServerExtensions(IDictionary serverExtensions)
- {
- }
-
- public virtual TlsKeyExchange GetKeyExchange()
- {
- switch (selectedCipherSuite)
- {
- case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
- return CreateRsaKeyExchange();
-
- case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
- return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
-
- case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
- return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
-
- case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
- return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
-
- case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
- return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
-
- case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
- return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
-
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
- return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
-
- case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
- return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
-
- case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
- return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
- public abstract TlsAuthentication GetAuthentication();
-
- public virtual TlsCompression GetCompression()
- {
- switch (selectedCompressionMethod)
- {
- case CompressionMethod.NULL:
- return new TlsNullCompression();
-
- case CompressionMethod.DEFLATE:
- return new TlsDeflateCompression();
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected compression method was in the list of client-offered compression
- * methods, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
- }
-
- public virtual TlsCipher GetCipher()
+ public override TlsCipher GetCipher()
{
- switch (selectedCipherSuite)
+ switch (mSelectedCipherSuite)
{
- case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
- MacAlgorithm.hmac_sha1);
-
- case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
-
- case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
- MacAlgorithm.hmac_sha1);
-
- case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
- MacAlgorithm.hmac_sha1);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC,
+ MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC,
+ MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC,
+ MacAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange)
{
- return new TlsDHKeyExchange(context, keyExchange);
+ return new TlsDHKeyExchange(mContext, keyExchange);
}
protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange)
{
- return new TlsDheKeyExchange(context, keyExchange);
+ return new TlsDheKeyExchange(mContext, keyExchange);
}
protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
{
- return new TlsECDHKeyExchange(context, keyExchange);
+ return new TlsECDHKeyExchange(mContext, keyExchange);
}
protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange)
{
- return new TlsECDheKeyExchange(context, keyExchange);
+ return new TlsECDheKeyExchange(mContext, keyExchange);
}
protected virtual TlsKeyExchange CreateRsaKeyExchange()
{
- return new TlsRsaKeyExchange(context);
+ return new TlsRsaKeyExchange(mContext);
}
}
}
diff --git a/crypto/src/crypto/tls/HeartbeatExtension.cs b/crypto/src/crypto/tls/HeartbeatExtension.cs
new file mode 100644
index 000000000..049837266
--- /dev/null
+++ b/crypto/src/crypto/tls/HeartbeatExtension.cs
@@ -0,0 +1,52 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class HeartbeatExtension
+ {
+ protected readonly byte mMode;
+
+ public HeartbeatExtension(byte mode)
+ {
+ if (!HeartbeatMode.IsValid(mode))
+ throw new ArgumentException("not a valid HeartbeatMode value", "mode");
+
+ this.mMode = mode;
+ }
+
+ public virtual byte Mode
+ {
+ get { return mMode; }
+ }
+
+ /**
+ * Encode this {@link HeartbeatExtension} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(mMode, output);
+ }
+
+ /**
+ * Parse a {@link HeartbeatExtension} from a {@link Stream}.
+ *
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link HeartbeatExtension} object.
+ * @throws IOException
+ */
+ public static HeartbeatExtension Parse(Stream input)
+ {
+ byte mode = TlsUtilities.ReadUint8(input);
+ if (!HeartbeatMode.IsValid(mode))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return new HeartbeatExtension(mode);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/HeartbeatMessage.cs b/crypto/src/crypto/tls/HeartbeatMessage.cs
new file mode 100644
index 000000000..f64a7baa4
--- /dev/null
+++ b/crypto/src/crypto/tls/HeartbeatMessage.cs
@@ -0,0 +1,102 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class HeartbeatMessage
+ {
+ protected readonly byte mType;
+ protected readonly byte[] mPayload;
+ protected readonly int mPaddingLength;
+
+ public HeartbeatMessage(byte type, byte[] payload, int paddingLength)
+ {
+ if (!HeartbeatMessageType.IsValid(type))
+ throw new ArgumentException("not a valid HeartbeatMessageType value", "type");
+ if (payload == null || payload.Length >= (1 << 16))
+ throw new ArgumentException("must have length < 2^16", "payload");
+ if (paddingLength < 16)
+ throw new ArgumentException("must be at least 16", "paddingLength");
+
+ this.mType = type;
+ this.mPayload = payload;
+ this.mPaddingLength = paddingLength;
+ }
+
+ /**
+ * Encode this {@link HeartbeatMessage} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(TlsContext context, Stream output)
+ {
+ TlsUtilities.WriteUint8(mType, output);
+
+ TlsUtilities.CheckUint16(mPayload.Length);
+ TlsUtilities.WriteUint16(mPayload.Length, output);
+ output.Write(mPayload, 0, mPayload.Length);
+
+ byte[] padding = new byte[mPaddingLength];
+ context.NonceRandomGenerator.NextBytes(padding);
+ output.Write(padding, 0, padding.Length);
+ }
+
+ /**
+ * Parse a {@link HeartbeatMessage} from a {@link Stream}.
+ *
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link HeartbeatMessage} object.
+ * @throws IOException
+ */
+ public static HeartbeatMessage Parse(Stream input)
+ {
+ byte type = TlsUtilities.ReadUint8(input);
+ if (!HeartbeatMessageType.IsValid(type))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ int payload_length = TlsUtilities.ReadUint16(input);
+
+ PayloadBuffer buf = new PayloadBuffer();
+ Streams.PipeAll(input, buf);
+
+ byte[] payload = buf.ToTruncatedByteArray(payload_length);
+ if (payload == null)
+ {
+ /*
+ * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the
+ * received HeartbeatMessage MUST be discarded silently.
+ */
+ return null;
+ }
+
+ TlsUtilities.CheckUint16(buf.Length);
+ int padding_length = (int)buf.Length - payload.Length;
+
+ /*
+ * RFC 6520 4. The padding of a received HeartbeatMessage message MUST be ignored
+ */
+ return new HeartbeatMessage(type, payload, padding_length);
+ }
+
+ internal class PayloadBuffer
+ : MemoryStream
+ {
+ internal byte[] ToTruncatedByteArray(int payloadLength)
+ {
+ /*
+ * RFC 6520 4. The padding_length MUST be at least 16.
+ */
+ int minimumCount = payloadLength + 16;
+ if (Length < minimumCount)
+ return null;
+ return Arrays.CopyOf(GetBuffer(), payloadLength);
+ }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index f6d83b5be..620a6d8f7 100644
--- a/crypto/src/crypto/tls/PskTlsClient.cs
+++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -4,44 +4,22 @@ using System.Collections;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class PskTlsClient
- :TlsClient
+ : AbstractTlsClient
{
- protected TlsCipherFactory cipherFactory;
- protected TlsPskIdentity pskIdentity;
-
- protected TlsClientContext context;
-
- protected byte selectedCompressionMethod;
- protected int selectedCipherSuite;
+ protected TlsPskIdentity mPskIdentity;
public PskTlsClient(TlsPskIdentity pskIdentity)
- : this(new DefaultTlsCipherFactory(), pskIdentity)
+ : this(new DefaultTlsCipherFactory(), pskIdentity)
{
}
public PskTlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity)
+ : base(cipherFactory)
{
- this.cipherFactory = cipherFactory;
- this.pskIdentity = pskIdentity;
- }
-
- public virtual void Init(TlsClientContext context)
- {
- this.context = context;
- }
-
- public virtual bool ShouldUseGmtUnixTime()
- {
- /*
- * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
- * TLS implementors MUST by default set the entire value the ClientHello.Random and
- * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
- * sequence.
- */
- return false;
+ this.mPskIdentity = pskIdentity;
}
- public virtual int[] GetCipherSuites()
+ public override int[] GetCipherSuites()
{
return new int[] {
CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
@@ -59,144 +37,79 @@ namespace Org.BouncyCastle.Crypto.Tls
};
}
- public virtual IDictionary GetClientExtensions()
- {
- return null;
- }
-
- public virtual byte[] GetCompressionMethods()
- {
- return new byte[] { CompressionMethod.NULL };
- }
-
- public virtual void NotifySessionID(byte[] sessionID)
- {
- // Currently ignored
- }
-
- public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
- {
- this.selectedCipherSuite = selectedCipherSuite;
- }
-
- public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
- {
- this.selectedCompressionMethod = selectedCompressionMethod;
- }
-
- public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+ public override TlsKeyExchange GetKeyExchange()
{
- if (!secureRenegotiation)
+ switch (mSelectedCipherSuite)
{
+ case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
+
+ case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
+
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+ default:
/*
- * RFC 5746 3.4. If the extension is not present, the server does not support
- * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
- * some clients may want to terminate the handshake instead of continuing; see
- * Section 4.1 for discussion.
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
*/
-// throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
- public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+ public override TlsCipher GetCipher()
{
- }
-
- public virtual TlsKeyExchange GetKeyExchange()
- {
- switch (selectedCipherSuite)
- {
- case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
- return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
-
- case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
- return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
-
- case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
- return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
- }
-
- public abstract TlsAuthentication GetAuthentication();
-
- public virtual TlsCompression GetCompression()
- {
- switch (selectedCompressionMethod)
+ switch (mSelectedCipherSuite)
{
- case CompressionMethod.NULL:
- return new TlsNullCompression();
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected compression method was in the list of client-offered compression
- * methods, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
- }
-
- public virtual TlsCipher GetCipher()
- {
- switch (selectedCipherSuite)
- {
- case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
- MacAlgorithm.hmac_sha1);
-
- case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
- MacAlgorithm.hmac_sha1);
-
- case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
- MacAlgorithm.hmac_sha1);
-
- case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
- case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
- case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128,
- MacAlgorithm.hmac_sha1);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC,
+ MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC,
+ MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC,
+ MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128,
+ MacAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange)
{
- return new TlsPskKeyExchange(context, keyExchange, pskIdentity);
+ return new TlsPskKeyExchange(mContext, keyExchange, mPskIdentity);
}
}
}
diff --git a/crypto/src/crypto/tls/ServerName.cs b/crypto/src/crypto/tls/ServerName.cs
new file mode 100644
index 000000000..3d1e8f844
--- /dev/null
+++ b/crypto/src/crypto/tls/ServerName.cs
@@ -0,0 +1,105 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class ServerName
+ {
+ protected readonly byte mNameType;
+ protected readonly object mName;
+
+ public ServerName(byte nameType, object name)
+ {
+ if (!IsCorrectType(nameType, name))
+ throw new ArgumentException("not an instance of the correct type", "name");
+
+ this.mNameType = nameType;
+ this.mName = name;
+ }
+
+ public virtual byte NameType
+ {
+ get { return mNameType; }
+ }
+
+ public virtual object Name
+ {
+ get { return mName; }
+ }
+
+ public virtual string GetHostName()
+ {
+ if (!IsCorrectType(Tls.NameType.host_name, mName))
+ throw new InvalidOperationException("'name' is not a HostName string");
+
+ return (string)mName;
+ }
+
+ /**
+ * Encode this {@link ServerName} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(Stream output)
+ {
+ TlsUtilities.WriteUint8(mNameType, output);
+
+ switch (mNameType)
+ {
+ case Tls.NameType.host_name:
+ byte[] utf8Encoding = Strings.ToUtf8ByteArray((string)mName);
+ if (utf8Encoding.Length < 1)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ TlsUtilities.WriteOpaque16(utf8Encoding, output);
+ break;
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ /**
+ * Parse a {@link ServerName} from a {@link Stream}.
+ *
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link ServerName} object.
+ * @throws IOException
+ */
+ public static ServerName Parse(Stream input)
+ {
+ byte name_type = TlsUtilities.ReadUint8(input);
+ object name;
+
+ switch (name_type)
+ {
+ case Tls.NameType.host_name:
+ {
+ byte[] utf8Encoding = TlsUtilities.ReadOpaque16(input);
+ if (utf8Encoding.Length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ name = Strings.FromUtf8ByteArray(utf8Encoding);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ }
+
+ return new ServerName(name_type, name);
+ }
+
+ protected static bool IsCorrectType(byte nameType, object name)
+ {
+ switch (nameType)
+ {
+ case Tls.NameType.host_name:
+ return name is string;
+ default:
+ throw new ArgumentException("unsupported value", "name");
+ }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/ServerNameList.cs b/crypto/src/crypto/tls/ServerNameList.cs
new file mode 100644
index 000000000..13da79bf6
--- /dev/null
+++ b/crypto/src/crypto/tls/ServerNameList.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class ServerNameList
+ {
+ protected readonly IList mServerNameList;
+
+ /**
+ * @param serverNameList an {@link IList} of {@link ServerName}.
+ */
+ public ServerNameList(IList serverNameList)
+ {
+ if (serverNameList == null || serverNameList.Count < 1)
+ throw new ArgumentException("must not be null or empty", "serverNameList");
+
+ this.mServerNameList = serverNameList;
+ }
+
+ /**
+ * @return an {@link IList} of {@link ServerName}.
+ */
+ public virtual IList ServerNames
+ {
+ get { return mServerNameList; }
+ }
+
+ /**
+ * Encode this {@link ServerNameList} to a {@link Stream}.
+ *
+ * @param output
+ * the {@link Stream} to encode to.
+ * @throws IOException
+ */
+ public virtual void Encode(Stream output)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ foreach (ServerName entry in ServerNames)
+ {
+ entry.Encode(buf);
+ }
+
+ TlsUtilities.CheckUint16(buf.Length);
+ TlsUtilities.WriteUint16((int)buf.Length, output);
+ buf.WriteTo(output);
+ }
+
+ /**
+ * Parse a {@link ServerNameList} from a {@link Stream}.
+ *
+ * @param input
+ * the {@link Stream} to parse from.
+ * @return a {@link ServerNameList} object.
+ * @throws IOException
+ */
+ public static ServerNameList Parse(Stream input)
+ {
+ int length = TlsUtilities.ReadUint16(input);
+ if (length < 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte[] data = TlsUtilities.ReadFully(length, input);
+
+ MemoryStream buf = new MemoryStream(data, false);
+
+ IList server_name_list = Platform.CreateArrayList();
+ while (buf.Position < buf.Length)
+ {
+ ServerName entry = ServerName.Parse(buf);
+ server_name_list.Add(entry);
+ }
+
+ return new ServerNameList(server_name_list);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/SrpTlsClient.cs b/crypto/src/crypto/tls/SrpTlsClient.cs
index dfd7603b8..a7c72b862 100644
--- a/crypto/src/crypto/tls/SrpTlsClient.cs
+++ b/crypto/src/crypto/tls/SrpTlsClient.cs
@@ -7,16 +7,10 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class SrpTlsClient
- : TlsClient
+ : AbstractTlsClient
{
- protected TlsCipherFactory cipherFactory;
- protected byte[] identity;
- protected byte[] password;
-
- protected TlsClientContext context;
-
- protected byte selectedCompressionMethod;
- protected int selectedCipherSuite;
+ protected byte[] mIdentity;
+ protected byte[] mPassword;
public SrpTlsClient(byte[] identity, byte[] password)
: this(new DefaultTlsCipherFactory(), identity, password)
@@ -24,179 +18,96 @@ namespace Org.BouncyCastle.Crypto.Tls
}
public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password)
+ : base(cipherFactory)
{
- this.cipherFactory = cipherFactory;
- this.identity = Arrays.Clone(identity);
- this.password = Arrays.Clone(password);
- }
-
- public virtual void Init(TlsClientContext context)
- {
- this.context = context;
+ this.mIdentity = Arrays.Clone(identity);
+ this.mPassword = Arrays.Clone(password);
}
- public virtual bool ShouldUseGmtUnixTime()
+ public override int[] GetCipherSuites()
{
- /*
- * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
- * TLS implementors MUST by default set the entire value the ClientHello.Random and
- * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
- * sequence.
- */
- return false;
+ return new int[] { CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA };
}
- public virtual int[] GetCipherSuites()
+ public override IDictionary GetClientExtensions()
{
- return new int[] {
- CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
- CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
- };
- }
-
- public virtual IDictionary GetClientExtensions()
- {
- IDictionary clientExtensions = Platform.CreateHashtable();
-
- MemoryStream srpData = new MemoryStream();
- TlsUtilities.WriteOpaque8(this.identity, srpData);
- clientExtensions[ExtensionType.srp] = srpData.ToArray();
-
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+ TlsSrpUtilities.AddSrpExtension(clientExtensions, this.mIdentity);
return clientExtensions;
}
- public virtual byte[] GetCompressionMethods()
+ public override void ProcessServerExtensions(IDictionary serverExtensions)
{
- return new byte[] { CompressionMethod.NULL };
- }
-
- public virtual void NotifySessionID(byte[] sessionID)
- {
- // Currently ignored
- }
-
- public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
- {
- this.selectedCipherSuite = selectedCipherSuite;
- }
-
- public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
- {
- this.selectedCompressionMethod = selectedCompressionMethod;
- }
-
- public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
- {
- if (!secureRenegotiation)
+ if (!TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.srp,
+ AlertDescription.illegal_parameter))
{
- /*
- * RFC 5746 3.4. If the extension is not present, the server does not support
- * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
- * some clients may want to terminate the handshake instead of continuing; see
- * Section 4.1 for discussion.
- */
-// throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
}
- }
- public virtual void ProcessServerExtensions(IDictionary serverExtensions)
- {
- // There is no server response for the SRP extension
+ base.ProcessServerExtensions(serverExtensions);
}
- public virtual TlsKeyExchange GetKeyExchange()
+ public override TlsKeyExchange GetKeyExchange()
{
- switch (selectedCipherSuite)
+ switch (mSelectedCipherSuite)
{
- case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
- return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
-
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
- return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
-
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
- return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
- }
- }
-
- public abstract TlsAuthentication GetAuthentication();
-
- public virtual TlsCompression GetCompression()
- {
- switch (selectedCompressionMethod)
- {
- case CompressionMethod.NULL:
- return new TlsNullCompression();
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected compression method was in the list of client-offered compression
- * methods, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
+
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
+
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
- public virtual TlsCipher GetCipher()
+ public override TlsCipher GetCipher()
{
- switch (selectedCipherSuite)
+ switch (mSelectedCipherSuite)
{
- case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
- MacAlgorithm.hmac_sha1);
-
- case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
- MacAlgorithm.hmac_sha1);
-
- case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
- case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
- MacAlgorithm.hmac_sha1);
-
- default:
- /*
- * Note: internal error here; the TlsProtocolHandler verifies that the
- * server-selected cipher suite was in the list of client-offered cipher
- * suites, so if we now can't produce an implementation, we shouldn't have
- * offered it!
- */
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; the TlsProtocol implementation verifies that the
+ * server-selected cipher suite was in the list of client-offered cipher suites, so if
+ * we now can't produce an implementation, we shouldn't have offered it!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange)
{
- return new TlsSrpKeyExchange(context, keyExchange, identity, password);
+ return new TlsSrpKeyExchange(mContext, keyExchange, mIdentity, mPassword);
}
}
}
diff --git a/crypto/src/crypto/tls/SupplementalDataEntry.cs b/crypto/src/crypto/tls/SupplementalDataEntry.cs
new file mode 100644
index 000000000..5adc4fa52
--- /dev/null
+++ b/crypto/src/crypto/tls/SupplementalDataEntry.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class SupplementalDataEntry
+ {
+ protected readonly int mDataType;
+ protected readonly byte[] mData;
+
+ public SupplementalDataEntry(int dataType, byte[] data)
+ {
+ this.mDataType = dataType;
+ this.mData = data;
+ }
+
+ public virtual int DataType
+ {
+ get { return mDataType; }
+ }
+
+ public virtual byte[] Data
+ {
+ get { return mData; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsClient.cs b/crypto/src/crypto/tls/TlsClient.cs
index a4cdc647d..cd5dfad13 100644
--- a/crypto/src/crypto/tls/TlsClient.cs
+++ b/crypto/src/crypto/tls/TlsClient.cs
@@ -15,6 +15,18 @@ namespace Org.BouncyCastle.Crypto.Tls
/// </param>
void Init(TlsClientContext context);
+ /// <summary>Return the session this client wants to resume, if any.</summary>
+ /// <remarks>Note that the peer's certificate chain for the session (if any) may need to be periodically revalidated.</remarks>
+ /// <returns>
+ /// A <see cref="TlsSession"/> representing the resumable session to be used for this connection,
+ /// or null to use a new session.
+ /// </returns>
+ TlsSession GetSessionToResume();
+
+ ProtocolVersion ClientHelloRecordLayerVersion { get; }
+
+ ProtocolVersion ClientVersion { get; }
+
/// <summary>
/// Get the list of cipher suites that this client supports.
/// </summary>
@@ -40,12 +52,13 @@ namespace Org.BouncyCastle.Crypto.Tls
/// <exception cref="IOException"></exception>
IDictionary GetClientExtensions();
+ /// <exception cref="IOException"></exception>
+ void NotifyServerVersion(ProtocolVersion selectedVersion);
+
/// <summary>
- /// Reports the session ID once it has been determined.
+ /// Notifies the client of the session_id sent in the ServerHello.
/// </summary>
- /// <param name="sessionID">
- /// A <see cref="System.Byte"/>
- /// </param>
+ /// <param name="sessionID">An array of <see cref="System.Byte"/></param>
void NotifySessionID(byte[] sessionID);
/// <summary>
@@ -73,18 +86,6 @@ namespace Org.BouncyCastle.Crypto.Tls
void NotifySelectedCompressionMethod(byte selectedCompressionMethod);
/// <summary>
- /// Report whether the server supports secure renegotiation
- /// </summary>
- /// <remarks>
- /// The protocol handler automatically processes the relevant extensions
- /// </remarks>
- /// <param name="secureRenegotiation">
- /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation
- /// </param>
- /// <exception cref="IOException"></exception>
- void NotifySecureRenegotiation(bool secureRenegotiation);
-
- /// <summary>
/// Report the extensions from an extended server hello.
/// </summary>
/// <remarks>
@@ -95,6 +96,10 @@ namespace Org.BouncyCastle.Crypto.Tls
/// </param>
void ProcessServerExtensions(IDictionary serverExtensions);
+ /// <param name="serverSupplementalData">A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></param>
+ /// <exception cref="IOException"/>
+ void ProcessServerSupplementalData(IList serverSupplementalData);
+
/// <summary>
/// Return an implementation of <see cref="TlsKeyExchange"/> to negotiate the key exchange
/// part of the protocol.
@@ -112,19 +117,18 @@ namespace Org.BouncyCastle.Crypto.Tls
/// <exception cref="IOException"/>
TlsAuthentication GetAuthentication();
- /// <summary>
- /// Return an implementation of <see cref="TlsCompression"/> to handle record compression.
- /// </summary>
+ /// <returns>A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></returns>
/// <exception cref="IOException"/>
- TlsCompression GetCompression();
+ IList GetClientSupplementalData();
- /// <summary>
- /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption.
- /// </summary>
- /// <returns>
- /// A <see cref="TlsCipher"/>
- /// </returns>
+ /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message</summary>
+ /// <remarks>
+ /// This method will be called (only) when a NewSessionTicket handshake message is received. The
+ /// ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption
+ /// that it complies with e.g. <i>RFC 5077 4. Recommended Ticket Construction</i>.
+ /// </remarks>
+ /// <param name="newSessionTicket">The <see cref="NewSessionTicket">ticket</see></param>
/// <exception cref="IOException"/>
- TlsCipher GetCipher();
+ void NotifyNewSessionTicket(NewSessionTicket newSessionTicket);
}
}
diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs
index 9bbfa844e..399438879 100644
--- a/crypto/src/crypto/tls/TlsEccUtilities.cs
+++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -18,29 +18,29 @@ namespace Org.BouncyCastle.Crypto.Tls
{
public class TlsEccUtilities
{
- private static readonly string[] curveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
+ private static readonly string[] CurveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
"sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
"sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
"secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",
"brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"};
- public static void AddSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves)
+ public static void AddSupportedEllipticCurvesExtension(IDictionary extensions, int[] namedCurves)
{
extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves);
}
- public static void AddSupportedPointFormatsExtension(Hashtable extensions, byte[] ecPointFormats)
+ public static void AddSupportedPointFormatsExtension(IDictionary extensions, byte[] ecPointFormats)
{
extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats);
}
- public static int[] GetSupportedEllipticCurvesExtension(Hashtable extensions)
+ public static int[] GetSupportedEllipticCurvesExtension(IDictionary extensions)
{
byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves);
return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData);
}
- public static byte[] GetSupportedPointFormatsExtension(Hashtable extensions)
+ public static byte[] GetSupportedPointFormatsExtension(IDictionary extensions)
{
byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats);
return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData);
@@ -117,7 +117,7 @@ namespace Org.BouncyCastle.Crypto.Tls
public static string GetNameOfNamedCurve(int namedCurve)
{
- return IsSupportedNamedCurve(namedCurve) ? curveNames[namedCurve - 1] : null;
+ return IsSupportedNamedCurve(namedCurve) ? CurveNames[namedCurve - 1] : null;
}
public static ECDomainParameters GetParametersForNamedCurve(int namedCurve)
@@ -142,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Tls
public static bool HasAnySupportedNamedCurves()
{
- return curveNames.Length > 0;
+ return CurveNames.Length > 0;
}
public static bool ContainsEccCipherSuites(int[] cipherSuites)
@@ -276,7 +276,7 @@ namespace Org.BouncyCastle.Crypto.Tls
public static bool IsSupportedNamedCurve(int namedCurve)
{
- return (namedCurve > 0 && namedCurve <= curveNames.Length);
+ return (namedCurve > 0 && namedCurve <= CurveNames.Length);
}
public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat)
diff --git a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs
new file mode 100644
index 000000000..ca1d4183b
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsExtensionsUtilities
+ {
+ public static IDictionary EnsureExtensionsInitialised(IDictionary extensions)
+ {
+ return extensions == null ? Platform.CreateHashtable() : extensions;
+ }
+
+ public static void AddEncryptThenMacExtension(IDictionary extensions)
+ {
+ extensions[ExtensionType.encrypt_then_mac] = CreateEncryptThenMacExtension();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static void AddHeartbeatExtension(IDictionary extensions, HeartbeatExtension heartbeatExtension)
+ {
+ extensions[ExtensionType.heartbeat] = CreateHeartbeatExtension(heartbeatExtension);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static void AddMaxFragmentLengthExtension(IDictionary extensions, byte maxFragmentLength)
+ {
+ extensions[ExtensionType.max_fragment_length] = CreateMaxFragmentLengthExtension(maxFragmentLength);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static void AddServerNameExtension(IDictionary extensions, ServerNameList serverNameList)
+ {
+ extensions[ExtensionType.server_name] = CreateServerNameExtension(serverNameList);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static void AddStatusRequestExtension(IDictionary extensions, CertificateStatusRequest statusRequest)
+ {
+ extensions[ExtensionType.status_request] = CreateStatusRequestExtension(statusRequest);
+ }
+
+ public static void AddTruncatedHMacExtension(IDictionary extensions)
+ {
+ extensions[ExtensionType.truncated_hmac] = CreateTruncatedHMacExtension();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static HeartbeatExtension GetHeartbeatExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.heartbeat);
+ return extensionData == null ? null : ReadHeartbeatExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static short GetMaxFragmentLengthExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.max_fragment_length);
+ return extensionData == null ? (short)-1 : (short)ReadMaxFragmentLengthExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static ServerNameList GetServerNameExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_name);
+ return extensionData == null ? null : ReadServerNameExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static CertificateStatusRequest GetStatusRequestExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.status_request);
+ return extensionData == null ? null : ReadStatusRequestExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool HasEncryptThenMacExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.encrypt_then_mac);
+ return extensionData == null ? false : ReadEncryptThenMacExtension(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool HasTruncatedHMacExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.truncated_hmac);
+ return extensionData == null ? false : ReadTruncatedHMacExtension(extensionData);
+ }
+
+ public static byte[] CreateEmptyExtensionData()
+ {
+ return TlsUtilities.EmptyBytes;
+ }
+
+ public static byte[] CreateEncryptThenMacExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static byte[] CreateHeartbeatExtension(HeartbeatExtension heartbeatExtension)
+ {
+ if (heartbeatExtension == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ heartbeatExtension.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static byte[] CreateMaxFragmentLengthExtension(byte maxFragmentLength)
+ {
+ if (!MaxFragmentLength.IsValid(maxFragmentLength))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return new byte[]{ maxFragmentLength };
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static byte[] CreateServerNameExtension(ServerNameList serverNameList)
+ {
+ if (serverNameList == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ MemoryStream buf = new MemoryStream();
+
+ serverNameList.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static byte[] CreateStatusRequestExtension(CertificateStatusRequest statusRequest)
+ {
+ if (statusRequest == null)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ MemoryStream buf = new MemoryStream();
+
+ statusRequest.Encode(buf);
+
+ return buf.ToArray();
+ }
+
+ public static byte[] CreateTruncatedHMacExtension()
+ {
+ return CreateEmptyExtensionData();
+ }
+
+ /// <exception cref="IOException"></exception>
+ private static bool ReadEmptyExtensionData(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ if (extensionData.Length != 0)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return true;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool ReadEncryptThenMacExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static HeartbeatExtension ReadHeartbeatExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ HeartbeatExtension heartbeatExtension = HeartbeatExtension.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return heartbeatExtension;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static short ReadMaxFragmentLengthExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ if (extensionData.Length != 1)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte maxFragmentLength = extensionData[0];
+
+ if (!MaxFragmentLength.IsValid(maxFragmentLength))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return maxFragmentLength;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static ServerNameList ReadServerNameExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ ServerNameList serverNameList = ServerNameList.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return serverNameList;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static CertificateStatusRequest ReadStatusRequestExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ CertificateStatusRequest statusRequest = CertificateStatusRequest.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return statusRequest;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public static bool ReadTruncatedHMacExtension(byte[] extensionData)
+ {
+ return ReadEmptyExtensionData(extensionData);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsPeer.cs b/crypto/src/crypto/tls/TlsPeer.cs
index 5b5c94a44..1ae41a41a 100644
--- a/crypto/src/crypto/tls/TlsPeer.cs
+++ b/crypto/src/crypto/tls/TlsPeer.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
@@ -15,5 +16,47 @@ namespace Org.BouncyCastle.Crypto.Tls
/// random value.
/// </returns>
bool ShouldUseGmtUnixTime();
+
+ /// <summary>
+ /// Report whether the server supports secure renegotiation
+ /// </summary>
+ /// <remarks>
+ /// The protocol handler automatically processes the relevant extensions
+ /// </remarks>
+ /// <param name="secureRenegotiation">
+ /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation
+ /// </param>
+ /// <exception cref="IOException"></exception>
+ void NotifySecureRenegotiation(bool secureRenegotiation);
+
+ /// <summary>
+ /// Return an implementation of <see cref="TlsCompression"/> to handle record compression.
+ /// </summary>
+ /// <returns>A <see cref="TlsCompression"/></returns>
+ /// <exception cref="IOException"/>
+ TlsCompression GetCompression();
+
+ /// <summary>
+ /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption.
+ /// </summary>
+ /// <returns>A <see cref="TlsCipher"/></returns>
+ /// <exception cref="IOException"/>
+ TlsCipher GetCipher();
+
+ /// <summary>This method will be called when an alert is raised by the protocol.</summary>
+ /// <param name="alertLevel"><see cref="AlertLevel"/></param>
+ /// <param name="alertDescription"><see cref="AlertDescription"/></param>
+ /// <param name="message">A human-readable message explaining what caused this alert. May be null.</param>
+ /// <param name="cause">The <c>Exception</c> that caused this alert to be raised. May be null.</param>
+ void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause);
+
+ /// <summary>This method will be called when an alert is received from the remote peer.</summary>
+ /// <param name="alertLevel"><see cref="AlertLevel"/></param>
+ /// <param name="alertDescription"><see cref="AlertDescription"/></param>
+ void NotifyAlertReceived(byte alertLevel, byte alertDescription);
+
+ /// <summary>Notifies the peer that the handshake has been successfully completed.</summary>
+ /// <exception cref="IOException"></exception>
+ void NotifyHandshakeComplete();
}
}
diff --git a/crypto/src/crypto/tls/TlsServer.cs b/crypto/src/crypto/tls/TlsServer.cs
new file mode 100644
index 000000000..93e62b9ac
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsServer.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsServer
+ : TlsPeer
+ {
+ void Init(TlsServerContext context);
+
+ /// <exception cref="IOException"></exception>
+ void NotifyClientVersion(ProtocolVersion clientVersion);
+
+ /// <exception cref="IOException"></exception>
+ void NotifyOfferedCipherSuites(int[] offeredCipherSuites);
+
+ /// <exception cref="IOException"></exception>
+ void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods);
+
+ /// <param name="clientExtensions">A <see cref="IDictionary"/> (Int32 -> byte[]). Will never be null.</param>
+ /// <exception cref="IOException"></exception>
+ void ProcessClientExtensions(IDictionary clientExtensions);
+
+ /// <exception cref="IOException"></exception>
+ ProtocolVersion GetServerVersion();
+
+ /// <exception cref="IOException"></exception>
+ int GetSelectedCipherSuite();
+
+ /// <exception cref="IOException"></exception>
+ byte GetSelectedCompressionMethod();
+
+ /// <summary>
+ /// Get the (optional) table of server extensions to be included in (extended) server hello.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="IDictionary"/> (Int32 -> byte[]). May be null.
+ /// </returns>
+ /// <exception cref="IOException"></exception>
+ IDictionary GetServerExtensions();
+
+ /// <returns>
+ /// A <see cref="IList"/> (<see cref="SupplementalDataEntry"/>). May be null.
+ /// </returns>
+ /// <exception cref="IOException"></exception>
+ IList GetServerSupplementalData();
+
+ /// <exception cref="IOException"></exception>
+ TlsCredentials GetCredentials();
+
+ /// <remarks>
+ /// This method will be called (only) if the server included an extension of type
+ /// "status_request" with empty "extension_data" in the extended server hello. See <i>RFC 3546
+ /// 3.6. Certificate Status Request</i>. If a non-null <see cref="CertificateStatus"/> is returned, it
+ /// is sent to the client as a handshake message of type "certificate_status".
+ /// </remarks>
+ /// <returns>A <see cref="CertificateStatus"/> to be sent to the client (or null for none).</returns>
+ /// <exception cref="IOException"></exception>
+ CertificateStatus GetCertificateStatus();
+
+ /// <exception cref="IOException"></exception>
+ TlsKeyExchange GetKeyExchange();
+
+ /// <exception cref="IOException"></exception>
+ CertificateRequest GetCertificateRequest();
+
+ /// <param name="clientSupplementalData"><see cref="IList"/> (<see cref="SupplementalDataEntry"/>)</param>
+ /// <exception cref="IOException"></exception>
+ void ProcessClientSupplementalData(IList clientSupplementalData);
+
+ /// <summary>
+ /// Called by the protocol handler to report the client certificate, only if <c>GetCertificateRequest</c>
+ /// returned non-null.
+ /// </summary>
+ /// <remarks>Note: this method is responsible for certificate verification and validation.</remarks>
+ /// <param name="clientCertificate">the effective client certificate (may be an empty chain).</param>
+ /// <exception cref="IOException"></exception>
+ void NotifyClientCertificate(Certificate clientCertificate);
+
+ /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message.</summary>
+ /// <remarks>
+ /// This method will be called (only) if a NewSessionTicket extension was sent by the server. See
+ /// <i>RFC 5077 4. Recommended Ticket Construction</i> for recommended format and protection.
+ /// </remarks>
+ /// <returns>The <see cref="NewSessionTicket">ticket</see>)</returns>
+ /// <exception cref="IOException"></exception>
+ NewSessionTicket GetNewSessionTicket();
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsSessionImpl.cs b/crypto/src/crypto/tls/TlsSessionImpl.cs
new file mode 100644
index 000000000..866392623
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSessionImpl.cs
@@ -0,0 +1,54 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ internal class TlsSessionImpl
+ : TlsSession
+ {
+ internal readonly byte[] mSessionID;
+ internal SessionParameters mSessionParameters;
+
+ internal TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters)
+ {
+ if (sessionID == null)
+ throw new ArgumentNullException("sessionID");
+ if (sessionID.Length < 1 || sessionID.Length > 32)
+ throw new ArgumentException("must have length between 1 and 32 bytes, inclusive", "sessionID");
+
+ this.mSessionID = Arrays.Clone(sessionID);
+ this.mSessionParameters = sessionParameters;
+ }
+
+ public virtual SessionParameters ExportSessionParameters()
+ {
+ lock (this)
+ {
+ return this.mSessionParameters == null ? null : this.mSessionParameters.Copy();
+ }
+ }
+
+ public virtual byte[] SessionID
+ {
+ get { lock (this) return mSessionID; }
+ }
+
+ public virtual void Invalidate()
+ {
+ lock (this)
+ {
+ if (this.mSessionParameters != null)
+ {
+ this.mSessionParameters.Clear();
+ this.mSessionParameters = null;
+ }
+ }
+ }
+
+ public virtual bool IsResumable
+ {
+ get { lock (this) return this.mSessionParameters != null; }
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsSrpUtilities.cs b/crypto/src/crypto/tls/TlsSrpUtilities.cs
new file mode 100644
index 000000000..ada08ef9f
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSrpUtilities.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsSrpUtilities
+ {
+ public static void AddSrpExtension(IDictionary extensions, byte[] identity)
+ {
+ extensions[ExtensionType.srp] = CreateSrpExtension(identity);
+ }
+
+ public static byte[] GetSrpExtension(IDictionary extensions)
+ {
+ byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.srp);
+ return extensionData == null ? null : ReadSrpExtension(extensionData);
+ }
+
+ public static byte[] CreateSrpExtension(byte[] identity)
+ {
+ if (identity == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ return TlsUtilities.EncodeOpaque8(identity);
+ }
+
+ public static byte[] ReadSrpExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+ byte[] identity = TlsUtilities.ReadOpaque8(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return identity;
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 33d10dcd0..3fc6c7df1 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -634,6 +634,80 @@ namespace Org.BouncyCastle.Crypto.Tls
return true;
}
+ public static TlsSession ImportSession(byte[] sessionID, SessionParameters sessionParameters)
+ {
+ return new TlsSessionImpl(sessionID, sessionParameters);
+ }
+
+ public static bool IsSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion)
+ {
+ return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(clientVersion.GetEquivalentTLSVersion());
+ }
+
+ /**
+ * Add a 'signature_algorithms' extension to existing extensions.
+ *
+ * @param extensions A {@link Hashtable} to add the extension to.
+ * @param supportedSignatureAlgorithms {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @throws IOException
+ */
+ public static void AddSignatureAlgorithmsExtension(IDictionary extensions, IList supportedSignatureAlgorithms)
+ {
+ extensions[ExtensionType.signature_algorithms] = CreateSignatureAlgorithmsExtension(supportedSignatureAlgorithms);
+ }
+
+ /**
+ * Get a 'signature_algorithms' extension from extensions.
+ *
+ * @param extensions A {@link Hashtable} to get the extension from, if it is present.
+ * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}, or null.
+ * @throws IOException
+ */
+ public static IList GetSignatureAlgorithmsExtension(IDictionary extensions)
+ {
+ byte[] extensionData = GetExtensionData(extensions, ExtensionType.signature_algorithms);
+ return extensionData == null ? null : ReadSignatureAlgorithmsExtension(extensionData);
+ }
+
+ /**
+ * Create a 'signature_algorithms' extension value.
+ *
+ * @param supportedSignatureAlgorithms A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @return A byte array suitable for use as an extension value.
+ * @throws IOException
+ */
+ public static byte[] CreateSignatureAlgorithmsExtension(IList supportedSignatureAlgorithms)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ // supported_signature_algorithms
+ EncodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf);
+
+ return buf.ToArray();
+ }
+
+ /**
+ * Read 'signature_algorithms' extension data.
+ *
+ * @param extensionData The extension data.
+ * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+ * @throws IOException
+ */
+ public static IList ReadSignatureAlgorithmsExtension(byte[] extensionData)
+ {
+ if (extensionData == null)
+ throw new ArgumentNullException("extensionData");
+
+ MemoryStream buf = new MemoryStream(extensionData, false);
+
+ // supported_signature_algorithms
+ IList supported_signature_algorithms = ParseSupportedSignatureAlgorithms(false, buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ return supported_signature_algorithms;
+ }
+
public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous,
Stream output)
{
@@ -645,8 +719,8 @@ namespace Org.BouncyCastle.Crypto.Tls
// supported_signature_algorithms
int length = 2 * supportedSignatureAlgorithms.Count;
- TlsUtilities.CheckUint16(length);
- TlsUtilities.WriteUint16(length, output);
+ CheckUint16(length);
+ WriteUint16(length, output);
foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms)
{
@@ -663,6 +737,30 @@ namespace Org.BouncyCastle.Crypto.Tls
}
}
+ public static IList ParseSupportedSignatureAlgorithms(bool allowAnonymous, Stream input)
+ {
+ // supported_signature_algorithms
+ int length = ReadUint16(input);
+ if (length < 2 || (length & 1) != 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+ int count = length / 2;
+ IList supportedSignatureAlgorithms = Platform.CreateArrayList(count);
+ for (int i = 0; i < count; ++i)
+ {
+ SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.Parse(input);
+ if (!allowAnonymous && entry.Signature == SignatureAlgorithm.anonymous)
+ {
+ /*
+ * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used
+ * in Section 7.4.3. It MUST NOT appear in this extension.
+ */
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ supportedSignatureAlgorithms.Add(entry);
+ }
+ return supportedSignatureAlgorithms;
+ }
+
public static byte[] PRF(TlsContext context, byte[] secret, string asciiLabel, byte[] seed, int size)
{
ProtocolVersion version = context.ServerVersion;
|