/// <summary> /// Send (resend) a ClientHello message to the server /// </summary> private void SendClientHello() { // Reset our verification stream this.nextEpoch.VerificationStream.SetLength(0); this.nextEpoch.ClientRandom.FillWithRandom(this.random); // Describe our ClientHello flight ClientHello clientHello = new ClientHello(); clientHello.Random = this.nextEpoch.ClientRandom; clientHello.Cookie = this.nextEpoch.Cookie; clientHello.CipherSuites = new byte[2]; clientHello.CipherSuites.WriteBigEndian16((ushort)CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); clientHello.SupportedCurves = new byte[2]; clientHello.SupportedCurves.WriteBigEndian16((ushort)NamedCurve.x25519); Handshake handshake = new Handshake(); handshake.MessageType = HandshakeType.ClientHello; handshake.Length = (uint)clientHello.CalculateSize(); handshake.MessageSequence = 0; handshake.FragmentOffset = 0; handshake.FragmentLength = handshake.Length; // Describe the record int plaintextLength = (int)(Handshake.Size + handshake.Length); Record outgoingRecord = new Record(); outgoingRecord.ContentType = ContentType.Handshake; outgoingRecord.Epoch = this.epoch; outgoingRecord.SequenceNumber = this.currentEpoch.NextOutgoingSequence; outgoingRecord.Length = (ushort)this.currentEpoch.RecordProtection.GetEncryptedSize(plaintextLength); ++this.currentEpoch.NextOutgoingSequence; // Convert the record to wire format ByteSpan packet = new byte[Record.Size + outgoingRecord.Length]; ByteSpan writer = packet; outgoingRecord.Encode(packet); writer = writer.Slice(Record.Size); handshake.Encode(writer); writer = writer.Slice(Handshake.Size); clientHello.Encode(writer); // Write ClientHello to the verification stream this.nextEpoch.VerificationStream.Write( packet.GetUnderlyingArray() , Record.Size , Handshake.Size + (int)handshake.Length ); // Protect the record this.currentEpoch.RecordProtection.EncryptClientPlaintext( packet.Slice(Record.Size, outgoingRecord.Length) , packet.Slice(Record.Size, plaintextLength) , ref outgoingRecord ); this.nextEpoch.State = HandshakeState.ExpectingServerHello; this.nextEpoch.NextPacketResendTime = DateTime.UtcNow + this.handshakeResendTimeout; base.WriteBytesToConnection(packet.GetUnderlyingArray(), packet.Length); }
/// <summary> /// Parse a Handshake ClientHello payload from wire format /// </summary> /// <returns>True if we successfully decode the ClientHello message. Otherwise false</returns> public static bool Parse(out ClientHello result, ByteSpan span) { result = new ClientHello(); if (span.Length < MinSize) { return(false); } ProtocolVersion clientVersion = (ProtocolVersion)span.ReadBigEndian16(); if (clientVersion != ProtocolVersion.DTLS1_2) { return(false); } span = span.Slice(2); result.Random = span.Slice(0, Dtls.Random.Size); span = span.Slice(Dtls.Random.Size); ///NOTE(mendsley): We ignore session id byte sessionIdSize = span[0]; if (span.Length < 1 + sessionIdSize) { return(false); } span = span.Slice(1 + sessionIdSize); byte cookieSize = span[0]; if (span.Length < 1 + cookieSize) { return(false); } result.Cookie = span.Slice(1, cookieSize); span = span.Slice(1 + cookieSize); ushort cipherSuiteSize = span.ReadBigEndian16(); if (span.Length < 2 + cipherSuiteSize) { return(false); } else if (cipherSuiteSize % 2 != 0) { return(false); } result.CipherSuites = span.Slice(2, cipherSuiteSize); span = span.Slice(2 + cipherSuiteSize); int compressionMethodsSize = span[0]; bool foundNullCompressionMethod = false; for (int ii = 0; ii != compressionMethodsSize; ++ii) { if (span[1 + ii] == (byte)CompressionMethod.Null) { foundNullCompressionMethod = true; break; } } span = span.Slice(1 + compressionMethodsSize); if (!foundNullCompressionMethod) { return(false); } // Parse extensions if (span.Length > 0) { ushort extensionsSize = span.ReadBigEndian16(); span = span.Slice(2); if (span.Length != extensionsSize) { return(false); } while (span.Length > 0) { // Parse extension header if (span.Length < 4) { return(false); } ExtensionType extensionType = (ExtensionType)span.ReadBigEndian16(0); ushort extensionLength = span.ReadBigEndian16(2); ByteSpan extensionData = span.Slice(4, extensionLength); if (extensionData.Length < extensionLength) { return(false); } span = span.Slice(4 + extensionLength); result.ParseExtension(extensionType, extensionData); } } return(true); }