예제 #1
0
        public unsafe void TryWriteUInt64LittleEndian_FailureCases(int spanLength, int offset)
        {
            // fabricate a span of the correct length - we can't deref it because it'll AV
            Span <byte> span = new Span <byte>((byte *)null, spanLength);

            Assert.False(SpanUtility.TryWriteUInt64LittleEndian(span, offset, 0xdeadbeef_deadbeef));
        }
예제 #2
0
        /// <summary>
        /// Parses a metadata.
        /// Metadata consists of a list of properties consisting of
        /// name and value as size-specified strings.
        /// </summary>
        /// <returns>Returns true on success and false on error.</returns>
        protected bool ParseMetadata(Span <byte> source)
        {
            while (source.Length > 1)
            {
                int nameLength = source[0];
                source = source.Slice(NameLengthSize);
                if (source.Length < nameLength)
                {
                    break;
                }

                string name = SpanUtility.ToAscii(source.Slice(0, nameLength));
                source = source.Slice(nameLength);
                if (source.Length < ValueLengthSize)
                {
                    break;
                }

                int valueLength = NetworkOrderBitsConverter.ToInt32(source);
                source = source.Slice(ValueLengthSize);
                if (source.Length < valueLength)
                {
                    break;
                }

                byte[] value = new byte[valueLength];
                source.Slice(0, valueLength).CopyTo(value);
                source = source.Slice(valueLength);

                if (name == ZmtpPropertyIdentity && Options.RecvIdentity)
                {
                    PeerIdentity = value;
                }
                else if (name == ZmtpPropertySocketType)
                {
                    if (!CheckSocketType(Encoding.ASCII.GetString(value)))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!GetProperty(name, value, valueLength))
                    {
                        return(false);
                    }
                }
            }

            if (source.Length > 0)
            {
                return(false);
            }

            return(true);
        }
예제 #3
0
            internal override int EncodeUtf16(Rune value, Span <char> destination)
            {
                uint utf8lsb = (uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue((uint)value.Value);

                if (!SpanUtility.IsValidIndex(destination, 2))
                {
                    goto OutOfSpace;
                }
                destination[0] = '%';
                HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 1);
                if ((utf8lsb >>= 8) == 0)
                {
                    return(3);
                }                                       // "%XX"

                if (!SpanUtility.IsValidIndex(destination, 5))
                {
                    goto OutOfSpace;
                }
                destination[3] = '%';
                HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 4);
                if ((utf8lsb >>= 8) == 0)
                {
                    return(6);
                }                                       // "%XX%YY"

                if (!SpanUtility.IsValidIndex(destination, 8))
                {
                    goto OutOfSpace;
                }
                destination[6] = '%';
                HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 7);
                if ((utf8lsb >>= 8) == 0)
                {
                    return(9);
                }                                       // "%XX%YY%ZZ"

                if (!SpanUtility.IsValidIndex(destination, 11))
                {
                    goto OutOfSpace;
                }
                destination[9] = '%';
                HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 10);
                return(12);  // "%XX%YY%ZZ%WW"

OutOfSpace:

                return(-1);
            }
예제 #4
0
        public void TryWriteUInt64LittleEndian_SuccessCases()
        {
            Span <byte> span = stackalloc byte[10] {
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09
            };

            Assert.True(SpanUtility.TryWriteUInt64LittleEndian(span, 0, 0x10203040_50607080));
            Assert.Equal(new byte[] { 0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x08, 0x09 }, span.ToArray());

            Assert.True(SpanUtility.TryWriteUInt64LittleEndian(span, 1, 0x1a2a3a4a_5a6a7a8a));
            Assert.Equal(new byte[] { 0x80, 0x8a, 0x7a, 0x6a, 0x5a, 0x4a, 0x3a, 0x2a, 0x1a, 0x09 }, span.ToArray());

            Assert.True(SpanUtility.TryWriteUInt64LittleEndian(span, 2, 0x1f2f3f4f_5f6f7f8f));
            Assert.Equal(new byte[] { 0x80, 0x8a, 0x8f, 0x7f, 0x6f, 0x5f, 0x4f, 0x3f, 0x2f, 0x1f }, span.ToArray());
        }
    }
예제 #5
0
            // Writes a scalar value as a JavaScript-escaped character (or sequence of characters).
            // See ECMA-262, Sec. 7.8.4, and ECMA-404, Sec. 9
            // https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
            // https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
            //
            // ECMA-262 allows encoding U+000B as "\v", but ECMA-404 does not.
            // Both ECMA-262 and ECMA-404 allow encoding U+002F SOLIDUS as "\/"
            // (in ECMA-262 this character is a NonEscape character); however, we
            // don't encode SOLIDUS by default unless the caller has provided an
            // explicit bitmap which does not contain it. In this case we'll assume
            // that the caller didn't want a SOLIDUS written to the output at all,
            // so it should be written using "\u002F" encoding.
            // HTML-specific characters (including apostrophe and quotes) will
            // be written out as numeric entities for defense-in-depth.

            internal override int EncodeUtf8(Rune value, Span <byte> destination)
            {
                if (_preescapedMap.TryLookup(value, out byte preescapedForm))
                {
                    if (!SpanUtility.IsValidIndex(destination, 1))
                    {
                        goto OutOfSpace;
                    }
                    destination[0] = (byte)'\\';
                    destination[1] = preescapedForm;
                    return(2);

OutOfSpace:
                    return(-1);
                }

                return(TryEncodeScalarAsHex(this, value, destination));
예제 #6
0
            internal override int EncodeUtf8(Rune value, Span <byte> destination)
            {
                if (value.Value == '<')
                {
                    if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'l', (byte)'t', (byte)';'))
                    {
                        goto OutOfSpace;
                    }
                    return(4);
                }
                else if (value.Value == '>')
                {
                    if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'g', (byte)'t', (byte)';'))
                    {
                        goto OutOfSpace;
                    }
                    return(4);
                }
                else if (value.Value == '&')
                {
                    if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'a', (byte)'m', (byte)'p', (byte)';'))
                    {
                        goto OutOfSpace;
                    }
                    return(5);
                }
                else if (value.Value == '\"')
                {
                    if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'q', (byte)'u', (byte)'o', (byte)'t', (byte)';'))
                    {
                        goto OutOfSpace;
                    }
                    return(6);
                }
                else
                {
                    return(TryEncodeScalarAsHex(this, (uint)value.Value, destination));
                }

OutOfSpace:

                return(-1);
예제 #7
0
#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller
                static int TryEncodeScalarAsHex(object @this, Rune value, Span <byte> destination)
#pragma warning restore IDE0060
                {
                    if (value.IsBmp)
                    {
                        // Write 6 bytes: "\uXXXX"
                        if (!SpanUtility.IsValidIndex(destination, 5))
                        {
                            goto OutOfSpaceInner;
                        }
                        destination[0] = (byte)'\\';
                        destination[1] = (byte)'u';
                        HexConverter.ToBytesBuffer((byte)value.Value, destination, 4);
                        HexConverter.ToBytesBuffer((byte)((uint)value.Value >> 8), destination, 2);
                        return(6);
                    }
                    else
                    {
                        // Write 12 bytes: "\uXXXX\uYYYY"
                        UnicodeHelpers.GetUtf16SurrogatePairFromAstralScalarValue((uint)value.Value, out char highSurrogate, out char lowSurrogate);
                        if (!SpanUtility.IsValidIndex(destination, 11))
                        {
                            goto OutOfSpaceInner;
                        }
                        destination[0] = (byte)'\\';
                        destination[1] = (byte)'u';
                        HexConverter.ToBytesBuffer((byte)highSurrogate, destination, 4);
                        HexConverter.ToBytesBuffer((byte)((uint)highSurrogate >> 8), destination, 2);
                        destination[6] = (byte)'\\';
                        destination[7] = (byte)'u';
                        HexConverter.ToBytesBuffer((byte)lowSurrogate, destination, 10);
                        HexConverter.ToBytesBuffer((byte)((uint)lowSurrogate >> 8), destination, 8);
                        return(12);
                    }

OutOfSpaceInner:

                    return(-1);
                }
예제 #8
0
        PushMsgResult ProcessErrorCommand(Span <byte> commandData)
        {
            int fixedPrefixSize = ErrorCommandName.Length + 1 + ErrorReasonLengthSize;

            if (commandData.Length < fixedPrefixSize)
            {
                return(PushMsgResult.Error);
            }

            int errorReasonLength = commandData[ErrorCommandName.Length + 1];

            if (errorReasonLength > commandData.Length - fixedPrefixSize)
            {
                return(PushMsgResult.Error);
            }

            string errorReason = SpanUtility.ToAscii(commandData.Slice(fixedPrefixSize, errorReasonLength));

            // TODO: handle error, nothing todo at the moment as monitoring and zap are not yet implemented

            m_errorCommandReceived = true;
            return(PushMsgResult.Ok);
        }
예제 #9
0
#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller
                static int TryEncodeScalarAsHex(object @this, uint scalarValue, Span <byte> destination)
#pragma warning restore IDE0060
                {
                    UnicodeDebug.AssertIsValidScalar(scalarValue);

                    // See comments in the UTF-16 equivalent method later in this file.

                    int idxOfSemicolon = (int)((uint)BitOperations.Log2(scalarValue) / 4) + 4;

                    Debug.Assert(4 <= idxOfSemicolon && idxOfSemicolon <= 9, "Expected '&#x0;'..'&#x10FFFF;'.");

                    if (!SpanUtility.IsValidIndex(destination, idxOfSemicolon))
                    {
                        goto OutOfSpaceInner;
                    }
                    destination[idxOfSemicolon] = (byte)';';

                    if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'#', (byte)'x', (byte)'0'))
                    {
                        Debug.Fail("We should've had enough room to write 4 bytes.");
                    }

                    destination = destination.Slice(3, idxOfSemicolon - 3);
                    for (int i = destination.Length - 1; SpanUtility.IsValidIndex(destination, i); i--)
                    {
                        char asUpperHex = HexConverter.ToCharUpper((int)scalarValue);
                        destination[i] = (byte)asUpperHex;
                        scalarValue  >>= 4; // write a nibble - not a byte - at a time
                    }

                    return(destination.Length + 4);

OutOfSpaceInner:

                    return(-1);
                }
예제 #10
0
        public void TryWriteFiveBytes()
        {
            Span <byte> span = stackalloc byte[0];

            Assert.False(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50));

            span = stackalloc byte[4] {
                100, 101, 102, 103
            };
            Assert.False(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50));
            Assert.Equal(new byte[] { 100, 101, 102, 103 }, span.ToArray());

            span = stackalloc byte[5] {
                100, 101, 102, 103, 104
            };
            Assert.True(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50));
            Assert.Equal(new byte[] { 10, 20, 30, 40, 50 }, span.ToArray());

            span = stackalloc byte[6] {
                100, 101, 102, 103, 104, 105
            };
            Assert.True(SpanUtility.TryWriteBytes(span, 10, 20, 30, 40, 50));
            Assert.Equal(new byte[] { 10, 20, 30, 40, 50, 105 }, span.ToArray());
        }
예제 #11
0
        public void TryWriteSixChars()
        {
            Span <char> span = stackalloc char[0];

            Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e', 'f'));

            span = stackalloc char[5] {
                '0', '1', '2', '3', '4'
            };
            Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e', 'f'));
            Assert.Equal(new char[] { '0', '1', '2', '3', '4' }, span.ToArray());

            span = stackalloc char[6] {
                '0', '1', '2', '3', '4', '5'
            };
            Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e', 'f'));
            Assert.Equal(new char[] { 'a', 'b', 'c', 'd', 'e', 'f' }, span.ToArray());

            span = stackalloc char[7] {
                '0', '1', '2', '3', '4', '5', '6'
            };
            Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd', 'e', 'f'));
            Assert.Equal(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', '6' }, span.ToArray());
        }
예제 #12
0
        public void TryWriteFourChars()
        {
            Span <char> span = stackalloc char[0];

            Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd'));

            span = stackalloc char[3] {
                '0', '1', '2'
            };
            Assert.False(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd'));
            Assert.Equal(new char[] { '0', '1', '2' }, span.ToArray());

            span = stackalloc char[4] {
                '0', '1', '2', '3'
            };
            Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd'));
            Assert.Equal(new char[] { 'a', 'b', 'c', 'd' }, span.ToArray());

            span = stackalloc char[5] {
                '0', '1', '2', '3', '4'
            };
            Assert.True(SpanUtility.TryWriteChars(span, 'a', 'b', 'c', 'd'));
            Assert.Equal(new char[] { 'a', 'b', 'c', 'd', '4' }, span.ToArray());
        }
예제 #13
0
        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);
        }
예제 #14
0
        public void IsValidIndex_Span(string inputData, int index, bool expectedValue)
        {
            Span <char> span = inputData.ToCharArray();

            Assert.Equal(expectedValue, SpanUtility.IsValidIndex(span, index));
        }
예제 #15
0
        public void IsValidIndex_ReadOnlySpan(string inputData, int index, bool expectedValue)
        {
            ReadOnlySpan <char> span = inputData.AsSpan();

            Assert.Equal(expectedValue, SpanUtility.IsValidIndex(span, index));
        }