public bool ReadPacket(Stream stream)
        {
            byte[] header    = new byte[5];
            int    readBytes = 0;

            while (readBytes < header.Length)
            {
                readBytes += stream.Read(header, readBytes, header.Length - readBytes);
            }

            Record record = new Record(header);

            readBytes = 0;
            while (readBytes < record.Fragment.Length)
            {
                readBytes += stream.Read(record.Fragment, readBytes, record.Fragment.Length - readBytes);
            }

            _recordHandler.ProcessInputRecord(record);

            if (record.Type == 22)
            {
                HandshakeMessage hs = HandshakeMessage.GetInstance(VERSION, record.Fragment);
                if (hs == null)
                {
                    Console.WriteLine("Skipped handling packet");
                    return(true);
                }

                Console.WriteLine("adding bytes to hash " + record.Fragment.Length);
                _handshakeStream.Write(record.Fragment, 0, record.Fragment.Length);

                if (hs.Type == HandshakeMessageType.ServerHello)
                {
                    HandshakeServerHello sh = (HandshakeServerHello)hs;
                    _serverRandom = sh.Random.GetBytes();
                    if (sh.ServerVersion != VERSION)
                    {
                        throw new Exception("Version doesn't match");
                    }
                }
                else if (hs.Type == HandshakeMessageType.Certificate)
                {
                    Console.WriteLine("Found certificate");
                    HandshakeCertificate cert = (HandshakeCertificate)hs;

                    _rsaPublicKey = null;
                    foreach (X509Certificate c in cert.CertificateList)
                    {
                        Console.WriteLine(c.ToString(true));

                        if (_rsaPublicKey == null)
                        {
                            X509Certificate c2 = new X509Certificate(c);
                            _rsaPublicKey = new CertificatePublicKey(c2);
                        }
                    }
                }
                else if (hs.Type == HandshakeMessageType.ServerHelloDone)
                {
                    SendClientKey(stream);

                    byte[] seed = new byte[64];
                    Array.Copy(_clientRandom, 0, seed, 0, 32);
                    Array.Copy(_serverRandom, 0, seed, 32, 32);
                    PrintBytes("hash seed", seed);

                    _masterSecret = _cipherSuite.KeyExchangeAlgorithm.GetMasterSecret(_cipherSuite.PseudoRandomFunction, seed);
                    PrintBytes("master secret", _masterSecret);

                    seed = new byte[64];
                    Array.Copy(_serverRandom, 0, seed, 0, 32);
                    Array.Copy(_clientRandom, 0, seed, 32, 32);

                    ConnectionState connectionState = new ConnectionState(_clientRandom, _serverRandom, _masterSecret);
                    _recordHandler.SetCipherSuite(_cipherSuite, connectionState);

                    SendFinished(stream);
                }
                else if (hs.Type == HandshakeMessageType.Finished)
                {
                    Console.WriteLine("Got Finished message!!!");
                    SendHttpGet(stream);
                }
            }
            else if (record.Type == 20)
            {
                Console.WriteLine("Got change cipher spec from server");
                _recordHandler.ChangeRemoteState();
            }
            else if (record.Type == 21)
            {
                // This is an alert
                if (record.Fragment.Length >= 2 && record.Fragment[1] == 0)
                {
                    // Close notify
                    Console.WriteLine("Close notify received");
                    return(false);
                }
            }
            else if (record.Type == 23)
            {
                Console.WriteLine("Got data: " + Encoding.UTF8.GetString(record.Fragment));
            }

            return(true);
        }
Example #2
0
        private async Task ReceiveChangeCipherSpecAndFinished(CancellationToken token)
        {
            var recordList = new List <Record>();
            var changeCipherSpecReceived = false;
            var finishedReceived         = false;

            while (!token.IsCancellationRequested)
            {
                var records = await _recordStream.ReceiveAsync(token);

                if (records == null || records.Count == 0)
                {
                    continue;
                }

                // Check for alerts
                foreach (var record in records)
                {
                    if (record.Type == RecordType.Alert)
                    {
                        logger?.Error($"Alert received: { (AlertDescription)record.Fragment[1]}");
                        if (record.Fragment.Length == 2)
                        {
                            var alert = (AlertDescription)record.Fragment[1];
                            throw new AlertException((AlertDescription)record.Fragment[1], $"Received TLS alert record: {alert}");
                        }
                    }
                }

                recordList.AddRange(records);
                if (recordList.Count < 2)
                {
                    continue;
                }

                foreach (var record in recordList)
                {
                    _recordHandler.ProcessInputRecord(record);

                    if (record.Type == RecordType.ChangeCipherSpec)
                    {
                        changeCipherSpecReceived = true;

                        if (record.Fragment.Length != 1 || record.Fragment[0] != 0x01)
                        {
                            throw new AlertException(AlertDescription.IllegalParameter, "Received an invalid ChangeCipherSpec record");
                        }

                        // NOTE: keep this before recordHandler.ChangeLocalState since it may generate masterSecret
                        _handshakeSession.RemoteChangeCipherSpec();

                        // Change cipher suite in record handler and handle it in handshake session
                        _recordHandler.SetCipherSuite(_handshakeSession.CipherSuite, _handshakeSession.ConnectionState);
                        _recordHandler.ChangeRemoteState();

                        continue;
                    }

                    if (record.Type == RecordType.Handshake)
                    {
                        finishedReceived = true;
                        HandshakeMessage[] handshakeMessages = _handshakePacketizer.ProcessHandshakeRecord(_handshakeSession.NegotiatedVersion, record);
                        foreach (HandshakeMessage hsm in handshakeMessages)
                        {
                            if (hsm.Type != HandshakeMessageType.Finished)
                            {
                                throw new AlertException(AlertDescription.UnexpectedMessage, "Finished handshake message was expected");
                            }

                            logger?.Debug("Received Handshake message {0}", hsm.Type);
                            _handshakeSession.ProcessMessage(hsm);
                        }
                    }

                    if (changeCipherSpecReceived && finishedReceived)
                    {
                        return;
                    }
                }
            }

            throw new TimeoutException("ReceiveAsync change cipher spec timed out");
        }
Example #3
0
        private void ReceiveHandshakeCallback(IAsyncResult asyncResult)
        {
            AsyncHandshakeResult asyncHandshakeResult = (AsyncHandshakeResult)asyncResult.AsyncState;

            try {
                Record[] records = _recordStream.EndReceive(asyncResult);
                for (int i = 0; i < records.Length; i++)
                {
                    Record record = records[i];
                    if (!_recordHandler.ProcessInputRecord(record))
                    {
                        // Ignore records from invalid epoch
                        continue;
                    }

                    lock (_handshakeLock) {
                        if (!_isAuthenticated && record.Type == RecordType.Data)
                        {
                            // Refuse data packets before authentication
                            throw new AlertException(AlertDescription.UnexpectedMessage,
                                                     "Data packet received before handshake");
                        }
                        if (!_isHandshaking && (record.Type == RecordType.ChangeCipherSpec || record.Type == RecordType.Handshake))
                        {
                            // Refuse ChangeCipherSpec and Handshake packets if not handshaking
                            // TODO: Should handle HelloRequest if renegotiation is supported
                            throw new AlertException(AlertDescription.UnexpectedMessage,
                                                     "Handshake packet received outside handshake");
                        }
                        if (_isHandshaking && record.Type == RecordType.Data)
                        {
                            // Queue data packets during new handshake to avoid sync problems
                            // TODO: Should handle the data correctly if renegotiation is supported
                            throw new AlertException(AlertDescription.UnexpectedMessage,
                                                     "Received a data packet during handshake");
                        }
                    }

                    switch (record.Type)
                    {
                    case RecordType.ChangeCipherSpec:
                        ProcessChangeCipherSpecRecord(record, asyncHandshakeResult);
                        break;

                    case RecordType.Alert:
                        ProcessAlertRecord(record, asyncHandshakeResult);
                        break;

                    case RecordType.Handshake:
                        ProcessHandshakeRecord(record, asyncHandshakeResult);
                        break;

                    case RecordType.Data:
                        // TODO: Implement this properly if renegotiation is supported
                        break;

                    default:
                        ProcessUnknownRecord(record, asyncHandshakeResult);
                        break;
                    }
                }
            } catch (AlertException ae) {
                ProcessSendFatalAlert(new Alert(ae.AlertDescription, _handshakeSession.NegotiatedVersion));
                asyncHandshakeResult.SetComplete(new Exception("Connection closed because of local alert", ae));
            } catch (IOException) {
                asyncHandshakeResult.SetComplete(new EndOfStreamException("Connection closed unexpectedly"));
            } catch (Exception e) {
                ProcessSendFatalAlert(new Alert(AlertDescription.InternalError, _handshakeSession.NegotiatedVersion));
                asyncHandshakeResult.SetComplete(new Exception("Connection closed because of local error", e));
            }
        }