Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 163cfff

Browse files
authored
Create a serializer and deserializer for ASN.1 data
This enables serialization and deserialization of [StructLayout(LayoutKind.Sequential)] types to/from ASN.1 BER/CER/DER data streams. Ambiguous types (like string) require attributes to identify their ASN.1 representation, and other attributes exist to optionally control the serialization mechanism (such as [OptionalValue], [ExpectedTag], [AnyValue]). For background, see ITU-T-REC-X.680-201508 (ASN.1 language).
1 parent 0555aa9 commit 163cfff

File tree

10 files changed

+3207
-3
lines changed

10 files changed

+3207
-3
lines changed

src/Common/src/System/Security/Cryptography/Asn1V2.Serializer.cs

Lines changed: 1589 additions & 0 deletions
Large diffs are not rendered by default.

src/Common/src/System/Security/Cryptography/Asn1V2.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public Asn1Tag(UniversalTagNumber universalTagNumber, bool isConstructed = false
120120
universalTagNumber > UniversalTagNumber.RelativeObjectIdentifierIRI ||
121121
universalTagNumber == ReservedIndex)
122122
{
123-
throw new ArgumentOutOfRangeException(nameof(universalTagNumber), universalTagNumber, null);
123+
throw new ArgumentOutOfRangeException(nameof(universalTagNumber));
124124
}
125125
}
126126

@@ -129,7 +129,12 @@ public Asn1Tag(TagClass tagClass, int tagValue, bool isConstructed = false)
129129
{
130130
if (tagClass < TagClass.Universal || tagClass > TagClass.Private)
131131
{
132-
throw new ArgumentOutOfRangeException(nameof(tagClass), tagClass, null);
132+
throw new ArgumentOutOfRangeException(nameof(tagClass));
133+
}
134+
135+
if (tagValue < 0)
136+
{
137+
throw new ArgumentOutOfRangeException(nameof(tagValue));
133138
}
134139
}
135140

src/Common/src/System/Security/Cryptography/AsnReader.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,38 @@ public ReadOnlyMemory<byte> GetIntegerBytes(Asn1Tag expectedTag)
432432
return contents;
433433
}
434434

435+
public BigInteger GetInteger() => GetInteger(Asn1Tag.Integer);
436+
437+
public BigInteger GetInteger(Asn1Tag expectedTag)
438+
{
439+
ReadOnlyMemory<byte> contents =
440+
GetIntegerContents(expectedTag, UniversalTagNumber.Integer, out int headerLength);
441+
442+
// TODO: Split this for netcoreapp/netstandard to use the Big-Endian BigInteger parsing
443+
byte[] tmp = ArrayPool<byte>.Shared.Rent(contents.Length);
444+
BigInteger value;
445+
446+
try
447+
{
448+
byte fill = (contents.Span[0] & 0x80) == 0 ? (byte)0 : (byte)0xFF;
449+
// Fill the unused portions of tmp with positive or negative padding.
450+
new Span<byte>(tmp, contents.Length, tmp.Length - contents.Length).Fill(fill);
451+
contents.CopyTo(tmp);
452+
// Convert to Little-Endian.
453+
AsnWriter.Reverse(new Span<byte>(tmp, 0, contents.Length));
454+
value = new BigInteger(tmp);
455+
}
456+
finally
457+
{
458+
// Clear the whole tmp so that not even the sign bit is returned to the array pool.
459+
Array.Clear(tmp, 0, tmp.Length);
460+
ArrayPool<byte>.Shared.Return(tmp);
461+
}
462+
463+
_data = _data.Slice(headerLength + contents.Length);
464+
return value;
465+
}
466+
435467
private bool TryReadSignedInteger(
436468
int sizeLimit,
437469
Asn1Tag expectedTag,

src/Common/src/System/Security/Cryptography/AsnWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1544,7 +1544,7 @@ private static void SortContents(byte[] buffer, int start, int end)
15441544
ArrayPool<byte>.Shared.Return(tmp);
15451545
}
15461546

1547-
private static void Reverse(Span<byte> span)
1547+
internal static void Reverse(Span<byte> span)
15481548
{
15491549
int i = 0;
15501550
int j = span.Length - 1;

src/System.Security.Cryptography.Encoding/tests/Asn1/Reader/ReadInteger.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Numerics;
56
using System.Security.Cryptography.Asn1;
67
using Test.Cryptography;
78
using Xunit;
@@ -415,6 +416,112 @@ public static void GetIntegerBytes()
415416
Assert.Equal(Payload, contents.ByteArrayToHex());
416417
}
417418

419+
[Theory]
420+
[InlineData(PublicEncodingRules.BER)]
421+
[InlineData(PublicEncodingRules.CER)]
422+
[InlineData(PublicEncodingRules.DER)]
423+
public static void GetBigInteger(PublicEncodingRules ruleSet)
424+
{
425+
byte[] inputData = (
426+
"0282010100A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" +
427+
"FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" +
428+
"F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" +
429+
"70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" +
430+
"EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" +
431+
"E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" +
432+
"34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" +
433+
"9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" +
434+
"DA13810B4D").HexToByteArray();
435+
436+
BigInteger expected = BigInteger.Parse(
437+
"2075455505718444046766086325128514549301113944667492053189486607" +
438+
"5638152321436011512404808361119326026027238444019992518081753153" +
439+
"5965931647037093368608713442955529617501657176146245891571745113" +
440+
"4028700771890451167051818999837042261788828826028681595867897235" +
441+
"7967091503500375497498573022675671178275171110498592645868107163" +
442+
"8525996766798322809764200941677343791419428587801897366593842552" +
443+
"7272226864578661449281241619675217353931828233756506947863330597" +
444+
"8338073826285687331647183058971791153730741973483420110408271570" +
445+
"1367336140572971505716740825623220507359429297584634909330541150" +
446+
"79473593821332264673455059897928082590541");
447+
448+
AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet);
449+
Assert.Equal(expected, reader.GetInteger());
450+
}
451+
452+
[Theory]
453+
[InlineData(PublicEncodingRules.BER)]
454+
[InlineData(PublicEncodingRules.CER)]
455+
[InlineData(PublicEncodingRules.DER)]
456+
public static void GetNegativeBigInteger(PublicEncodingRules ruleSet)
457+
{
458+
// This uses a length that doesn't line up with the array pool size so
459+
// the fill code gets tested on netstandard.
460+
// It's the same data as above, minus the padding and lead byte (and the
461+
// next byte changed from 0x68 to 0x88)
462+
byte[] inputData = (
463+
"0281FF8861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" +
464+
"FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" +
465+
"F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" +
466+
"70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" +
467+
"EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" +
468+
"E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" +
469+
"34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" +
470+
"9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" +
471+
"DA13810B4D").HexToByteArray();
472+
473+
BigInteger expected = BigInteger.Parse(
474+
"-" +
475+
"5898547409447487884446992857601985651316300515844052200158198046" +
476+
"7814538020408452501006415149581619776188797413593169277984980446" +
477+
"1302361382932378450492052337986628823880000831383555853860116718" +
478+
"5361729331647715885538858385106930514758305144777880150203212976" +
479+
"6715081632226412951106013360243549075631850526067219857352295397" +
480+
"2308328327377769665309386917336850273904442596855844458638806936" +
481+
"6169824439111394938336579524651037239551388910737675470211780509" +
482+
"8035477769907389338548451561341965157059382875181284370047601682" +
483+
"6924486017215979427815833587119797658480104671279234402026468966" +
484+
"86109928634475300812601680679147599027");
485+
486+
AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet);
487+
Assert.Equal(expected, reader.GetInteger());
488+
}
489+
490+
[Theory]
491+
[InlineData(PublicEncodingRules.BER)]
492+
[InlineData(PublicEncodingRules.CER)]
493+
[InlineData(PublicEncodingRules.DER)]
494+
public static void GetDiminutiveBigInteger(PublicEncodingRules ruleSet)
495+
{
496+
// GetBigInteger with the last byte removed.
497+
// Since it is no longer an ArrayPool alignment size the fill code gets tested on netstandard.
498+
byte[] inputData = (
499+
"0282010000A46861FA9D5DB763633BF5A64EF6E7C2C2367F48D2D46643A22DFC" +
500+
"FCCB24E58A14D0F06BDC956437F2A56BA4BEF70BA361BF12964A0D665AFD84B0" +
501+
"F7494C8FA4ABC5FCA2E017C06178AEF2CDAD1B5F18E997A14B965C074E8F5649" +
502+
"70607276B00583932240FE6E2DD013026F9AE13D7C91CC07C4E1E8E87737DC06" +
503+
"EF2B575B89D62EFE46859F8255A123692A706C68122D4DAFE11CB205A7B3DE06" +
504+
"E553F7B95F978EF8601A8DF819BF32040BDF92A0DE0DF269B4514282E17AC699" +
505+
"34E8440A48AB9D1F5DF89A502CEF6DFDBE790045BD45E0C94E5CA8ADD76A013E" +
506+
"9C978440FC8A9E2A9A4940B2460819C3E302AA9C9F355AD754C86D3ED77DDAA3" +
507+
"DA13810B").HexToByteArray();
508+
509+
BigInteger expected = BigInteger.Parse(
510+
"2075455505718444046766086325128514549301113944667492053189486607" +
511+
"5638152321436011512404808361119326026027238444019992518081753153" +
512+
"5965931647037093368608713442955529617501657176146245891571745113" +
513+
"4028700771890451167051818999837042261788828826028681595867897235" +
514+
"7967091503500375497498573022675671178275171110498592645868107163" +
515+
"8525996766798322809764200941677343791419428587801897366593842552" +
516+
"7272226864578661449281241619675217353931828233756506947863330597" +
517+
"8338073826285687331647183058971791153730741973483420110408271570" +
518+
"1367336140572971505716740825623220507359429297584634909330541150" +
519+
"79473593821332264673455059897928082590541") >> 8;
520+
521+
AsnReader reader = new AsnReader(inputData, (AsnEncodingRules)ruleSet);
522+
Assert.Equal(expected, reader.GetInteger());
523+
}
524+
418525
[Theory]
419526
[InlineData(PublicEncodingRules.BER)]
420527
[InlineData(PublicEncodingRules.CER)]

0 commit comments

Comments
 (0)