示例#1
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);
        }
示例#2
0
        /// <summary>
        /// Releases all resources used by the current
        /// instance of the <see cref="NoiseSocket"/> class.
        /// </summary>
        public void Dispose()
        {
            if (!disposed)
            {
                if (!leaveOpen)
                {
                    stream.Dispose();
                }

                handshakeState?.Dispose();
                transport?.Dispose();

                disposed = true;
            }
        }
示例#3
0
        /// <summary>
        /// Perform handshake protocol if it has not been already run.
        /// </summary>
        /// <remarks>
        /// Most uses of this package need not call Handshake explicitly:
        /// the first Read or Write will call it automatically.
        /// </remarks>
        internal void HandShake()
        {
            // Locking the handshakeMutex
            this.handshakeMutex.WaitOne();

            HandshakeState handshakeState = null;

            try
            {
                Strobe c1;
                Strobe c2;
                byte[] receivedPayload = null;

                // did we already go through the handshake?
                if (this.handshakeComplite)
                {
                    return;
                }

                KeyPair remoteKeyPair = null;
                if (this.config.RemoteKey != null)
                {
                    if (this.config.RemoteKey.Length != Asymmetric.DhLen)
                    {
                        throw new Exception($"disco: the provided remote key is not {Asymmetric.DhLen}-byte");
                    }

                    remoteKeyPair = new KeyPair {
                        PublicKey = new byte[this.config.RemoteKey.Length]
                    };
                    Array.Copy(this.config.RemoteKey, remoteKeyPair.PublicKey, this.config.RemoteKey.Length);
                }

                handshakeState = DiscoHelper.InitializeDisco(
                    this.config.HandshakePattern,
                    this.isClient,
                    this.config.Prologue,
                    this.config.KeyPair,
                    null,
                    remoteKeyPair,
                    null);

                // pre-shared key
                handshakeState.Psk = this.config.PreSharedKey;

                do
                {
                    // start handshake
                    if (handshakeState.ShouldWrite)
                    {
                        // we're writing the next message pattern
                        // if it's the message pattern and we're sending a static key, we also send a proof
                        // TODO: is this the best way of sending a proof :/ ?
                        byte[] bufToWrite;

                        if (handshakeState.MessagePatterns.Length <= 2 && this.config.StaticPublicKeyProof != null)
                        {
                            (c1, c2) = handshakeState.WriteMessage(this.config.StaticPublicKeyProof, out bufToWrite);
                        }
                        else
                        {
                            (c1, c2) = handshakeState.WriteMessage(new byte[] { }, out bufToWrite);
                        }

                        // header (length)
                        var length = new[] { (byte)(bufToWrite.Length >> 8), (byte)(bufToWrite.Length % 256) };
                        // write
                        var dataToWrite = length.Concat(bufToWrite).ToArray();
                        this.connectionStream.Write(dataToWrite, 0, dataToWrite.Length);
                    }
                    else
                    {
                        var bufHeader = this.ReadFromUntil(this.connectionStream, 2);

                        var length = (bufHeader[0] << 8) | bufHeader[1];

                        if (length > Config.NoiseMessageLength)
                        {
                            throw new Exception("disco: Disco message received exceeds DiscoMessageLength");
                        }

                        var noiseMessage = this.ReadFromUntil(this.connectionStream, length);

                        (c1, c2) = handshakeState.ReadMessage(noiseMessage, out receivedPayload);
                    }
                }while (c1 == null);

                // Has the other peer been authenticated so far?
                if (!this.isRemoteAuthenticated && this.config.PublicKeyVerifier != null)
                {
                    byte isRemoteStaticKeySet = 0;
                    // test if remote static key is empty
                    foreach (var val in handshakeState.Rs.PublicKey)
                    {
                        isRemoteStaticKeySet |= val;
                    }

                    if (isRemoteStaticKeySet != 0)
                    {
                        // a remote static key has been received. Verify it
                        if (!this.config.PublicKeyVerifier(handshakeState.Rs.PublicKey, receivedPayload))
                        {
                            throw new Exception("disco: the received public key could not be authenticated");
                        }

                        this.isRemoteAuthenticated = true;
                        this.RemotePublicKey       = handshakeState.Rs.PublicKey;
                    }
                }

                // Processing the final handshake message returns two CipherState objects
                // the first for encrypting transport messages from initiator to responder
                // and the second for messages in the other direction.
                if (c2 != null)
                {
                    if (this.isClient)
                    {
                        (this.strobeOut, this.strobeIn) = (c1, c2);
                    }
                    else
                    {
                        (this.strobeOut, this.strobeIn) = (c2, c1);
                    }
                }
                else
                {
                    this.IsHalfDuplex = true;
                    this.strobeIn     = c1;
                    this.strobeOut    = c1;
                }

                // TODO: preserve c.hs.symmetricState.h
                // At that point the HandshakeState should be deleted except for the hash value h, which may be used for post-handshake channel binding (see Section 11.2).
                handshakeState.Dispose();

                // no errors :)
                this.handshakeComplite = true;
            }
            finally
            {
                handshakeState?.Dispose();
                this.handshakeMutex.ReleaseMutex();
            }
        }
示例#4
0
        /// <summary>
        /// Disco peer initialization
        /// </summary>
        /// <param name="handshakeType">Noise handshake pattern</param>
        /// <param name="initiator">This party initiates connection</param>
        /// <param name="prologue">Prologue string, some data prior to handshake</param>
        /// <param name="s">local static key</param>
        /// <param name="e">local ephemeral key</param>
        /// <param name="rs">remote static key</param>
        /// <param name="re">remote ephemeral key</param>
        /// <returns>Initialized Disco handshake state</returns>
        public static HandshakeState InitializeDisco(
            NoiseHandshakeType handshakeType,
            bool initiator,
            byte[] prologue,
            KeyPair s,
            KeyPair e,
            KeyPair rs,
            KeyPair re)
        {
            var handshakePattern = HandshakePattern.GetPattern(handshakeType);

            var handshakeState = new HandshakeState
            {
                SymmetricState = new SymmetricState($"Noise_{handshakePattern.Name}_25519_STROBEv1.0.2"),
                Initiator      = initiator,
                ShouldWrite    = initiator
            };

            try
            {
                if (prologue != null)
                {
                    handshakeState.SymmetricState.MixHash(prologue);
                }

                if (s != null)
                {
                    handshakeState.S = s;
                }

                if (e != null)
                {
                    throw new NotSupportedException("disco: fallback patterns are not implemented");
                }

                if (rs != null)
                {
                    handshakeState.Rs = rs;
                }

                if (re != null)
                {
                    throw new NotSupportedException("disco: fallback patterns are not implemented");
                }

                //Calls MixHash() once for each public key listed in the pre-messages from handshake_pattern,
                //with the specified public key as input (see Section 7 for an explanation of pre-messages).
                //If both initiator and responder have pre-messages, the initiator's public keys are hashed first.

                // initiator pre-message pattern
                foreach (var token in handshakePattern.PreMessagePatterns[0])
                {
                    if (token == Tokens.TokenS)
                    {
                        if (initiator)
                        {
                            if (s == null)
                            {
                                throw new Exception("disco: the static key of the client should be set");
                            }

                            handshakeState.SymmetricState.MixHash(s.PublicKey);
                        }
                        else
                        {
                            if (rs == null)
                            {
                                throw new Exception("disco: the remote static key of the server should be set");
                            }

                            handshakeState.SymmetricState.MixHash(rs.PublicKey);
                        }
                    }
                    else
                    {
                        throw new Exception("disco: token of pre-message not supported");
                    }
                }

                // responder pre-message pattern
                foreach (var token in handshakePattern.PreMessagePatterns[1])
                {
                    if (token == Tokens.TokenS)
                    {
                        if (initiator)
                        {
                            if (rs == null)
                            {
                                throw new Exception("disco: the remote static key of the client should be set");
                            }

                            handshakeState.SymmetricState.MixHash(rs.PublicKey);
                        }
                        else
                        {
                            if (s == null)
                            {
                                throw new Exception("disco: the static key of the server should be set");
                            }

                            handshakeState.SymmetricState.MixHash(s.PublicKey);
                        }
                    }
                    else
                    {
                        throw new NotSupportedException("disco: token of pre - message not supported");
                    }
                }

                handshakeState.MessagePatterns = handshakePattern.MessagePatterns;
                return(handshakeState);
            }
            catch (Exception)
            {
                handshakeState.Dispose();
                throw;
            }
        }