Skip to content
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
6 changes: 6 additions & 0 deletions src/libraries/System.Formats.Cbor/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@
<data name="Cbor_Reader_InvalidCbor_InvalidIntegerEncoding" xml:space="preserve">
<value>CBOR initial byte contains invalid integer encoding</value>
</data>
<data name="Cbor_Reader_InvalidCbor_InvalidSimpleValueEncoding" xml:space="preserve">
<value>CBOR initial byte contains reserved additional information.</value>
</data>
<data name="Cbor_Reader_InvalidCbor_KeyMissingValue" xml:space="preserve">
<value>The current CBOR map contains an incomplete key/value pair.</value>
</data>
Expand Down Expand Up @@ -225,6 +228,9 @@
<data name="Cbor_Reader_InvalidUnixTimeEncoding" xml:space="preserve">
<value>Not a valid tagged unix time encoding.</value>
</data>
<data name="Cbor_Reader_UnixTimeOutOfRange" xml:space="preserve">
<value>The decoded Unix time is outside the range of DateTimeOffset.</value>
</data>
<data name="Cbor_Reader_InvalidBigNumEncoding" xml:space="preserve">
<value>Not a valid tagged bignum encoding.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ public CborSimpleValue ReadSimpleValue()
AdvanceDataItemCounters();
return (CborSimpleValue)value;
default:
// Additional information values 28-30 are reserved: https://www.rfc-editor.org/rfc/rfc8949.html#section-3.3
if (header.AdditionalInfo is > CborAdditionalInfo.Additional64BitData and < CborAdditionalInfo.IndefiniteLength)
{
throw new CborContentException(SR.Cbor_Reader_InvalidCbor_InvalidSimpleValueEncoding);
}

throw new InvalidOperationException(SR.Cbor_Reader_NotASimpleValueEncoding);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ public DateTimeOffset ReadDateTimeOffset()
/// <returns>The decoded value.</returns>
/// <exception cref="InvalidOperationException"><para>The next data item does not have the correct major type.</para>
/// <para>-or-</para>
/// <para>The next date item does not have the correct semantic tag.</para></exception>
/// <para>The next date item does not have the correct semantic tag.</para>
/// <para>-or-</para>
/// <para>The decoded Unix time is outside the range supported by <see cref="DateTimeOffset" />.</para></exception>
/// <exception cref="CborContentException"><para>The next value has an invalid CBOR encoding.</para>
/// <para>-or-</para>
/// <para>There was an unexpected end of CBOR encoding data.</para>
Expand Down Expand Up @@ -131,9 +133,15 @@ public DateTimeOffset ReadUnixTimeSeconds()
throw new CborContentException(SR.Cbor_Reader_InvalidUnixTimeEncoding);
}
}
catch
catch (Exception e)
{
RestoreCheckpoint(in checkpoint);

if (e is ArgumentOutOfRangeException or OverflowException)
{
throw new InvalidOperationException(SR.Cbor_Reader_UnixTimeOutOfRange, e);
}

throw;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Test.Cryptography;
Expand Down Expand Up @@ -310,8 +311,21 @@ public static void VerifyMap(CborReader reader, object[] expectedValues, bool ex
{
"bf01ff", // indefinite-length map key missing a value
"daffffffffff", // tag followed by break byte
"fc", // reserved additional information in major type 7
"fd",
"fe",
"81fc", // reserved initial byte in an array
"a101fd", // reserved initial byte as a map value
"c1fe", // tag followed by a reserved initial byte
};

public static string[] InvalidCborValues => TruncatedCborValues.Concat(MalformedCborValues).ToArray();

public static IEnumerable<object[]> ReservedSimpleValueInputs =>
from mode in new[] { CborConformanceMode.Lax, CborConformanceMode.Strict, CborConformanceMode.Canonical, CborConformanceMode.Ctap2Canonical }
from isFinalBlock in new[] { true, false }
where isFinalBlock || mode == CborConformanceMode.Lax
from hexEncoding in new[] { "fc", "fd", "fe" }
select new object[] { mode, isFinalBlock, hexEncoding };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,42 @@ public static void ReadSimpleValue_UnsupportedRanges_UnsupportedConformance_Shou
Assert.Throws<CborContentException>(() => reader.ReadSimpleValue());
}

[Theory]
[MemberData(nameof(ReservedSimpleValueInputs))]
public static void ReadSimpleValue_ReservedAdditionalInfo_ShouldThrowCborContentException(CborConformanceMode conformanceMode, bool isFinalBlock, string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var reader = new CborReader(encoding, new CborReaderOptions { ConformanceMode = conformanceMode }, isFinalBlock);

Assert.Throws<CborContentException>(() => reader.ReadSimpleValue());
Assert.Equal(encoding.Length, reader.BytesRemaining);
Assert.Equal(0, reader.CurrentDepth);
}

[Fact]
public static void ReadSimpleValue_EndOfIndefiniteLengthArray_ShouldThrowInvalidOperationException()
{
var reader = new CborReader("9fff".HexToByteArray());
reader.ReadStartArray();

Assert.Throws<InvalidOperationException>(() => reader.ReadSimpleValue());
Assert.Equal(1, reader.BytesRemaining);
Assert.Equal(CborReaderState.EndArray, reader.PeekState());

reader.ReadEndArray();
Assert.Equal(CborReaderState.Finished, reader.PeekState());
}

[Theory]
[InlineData("01")] // integer
[InlineData("40")] // empty text string
[InlineData("60")] // empty byte string
[InlineData("80")] // []
[InlineData("a0")] // {}
[InlineData("c202")] // tagged value
[InlineData("f93c00")] // half-precision float
[InlineData("fa3f800000")] // single-precision float
[InlineData("fb3ff0000000000000")] // double-precision float
public static void ReadSimpleValue_InvalidTypes_ShouldThrowInvalidOperationException(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,61 @@ public static void SkipValue_InvalidFormat_ShouldThrowCborContentException(strin
Assert.Equal(encoding.Length, reader.BytesRemaining);
}

[Theory]
[MemberData(nameof(ReservedSimpleValueInputs))]
public static void SkipValue_ReservedAdditionalInfo_ShouldThrowCborContentException(CborConformanceMode conformanceMode, bool isFinalBlock, string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var options = new CborReaderOptions { ConformanceMode = conformanceMode, AllowMultipleRootLevelValues = true };
var reader = new CborReader(encoding, options, isFinalBlock);

foreach (bool disableConformanceModeChecks in new[] { false, true })
{
Assert.Throws<CborContentException>(() => reader.SkipValue(disableConformanceModeChecks));
Assert.Equal(encoding.Length, reader.BytesRemaining);
Assert.Equal(0, reader.CurrentDepth);

Assert.Throws<CborContentException>(() => reader.TrySkipValue(disableConformanceModeChecks));
Assert.Equal(encoding.Length, reader.BytesRemaining);
Assert.Equal(0, reader.CurrentDepth);

Assert.Throws<CborContentException>(() => reader.ReadEncodedValue(disableConformanceModeChecks));
Assert.Equal(encoding.Length, reader.BytesRemaining);
Assert.Equal(0, reader.CurrentDepth);
}
}

[Theory]
[MemberData(nameof(ReservedSimpleValueInputs))]
public static void SkipToParent_ReservedAdditionalInfo_ShouldThrowCborContentExceptionAndPreserveState(CborConformanceMode conformanceMode, bool isFinalBlock, string hexEncoding)
{
byte[] encoding = $"a201018102{hexEncoding}".HexToByteArray(); // { 1: 1, [2]: <invalid> }
var reader = new CborReader(encoding, new CborReaderOptions { ConformanceMode = conformanceMode }, isFinalBlock);
reader.ReadStartMap();
Assert.Equal(1, reader.ReadInt32());
Assert.Equal(1, reader.ReadInt32());
int bytesRemaining = reader.BytesRemaining;

foreach (bool disableConformanceModeChecks in new[] { false, true })
{
Assert.Throws<CborContentException>(() => reader.SkipToParent(disableConformanceModeChecks));
Assert.Equal(bytesRemaining, reader.BytesRemaining);
Assert.Equal(1, reader.CurrentDepth);
Assert.Equal(CborReaderState.StartArray, reader.PeekState());

Assert.Throws<CborContentException>(() => reader.TrySkipToParent(disableConformanceModeChecks));
Assert.Equal(bytesRemaining, reader.BytesRemaining);
Assert.Equal(1, reader.CurrentDepth);
Assert.Equal(CborReaderState.StartArray, reader.PeekState());
}

Assert.Equal(1, reader.ReadStartArray());
Assert.Equal(2, reader.ReadInt32());
reader.ReadEndArray();
Assert.Throws<CborContentException>(() => reader.ReadSimpleValue());
Assert.Equal(1, reader.BytesRemaining);
}

[Theory]
[InlineData("61ff")]
[InlineData("62f090")]
Expand Down Expand Up @@ -364,6 +419,12 @@ public static void TrySkipValue_FinalBlock_InvalidValue_ShouldThrowCborContentEx
[InlineData("daffffffffff")] // tag followed by break byte
[InlineData("1c")] // reserved additional information value
[InlineData("ff")] // break byte at the root context
[InlineData("fc")]
[InlineData("fd")]
[InlineData("fe")]
[InlineData("81fc")]
[InlineData("a101fd")]
[InlineData("c1fe")]
public static void TrySkipValue_NotFinalBlock_MalformedData_ShouldThrowCborContentException(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ public static void ReadDateTimeOffset_StrictConformance_OnError_ShouldPreserveRe
[InlineData("1970-01-01T00:00:00Z", "c100")]
[InlineData("1969-12-31T23:59:59Z", "c120")]
[InlineData("1960-01-01T00:00:00Z", "c13a12cff77f")]
[InlineData("0001-01-01T00:00:00Z", "c13b0000000e7791f6ff")]
[InlineData("9999-12-31T23:59:59Z", "c11b0000003afff4417f")]
public static void ReadUnixTimeSeconds_SingleValue_HappyPath(string expectedValueString, string hexEncoding)
{
DateTimeOffset expectedValue = DateTimeOffset.Parse(expectedValueString);
Expand Down Expand Up @@ -334,6 +336,74 @@ public static void ReadUnixTimeSeconds_InvalidFloatPayload_ShouldThrowCborConten
Assert.Equal(encoding.Length, reader.BytesRemaining);
}

[Theory]
[InlineData("c11b7fffffffffffffff")] // long.MaxValue
[InlineData("c13b7fffffffffffffff")] // long.MinValue
[InlineData("c13b0000000e7791f700")] // one second before DateTimeOffset.MinValue
[InlineData("c11b0000003afff44180")] // one second after DateTimeOffset.MaxValue
[InlineData("c1fb425d1a94a2000000")] // 5e11 seconds, within TimeSpan's range
[InlineData("c1fbc2374876e8000000")] // -1e11 seconds, within TimeSpan's range
public static void ReadUnixTimeSeconds_OutOfDateTimeRange_ShouldThrowInvalidOperationException(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var reader = new CborReader(encoding);

InvalidOperationException exception = Assert.Throws<InvalidOperationException>(() => reader.ReadUnixTimeSeconds());
Assert.IsType<ArgumentOutOfRangeException>(exception.InnerException);
Assert.Equal(encoding.Length, reader.BytesRemaining);
Assert.Equal(0, reader.CurrentDepth);
Assert.Equal(CborReaderState.Tag, reader.PeekState());

reader.SkipValue();
Assert.Equal(CborReaderState.Finished, reader.PeekState());
}

[Theory]
[InlineData("c11bffffffffffffffff")] // ulong.MaxValue
[InlineData("c13bffffffffffffffff")] // -1 - ulong.MaxValue
[InlineData("c1fb7e37e43c8800759c")] // 1e300 seconds
[InlineData("c1fbfe37e43c8800759c")] // -1e300 seconds
[InlineData("c1fa7f7fffff")] // float.MaxValue seconds
[InlineData("c1faff7fffff")] // -float.MaxValue seconds
public static void ReadUnixTimeSeconds_OutOfNumericRange_ShouldThrowInvalidOperationException(string hexEncoding)
{
byte[] encoding = hexEncoding.HexToByteArray();
var reader = new CborReader(encoding);

InvalidOperationException exception = Assert.Throws<InvalidOperationException>(() => reader.ReadUnixTimeSeconds());
Assert.IsType<OverflowException>(exception.InnerException);
Comment thread
eiriktsarpalis marked this conversation as resolved.
Assert.Equal(encoding.Length, reader.BytesRemaining);
Assert.Equal(0, reader.CurrentDepth);
Assert.Equal(CborReaderState.Tag, reader.PeekState());

reader.SkipValue();
Assert.Equal(CborReaderState.Finished, reader.PeekState());
}

[Theory]
[InlineData("c11b7fffffffffffffff")]
[InlineData("c1fb425d1a94a2000000")]
[InlineData("c11bffffffffffffffff")]
public static void ReadUnixTimeSeconds_OutOfRangeMapKey_ShouldPreserveReaderState(string hexEncoding)
{
byte[] encoding = $"a20101{hexEncoding}02".HexToByteArray();
var reader = new CborReader(encoding, CborConformanceMode.Strict);
reader.ReadStartMap();
Assert.Equal(1, reader.ReadInt32());
Assert.Equal(1, reader.ReadInt32());
int bytesRemaining = reader.BytesRemaining;

Assert.Throws<InvalidOperationException>(() => reader.ReadUnixTimeSeconds());
Assert.Equal(bytesRemaining, reader.BytesRemaining);
Assert.Equal(1, reader.CurrentDepth);
Assert.Equal(CborReaderState.Tag, reader.PeekState());

reader.SkipValue();
Assert.Equal(2, reader.ReadInt32());
reader.ReadEndMap();
Assert.Equal(CborReaderState.Finished, reader.PeekState());
}

[Theory]
[InlineData("01")] // numeric value without tag
[InlineData("c301")] // non-datetime tag
Expand Down Expand Up @@ -456,6 +526,9 @@ public static void ReadDecimal_SingleValue_HappyPath(string expectedStringValue,
[Theory]
[InlineData("c482181d02")] // 2 * 10^29
[InlineData("c482381c02")] // 2 * 10^-29
[InlineData("c4821903e801")] // 1 * 10^1000
[InlineData("c4821bffffffffffffffff01")] // exponent exceeds long.MaxValue
[InlineData("c4823bffffffffffffffff01")] // exponent is less than long.MinValue
[InlineData("c48201c24cffffffffffffffffffffffff")] // decimal.MaxValue * 10^1
[InlineData("c48200c24d01000000000000000000000000")] // (decimal.MaxValue + 1) * 10^0
[InlineData("c48200c34cffffffffffffffffffffffff")] // (decimal.MinValue - 1) * 10^0
Expand All @@ -465,6 +538,11 @@ public static void ReadDecimal_LargeValues_ShouldThrowOverflowException(string h
var reader = new CborReader(encoding);
Assert.Throws<OverflowException>(() => reader.ReadDecimal());
Assert.Equal(encoding.Length, reader.BytesRemaining);
Assert.Equal(0, reader.CurrentDepth);
Assert.Equal(CborReaderState.Tag, reader.PeekState());

reader.SkipValue();
Assert.Equal(CborReaderState.Finished, reader.PeekState());
}

[Theory]
Expand Down
Loading