public RecordMessage(MessageType messageType, ContentType contentType, ProtocolVersion version, byte[] bytes) { this.messageType = messageType; this.contentType = contentType; this.version = version; if (bytes != null) this.fragment = bytes; else this.fragment = new byte[0]; this.length = (ushort)this.fragment.Length; }
public RecordMessage(byte[] bytes, int offset) { if (bytes == null) throw new ArgumentNullException(); if (offset < 0 || offset >= bytes.Length) throw new ArgumentException(); this.messageType = MessageType.Encrypted; this.contentType = (ContentType)bytes[offset]; this.version = new ProtocolVersion(bytes[offset + 1], bytes[offset + 2]); this.length = (ushort)(bytes[offset + 3] * 256 + bytes[offset + 4]); this.fragment = new byte[this.length]; Buffer.BlockCopy(bytes, offset + 5, this.fragment, 0, this.length); }
private CompatibilityResult ProcessServerHello(byte[] bytes, int offset, int size) { byte[] temp = new byte[m_Buffer.Length + size]; Array.Copy(m_Buffer, 0, temp, 0, m_Buffer.Length); Array.Copy(bytes, offset, temp, m_Buffer.Length, size); if (IsInvalidSsl3Hello(temp)) throw new SslException(AlertDescription.HandshakeFailure, "The server hello message uses a protocol that was not recognized."); if (m_Buffer.Length + size < 11) { // not enough bytes m_Buffer = temp; return new CompatibilityResult(null, new SslRecordStatus(SslStatus.MessageIncomplete, null, null)); } ProtocolVersion pv = new ProtocolVersion(temp[9], temp[10]); if (SupportsProtocol(m_Options.Protocol, pv)) { if (m_MinLayer.HandshakeLayer.GetVersion().GetVersionInt() != pv.GetVersionInt()) { if (pv.GetVersionInt() == 30) { // SSL 3.0 m_MinLayer.HandshakeLayer = new Ssl3ClientHandshakeLayer(m_MinLayer.HandshakeLayer); } else { // TLS 1.0 m_MinLayer.HandshakeLayer = new Tls1ClientHandshakeLayer(m_MinLayer.HandshakeLayer); } } return new CompatibilityResult(m_MinLayer, m_MinLayer.ProcessBytes(temp, 0, temp.Length)); } else { throw new SslException(AlertDescription.HandshakeFailure, "The client and server could not agree on the protocol version to use."); } }
protected SslHandshakeStatus ProcessClientHello(HandshakeMessage message) { if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished) throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message."); m_IsNegotiating = true; UpdateHashes(message, HashUpdate.All); // input message // process ClientHello ProtocolVersion pv = new ProtocolVersion(message.fragment[0], message.fragment[1]); m_MaxClientVersion = pv; if (CompatibilityLayer.SupportsProtocol(m_Options.Protocol, pv) && pv.GetVersionInt() != GetVersion().GetVersionInt()) throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client."); try { // extract the time from the client [== 1 uint] m_ClientTime = new byte[4]; Array.Copy(message.fragment, 2, m_ClientTime, 0, 4); // extract the random bytes [== 28 bytes] m_ClientRandom = new byte[28]; Array.Copy(message.fragment, 6, m_ClientRandom, 0, 28); // extact the session ID [== 0..32 bytes] int length = message.fragment[34]; if (length > 32) throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes."); m_SessionID = new byte[length]; Array.Copy(message.fragment, 35, m_SessionID, 0, length); // extract the available cipher suites length += 35; int ciphers_size = message.fragment[length] * 256 + message.fragment[length + 1]; if (ciphers_size < 2 || ciphers_size % 2 != 0) throw new SslException(AlertDescription.IllegalParameter, "The number of ciphers is invalid -or- the cipher length is not even."); byte[] ciphers = new byte[ciphers_size]; Array.Copy(message.fragment, length + 2, ciphers, 0, ciphers_size); m_EncryptionScheme = CipherSuites.GetCipherSuiteAlgorithm(ciphers, m_Options.AllowedAlgorithms); // extract the available compression algorithms length += ciphers_size + 2; int compressors_size = message.fragment[length]; if (compressors_size == 0) throw new SslException(AlertDescription.IllegalParameter, "No compressor specified."); byte[] compressors = new byte[compressors_size]; Array.Copy(message.fragment, length + 1, compressors, 0, compressors_size); m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithm(compressors, m_Options.AllowedAlgorithms); } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "The message is invalid."); } // create reply return GetClientHelloResult(); }
// Thanks to Brandon for notifying us about a bug in this method public override SslHandshakeStatus ProcessSsl2Hello(byte[] hello) { if (m_State != HandshakeType.Nothing) throw new SslException(AlertDescription.UnexpectedMessage, "SSL2 ClientHello message must be the first message."); m_IsNegotiating = true; m_State = HandshakeType.ClientHello; UpdateHashes(hello, HashUpdate.All); // input message // process ClientHello ProtocolVersion pv = new ProtocolVersion(hello[1], hello[2]); m_MaxClientVersion = pv; if (CompatibilityLayer.SupportsProtocol(m_Options.Protocol, pv) && pv.GetVersionInt() != GetVersion().GetVersionInt()) throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client."); int csl = hello[3] * 256 + hello[4]; // cipher spec length int sidl = hello[5] * 256 + hello[6]; // session id length int cl = hello[7] * 256 + hello[8]; // challenge length // process ciphers byte[] ciphers = new byte[(csl / 3) * 2]; int offset = 10; for(int i = 0; i < ciphers.Length; i+=2) { Array.Copy(hello, offset, ciphers, i, 2); offset += 3; } m_EncryptionScheme = CipherSuites.GetCipherSuiteAlgorithm(ciphers, m_Options.AllowedAlgorithms); // process session id m_SessionID = new byte[sidl]; Array.Copy(hello, 9 + csl, m_SessionID, 0, sidl); // process random data [challenge] m_ClientTime = new byte[4]; m_ClientRandom = new byte[28]; if (cl <= 28) { Array.Copy(hello, 9 + csl + sidl, m_ClientRandom, m_ClientRandom.Length - cl, cl); } else { Array.Copy(hello, 9 + csl + sidl + (cl - 28), m_ClientRandom, 0, 28); Array.Copy(hello, 9 + csl + sidl, m_ClientTime, 4 - (cl - 28), cl - 28); } m_CompressionMethod = SslAlgorithms.NULL_COMPRESSION; return GetClientHelloResult(); }
public CompatibilityLayer(SocketController controller, SecurityOptions options) { m_Buffer = new byte[0]; m_MinVersion = GetMinProtocol(options.Protocol); m_MaxVersion = GetMaxProtocol(options.Protocol); this.handshakeMonitor = new SslTlsHandshakeMonitor(); HandshakeLayer layer = null; if (m_MinVersion.GetVersionInt() == 30) { // SSL 3.0 if (options.Entity == ConnectionEnd.Client) { layer = new Ssl3ClientHandshakeLayer(null, options); m_MinLayer = new RecordLayer(controller, layer as ClientHandshakeLayer); this.handshakeMonitor.Attach(controller.Parent, layer as ClientHandshakeLayer); } else { layer = new Ssl3ServerHandshakeLayer(null, options); m_MinLayer = new RecordLayer(controller, layer as ServerHandshakeLayer); this.handshakeMonitor.Attach(controller.Parent, layer as ServerHandshakeLayer); } } else { // TLS 1.0 if (options.Entity == ConnectionEnd.Client) { layer = new Tls1ClientHandshakeLayer(null, options); m_MinLayer = new RecordLayer(controller, layer as ClientHandshakeLayer); this.handshakeMonitor.Attach(controller.Parent, layer as ClientHandshakeLayer); } else { layer = new Tls1ServerHandshakeLayer(null, options); m_MinLayer = new RecordLayer(controller, layer as ServerHandshakeLayer); this.handshakeMonitor.Attach(controller.Parent, layer as ServerHandshakeLayer); } } m_MinLayer.HandshakeLayer.RecordLayer = m_MinLayer; m_Options = options; }
private CompatibilityResult ProcessClientHello(byte[] bytes, int offset, int size) { byte[] temp = new byte[m_Buffer.Length + size]; Buffer.BlockCopy(m_Buffer, 0, temp, 0, m_Buffer.Length); Buffer.BlockCopy(bytes, offset, temp, m_Buffer.Length, size); if (IsInvalidSsl3Hello(temp) && IsInvalidSsl2Hello(temp)) // SSL2 hello throw new SslException(AlertDescription.HandshakeFailure, "The client hello message uses a protocol that was not recognized."); if (m_Buffer.Length + bytes.Length < 11 || (IsInvalidSsl3Hello(temp) && !IsSsl2HelloComplete(temp))) { // not enough bytes m_Buffer = temp; return new CompatibilityResult(null, new SslRecordStatus(SslStatus.MessageIncomplete, null, null)); } ProtocolVersion pv; if (!IsInvalidSsl3Hello(temp)) pv = new ProtocolVersion(temp[9], temp[10]); else pv = ExtractSsl2Version(temp); if (pv.GetVersionInt() > m_MaxVersion.GetVersionInt()) pv = m_MaxVersion; if (SupportsProtocol(m_Options.Protocol, pv)) { if (m_MinLayer.HandshakeLayer.GetVersion().GetVersionInt() != pv.GetVersionInt()) { if (pv.GetVersionInt() == 30) { // SSL 3.0 m_MinLayer.HandshakeLayer = new Ssl3ServerHandshakeLayer(m_MinLayer.HandshakeLayer); } else { // TLS 1.0 m_MinLayer.HandshakeLayer = new Tls1ServerHandshakeLayer(m_MinLayer.HandshakeLayer); } } if (!IsInvalidSsl3Hello(temp)) { return new CompatibilityResult(m_MinLayer, m_MinLayer.ProcessBytes(temp, 0, temp.Length)); } else { return new CompatibilityResult(m_MinLayer, m_MinLayer.ProcessSsl2Hello(ExtractSsl2Content(temp))); } } else { throw new SslException(AlertDescription.HandshakeFailure, "The client and server could not agree on the protocol version to use."); } }
public static bool SupportsProtocol(SecureProtocol protocol, ProtocolVersion pv) { if (pv.GetVersionInt() == 30) return SupportsSsl3(protocol); else return SupportsTls1(protocol); }
public ServerHandshakeLayer(HandshakeLayer handshakeLayer) : base(handshakeLayer) { m_MaxClientVersion = ((ServerHandshakeLayer)handshakeLayer).m_MaxClientVersion; }
protected SslHandshakeStatus ProcessClientHello(HandshakeMessage message) { if (m_State != HandshakeType.Nothing && m_State != HandshakeType.Finished) throw new SslException(AlertDescription.UnexpectedMessage, "ClientHello message must be the first message or must be preceded by a Finished message."); Alert alert = null; m_IsNegotiating = true; UpdateHashes(message, HashUpdate.All); // input message // process ClientHello int currentLen = 0; ProtocolVersion pv = new ProtocolVersion(message.fragment[currentLen++], message.fragment[currentLen++]); m_MaxClientVersion = pv; //Violation with tls spec. If remote side uses higher protocol version than your, then we must answer with your highest version //if (CompatibilityLayer.SupportsProtocol(m_Options.Protocol, pv) && pv.GetVersionInt() != GetVersion().GetVersionInt()) // throw new SslException(AlertDescription.IllegalParameter, "Unknown protocol version of the client."); try { // extract the time from the client [== 1 uint] m_ClientTime = new byte[4]; Buffer.BlockCopy(message.fragment, currentLen, m_ClientTime, 0, 4); currentLen += 4; // extract the random bytes [== 28 bytes] m_ClientRandom = new byte[28]; Buffer.BlockCopy(message.fragment, currentLen, m_ClientRandom, 0, 28); currentLen += 28; // extact the session ID [== 0..32 bytes] byte length = message.fragment[currentLen++]; if (length > 32) throw new SslException(AlertDescription.IllegalParameter, "The length of the SessionID cannot be more than 32 bytes."); m_SessionID = new byte[length]; Buffer.BlockCopy(message.fragment, currentLen, m_SessionID, 0, length); currentLen += length; // extract the available cipher suites Int16 ciphers_size = BinaryHelper.Int16FromBytes(message.fragment[currentLen++], message.fragment[currentLen++]);//message.fragment[length] * 256 + message.fragment[length + 1]; if (ciphers_size < 2 || ciphers_size % 2 != 0) throw new SslException(AlertDescription.IllegalParameter, "The number of ciphers is invalid -or- the cipher length is not even."); byte[] ciphers = new byte[ciphers_size]; Buffer.BlockCopy(message.fragment, currentLen, ciphers, 0, ciphers_size); currentLen += ciphers_size; m_EncryptionScheme = CipherSuites.GetCipherSuiteAlgorithm(ciphers, m_Options.AllowedAlgorithms); // extract the available compression algorithms int compressors_size = message.fragment[currentLen++]; if (compressors_size == 0) throw new SslException(AlertDescription.IllegalParameter, "No compressor specified."); byte[] compressors = new byte[compressors_size]; Buffer.BlockCopy(message.fragment, currentLen, compressors, 0, compressors_size); m_CompressionMethod = CompressionAlgorithm.GetCompressionAlgorithm(compressors, m_Options.AllowedAlgorithms); currentLen += compressors_size; try { if (message.fragment.Length > currentLen) this.ProcessExtensions(message.fragment, ref currentLen, ConnectionEnd.Server); } catch (ALPNCantSelectProtocolException ex) { alert = new Alert(ex.Description, ex.Level); } } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "The message is invalid."); } // create reply return GetClientHelloResult(alert); }
public ServerHandshakeLayer(HandshakeLayer handshakeLayer) : base(handshakeLayer) { m_MaxClientVersion = ((ServerHandshakeLayer)handshakeLayer).m_MaxClientVersion; this.serverHelloExts = m_Options.ExtensionList; }
protected SslHandshakeStatus ProcessServerHelloDone(HandshakeMessage message) { if (m_State != HandshakeType.Certificate && m_State != HandshakeType.ServerKeyExchange && m_State != HandshakeType.CertificateRequest) { throw new SslException(AlertDescription.UnexpectedMessage, "ServerHello message must be preceded by a ClientHello message."); } if (message.fragment.Length != 0) { throw new SslException(AlertDescription.IllegalParameter, "The ServerHelloDone message is invalid."); } UpdateHashes(message, HashUpdate.All); // input message MemoryStream ms = new MemoryStream(); HandshakeMessage hm = new HandshakeMessage(HandshakeType.ClientKeyExchange, null); byte[] buffer; // send Certificate [optional] if (m_MutualAuthentication) { hm.type = HandshakeType.Certificate; hm.fragment = GetCertificateBytes(m_Options.Certificate); buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.All); // output message } // send ClientKeyExchange if (m_KeyCipher == null) { m_KeyCipher = (RSACryptoServiceProvider)m_RemoteCertificate.PublicKey; } RSAKeyTransform kf = new RSAKeyTransform(m_KeyCipher); byte[] preMasterSecret = new byte[48]; m_RNG.GetBytes(preMasterSecret); ProtocolVersion pv = CompatibilityLayer.GetMaxProtocol(m_Options.Protocol); preMasterSecret[0] = pv.major; preMasterSecret[1] = pv.minor; buffer = kf.CreateKeyExchange(preMasterSecret); // public-key-encrypt the preMasterSecret hm.type = HandshakeType.ClientKeyExchange; if (GetProtocol() == SecureProtocol.Ssl3) // SSL { hm.fragment = buffer; } else // TLS { hm.fragment = new byte[buffer.Length + 2]; Array.Copy(buffer, 0, hm.fragment, 2, buffer.Length); hm.fragment[0] = (byte)(buffer.Length / 256); // prepend the length of the preMasterSecret hm.fragment[1] = (byte)(buffer.Length % 256); } GenerateCiphers(preMasterSecret); // generate the local ciphers buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.All); // output message m_KeyCipher.Clear(); m_KeyCipher = null; // send CertificateVerify [optional] if (m_MutualAuthentication && m_Options.Certificate != null) { m_CertSignHash.MasterKey = this.m_MasterSecret; m_CertSignHash.TransformFinalBlock(buffer, 0, 0); // finalize hash buffer = m_CertSignHash.CreateSignature(m_Options.Certificate); hm.type = HandshakeType.CertificateVerify; hm.fragment = new byte[buffer.Length + 2]; hm.fragment[0] = (byte)(buffer.Length / 256); hm.fragment[1] = (byte)(buffer.Length % 256); Array.Copy(buffer, 0, hm.fragment, 2, buffer.Length); buffer = m_RecordLayer.EncryptBytes(hm.ToBytes(), 0, hm.fragment.Length + 4, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); UpdateHashes(hm, HashUpdate.LocalRemote); // output message } // send ChangeCipherSpec buffer = m_RecordLayer.EncryptBytes(new byte[] { 1 }, 0, 1, ContentType.ChangeCipherSpec); ms.Write(buffer, 0, buffer.Length); m_RecordLayer.ChangeLocalState(null, m_CipherSuite.Encryptor, m_CipherSuite.LocalHasher); // send Finished buffer = GetFinishedMessage(); UpdateHashes(buffer, HashUpdate.Remote); // output message buffer = m_RecordLayer.EncryptBytes(buffer, 0, buffer.Length, ContentType.Handshake); ms.Write(buffer, 0, buffer.Length); // send empty record [http://www.openssl.org/~bodo/tls-cbc.txt] if (this.m_CipherSuite.Encryptor.OutputBlockSize != 1) // is bulk cipher? { if (((int)m_Options.Flags & (int)SecurityFlags.DontSendEmptyRecord) == 0) { byte[] empty = m_RecordLayer.EncryptBytes(new byte[0], 0, 0, ContentType.ApplicationData); ms.Write(empty, 0, empty.Length); } } // finalize buffer = ms.ToArray(); ms.Close(); return(new SslHandshakeStatus(SslStatus.ContinueNeeded, buffer)); }