diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-08-22 16:42:01 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-08-22 16:42:01 +0700 |
commit | 15ff1e2c34e44e9543b875713a36b83a96c0417c (patch) | |
tree | 05855e9c035f17e6577bb842d897b9f3c633872e | |
parent | Add support for a 'cause' Exception to TlsFatalAlert and use (diff) | |
download | BouncyCastle.NET-ed25519-15ff1e2c34e44e9543b875713a36b83a96c0417c.tar.xz |
More TLS porting from Java API
20 files changed, 1882 insertions, 558 deletions
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; |