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)); }
/// <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); }
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); }
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()); } }
// 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));
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);
#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); }
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); }
#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 '�'..''."); 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); }
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()); }
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()); }
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()); }
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 void IsValidIndex_Span(string inputData, int index, bool expectedValue) { Span <char> span = inputData.ToCharArray(); Assert.Equal(expectedValue, SpanUtility.IsValidIndex(span, index)); }
public void IsValidIndex_ReadOnlySpan(string inputData, int index, bool expectedValue) { ReadOnlySpan <char> span = inputData.AsSpan(); Assert.Equal(expectedValue, SpanUtility.IsValidIndex(span, index)); }