/// <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)); } } }
/// <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); }
/// <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); }
/// <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; }
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); }
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); } }
/// <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)); }