PushMsgResult ProcessReady(ref Msg msg) { if (msg.Size < 30) { return(PushMsgResult.Error); } Span <byte> readyNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; var readyPlaintext = new byte[msg.Size - 14 - Curve25519XSalsa20Poly1305.TagLength]; Span <byte> readyBox = msg.Slice(14); ReadyNoncePrefix.CopyTo(readyNonce); msg.Slice(6, 8).CopyTo(readyNonce.Slice(16)); m_peerNonce = NetworkOrderBitsConverter.ToUInt64(msg, 6); bool isDecrypted = m_box.TryDecrypt(readyPlaintext, readyBox, readyNonce); if (!isDecrypted) { return(PushMsgResult.Error); } if (!ParseMetadata(readyPlaintext)) { return(PushMsgResult.Error); } Array.Clear(readyPlaintext, 0, readyPlaintext.Length); m_state = State.Connected; return(PushMsgResult.Ok); }
PushMsgResult ProcessHello(ref Msg msg) { if (!CheckBasicCommandStructure(ref msg)) { return(PushMsgResult.Error); } Span <byte> hello = msg; if (!IsCommand("HELLO", ref msg)) { return(PushMsgResult.Error); } if (hello.Length != 200) { return(PushMsgResult.Error); } byte major = hello[6]; byte minor = hello[7]; if (major != 1 || minor != 0) { // client HELLO has unknown version number return(PushMsgResult.Error); } // Save client's short-term public key (C') hello.Slice(80, 32).CopyTo(m_cnClientKey); Span <byte> helloNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; HelloNoncePrefix.CopyTo(helloNonce); hello.Slice(112, 8).CopyTo(helloNonce.Slice(16)); m_peerNonce = NetworkOrderBitsConverter.ToUInt64(hello, 112); using var box = new Curve25519XSalsa20Poly1305(m_secretKey, m_cnClientKey); Span <byte> helloPlaintext = stackalloc byte[80]; bool isDecrypted = box.TryDecrypt(helloPlaintext, hello.Slice(120, 80), helloNonce); if (!isDecrypted) { return(PushMsgResult.Error); } helloPlaintext.Clear(); m_state = State.SendingWelcome; return(PushMsgResult.Ok); }
PushMsgResult ProcessInitiate(ref Msg msg) { if (!CheckBasicCommandStructure(ref msg)) { return(PushMsgResult.Error); } Span <byte> initiate = msg; if (!IsCommand("INITIATE", ref msg)) { return(PushMsgResult.Error); } if (initiate.Length < 257) { return(PushMsgResult.Error); } Span <byte> cookieNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; Span <byte> cookiePlaintext = stackalloc byte[64]; Span <byte> cookieBox = initiate.Slice(25, 80); CookieNoncePrefix.CopyTo(cookieNonce); initiate.Slice(9, 16).CopyTo(cookieNonce.Slice(8)); using var secretBox = new XSalsa20Poly1305(m_cookieKey); bool decrypted = secretBox.TryDecrypt(cookiePlaintext, cookieBox, cookieNonce); if (!decrypted) { return(PushMsgResult.Error); } // Check cookie plain text is as expected [C' + s'] if (!SpanUtility.Equals(m_cnClientKey, cookiePlaintext.Slice(0, 32)) || !SpanUtility.Equals(m_cnSecretKey, cookiePlaintext.Slice(32, 32))) { return(PushMsgResult.Error); } Span <byte> initiateNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; byte[] initiatePlaintext = new byte[msg.Size - 113]; var initiateBox = initiate.Slice(113); InitiatieNoncePrefix.CopyTo(initiateNonce); initiate.Slice(105, 8).CopyTo(initiateNonce.Slice(16)); m_peerNonce = NetworkOrderBitsConverter.ToUInt64(initiate, 105); using var box = new Curve25519XSalsa20Poly1305(m_cnSecretKey, m_cnClientKey); bool decrypt = box.TryDecrypt(initiatePlaintext, initiateBox, initiateNonce); if (!decrypt) { return(PushMsgResult.Error); } Span <byte> vouchNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; Span <byte> vouchPlaintext = stackalloc byte[64]; Span <byte> vouchBox = new Span <byte>(initiatePlaintext, 48, 80); var clientKey = new Span <byte>(initiatePlaintext, 0, 32); VouchNoncePrefix.CopyTo(vouchNonce); new Span <byte>(initiatePlaintext, 32, 16).CopyTo(vouchNonce.Slice(8)); using var box2 = new Curve25519XSalsa20Poly1305(m_cnSecretKey, clientKey); decrypt = box2.TryDecrypt(vouchPlaintext, vouchBox, vouchNonce); if (!decrypt) { return(PushMsgResult.Error); } // What we decrypted must be the client's short-term public key if (!SpanUtility.Equals(vouchPlaintext.Slice(0, 32), m_cnClientKey)) { return(PushMsgResult.Error); } // Create the session box m_box = new Curve25519XSalsa20Poly1305(m_cnSecretKey, m_cnClientKey); // This supports the Stonehouse pattern (encryption without authentication). m_state = State.SendingReady; if (!ParseMetadata(new Span <byte>(initiatePlaintext, 128, initiatePlaintext.Length - 128 - 16))) { return(PushMsgResult.Error); } vouchPlaintext.Clear(); Array.Clear(initiatePlaintext, 0, initiatePlaintext.Length); return(PushMsgResult.Ok); }
public override PushMsgResult Decode(ref Msg msg) { if (!CheckBasicCommandStructure(ref msg)) { return(PushMsgResult.Error); } int size = msg.Size; if (!IsCommand("MESSAGE", ref msg)) { return(PushMsgResult.Error); } if (size < 33) // Size of 16 bytes of command + 16 bytes of MAC + 1 byte for flag { return(PushMsgResult.Error); } Span <byte> messageNonce = stackalloc byte[Curve25519XSalsa20Poly1305.NonceLength]; m_decodeNoncePrefix.CopyTo(messageNonce); msg.Slice(8, 8).CopyTo(messageNonce.Slice(16)); UInt64 nonce = NetworkOrderBitsConverter.ToUInt64(msg, 8); if (nonce <= m_peerNonce) { return(PushMsgResult.Error); } m_peerNonce = nonce; Msg plain = new Msg(); plain.InitPool(msg.Size - 16 - Curve25519XSalsa20Poly1305.TagLength); Assumes.NotNull(m_box); var isAuthenticate = m_box.TryDecrypt(plain, msg.Slice(16), messageNonce); if (!isAuthenticate) { plain.Close(); return(PushMsgResult.Error); } msg.Move(ref plain); byte flags = msg[0]; if ((flags & 0x01) != 0) { msg.SetFlags(MsgFlags.More); } if ((flags & 0x02) != 0) { msg.SetFlags(MsgFlags.Command); } msg.TrimPrefix(1); return(PushMsgResult.Ok); }