123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911 |
- #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
- using System;
- using System.Collections;
- using System.IO;
- using Org.BouncyCastle.Security;
- using Org.BouncyCastle.Utilities;
- namespace Org.BouncyCastle.Crypto.Tls
- {
- public class TlsClientProtocol
- : TlsProtocol
- {
- protected TlsClient mTlsClient = null;
- internal TlsClientContextImpl mTlsClientContext = null;
- protected byte[] mSelectedSessionID = null;
- protected TlsKeyExchange mKeyExchange = null;
- protected TlsAuthentication mAuthentication = null;
- protected CertificateStatus mCertificateStatus = null;
- protected CertificateRequest mCertificateRequest = null;
- /**
- * Constructor for blocking mode.
- * @param stream The bi-directional stream of data to/from the server
- * @param secureRandom Random number generator for various cryptographic functions
- */
- public TlsClientProtocol(Stream stream, SecureRandom secureRandom)
- : base(stream, secureRandom)
- {
- }
- /**
- * Constructor for blocking mode.
- * @param input The stream of data from the server
- * @param output The stream of data to the server
- * @param secureRandom Random number generator for various cryptographic functions
- */
- public TlsClientProtocol(Stream input, Stream output, SecureRandom secureRandom)
- : base(input, output, secureRandom)
- {
- }
- /**
- * Constructor for non-blocking mode.<br/>
- * <br/>
- * When data is received, use {@link #offerInput(java.nio.ByteBuffer)} to
- * provide the received ciphertext, then use
- * {@link #readInput(byte[], int, int)} to read the corresponding cleartext.<br/>
- * <br/>
- * Similarly, when data needs to be sent, use
- * {@link #offerOutput(byte[], int, int)} to provide the cleartext, then use
- * {@link #readOutput(byte[], int, int)} to get the corresponding
- * ciphertext.
- *
- * @param secureRandom
- * Random number generator for various cryptographic functions
- */
- public TlsClientProtocol(SecureRandom secureRandom)
- : base(secureRandom)
- {
- }
- /**
- * Initiates a TLS handshake in the role of client.<br/>
- * <br/>
- * In blocking mode, this will not return until the handshake is complete.
- * In non-blocking mode, use {@link TlsPeer#NotifyHandshakeComplete()} to
- * receive a Callback when the handshake is complete.
- *
- * @param tlsClient The {@link TlsClient} to use for the handshake.
- * @throws IOException If in blocking mode and handshake was not successful.
- */
- public virtual void Connect(TlsClient tlsClient)
- {
- if (tlsClient == null)
- throw new ArgumentNullException("tlsClient");
- if (this.mTlsClient != null)
- throw new InvalidOperationException("'Connect' can only be called once");
- this.mTlsClient = tlsClient;
- this.mSecurityParameters = new SecurityParameters();
- this.mSecurityParameters.entity = ConnectionEnd.client;
- this.mTlsClientContext = new TlsClientContextImpl(mSecureRandom, mSecurityParameters);
- this.mSecurityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(),
- mTlsClientContext.NonceRandomGenerator);
- this.mTlsClient.Init(mTlsClientContext);
- this.mRecordStream.Init(mTlsClientContext);
- TlsSession sessionToResume = tlsClient.GetSessionToResume();
- if (sessionToResume != null && sessionToResume.IsResumable)
- {
- SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
- if (sessionParameters != null)
- {
- this.mTlsSession = sessionToResume;
- this.mSessionParameters = sessionParameters;
- }
- }
- SendClientHelloMessage();
- this.mConnectionState = CS_CLIENT_HELLO;
- BlockForHandshake();
- }
- protected override void CleanupHandshake()
- {
- base.CleanupHandshake();
- this.mSelectedSessionID = null;
- this.mKeyExchange = null;
- this.mAuthentication = null;
- this.mCertificateStatus = null;
- this.mCertificateRequest = null;
- }
- protected override TlsContext Context
- {
- get { return mTlsClientContext; }
- }
- internal override AbstractTlsContext ContextAdmin
- {
- get { return mTlsClientContext; }
- }
- protected override TlsPeer Peer
- {
- get { return mTlsClient; }
- }
- protected override void HandleHandshakeMessage(byte type, byte[] data)
- {
- MemoryStream buf = new MemoryStream(data, false);
- if (this.mResumedSession)
- {
- if (type != HandshakeType.finished || this.mConnectionState != CS_SERVER_HELLO)
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- ProcessFinishedMessage(buf);
- this.mConnectionState = CS_SERVER_FINISHED;
- SendFinishedMessage();
- this.mConnectionState = CS_CLIENT_FINISHED;
- this.mConnectionState = CS_END;
- CompleteHandshake();
- return;
- }
- switch (type)
- {
- case HandshakeType.certificate:
- {
- switch (this.mConnectionState)
- {
- case CS_SERVER_HELLO:
- case CS_SERVER_SUPPLEMENTAL_DATA:
- {
- if (this.mConnectionState == CS_SERVER_HELLO)
- {
- HandleSupplementalData(null);
- }
- // Parse the Certificate message and Send to cipher suite
- this.mPeerCertificate = Certificate.Parse(buf);
- AssertEmpty(buf);
- // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
- if (this.mPeerCertificate == null || this.mPeerCertificate.IsEmpty)
- {
- this.mAllowCertificateStatus = false;
- }
- this.mKeyExchange.ProcessServerCertificate(this.mPeerCertificate);
- this.mAuthentication = mTlsClient.GetAuthentication();
- this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate);
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- this.mConnectionState = CS_SERVER_CERTIFICATE;
- break;
- }
- case HandshakeType.certificate_status:
- {
- switch (this.mConnectionState)
- {
- case CS_SERVER_CERTIFICATE:
- {
- if (!this.mAllowCertificateStatus)
- {
- /*
- * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the
- * server MUST have included an extension of type "status_request" with empty
- * "extension_data" in the extended server hello..
- */
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- this.mCertificateStatus = CertificateStatus.Parse(buf);
- AssertEmpty(buf);
- // TODO[RFC 3546] Figure out how to provide this to the client/authentication.
- this.mConnectionState = CS_CERTIFICATE_STATUS;
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- break;
- }
- case HandshakeType.finished:
- {
- switch (this.mConnectionState)
- {
- case CS_CLIENT_FINISHED:
- case CS_SERVER_SESSION_TICKET:
- {
- if (this.mConnectionState == CS_CLIENT_FINISHED && this.mExpectSessionTicket)
- {
- /*
- * RFC 5077 3.3. This message MUST be sent if the server included a
- * SessionTicket extension in the ServerHello.
- */
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- ProcessFinishedMessage(buf);
- this.mConnectionState = CS_SERVER_FINISHED;
- this.mConnectionState = CS_END;
- CompleteHandshake();
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- break;
- }
- case HandshakeType.server_hello:
- {
- switch (this.mConnectionState)
- {
- case CS_CLIENT_HELLO:
- {
- ReceiveServerHelloMessage(buf);
- this.mConnectionState = CS_SERVER_HELLO;
- this.mRecordStream.NotifyHelloComplete();
- ApplyMaxFragmentLengthExtension();
- if (this.mResumedSession)
- {
- this.mSecurityParameters.masterSecret = Arrays.Clone(this.mSessionParameters.MasterSecret);
- this.mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
- SendChangeCipherSpecMessage();
- }
- else
- {
- InvalidateSession();
- if (this.mSelectedSessionID.Length > 0)
- {
- this.mTlsSession = new TlsSessionImpl(this.mSelectedSessionID, null);
- }
- }
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- break;
- }
- case HandshakeType.supplemental_data:
- {
- switch (this.mConnectionState)
- {
- case CS_SERVER_HELLO:
- {
- HandleSupplementalData(ReadSupplementalDataMessage(buf));
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- break;
- }
- case HandshakeType.server_hello_done:
- {
- switch (this.mConnectionState)
- {
- case CS_SERVER_HELLO:
- case CS_SERVER_SUPPLEMENTAL_DATA:
- case CS_SERVER_CERTIFICATE:
- case CS_CERTIFICATE_STATUS:
- case CS_SERVER_KEY_EXCHANGE:
- case CS_CERTIFICATE_REQUEST:
- {
- if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
- {
- HandleSupplementalData(null);
- }
- if (mConnectionState < CS_SERVER_CERTIFICATE)
- {
- // There was no server certificate message; check it's OK
- this.mKeyExchange.SkipServerCredentials();
- this.mAuthentication = null;
- }
- if (mConnectionState < CS_SERVER_KEY_EXCHANGE)
- {
- // There was no server key exchange message; check it's OK
- this.mKeyExchange.SkipServerKeyExchange();
- }
- AssertEmpty(buf);
- this.mConnectionState = CS_SERVER_HELLO_DONE;
- this.mRecordStream.HandshakeHash.SealHashAlgorithms();
- IList clientSupplementalData = mTlsClient.GetClientSupplementalData();
- if (clientSupplementalData != null)
- {
- SendSupplementalDataMessage(clientSupplementalData);
- }
- this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
- TlsCredentials clientCreds = null;
- if (mCertificateRequest == null)
- {
- this.mKeyExchange.SkipClientCredentials();
- }
- else
- {
- clientCreds = this.mAuthentication.GetClientCredentials(Context, mCertificateRequest);
- if (clientCreds == null)
- {
- this.mKeyExchange.SkipClientCredentials();
- /*
- * RFC 5246 If no suitable certificate is available, the client MUST Send a
- * certificate message containing no certificates.
- *
- * NOTE: In previous RFCs, this was SHOULD instead of MUST.
- */
- SendCertificateMessage(Certificate.EmptyChain);
- }
- else
- {
- this.mKeyExchange.ProcessClientCredentials(clientCreds);
- SendCertificateMessage(clientCreds.Certificate);
- }
- }
- this.mConnectionState = CS_CLIENT_CERTIFICATE;
- /*
- * Send the client key exchange message, depending on the key exchange we are using
- * in our CipherSuite.
- */
- SendClientKeyExchangeMessage();
- this.mConnectionState = CS_CLIENT_KEY_EXCHANGE;
- TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish();
- this.mSecurityParameters.sessionHash = GetCurrentPrfHash(Context, prepareFinishHash, null);
- EstablishMasterSecret(Context, mKeyExchange);
- mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
- if (clientCreds != null && clientCreds is TlsSignerCredentials)
- {
- TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds;
- /*
- * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
- */
- SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
- Context, signerCredentials);
- byte[] hash;
- if (signatureAndHashAlgorithm == null)
- {
- hash = mSecurityParameters.SessionHash;
- }
- else
- {
- hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
- }
- byte[] signature = signerCredentials.GenerateCertificateSignature(hash);
- DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
- SendCertificateVerifyMessage(certificateVerify);
- this.mConnectionState = CS_CERTIFICATE_VERIFY;
- }
- SendChangeCipherSpecMessage();
- SendFinishedMessage();
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.handshake_failure);
- }
- this.mConnectionState = CS_CLIENT_FINISHED;
- break;
- }
- case HandshakeType.server_key_exchange:
- {
- switch (this.mConnectionState)
- {
- case CS_SERVER_HELLO:
- case CS_SERVER_SUPPLEMENTAL_DATA:
- case CS_SERVER_CERTIFICATE:
- case CS_CERTIFICATE_STATUS:
- {
- if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
- {
- HandleSupplementalData(null);
- }
- if (mConnectionState < CS_SERVER_CERTIFICATE)
- {
- // There was no server certificate message; check it's OK
- this.mKeyExchange.SkipServerCredentials();
- this.mAuthentication = null;
- }
- this.mKeyExchange.ProcessServerKeyExchange(buf);
- AssertEmpty(buf);
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- this.mConnectionState = CS_SERVER_KEY_EXCHANGE;
- break;
- }
- case HandshakeType.certificate_request:
- {
- switch (this.mConnectionState)
- {
- case CS_SERVER_CERTIFICATE:
- case CS_CERTIFICATE_STATUS:
- case CS_SERVER_KEY_EXCHANGE:
- {
- if (this.mConnectionState != CS_SERVER_KEY_EXCHANGE)
- {
- // There was no server key exchange message; check it's OK
- this.mKeyExchange.SkipServerKeyExchange();
- }
- if (this.mAuthentication == null)
- {
- /*
- * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server
- * to request client identification.
- */
- throw new TlsFatalAlert(AlertDescription.handshake_failure);
- }
- this.mCertificateRequest = CertificateRequest.Parse(Context, buf);
- AssertEmpty(buf);
- this.mKeyExchange.ValidateCertificateRequest(this.mCertificateRequest);
- /*
- * TODO Give the client a chance to immediately select the CertificateVerify hash
- * algorithm here to avoid tracking the other hash algorithms unnecessarily?
- */
- TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash,
- this.mCertificateRequest.SupportedSignatureAlgorithms);
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- this.mConnectionState = CS_CERTIFICATE_REQUEST;
- break;
- }
- case HandshakeType.session_ticket:
- {
- switch (this.mConnectionState)
- {
- case CS_CLIENT_FINISHED:
- {
- if (!this.mExpectSessionTicket)
- {
- /*
- * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a
- * SessionTicket extension in the ServerHello.
- */
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- /*
- * RFC 5077 3.4. If the client receives a session ticket from the server, then it
- * discards any Session ID that was sent in the ServerHello.
- */
- InvalidateSession();
- ReceiveNewSessionTicketMessage(buf);
- break;
- }
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- this.mConnectionState = CS_SERVER_SESSION_TICKET;
- break;
- }
- case HandshakeType.hello_request:
- {
- AssertEmpty(buf);
- /*
- * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the
- * client is currently negotiating a session. This message may be ignored by the client
- * if it does not wish to renegotiate a session, or the client may, if it wishes,
- * respond with a no_renegotiation alert.
- */
- if (this.mConnectionState == CS_END)
- {
- RefuseRenegotiation();
- }
- break;
- }
- case HandshakeType.client_hello:
- case HandshakeType.client_key_exchange:
- case HandshakeType.certificate_verify:
- case HandshakeType.hello_verify_request:
- default:
- throw new TlsFatalAlert(AlertDescription.unexpected_message);
- }
- }
- protected virtual void HandleSupplementalData(IList serverSupplementalData)
- {
- this.mTlsClient.ProcessServerSupplementalData(serverSupplementalData);
- this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA;
- this.mKeyExchange = mTlsClient.GetKeyExchange();
- this.mKeyExchange.Init(Context);
- }
- protected virtual void ReceiveNewSessionTicketMessage(MemoryStream buf)
- {
- NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf);
- AssertEmpty(buf);
- mTlsClient.NotifyNewSessionTicket(newSessionTicket);
- }
- protected virtual void ReceiveServerHelloMessage(MemoryStream buf)
- {
- {
- ProtocolVersion server_version = TlsUtilities.ReadVersion(buf);
- if (server_version.IsDtls)
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- // Check that this matches what the server is Sending in the record layer
- if (!server_version.Equals(this.mRecordStream.ReadVersion))
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- ProtocolVersion client_version = Context.ClientVersion;
- if (!server_version.IsEqualOrEarlierVersionOf(client_version))
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- this.mRecordStream.SetWriteVersion(server_version);
- ContextAdmin.SetServerVersion(server_version);
- this.mTlsClient.NotifyServerVersion(server_version);
- }
- /*
- * Read the server random
- */
- this.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf);
- this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf);
- if (this.mSelectedSessionID.Length > 32)
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- this.mTlsClient.NotifySessionID(this.mSelectedSessionID);
- this.mResumedSession = this.mSelectedSessionID.Length > 0 && this.mTlsSession != null
- && Arrays.AreEqual(this.mSelectedSessionID, this.mTlsSession.SessionID);
- /*
- * Find out which CipherSuite the server has chosen and check that it was one of the offered
- * ones, and is a valid selection for the negotiated version.
- */
- int selectedCipherSuite = TlsUtilities.ReadUint16(buf);
- if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite)
- || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
- || CipherSuite.IsScsv(selectedCipherSuite)
- || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion))
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
- this.mTlsClient.NotifySelectedCipherSuite(selectedCipherSuite);
- /*
- * Find out which CompressionMethod the server has chosen and check that it was one of the
- * offered ones.
- */
- byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf);
- if (!Arrays.Contains(this.mOfferedCompressionMethods, selectedCompressionMethod))
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);
- /*
- * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server
- * hello message when the client has requested extended functionality via the extended
- * client hello message specified in Section 2.1. ... Note that the extended server hello
- * message is only sent in response to an extended client hello message. This prevents the
- * possibility that the extended server hello message could "break" existing TLS 1.0
- * clients.
- */
- this.mServerExtensions = ReadExtensions(buf);
- /*
- * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
- * extended client hello message.
- *
- * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
- * Hello is always allowed.
- */
- if (this.mServerExtensions != null)
- {
- foreach (int extType in this.mServerExtensions.Keys)
- {
- /*
- * RFC 5746 3.6. Note that Sending a "renegotiation_info" extension in response to a
- * ClientHello containing only the SCSV is an explicit exception to the prohibition
- * in RFC 5246, Section 7.4.1.4, on the server Sending unsolicited extensions and is
- * only allowed because the client is signaling its willingness to receive the
- * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
- */
- if (extType == ExtensionType.renegotiation_info)
- continue;
- /*
- * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the
- * same extension type appeared in the corresponding ClientHello. If a client
- * receives an extension type in ServerHello that it did not request in the
- * associated ClientHello, it MUST abort the handshake with an unsupported_extension
- * fatal alert.
- */
- if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType))
- throw new TlsFatalAlert(AlertDescription.unsupported_extension);
- /*
- * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
- * extensions appearing in the client hello, and Send a server hello containing no
- * extensions[.]
- */
- if (this.mResumedSession)
- {
- // TODO[compat-gnutls] GnuTLS test server Sends server extensions e.g. ec_point_formats
- // TODO[compat-openssl] OpenSSL test server Sends server extensions e.g. ec_point_formats
- // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats
- // throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
- }
- }
- /*
- * RFC 5746 3.4. Client Behavior: Initial Handshake
- */
- {
- /*
- * When a ServerHello is received, the client MUST check if it includes the
- * "renegotiation_info" extension:
- */
- byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info);
- if (renegExtData != null)
- {
- /*
- * If the extension is present, set the secure_renegotiation flag to TRUE. The
- * client MUST then verify that the length of the "renegotiated_connection"
- * field is zero, and if it is not, MUST abort the handshake (by Sending a fatal
- * handshake_failure alert).
- */
- this.mSecureRenegotiation = true;
- if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
- throw new TlsFatalAlert(AlertDescription.handshake_failure);
- }
- }
- // TODO[compat-gnutls] GnuTLS test server fails to Send renegotiation_info extension when resuming
- this.mTlsClient.NotifySecureRenegotiation(this.mSecureRenegotiation);
- IDictionary sessionClientExtensions = mClientExtensions, sessionServerExtensions = mServerExtensions;
- if (this.mResumedSession)
- {
- if (selectedCipherSuite != this.mSessionParameters.CipherSuite
- || selectedCompressionMethod != this.mSessionParameters.CompressionAlgorithm)
- {
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- }
- sessionClientExtensions = null;
- sessionServerExtensions = this.mSessionParameters.ReadServerExtensions();
- }
- this.mSecurityParameters.cipherSuite = selectedCipherSuite;
- this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod;
- if (sessionServerExtensions != null)
- {
- {
- /*
- * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client
- * and then selects a stream or Authenticated Encryption with Associated Data (AEAD)
- * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the
- * client.
- */
- bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions);
- if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(selectedCipherSuite))
- throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC;
- }
- this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions);
- this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions,
- sessionServerExtensions, AlertDescription.illegal_parameter);
- this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions);
- /*
- * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
- * a session resumption handshake.
- */
- this.mAllowCertificateStatus = !this.mResumedSession
- && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request,
- AlertDescription.illegal_parameter);
- this.mExpectSessionTicket = !this.mResumedSession
- && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket,
- AlertDescription.illegal_parameter);
- }
- /*
- * TODO[session-hash]
- *
- * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
- * that do not use the extended master secret [..]. (and see 5.2, 5.3)
- */
- if (sessionClientExtensions != null)
- {
- this.mTlsClient.ProcessServerExtensions(sessionServerExtensions);
- }
- this.mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, this.mSecurityParameters.CipherSuite);
- /*
- * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
- * verify_data_length has a verify_data_length equal to 12. This includes all
- * existing cipher suites.
- */
- this.mSecurityParameters.verifyDataLength = 12;
- }
- protected virtual void SendCertificateVerifyMessage(DigitallySigned certificateVerify)
- {
- HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify);
- certificateVerify.Encode(message);
- message.WriteToRecordStream(this);
- }
- protected virtual void SendClientHelloMessage()
- {
- this.mRecordStream.SetWriteVersion(this.mTlsClient.ClientHelloRecordLayerVersion);
- ProtocolVersion client_version = this.mTlsClient.ClientVersion;
- if (client_version.IsDtls)
- throw new TlsFatalAlert(AlertDescription.internal_error);
- ContextAdmin.SetClientVersion(client_version);
- /*
- * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a
- * Session ID in the TLS ClientHello.
- */
- byte[] session_id = TlsUtilities.EmptyBytes;
- if (this.mTlsSession != null)
- {
- session_id = this.mTlsSession.SessionID;
- if (session_id == null || session_id.Length > 32)
- {
- session_id = TlsUtilities.EmptyBytes;
- }
- }
- bool fallback = this.mTlsClient.IsFallback;
- this.mOfferedCipherSuites = this.mTlsClient.GetCipherSuites();
- this.mOfferedCompressionMethods = this.mTlsClient.GetCompressionMethods();
- if (session_id.Length > 0 && this.mSessionParameters != null)
- {
- if (!Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite)
- || !Arrays.Contains(this.mOfferedCompressionMethods, mSessionParameters.CompressionAlgorithm))
- {
- session_id = TlsUtilities.EmptyBytes;
- }
- }
- this.mClientExtensions = this.mTlsClient.GetClientExtensions();
- HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
- TlsUtilities.WriteVersion(client_version, message);
- message.Write(this.mSecurityParameters.ClientRandom);
- TlsUtilities.WriteOpaque8(session_id, message);
- // Cipher Suites (and SCSV)
- {
- /*
- * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
- * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
- * ClientHello. Including both is NOT RECOMMENDED.
- */
- byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info);
- bool noRenegExt = (null == renegExtData);
- bool noRenegScsv = !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
- if (noRenegExt && noRenegScsv)
- {
- // TODO Consider whether to default to a client extension instead
- // this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mClientExtensions);
- // this.mClientExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes);
- this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
- }
- /*
- * RFC 7507 4. If a client sends a ClientHello.client_version containing a lower value
- * than the latest (highest-valued) version supported by the client, it SHOULD include
- * the TLS_FALLBACK_SCSV cipher suite value in ClientHello.cipher_suites [..]. (The
- * client SHOULD put TLS_FALLBACK_SCSV after all cipher suites that it actually intends
- * to negotiate.)
- */
- if (fallback && !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
- {
- this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
- }
- TlsUtilities.WriteUint16ArrayWithUint16Length(mOfferedCipherSuites, message);
- }
- TlsUtilities.WriteUint8ArrayWithUint8Length(mOfferedCompressionMethods, message);
- if (mClientExtensions != null)
- {
- WriteExtensions(message, mClientExtensions);
- }
- message.WriteToRecordStream(this);
- }
- protected virtual void SendClientKeyExchangeMessage()
- {
- HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange);
- this.mKeyExchange.GenerateClientKeyExchange(message);
- message.WriteToRecordStream(this);
- }
- }
- }
- #endif
|