예제 #1
0
        internal DtlsRecordLayer(DatagramTransport transport, TlsContext context, TlsPeer peer, byte contentType)
        {
            this.mTransport = transport;
            this.mContext   = context;
            this.mPeer      = peer;

            this.mInHandshake = true;

            this.mCurrentEpoch = new DtlsEpoch(0, new TlsNullCipher(context));
            this.mPendingEpoch = null;
            this.mReadEpoch    = mCurrentEpoch;
            this.mWriteEpoch   = mCurrentEpoch;

            SetPlaintextLimit(MAX_FRAGMENT_LENGTH);
        }
예제 #2
0
        internal virtual void HandshakeSuccessful(DtlsHandshakeRetransmit retransmit)
        {
            if (mReadEpoch == mCurrentEpoch || mWriteEpoch == mCurrentEpoch)
            {
                // TODO
                throw new InvalidOperationException();
            }

            if (retransmit != null)
            {
                this.mRetransmit       = retransmit;
                this.mRetransmitEpoch  = mCurrentEpoch;
                this.mRetransmitExpiry = DateTimeUtilities.CurrentUnixMs() + RETRANSMIT_TIMEOUT;
            }

            this.mInHandshake  = false;
            this.mCurrentEpoch = mPendingEpoch;
            this.mPendingEpoch = null;
        }
예제 #3
0
        /// <exception cref="IOException"/>
        public virtual void Send(byte[] buf, int off, int len)
        {
            byte contentType = ContentType.application_data;

            if (this.mInHandshake || this.mWriteEpoch == this.mRetransmitEpoch)
            {
                contentType = ContentType.handshake;

                byte handshakeType = TlsUtilities.ReadUint8(buf, off);
                if (handshakeType == HandshakeType.finished)
                {
                    DtlsEpoch nextEpoch = null;
                    if (this.mInHandshake)
                    {
                        nextEpoch = mPendingEpoch;
                    }
                    else if (this.mWriteEpoch == this.mRetransmitEpoch)
                    {
                        nextEpoch = mCurrentEpoch;
                    }

                    if (nextEpoch == null)
                    {
                        // TODO
                        throw new InvalidOperationException();
                    }

                    // Implicitly send change_cipher_spec and change to pending cipher state

                    // TODO Send change_cipher_spec and finished records in single datagram?
                    byte[] data = new byte[] { 1 };
                    SendRecord(ContentType.change_cipher_spec, data, 0, data.Length);

                    mWriteEpoch = nextEpoch;
                }
            }

            SendRecord(contentType, buf, off, len);
        }
예제 #4
0
        public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
        {
            byte[] record = null;

            for (;;)
            {
                int receiveLimit = System.Math.Min(len, GetReceiveLimit()) + RECORD_HEADER_LENGTH;
                if (record == null || record.Length < receiveLimit)
                {
                    record = new byte[receiveLimit];
                }

                try
                {
                    if (mRetransmit != null && DateTimeUtilities.CurrentUnixMs() > mRetransmitExpiry)
                    {
                        mRetransmit      = null;
                        mRetransmitEpoch = null;
                    }

                    int received = ReceiveRecord(record, 0, receiveLimit, waitMillis);
                    if (received < 0)
                    {
                        return(received);
                    }
                    if (received < RECORD_HEADER_LENGTH)
                    {
                        continue;
                    }
                    int length = TlsUtilities.ReadUint16(record, 11);
                    if (received != (length + RECORD_HEADER_LENGTH))
                    {
                        continue;
                    }

                    byte type = TlsUtilities.ReadUint8(record, 0);

                    // TODO Support user-specified custom protocols?
                    switch (type)
                    {
                    case ContentType.alert:
                    case ContentType.application_data:
                    case ContentType.change_cipher_spec:
                    case ContentType.handshake:
                    case ContentType.heartbeat:
                        break;

                    default:
                        // TODO Exception?
                        continue;
                    }

                    int epoch = TlsUtilities.ReadUint16(record, 3);

                    DtlsEpoch recordEpoch = null;
                    if (epoch == mReadEpoch.Epoch)
                    {
                        recordEpoch = mReadEpoch;
                    }
                    else if (type == ContentType.handshake && mRetransmitEpoch != null &&
                             epoch == mRetransmitEpoch.Epoch)
                    {
                        recordEpoch = mRetransmitEpoch;
                    }

                    if (recordEpoch == null)
                    {
                        continue;
                    }

                    long seq = TlsUtilities.ReadUint48(record, 5);
                    if (recordEpoch.ReplayWindow.ShouldDiscard(seq))
                    {
                        continue;
                    }

                    ProtocolVersion version = TlsUtilities.ReadVersion(record, 1);
                    if (!version.IsDtls)
                    {
                        continue;
                    }

                    if (mReadVersion != null && !mReadVersion.Equals(version))
                    {
                        continue;
                    }

                    byte[] plaintext = recordEpoch.Cipher.DecodeCiphertext(
                        GetMacSequenceNumber(recordEpoch.Epoch, seq), type, record, RECORD_HEADER_LENGTH,
                        received - RECORD_HEADER_LENGTH);

                    recordEpoch.ReplayWindow.ReportAuthenticated(seq);

                    if (plaintext.Length > this.mPlaintextLimit)
                    {
                        continue;
                    }

                    if (mReadVersion == null)
                    {
                        mReadVersion = version;
                    }

                    switch (type)
                    {
                    case ContentType.alert:
                    {
                        if (plaintext.Length == 2)
                        {
                            byte alertLevel       = plaintext[0];
                            byte alertDescription = plaintext[1];

                            mPeer.NotifyAlertReceived(alertLevel, alertDescription);

                            if (alertLevel == AlertLevel.fatal)
                            {
                                Fail(alertDescription);
                                throw new TlsFatalAlert(alertDescription);
                            }

                            // TODO Can close_notify be a fatal alert?
                            if (alertDescription == AlertDescription.close_notify)
                            {
                                CloseTransport();
                            }
                        }

                        continue;
                    }

                    case ContentType.application_data:
                    {
                        if (mInHandshake)
                        {
                            // TODO Consider buffering application data for new epoch that arrives
                            // out-of-order with the Finished message
                            continue;
                        }
                        break;
                    }

                    case ContentType.change_cipher_spec:
                    {
                        // Implicitly receive change_cipher_spec and change to pending cipher state

                        for (int i = 0; i < plaintext.Length; ++i)
                        {
                            byte message = TlsUtilities.ReadUint8(plaintext, i);
                            if (message != ChangeCipherSpec.change_cipher_spec)
                            {
                                continue;
                            }

                            if (mPendingEpoch != null)
                            {
                                mReadEpoch = mPendingEpoch;
                            }
                        }

                        continue;
                    }

                    case ContentType.handshake:
                    {
                        if (!mInHandshake)
                        {
                            if (mRetransmit != null)
                            {
                                mRetransmit.ReceivedHandshakeRecord(epoch, plaintext, 0, plaintext.Length);
                            }

                            // TODO Consider support for HelloRequest
                            continue;
                        }
                        break;
                    }

                    case ContentType.heartbeat:
                    {
                        // TODO[RFC 6520]
                        continue;
                    }
                    }

                    /*
                     * NOTE: If we receive any non-handshake data in the new epoch implies the peer has
                     * received our final flight.
                     */
                    if (!mInHandshake && mRetransmit != null)
                    {
                        this.mRetransmit      = null;
                        this.mRetransmitEpoch = null;
                    }

                    Array.Copy(plaintext, 0, buf, off, plaintext.Length);
                    return(plaintext.Length);
                }
                catch (IOException e)
                {
                    // NOTE: Assume this is a timeout for the moment
                    throw e;
                }
            }
        }