Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CBOR] Implement Write/ReadEncodedValue() methods #34650

Merged
merged 5 commits into from
Apr 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -158,5 +158,101 @@ public static void VerifyMap(CborReader reader, object[] expectedValues, bool ex
reader.ReadEndMap();
}
}

public static string[] SampleCborValues =>
new[]
{
// numeric values
"01",
"37",
"3818",
"1818",
"190100",
"390100",
"1a000f4240",
"3affffffff",
// byte strings
"40",
"4401020304",
"5f41ab40ff",
// text strings
"60",
"6161",
"6449455446",
"7f62616260ff",
// Arrays
"80",
"840120604107",
"8301820203820405",
"9f182aff",
// Maps
"a0",
"a201020304",
"a1a1617802182a",
"bf01020304ff",
// tagged values
"c202",
"d82076687474703a2f2f7777772e6578616d706c652e636f6d",
// special values
"f4",
"f6",
"fa47c35000",
};

public static string[] InvalidCborValues =>
new[]
{
"",
// numeric types with missing bytes
"18",
"19ff",
"1affffff",
"1bffffffffffffff",
"38",
"39ff",
"3affffff",
"3bffffffffffffff",
// definite-length strings with missing bytes
"41",
"4201",
"61",
"6261",
// invalid utf8 strings
"61ff",
"62f090",
// indefinite-length strings with missing break byte
"5f41ab40",
"7f62616260",
// definite-length arrays with missing elements
"81",
"8201",
// definite-length maps with missing fields
"a1",
"a20102",
// maps with odd number of elements
"a101",
"a2010203",
"bf01ff",
// indefinite-length collections with missing break byte
"9f",
"9f01",
"bf",
"bf0102",
// tags missing data
"d8",
"d9ff",
"daffffff",
"daffffffffff",
// valid tag not followed by value
"c2",
// floats missing data
"f9ff",
"faffffff",
"fbffffffffffffff",
// special value missing data
"f8",
// invalid special value
"f81f",
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace System.Security.Cryptography.Encoding.Tests.Cbor
public partial class CborReaderTests
{
[Theory]
[MemberData(nameof(SampleValues))]
[MemberData(nameof(SkipTestInputs))]
public static void SkipValue_RootValue_HappyPath(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
Expand All @@ -26,7 +26,7 @@ public static void SkipValue_RootValue_HappyPath(string hexEncoding)
}

[Theory]
[MemberData(nameof(SampleValues))]
[MemberData(nameof(SkipTestInputs))]
public static void SkipValue_NestedValue_HappyPath(string hexEncoding)
{
byte[] encoding = $"8301{hexEncoding}03".HexToByteArray();
Expand All @@ -41,7 +41,7 @@ public static void SkipValue_NestedValue_HappyPath(string hexEncoding)
}

[Theory]
[MemberData(nameof(SampleValues))]
[MemberData(nameof(SkipTestInputs))]
public static void SkipValue_TaggedValue_HappyPath(string hexEncoding)
{
byte[] encoding = $"c2{hexEncoding}".HexToByteArray();
Expand All @@ -63,11 +63,7 @@ public static void SkipValue_NotAtValue_ShouldThrowInvalidOperationException()
}

[Theory]
[InlineData("")]
[InlineData("ff")]
[InlineData("c2")]
[InlineData("bf01ff")]
[InlineData("7f01ff")]
[MemberData(nameof(SkipTestInvalidCborInputs))]
public static void SkipValue_InvalidFormat_ShouldThrowFormatException(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
Expand All @@ -79,12 +75,14 @@ public static void SkipValue_InvalidFormat_ShouldThrowFormatException(string hex
[Theory]
[InlineData("61ff")]
[InlineData("62f090")]
public static void SkipValue_InvalidUtf8_ShouldDecoderFallbackException(string hexEncoding)
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
public static void SkipValue_InvalidUtf8_ShouldThrowFormatException(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var reader = new CborReader(encoding);

Assert.Throws<DecoderFallbackException>(() => reader.SkipValue());
FormatException exn = Assert.Throws<FormatException>(() => reader.SkipValue());
Assert.NotNull(exn.InnerException);
Assert.IsType<DecoderFallbackException>(exn.InnerException);
}

[Theory]
Expand All @@ -104,39 +102,7 @@ public static void SkipValue_ExtremelyNestedValues_ShouldNotStackOverflow(int de
Assert.Equal(CborReaderState.Finished, reader.Peek());
}

public static IEnumerable<object[]> SampleValues =>
new string[]
{
// numeric values
"01",
"1a000f4240",
"3affffffff",
// byte strings
"40",
"4401020304",
"5f41ab40ff",
// text strings
"60",
"6161",
"6449455446",
"7f62616260ff",
// Arrays
"80",
"840120604107",
"8301820203820405",
"9f182aff",
// Maps
"a0",
"a201020304",
"a1a1617802182a",
"bf01020304ff",
// tagged values
"c202",
"d82076687474703a2f2f7777772e6578616d706c652e636f6d",
// special values
"f4",
"f6",
"fa47c35000",
}.Select(x => new object[] { x });
public static IEnumerable<object[]> SkipTestInputs => SampleCborValues.Select(x => new [] { x });
public static IEnumerable<object[]> SkipTestInvalidCborInputs => InvalidCborValues.Select(x => new[] { x });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -486,23 +486,27 @@ public static void ReadTextString_StringLengthTooLarge_ShouldThrowOverflowExcept
[Theory]
[InlineData("61ff")]
[InlineData("62f090")]
public static void ReadTextString_InvalidUnicode_ShouldThrowDecoderFallbackException(string hexEncoding)
public static void ReadTextString_InvalidUnicode_ShouldThrowFormatException(string hexEncoding)
{
byte[] data = hexEncoding.HexToByteArray();
var reader = new CborReader(data);
Assert.Throws<System.Text.DecoderFallbackException>(() => reader.ReadTextString());
FormatException exn = Assert.Throws<FormatException>(() => reader.ReadTextString());
Assert.NotNull(exn.InnerException);
Assert.IsType<System.Text.DecoderFallbackException>(exn.InnerException);
}

[Theory]
[InlineData("61ff")]
[InlineData("62f090")]
public static void TryReadTextString_InvalidUnicode_ShouldThrowDecoderFallbackException(string hexEncoding)
public static void TryReadTextString_InvalidUnicode_ShouldThrowFormatException(string hexEncoding)
{
byte[] data = hexEncoding.HexToByteArray();
char[] buffer = new char[32];
var reader = new CborReader(data);

Assert.Throws<System.Text.DecoderFallbackException>(() => reader.TryReadTextString(buffer, out int _));
FormatException exn = Assert.Throws<FormatException>(() => reader.TryReadTextString(buffer, out int _));
Assert.NotNull(exn.InnerException);
Assert.IsType<System.Text.DecoderFallbackException>(exn.InnerException);
}

[Fact]
Expand Down Expand Up @@ -614,15 +618,15 @@ public static void ReadTextString_IndefiniteLengthConcatenated_ContainingInvalid
}

[Fact]
public static void ReadTextString_IndefiniteLengthConcatenated_InvalidUtf8Chunks_ShouldThrowDecoderFallbackException()
public static void ReadTextString_IndefiniteLengthConcatenated_InvalidUtf8Chunks_ShouldThrowFormatException()
{
// while the concatenated string is valid utf8, the individual chunks are not,
// which is in violation of the CBOR format.

string hexEncoding = "7f62f090628591ff";
byte[] data = hexEncoding.HexToByteArray();
var reader = new CborReader(data);
Assert.Throws<DecoderFallbackException>(() => reader.ReadTextString());
Assert.Throws<FormatException>(() => reader.ReadTextString());
eiriktsarpalis marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Test.Cryptography;
using Xunit;

Expand Down Expand Up @@ -74,5 +76,44 @@ public static void CborReader_ReadingTwoPrimitiveValues_ShouldThrowInvalidOperat
Assert.Equal(CborReaderState.Finished, reader.Peek());
Assert.Throws<InvalidOperationException>(() => reader.ReadInt64());
}

[Theory]
[MemberData(nameof(EncodedValueInputs))]
public static void ReadEncodedValue_RootValue_HappyPath(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var reader = new CborReader(encoding);

byte[] encodedValue = reader.ReadEncodedValue().ToArray();
Assert.Equal(hexEncoding, encodedValue.ByteArrayToHex().ToLower());
}

[Theory]
[MemberData(nameof(EncodedValueInputs))]
public static void ReadEncodedValue_NestedValue_HappyPath(string hexEncoding)
{
byte[] encoding = $"8301{hexEncoding}60".HexToByteArray();

var reader = new CborReader(encoding);

reader.ReadStartArray();
reader.ReadInt64();
byte[] encodedValue = reader.ReadEncodedValue().ToArray();

Assert.Equal(hexEncoding, encodedValue.ByteArrayToHex().ToLower());
}

[Theory]
[MemberData(nameof(EncodedValueInvalidInputs))]
public static void ReadEncodedValue_InvalidCbor_ShouldThrowFormatException(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var reader = new CborReader(encoding);

Assert.Throws<FormatException>(() => reader.ReadEncodedValue());
}

public static IEnumerable<object[]> EncodedValueInputs => CborReaderTests.SampleCborValues.Select(x => new[] { x });
public static IEnumerable<object[]> EncodedValueInvalidInputs => CborReaderTests.InvalidCborValues.Select(x => new[] { x });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#nullable enable

using System.Linq;
using Test.Cryptography;
using Xunit;

namespace System.Security.Cryptography.Encoding.Tests.Cbor
Expand All @@ -15,13 +16,22 @@ internal static class Helpers
{
public const string MapPrefixIdentifier = "_map";

public const string EncodedPrefixIdentifier = "_encodedValue";

// Since we inject test data using attributes, meed to represent both arrays and maps using object arrays.
// To distinguish between the two types, we prepend map representations using a string constant.
public static bool IsCborMapRepresentation(object[] values)
{
return values.Length % 2 == 1 && values[0] is string s && s == MapPrefixIdentifier;
}

public static bool IsEncodedValueRepresentation(object[] values)
{
return values.Length == 2 &&
values[0] is string s && s == EncodedPrefixIdentifier &&
values[1] is string;
}

public static void WriteValue(CborWriter writer, object value, bool useDefiniteLengthCollections = true)
{
switch (value)
Expand All @@ -38,6 +48,11 @@ public static void WriteValue(CborWriter writer, object value, bool useDefiniteL
case byte[][] chunks: WriteChunkedByteString(writer, chunks); break;
case string[] chunks: WriteChunkedTextString(writer, chunks); break;
case object[] nested when IsCborMapRepresentation(nested): WriteMap(writer, nested, useDefiniteLengthCollections); break;
case object[] nested when IsEncodedValueRepresentation(nested):
byte[] encodedValue = ((string)nested[1]).HexToByteArray();
writer.WriteEncodedValue(encodedValue);
break;

case object[] nested: WriteArray(writer, nested, useDefiniteLengthCollections); break;
default: throw new ArgumentException($"Unrecognized argument type {value.GetType()}");
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ public static void WriteTextString_IndefiniteLength_SingleValue_HappyPath(string
}

[Fact]
public static void WriteTextString_InvalidUnicodeString_ShouldThrowEncoderFallbackException()
public static void WriteTextString_InvalidUnicodeString_ShouldThrowArgumentException()
{
// NB Xunit's InlineDataAttribute will corrupt string literals containing invalid unicode
string invalidUnicodeString = "\ud800";
using var writer = new CborWriter();
Assert.Throws<System.Text.EncoderFallbackException>(() => writer.WriteTextString(invalidUnicodeString));
ArgumentException exn = Assert.Throws<ArgumentException>(() => writer.WriteTextString(invalidUnicodeString));
Assert.NotNull(exn.InnerException);
Assert.IsType<System.Text.EncoderFallbackException>(exn.InnerException);
}

[Theory]
Expand Down
Loading