using System; using System.Collections.Generic; using System.IO; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Tls.Crypto; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Tls { /// Base class for a TLS client. public abstract class AbstractTlsClient : AbstractTlsPeer, TlsClient { protected TlsClientContext m_context; protected ProtocolVersion[] m_protocolVersions; protected int[] m_cipherSuites; protected IList m_supportedGroups; protected IList m_supportedSignatureAlgorithms; protected IList m_supportedSignatureAlgorithmsCert; protected AbstractTlsClient(TlsCrypto crypto) : base(crypto) { } /// protected virtual bool AllowUnexpectedServerExtension(int extensionType, byte[] extensionData) { switch (extensionType) { case ExtensionType.supported_groups: /* * Exception added based on field reports that some servers do send this, although the * Supported Elliptic Curves Extension is clearly intended to be client-only. If * present, we still require that it is a valid EllipticCurveList. */ TlsExtensionsUtilities.ReadSupportedGroupsExtension(extensionData); return true; case ExtensionType.ec_point_formats: /* * Exception added based on field reports that some servers send this even when they * didn't negotiate an ECC cipher suite. If present, we still require that it is a valid * ECPointFormatList. */ TlsExtensionsUtilities.ReadSupportedPointFormatsExtension(extensionData); return true; default: return false; } } protected virtual IList GetNamedGroupRoles() { var namedGroupRoles = TlsUtilities.GetNamedGroupRoles(GetCipherSuites()); var sigAlgs = m_supportedSignatureAlgorithms; var sigAlgsCert = m_supportedSignatureAlgorithmsCert; if ((null == sigAlgs || TlsUtilities.ContainsAnySignatureAlgorithm(sigAlgs, SignatureAlgorithm.ecdsa)) || (null != sigAlgsCert && TlsUtilities.ContainsAnySignatureAlgorithm(sigAlgsCert, SignatureAlgorithm.ecdsa))) { TlsUtilities.AddToSet(namedGroupRoles, NamedGroupRole.ecdsa); } return namedGroupRoles; } /// protected virtual void CheckForUnexpectedServerExtension(IDictionary serverExtensions, int extensionType) { byte[] extensionData = TlsUtilities.GetExtensionData(serverExtensions, extensionType); if (extensionData != null && !AllowUnexpectedServerExtension(extensionType, extensionData)) throw new TlsFatalAlert(AlertDescription.illegal_parameter); } /// RFC 9146 DTLS connection ID. /// /// The default implementation calls this to get the connection_id extension /// the client will send. As future communication doesn't include the connection IDs length, this should either /// be fixed-length or include the connection ID's length. (see explanation in RFC 9146 4. "cid:") /// /// The connection ID to use. protected virtual byte[] GetNewConnectionID() => null; /// public virtual TlsPskIdentity GetPskIdentity() { return null; } /// public virtual TlsSrpIdentity GetSrpIdentity() { return null; } public virtual TlsDHGroupVerifier GetDHGroupVerifier() { return new DefaultTlsDHGroupVerifier(); } public virtual TlsSrpConfigVerifier GetSrpConfigVerifier() { return new DefaultTlsSrpConfigVerifier(); } protected virtual IList GetCertificateAuthorities() { return null; } protected virtual IList GetProtocolNames() { return null; } protected virtual CertificateStatusRequest GetCertificateStatusRequest() { return new CertificateStatusRequest(CertificateStatusType.ocsp, new OcspStatusRequest(null, null)); } /// an of (or null). protected virtual IList GetMultiCertStatusRequest() { return null; } protected virtual IList GetSniServerNames() { return null; } /// The default implementation calls this to determine which named /// groups to include in the supported_groups extension for the ClientHello. /// The named group roles for which there should /// be at least one supported group. By default this is inferred from the offered cipher suites and signature /// algorithms. /// an of . See for group constants. /// protected virtual IList GetSupportedGroups(IList namedGroupRoles) { TlsCrypto crypto = Crypto; var supportedGroups = new List(); if (namedGroupRoles.Contains(NamedGroupRole.ecdh)) { TlsUtilities.AddIfSupported(supportedGroups, crypto, new int[]{ NamedGroup.x25519, NamedGroup.x448 }); } if (namedGroupRoles.Contains(NamedGroupRole.ecdh) || namedGroupRoles.Contains(NamedGroupRole.ecdsa)) { TlsUtilities.AddIfSupported(supportedGroups, crypto, new int[]{ NamedGroup.secp256r1, NamedGroup.secp384r1 }); } if (namedGroupRoles.Contains(NamedGroupRole.dh)) { TlsUtilities.AddIfSupported(supportedGroups, crypto, new int[]{ NamedGroup.ffdhe2048, NamedGroup.ffdhe3072, NamedGroup.ffdhe4096 }); } return supportedGroups; } protected virtual IList GetSupportedSignatureAlgorithms() { return TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context); } protected virtual IList GetSupportedSignatureAlgorithmsCert() { return null; } protected virtual IList GetTrustedCAIndication() { return null; } protected virtual short[] GetAllowedClientCertificateTypes() { return null; } protected virtual short[] GetAllowedServerCertificateTypes() { return null; } public virtual void Init(TlsClientContext context) { this.m_context = context; this.m_protocolVersions = GetSupportedVersions(); this.m_cipherSuites = GetSupportedCipherSuites(); } public override ProtocolVersion[] GetProtocolVersions() { return m_protocolVersions; } public override int[] GetCipherSuites() { return m_cipherSuites; } /// public override void NotifyHandshakeBeginning() { base.NotifyHandshakeBeginning(); this.m_supportedGroups = null; this.m_supportedSignatureAlgorithms = null; this.m_supportedSignatureAlgorithmsCert = null; } public virtual TlsSession GetSessionToResume() { return null; } public virtual IList GetExternalPsks() { return null; } public virtual bool IsFallback() { /* * RFC 7507 4. The TLS_FALLBACK_SCSV cipher suite value is meant for use by clients that * repeat a connection attempt with a downgraded protocol (perform a "fallback retry") in * order to work around interoperability problems with legacy servers. */ return false; } /// public virtual IDictionary GetClientExtensions() { var clientExtensions = new Dictionary(); bool offeringTlsV13Plus = false; bool offeringPreTlsV13 = false; bool offeringDtlsV12 = false; { ProtocolVersion[] supportedVersions = GetProtocolVersions(); for (int i = 0; i < supportedVersions.Length; ++i) { var supportedVersion = supportedVersions[i]; if (TlsUtilities.IsTlsV13(supportedVersion)) { offeringTlsV13Plus = true; } else { offeringPreTlsV13 = true; } offeringDtlsV12 |= ProtocolVersion.DTLSv12.Equals(supportedVersion); } } var protocolNames = GetProtocolNames(); if (protocolNames != null) { TlsExtensionsUtilities.AddAlpnExtensionClient(clientExtensions, protocolNames); } var sniServerNames = GetSniServerNames(); if (sniServerNames != null) { TlsExtensionsUtilities.AddServerNameExtensionClient(clientExtensions, sniServerNames); } CertificateStatusRequest statusRequest = GetCertificateStatusRequest(); if (statusRequest != null) { TlsExtensionsUtilities.AddStatusRequestExtension(clientExtensions, statusRequest); } if (offeringTlsV13Plus) { var certificateAuthorities = GetCertificateAuthorities(); if (certificateAuthorities != null) { TlsExtensionsUtilities.AddCertificateAuthoritiesExtension(clientExtensions, certificateAuthorities); } } if (offeringPreTlsV13) { // TODO Shouldn't add if no offered cipher suite uses a block cipher? TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions); var statusRequestV2 = GetMultiCertStatusRequest(); if (statusRequestV2 != null) { TlsExtensionsUtilities.AddStatusRequestV2Extension(clientExtensions, statusRequestV2); } var trustedCAKeys = GetTrustedCAIndication(); if (trustedCAKeys != null) { TlsExtensionsUtilities.AddTrustedCAKeysExtensionClient(clientExtensions, trustedCAKeys); } } ProtocolVersion clientVersion = m_context.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)) { var supportedSigAlgs = GetSupportedSignatureAlgorithms(); if (null != supportedSigAlgs && supportedSigAlgs.Count > 0) { this.m_supportedSignatureAlgorithms = supportedSigAlgs; TlsExtensionsUtilities.AddSignatureAlgorithmsExtension(clientExtensions, supportedSigAlgs); } var supportedSigAlgsCert = GetSupportedSignatureAlgorithmsCert(); if (null != supportedSigAlgsCert && supportedSigAlgsCert.Count > 0) { this.m_supportedSignatureAlgorithmsCert = supportedSigAlgsCert; TlsExtensionsUtilities.AddSignatureAlgorithmsCertExtension(clientExtensions, supportedSigAlgsCert); } } var namedGroupRoles = GetNamedGroupRoles(); var supportedGroups = GetSupportedGroups(namedGroupRoles); if (supportedGroups != null && supportedGroups.Count > 0) { this.m_supportedGroups = supportedGroups; TlsExtensionsUtilities.AddSupportedGroupsExtension(clientExtensions, supportedGroups); } if (offeringPreTlsV13) { if (namedGroupRoles.Contains(NamedGroupRole.ecdh) || namedGroupRoles.Contains(NamedGroupRole.ecdsa)) { TlsExtensionsUtilities.AddSupportedPointFormatsExtension(clientExtensions, new short[]{ ECPointFormat.uncompressed }); } } /* * RFC 7250 4.1: * * If the client has no remaining certificate types to send in * the client hello, other than the default X.509 type, it MUST omit the * client_certificate_type extension in the client hello. */ short[] clientCertTypes = GetAllowedClientCertificateTypes(); if (clientCertTypes != null && (clientCertTypes.Length > 1 || clientCertTypes[0] != CertificateType.X509)) { TlsExtensionsUtilities.AddClientCertificateTypeExtensionClient(clientExtensions, clientCertTypes); } /* * RFC 7250 4.1: * * If the client has no remaining certificate types to send in * the client hello, other than the default X.509 certificate type, it * MUST omit the entire server_certificate_type extension from the * client hello. */ short[] serverCertTypes = GetAllowedServerCertificateTypes(); if (serverCertTypes != null && (serverCertTypes.Length > 1 || serverCertTypes[0] != CertificateType.X509)) { TlsExtensionsUtilities.AddServerCertificateTypeExtensionClient(clientExtensions, serverCertTypes); } if (offeringDtlsV12) { /* * RFC 9146 3. When a DTLS session is resumed or renegotiated, the "connection_id" extension is * negotiated afresh. */ var clientConnectionID = GetNewConnectionID(); if (clientConnectionID != null) { TlsExtensionsUtilities.AddConnectionIDExtension(clientExtensions, clientConnectionID); } } return clientExtensions; } public virtual IList GetEarlyKeyShareGroups() { /* * RFC 8446 4.2.8. Each KeyShareEntry value MUST correspond to a group offered in the * "supported_groups" extension and MUST appear in the same order. However, the values MAY * be a non-contiguous subset of the "supported_groups" extension and MAY omit the most * preferred groups. */ if (null == m_supportedGroups || m_supportedGroups.Count < 1) return null; if (m_supportedGroups.Contains(NamedGroup.x25519)) return TlsUtilities.VectorOfOne(NamedGroup.x25519); if (m_supportedGroups.Contains(NamedGroup.secp256r1)) return TlsUtilities.VectorOfOne(NamedGroup.secp256r1); return TlsUtilities.VectorOfOne(m_supportedGroups[0]); } public virtual bool ShouldUseCompatibilityMode() { return true; } /// public virtual void NotifyServerVersion(ProtocolVersion serverVersion) { } public virtual void NotifySessionToResume(TlsSession session) { } public virtual void NotifySessionID(byte[] sessionID) { } public virtual void NotifySelectedCipherSuite(int selectedCipherSuite) { } /// public virtual void NotifySelectedPsk(TlsPsk selectedPsk) { } /// public virtual void ProcessServerExtensions(IDictionary serverExtensions) { if (null == serverExtensions) return; SecurityParameters securityParameters = m_context.SecurityParameters; bool isTlsV13 = TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion); if (isTlsV13) { /* * NOTE: From TLS 1.3 the protocol classes are strict about what extensions can appear. */ } else { /* * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension. */ CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.signature_algorithms); CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.signature_algorithms_cert); CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.supported_groups); int selectedCipherSuite = securityParameters.CipherSuite; if (TlsEccUtilities.IsEccCipherSuite(selectedCipherSuite)) { // We only support uncompressed format, this is just to validate the extension, if present. TlsExtensionsUtilities.GetSupportedPointFormatsExtension(serverExtensions); } else { CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.ec_point_formats); } /* * RFC 7685 3. The server MUST NOT echo the extension. */ CheckForUnexpectedServerExtension(serverExtensions, ExtensionType.padding); } } /// public virtual void ProcessServerSupplementalData(IList serverSupplementalData) { if (serverSupplementalData != null) throw new TlsFatalAlert(AlertDescription.unexpected_message); } public abstract TlsAuthentication GetAuthentication(); /// public virtual IList GetClientSupplementalData() { return null; } /// public virtual void NotifyNewSessionTicket(NewSessionTicket newSessionTicket) { } } }