protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client)
{
MemoryStream buf = new MemoryStream();
ProtocolVersion client_version = client.ClientVersion;
if (!client_version.IsDtls)
throw new TlsFatalAlert(AlertDescription.internal_error);
TlsClientContextImpl context = state.clientContext;
context.SetClientVersion(client_version);
TlsUtilities.WriteVersion(client_version, buf);
SecurityParameters securityParameters = context.SecurityParameters;
buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length);
// Session ID
byte[] session_id = TlsUtilities.EmptyBytes;
if (state.tlsSession != null)
{
session_id = state.tlsSession.SessionID;
if (session_id == null || session_id.Length > 32)
{
session_id = TlsUtilities.EmptyBytes;
}
}
TlsUtilities.WriteOpaque8(session_id, buf);
// Cookie
TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf);
bool fallback = client.IsFallback;
/*
* Cipher suites
*/
state.offeredCipherSuites = client.GetCipherSuites();
// Integer -> byte[]
state.clientExtensions = client.GetClientExtensions();
// 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(state.clientExtensions, ExtensionType.renegotiation_info);
bool noRenegExt = (null == renegExtData);
bool noRenegSCSV = !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
if (noRenegExt && noRenegSCSV)
{
// TODO Consider whether to default to a client extension instead
state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, 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(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV))
{
state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV);
}
TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, buf);
}
// TODO Add support for compression
// Compression methods
// state.offeredCompressionMethods = client.getCompressionMethods();
state.offeredCompressionMethods = new byte[]{ CompressionMethod.cls_null };
TlsUtilities.WriteUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf);
// Extensions
if (state.clientExtensions != null)
{
TlsProtocol.WriteExtensions(buf, state.clientExtensions);
}
return buf.ToArray();
}