Пример #1
0
        /// <summary>
        /// Creates an initial <see cref="HandshakeState"/>.
        /// </summary>
        /// <param name="config">
        /// A set of parameters used to instantiate an
        /// initial <see cref="HandshakeState"/>.
        /// </param>
        /// <returns>The initial handshake state.</returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown if the <paramref name="config"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if any of the following conditions is satisfied:
        /// <para>- <paramref name="config"/> does not contain a valid DH private key.</para>
        /// <para>- <paramref name="config"/> does not contain a valid DH public key.</para>
        /// <para>- <see cref="HandshakePattern"/> requires the <see cref="HandshakeState"/>
        /// to be initialized with local and/or remote static key,
        /// but <see cref="ProtocolConfig.LocalStatic"/> and/or
        /// <see cref="ProtocolConfig.RemoteStatic"/> is null.</para>
        /// <para>- One or more pre-shared keys are not 32 bytes in length.</para>
        /// <para>- Number of pre-shared keys does not match the number of PSK modifiers.</para>
        /// </exception>
        public HandshakeState Create(ProtocolConfig config)
        {
            Exceptions.ThrowIfNull(config, nameof(config));

            unsafe
            {
                fixed(byte *xls = config.LocalStatic)
                {
                    return(Create(config.Initiator, config.Prologue, xls, config.LocalStatic.Length, config.RemoteStatic, config.PreSharedKeys));
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Reinitializes the current instance of the <see cref="NoiseSocket"/>
        /// class with a new Noise protocol, different from the initial Noise protocol.
        /// The reason for the reinitialization was responder's retry request.
        /// </summary>
        /// <param name="protocol">A concrete Noise protocol (e.g. Noise_XX_25519_AESGCM_BLAKE2b).</param>
        /// <param name="config">
        /// A set of parameters used to instantiate a <see cref="HandshakeState"/>.
        /// </param>
        /// <exception cref="ObjectDisposedException">
        /// Thrown if the current instance has already been disposed.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// Thrown if the selected handshake pattern was a one-way pattern.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the handshake has already been completed
        /// or the protocol has already been changed once.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Thrown if either <paramref name="protocol"/> or <paramref name="config"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if the client attempted to retry with a new protocol as a responder,
        /// or the server attempted to retry with a new protocol as an initiator.
        /// </exception>
        public void Retry(Protocol protocol, ProtocolConfig config)
        {
            ThrowIfDisposed();
            ThrowIfNull(protocol, nameof(protocol));
            ThrowIfNull(config, nameof(config));

            if (client && !config.Initiator)
            {
                throw new ArgumentException("Client cannot retry with a new protocol as a responder.");
            }

            if (!client && config.Initiator)
            {
                throw new ArgumentException("Server cannot retry with a new protocol as an initiator.");
            }

            Reinitialize(protocol, config, State.Retry);
        }
Пример #3
0
        /// <summary>
        /// Initializes the current instance of the <see cref="NoiseSocket"/>
        /// class with an initiator's choice of the Noise protocol.
        /// </summary>
        /// <param name="protocol">A concrete Noise protocol (e.g. Noise_XX_25519_AESGCM_BLAKE2b).</param>
        /// <param name="config">
        /// A set of parameters used to instantiate a <see cref="HandshakeState"/>.
        /// </param>
        /// <exception cref="ObjectDisposedException">
        /// Thrown if the current instance has already been disposed.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// Thrown if the selected handshake pattern was a one-way pattern.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// Thrown if the handshake has already been completed,
        /// the protocol has already been changed once, or this
        /// method was called by the client.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// Thrown if either <paramref name="protocol"/> or <paramref name="config"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if the server attempted to accept a new protocol as an initiator.
        /// </exception>
        public void Accept(Protocol protocol, ProtocolConfig config)
        {
            ThrowIfDisposed();
            ThrowIfNull(protocol, nameof(protocol));
            ThrowIfNull(config, nameof(config));

            if (client)
            {
                throw new InvalidOperationException($"{nameof(Accept)} can be called only by the server.");
            }

            if (!client && config.Initiator)
            {
                throw new ArgumentException("Server cannot accept a new protocol as an initiator.");
            }

            Reinitialize(protocol, config, State.Accept);
        }
Пример #4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="NoiseSocket"/> class.
        /// </summary>
        /// <param name="protocol">A concrete Noise protocol (e.g. Noise_XX_25519_AESGCM_BLAKE2b).</param>
        /// <param name="config">
        /// A set of parameters used to instantiate an initial <see cref="HandshakeState"/>.
        /// </param>
        /// <param name="stream">The stream for reading and writing encoded protocol messages.</param>
        /// <param name="leaveOpen">
        /// True to leave the stream open after the
        /// <see cref="NoiseSocket"/> object is disposed, false otherwise.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown if either <paramref name="protocol"/>,
        /// <paramref name="config"/>, or <paramref name="stream"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if the selected handshake pattern was a one-way pattern.
        /// </exception>
        public NoiseSocket(Protocol protocol, ProtocolConfig config, Stream stream, bool leaveOpen = false)
        {
            ThrowIfNull(protocol, nameof(protocol));
            ThrowIfNull(config, nameof(config));
            ThrowIfNull(stream, nameof(stream));

            if (protocol.HandshakePattern.Patterns.Count() == 1)
            {
                throw new ArgumentException("One-way patterns are not yet supported.");
            }

            this.client    = config.Initiator;
            this.protocol  = protocol;
            this.config    = config;
            this.stream    = stream;
            this.leaveOpen = leaveOpen;

            savedMessages          = new List <Memory <byte> >();
            isNextMessageEncrypted = protocol != null && IsInitialMessageEncrypted(protocol);
            lastOperation          = config.Initiator ? HandshakeOperation.ReadHandshakeMessage : HandshakeOperation.WriteHandshakeMessage;
        }
Пример #5
0
        private void Reinitialize(Protocol protocol, ProtocolConfig config, State state)
        {
            bool fallback = state == State.Switch && (protocol.Modifiers & PatternModifiers.Fallback) != 0;

            if (fallback && handshakeState == null)
            {
                throw new InvalidOperationException("Cannot perform the fallback handshake on an uninitialized server.");
            }

            if (protocol.HandshakePattern.Patterns.Count() == 1)
            {
                throw new NotSupportedException("One-way patterns are not yet supported.");
            }

            if (transport != null)
            {
                throw new InvalidOperationException($"Cannot change protocol after the handshake has been completed.");
            }

            if (this.state != State.Initial)
            {
                throw new InvalidOperationException($"Cannot change protocol more than once.");
            }

            this.protocol = protocol;
            this.config   = config;
            this.state    = state;
            this.fallback = fallback;

            if (!fallback && handshakeState != null)
            {
                handshakeState.Dispose();
                handshakeState = null;
            }

            isNextMessageEncrypted = IsInitialMessageEncrypted(protocol);
        }
Пример #6
0
        private void InitializeHandshakeState()
        {
            if (fallback)
            {
                Debug.Assert(protocol != null);

                var config = new ProtocolConfig(
                    this.config.Initiator,
                    CalculatePrologue(),
                    this.config.LocalStatic,
                    this.config.RemoteStatic,
                    this.config.PreSharedKeys
                    );

                handshakeState.Fallback(protocol, config);
                fallback = false;
            }
            else if (handshakeState == null)
            {
                if (protocol == null)
                {
                    string error = $"Cannot perform the handshake before calling either {nameof(Accept)}, {nameof(Switch)}, or {nameof(Retry)}.";
                    throw new InvalidOperationException(error);
                }

                handshakeState = protocol.Create(
                    config.Initiator,
                    CalculatePrologue(),
                    config.LocalStatic,
                    config.RemoteStatic,
                    config.PreSharedKeys
                    );

                initializer?.Invoke(handshakeState);
            }
        }
Пример #7
0
        /// <summary>
        /// Creates an initial <see cref="HandshakeState"/>.
        /// </summary>
        /// <param name="config">
        /// A set of parameters used to instantiate an
        /// initial <see cref="HandshakeState"/>.
        /// </param>
        /// <returns>The initial handshake state.</returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown if the <paramref name="config"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if any of the following conditions is satisfied:
        /// <para>- <paramref name="config"/> does not contain a valid DH private key.</para>
        /// <para>- <paramref name="config"/> does not contain a valid DH public key.</para>
        /// <para>- <see cref="HandshakePattern"/> requires the <see cref="HandshakeState"/>
        /// to be initialized with local and/or remote static key,
        /// but <see cref="ProtocolConfig.LocalStatic"/> and/or
        /// <see cref="ProtocolConfig.RemoteStatic"/> is null.</para>
        /// <para>- One or more pre-shared keys are not 32 bytes in length.</para>
        /// <para>- Number of pre-shared keys does not match the number of PSK modifiers.</para>
        /// </exception>
        public HandshakeState Create(ProtocolConfig config)
        {
            Exceptions.ThrowIfNull(config, nameof(config));

            return(Create(config.Initiator, config.Prologue, config.LocalStatic, config.RemoteStatic, config.PreSharedKeys));
        }