package org.bouncycastle.crypto.tls; import java.io.IOException; import java.util.Hashtable; import java.util.Vector; public abstract class AbstractTlsClient extends AbstractTlsPeer implements TlsClient { protected TlsCipherFactory cipherFactory; protected TlsClientContext context; protected Vector supportedSignatureAlgorithms; protected int[] namedCurves; protected short[] clientECPointFormats, serverECPointFormats; protected int selectedCipherSuite; protected short selectedCompressionMethod; public AbstractTlsClient() { this(new DefaultTlsCipherFactory()); } public AbstractTlsClient(TlsCipherFactory cipherFactory) { this.cipherFactory = cipherFactory; } protected boolean allowUnexpectedServerExtension(Integer extensionType, byte[] extensionData) throws IOException { switch (extensionType.intValue()) { case ExtensionType.elliptic_curves: /* * 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. */ TlsECCUtils.readSupportedEllipticCurvesExtension(extensionData); return true; default: return false; } } protected void checkForUnexpectedServerExtension(Hashtable serverExtensions, Integer extensionType) throws IOException { byte[] extensionData = TlsUtils.getExtensionData(serverExtensions, extensionType); if (extensionData != null && !allowUnexpectedServerExtension(extensionType, extensionData)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } public void init(TlsClientContext context) { this.context = context; } public TlsSession getSessionToResume() { return null; } public ProtocolVersion getClientHelloRecordLayerVersion() { // "{03,00}" // return ProtocolVersion.SSLv3; // "the lowest version number supported by the client" // return getMinimumVersion(); // "the value of ClientHello.client_version" return getClientVersion(); } public ProtocolVersion getClientVersion() { return ProtocolVersion.TLSv12; } public boolean 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 Hashtable getClientExtensions() throws IOException { Hashtable clientExtensions = null; ProtocolVersion clientVersion = context.getClientVersion(); /* * 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 (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion)) { // TODO Provide a way for the user to specify the acceptable hash/signature algorithms. this.supportedSignatureAlgorithms = TlsUtils.getDefaultSupportedSignatureAlgorithms(); clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions); TlsUtils.addSignatureAlgorithmsExtension(clientExtensions, supportedSignatureAlgorithms); } if (TlsECCUtils.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.namedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 }; this.clientECPointFormats = new short[]{ ECPointFormat.uncompressed, ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, }; clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions); TlsECCUtils.addSupportedEllipticCurvesExtension(clientExtensions, namedCurves); TlsECCUtils.addSupportedPointFormatsExtension(clientExtensions, clientECPointFormats); } return clientExtensions; } public ProtocolVersion getMinimumVersion() { return ProtocolVersion.TLSv10; } public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException { if (!getMinimumVersion().isEqualOrEarlierVersionOf(serverVersion)) { throw new TlsFatalAlert(AlertDescription.protocol_version); } } public short[] getCompressionMethods() { return new short[]{CompressionMethod._null}; } public void notifySessionID(byte[] sessionID) { // Currently ignored } public void notifySelectedCipherSuite(int selectedCipherSuite) { this.selectedCipherSuite = selectedCipherSuite; } public void notifySelectedCompressionMethod(short selectedCompressionMethod) { this.selectedCompressionMethod = selectedCompressionMethod; } public void processServerExtensions(Hashtable serverExtensions) throws IOException { /* * 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. */ checkForUnexpectedServerExtension(serverExtensions, TlsUtils.EXT_signature_algorithms); checkForUnexpectedServerExtension(serverExtensions, TlsECCUtils.EXT_elliptic_curves); if (TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite)) { this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions); } else { checkForUnexpectedServerExtension(serverExtensions, TlsECCUtils.EXT_ec_point_formats); } /* * RFC 7685 3. The server MUST NOT echo the extension. */ checkForUnexpectedServerExtension(serverExtensions, TlsExtensionsUtils.EXT_padding); } } public void processServerSupplementalData(Vector serverSupplementalData) throws IOException { if (serverSupplementalData != null) { throw new TlsFatalAlert(AlertDescription.unexpected_message); } } public Vector getClientSupplementalData() throws IOException { return null; } public TlsCompression getCompression() throws IOException { switch (selectedCompressionMethod) { case CompressionMethod._null: return new TlsNullCompression(); 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 TlsCipher getCipher() throws IOException { int encryptionAlgorithm = TlsUtils.getEncryptionAlgorithm(selectedCipherSuite); int macAlgorithm = TlsUtils.getMACAlgorithm(selectedCipherSuite); return cipherFactory.createCipher(context, encryptionAlgorithm, macAlgorithm); } public void notifyNewSessionTicket(NewSessionTicket newSessionTicket) throws IOException { } }