Exemple #1
0
        /// <summary>
        /// Handle an incoming datagram
        /// </summary>
        /// <param name="span">Bytes of the datagram</param>
        private void HandleReceive(ByteSpan span)
        {
            // Each incoming packet may contain multiple DTLS
            // records
            while (span.Length > 0)
            {
                Record record;
                if (!Record.Parse(out record, span))
                {
                    this.logger.WriteError("Dropping malformed record");
                    return;
                }
                span = span.Slice(Record.Size);

                if (span.Length < record.Length)
                {
                    this.logger.WriteError($"Dropping malformed record. Length({record.Length}) Available Bytes({span.Length})");
                    return;
                }

                ByteSpan recordPayload = span.Slice(0, record.Length);
                span = span.Slice(record.Length);

                // Early out and drop ApplicationData records
                if (record.ContentType == ContentType.ApplicationData && this.nextEpoch.State != HandshakeState.Established)
                {
                    this.logger.WriteError("Dropping ApplicationData record. Cannot process yet");
                    continue;
                }

                // Drop records from a different epoch
                if (record.Epoch != this.epoch)
                {
                    this.logger.WriteError($"Dropping bad-epoch record. RecordEpoch({record.Epoch}) Epoch({this.epoch})");
                    continue;
                }

                // Prevent replay attacks by dropping records
                // we've already processed
                int   windowIndex = (int)(this.currentEpoch.NextExpectedSequence - record.SequenceNumber - 1);
                ulong windowMask  = 1ul << windowIndex;
                if (record.SequenceNumber < this.currentEpoch.NextExpectedSequence)
                {
                    if (windowIndex >= 64)
                    {
                        this.logger.WriteError($"Dropping too-old record: Sequnce({record.SequenceNumber}) Expected({this.currentEpoch.NextExpectedSequence})");
                        continue;
                    }

                    if ((this.currentEpoch.PreviousSequenceWindowBitmask & windowMask) != 0)
                    {
                        this.logger.WriteError("Dropping duplicate record");
                        continue;
                    }
                }

                // Verify record authenticity
                int      decryptedSize    = this.currentEpoch.RecordProtection.GetDecryptedSize(recordPayload.Length);
                ByteSpan decryptedPayload = recordPayload.ReuseSpanIfPossible(decryptedSize);

                if (!this.currentEpoch.RecordProtection.DecryptCiphertextFromServer(decryptedPayload, recordPayload, ref record))
                {
                    this.logger.WriteError("Dropping non-authentic record");
                    return;
                }

                recordPayload = decryptedPayload;

                // Update out sequence number bookkeeping
                if (record.SequenceNumber >= this.currentEpoch.NextExpectedSequence)
                {
                    int windowShift = (int)(record.SequenceNumber + 1 - this.currentEpoch.NextExpectedSequence);
                    this.currentEpoch.PreviousSequenceWindowBitmask <<= windowShift;
                    this.currentEpoch.NextExpectedSequence            = record.SequenceNumber + 1;
                }
                else
                {
                    this.currentEpoch.PreviousSequenceWindowBitmask |= windowMask;
                }

                switch (record.ContentType)
                {
                case ContentType.ChangeCipherSpec:
                    if (this.nextEpoch.State != HandshakeState.ExpectingChangeCipherSpec)
                    {
                        this.logger.WriteError($"Dropping unexpected ChangeCipherSpec State({this.nextEpoch.State})");
                        break;
                    }
                    else if (this.nextEpoch.RecordProtection == null)
                    {
                        ///NOTE(mendsley): This _should_ not
                        /// happen on a well-formed client.
                        Debug.Assert(false, "How did we receive a ChangeCipherSpec message without a pending record protection instance?");
                        break;
                    }

                    if (!ChangeCipherSpec.Parse(recordPayload))
                    {
                        this.logger.WriteError("Dropping malformed ChangeCipherSpec message");
                        break;
                    }

                    // Migrate to the next epoch
                    this.epoch = this.nextEpoch.Epoch;
                    this.currentEpoch.RecordProtection              = this.nextEpoch.RecordProtection;
                    this.currentEpoch.NextOutgoingSequence          = this.nextEpoch.NextOutgoingSequence;
                    this.currentEpoch.NextExpectedSequence          = 1;
                    this.currentEpoch.PreviousSequenceWindowBitmask = 0;

                    this.nextEpoch.State = HandshakeState.ExpectingFinished;
                    this.nextEpoch.SelectedCipherSuite = CipherSuite.TLS_NULL_WITH_NULL_NULL;
                    this.nextEpoch.RecordProtection    = null;
                    this.nextEpoch.Handshake?.Dispose();
                    this.nextEpoch.Cookie = ByteSpan.Empty;
                    this.nextEpoch.VerificationStream.SetLength(0);
                    this.nextEpoch.ServerPublicKey = null;
                    this.nextEpoch.ServerRandom.SecureClear();
                    this.nextEpoch.ClientRandom.SecureClear();
                    this.nextEpoch.MasterSecret.SecureClear();
                    break;

                case ContentType.Alert:
                    this.logger.WriteError("Dropping unsupported alert record");
                    continue;

                case ContentType.Handshake:
                    if (!ProcessHandshake(ref record, recordPayload))
                    {
                        return;
                    }
                    break;

                case ContentType.ApplicationData:
                    // Forward data to the application
                    MessageReader reader = MessageReader.GetSized(recordPayload.Length);
                    reader.Length = recordPayload.Length;
                    recordPayload.CopyTo(reader.Buffer);

                    base.HandleReceive(reader, recordPayload.Length);
                    break;
                }
            }
        }