75 Commits
1.2.0 ... main

Author SHA1 Message Date
Matt Nadareski
0bdfc5426d Update notice 2025-09-24 11:12:22 -04:00
Matt Nadareski
827913d09c Add notice 2025-09-24 08:24:43 -04:00
Matt Nadareski
2d955c8ef7 Update IO to 1.7.5 2025-09-24 08:16:16 -04:00
Matt Nadareski
ebdab151be Bump version 2025-09-22 17:55:59 -04:00
Matt Nadareski
a664cff34c Update IO to 1.7.4 2025-09-22 17:54:53 -04:00
Matt Nadareski
8110cdd0cb Bump version 2025-09-20 22:22:45 -04:00
Matt Nadareski
7a10aab5d1 Update IO to 1.7.3 2025-09-20 22:21:34 -04:00
Matt Nadareski
1516a8647a There 2025-09-10 21:53:37 -04:00
Matt Nadareski
4ee64a2c49 Bump version 2025-09-05 10:29:40 -04:00
Matt Nadareski
9cdf10d481 Suppress TFM issues 2025-09-05 10:27:16 -04:00
Matt Nadareski
04996584b5 Update IO to 1.7.2 2025-09-05 10:23:47 -04:00
Matt Nadareski
98b930e231 Bump version 2025-09-01 15:31:18 -04:00
Matt Nadareski
2da26d2dca Update packages 2025-09-01 15:30:58 -04:00
Matt Nadareski
32a226ede6 Bump version 2025-07-24 08:16:02 -04:00
Matt Nadareski
e9abf681c3 Be consistent about end-of-file newlines 2025-07-24 08:12:50 -04:00
Matt Nadareski
1c10ebf648 Add .NET Standard 2.0 and 2.1 2025-07-24 08:12:11 -04:00
Matt Nadareski
ea6d89d2da Update nuget packages 2025-07-24 08:11:12 -04:00
Matt Nadareski
f457e12d85 Update IO to 1.6.3 2025-05-12 08:25:05 -04:00
Matt Nadareski
3c561a60e7 Fix how conditions are used for references 2025-02-25 21:13:56 -05:00
Matt Nadareski
5830e2a2fd Ensure .NET versions are installed for testing 2024-12-19 10:50:45 -05:00
Matt Nadareski
10163eb9eb Ensure .NET versions are installed for testing 2024-12-19 10:48:13 -05:00
Matt Nadareski
4e45de3fab Bump version 2024-12-16 14:09:59 -05:00
Matt Nadareski
ec92bb22aa Update IO to 1.6.2 2024-12-16 14:08:54 -05:00
Matt Nadareski
6a0826e95c Allow symbols to be packed 2024-12-16 14:07:12 -05:00
Matt Nadareski
b7010349a0 Use publish script and update README 2024-12-06 11:03:01 -05:00
Matt Nadareski
005a447e35 Add automatic testing 2024-11-26 14:44:55 -05:00
Matt Nadareski
ca1cb5a66e Bump version 2024-11-26 14:42:59 -05:00
Matt Nadareski
4c6ef33af2 Update OID and add tests 2024-11-26 13:16:02 -05:00
Matt Nadareski
eb519af488 Use collection expression in OID 2024-11-26 12:58:40 -05:00
Matt Nadareski
402fe65e20 Sync modified OIDIRI to regular 2024-11-26 12:57:45 -05:00
Matt Nadareski
c22f0f2c6e Update static ASN1 and add tests 2024-11-26 12:55:58 -05:00
Matt Nadareski
92d5a097a8 Write TLV tests, update code accordingly 2024-11-26 12:47:15 -05:00
Matt Nadareski
d722f1fad2 Comment out V_ASN1_UNIVERSAL 2024-11-26 11:58:20 -05:00
Matt Nadareski
a570f9a96a Modifiers should come after values 2024-11-26 11:56:41 -05:00
Matt Nadareski
d4a118ba04 Add non-formatting tests for TLV 2024-11-26 11:49:31 -05:00
Matt Nadareski
9ceea4f85a Normalize exceptions 2024-11-26 11:49:07 -05:00
Matt Nadareski
a0a7f23b6c Simplify unknown format printing 2024-11-26 11:48:14 -05:00
Matt Nadareski
bca1754d42 0 is not an unknown type 2024-11-26 11:45:48 -05:00
Matt Nadareski
a6a281deb8 Assemble length byte-wise instead of trickery 2024-11-26 11:39:57 -05:00
Matt Nadareski
37bc010762 Enable stream construction, fix safety issues 2024-11-26 10:37:26 -05:00
Matt Nadareski
0a217e5e6b Fix field access in TLV 2024-11-26 09:57:01 -05:00
Matt Nadareski
73e7b035b8 Update IO to 1.6.0 2024-11-26 09:54:31 -05:00
Matt Nadareski
7276ab2199 Update test package versions 2024-11-26 09:53:44 -05:00
Matt Nadareski
fef4d30add Add skeleton test project 2024-11-26 09:51:28 -05:00
Matt Nadareski
e632f8537b Bump version 2024-11-20 15:55:54 -05:00
Matt Nadareski
ce8c4b1831 Fix issue with 0 unused bits 2024-11-20 15:03:26 -05:00
Matt Nadareski
2bdb56aca2 Bump version 2024-11-15 20:51:12 -05:00
Matt Nadareski
ce6ffd3f7f Framework only matters for executable 2024-11-15 20:48:13 -05:00
Matt Nadareski
c06618c66c Update IO to 1.5.1 2024-11-15 20:46:27 -05:00
Matt Nadareski
eb356483b4 Fix path in GHA 2024-11-13 11:58:19 -05:00
Matt Nadareski
0e76552df8 Fix path in GHA 2024-11-13 11:55:55 -05:00
Matt Nadareski
070274e33f Bump version 2024-11-13 00:50:36 -05:00
Matt Nadareski
dd64d6f843 Add .NET 9 to target frameworks 2024-11-13 00:50:16 -05:00
Matt Nadareski
69992f64be Add proper multi-framework support 2024-11-05 19:40:49 -05:00
Matt Nadareski
e63921fbc2 Linq just isn't needed 2024-11-05 19:38:24 -05:00
Matt Nadareski
69dc21c814 Bump version 2024-10-01 13:32:24 -04:00
Matt Nadareski
4c07f24e3c Update IO to 1.4.12 2024-10-01 13:31:41 -04:00
Matt Nadareski
b6b43d9e1b Fix README location for package 2024-10-01 13:09:54 -04:00
Matt Nadareski
7c972c1ea2 Move project to subfolder 2024-10-01 13:07:45 -04:00
Matt Nadareski
7c40583898 Some old .NET simplification 2024-10-01 12:41:53 -04:00
Matt Nadareski
ef479c783a Remove Linq requirement from old .NET 2024-10-01 01:53:26 -04:00
Matt Nadareski
1ebd9f82ee Update IO to 1.4.11 2024-09-25 10:54:48 -04:00
Matt Nadareski
774597b17c Bump version 2024-04-26 21:22:20 -04:00
Matt Nadareski
29437475fb Make Linux publish script executable 2024-04-26 21:21:19 -04:00
Matt Nadareski
214d4e4f9e Add publish scripts 2024-04-26 21:20:50 -04:00
Matt Nadareski
06c742cd15 Update SabreTools.IO 2024-04-26 21:18:34 -04:00
Matt Nadareski
bc591f367f Bump version 2024-03-05 11:07:01 -05:00
Matt Nadareski
8c6b962bd6 Update SabreTools.IO 2024-03-05 10:47:16 -05:00
Matt Nadareski
306b9a49ee Use "main" instead of "master" 2024-02-27 19:07:03 -05:00
Matt Nadareski
21ad30e65c Update copyright date 2024-02-27 17:20:37 -05:00
Matt Nadareski
50b8df07f1 Add nuget package and PR workflows 2024-02-27 17:20:20 -05:00
Matt Nadareski
fb33b1be85 Bump version 2023-11-22 09:11:08 -05:00
Matt Nadareski
965a8a5187 Use switch expression for leaves 2023-11-22 02:19:55 -05:00
Matt Nadareski
513e03c124 Support .NET Framework 2.0 2023-11-21 11:40:25 -05:00
Matt Nadareski
6d78f1f779 Update SabreTools.IO to 1.3.0 2023-11-21 11:30:00 -05:00
24 changed files with 19897 additions and 18204 deletions

46
.github/workflows/build_and_test.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Build and Test
on:
push:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
8.0.x
9.0.x
- name: Run tests
run: dotnet test
- name: Run publish script
run: ./publish-nix.sh
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: 'Nuget Package'
path: "*.nupkg,*.snupkg"
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: "*.nupkg,*.snupkg"
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

23
.github/workflows/check_pr.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Build PR
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
8.0.x
9.0.x
- name: Build
run: dotnet build
- name: Run tests
run: dotnet test

View File

@@ -1,31 +0,0 @@
using System.Collections.Generic;
namespace SabreTools.ASN1
{
/// <summary>
/// ASN.1 Parser
/// </summary>
public static class AbstractSyntaxNotationOne
{
/// <summary>
/// Parse a byte array into a DER-encoded ASN.1 structure
/// </summary>
/// <param name="data">Byte array representing the data</param>
/// <param name="pointer">Current pointer into the data</param>
/// <returns></returns>
public static List<TypeLengthValue> Parse(byte[] data, int pointer)
{
// Create the output list to return
var topLevelValues = new List<TypeLengthValue>();
// Loop through the data and return all top-level values
while (pointer < data.Length)
{
var topLevelValue = new TypeLengthValue(data, ref pointer);
topLevelValues.Add(topLevelValue);
}
return topLevelValues;
}
}
}

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright (c) 2018-2025 Matt Nadareski
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@@ -1,866 +0,0 @@
using System.Linq;
using System.Text;
namespace SabreTools.ASN1
{
/// <summary>
/// Methods related to Object Identifiers (OID) and OID-IRI formatting
/// </summary>
public static partial class ObjectIdentifier
{
/// <summary>
/// Parse an OID in separated-value notation into OID-IRI notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>OID-IRI formatted string, if possible</returns>
/// <see href="http://www.oid-info.com/index.htm"/>
public static string? ParseOIDToOIDIRINotation(ulong[]? values)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
// Set the initial index
int index = 0;
// Get a string builder for the path
var nameBuilder = new StringBuilder();
// Try to parse the standard value
string? standard = ParseOIDToOIDIRINotation(values, ref index);
if (standard == null)
return null;
// Add the standard value to the output
nameBuilder.Append(standard);
// If we have no more items
if (index == values.Length)
return nameBuilder.ToString();
// Add trailing items as just values
nameBuilder.Append("/");
nameBuilder.Append(string.Join("/", values.Skip(index)));
// Create and return the string
return nameBuilder.ToString();
}
/// <summary>
/// Parse an OID in separated-value notation into OID-IRI notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>OID-IRI formatted string, if possible</returns>
/// <see href="http://www.oid-info.com/index.htm"/>
private static string? ParseOIDToOIDIRINotation(ulong[]? values, ref int index)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
// If we have an invalid index, we can't do anything
if (index < 0 || index >= values.Length)
return null;
#region Start
switch (values[index++])
{
case 0: goto oid_0;
case 1: goto oid_1;
case 2: goto oid_2;
default: return $"/{values[index - 1]}";
}
#endregion
// itu-t, ccitt, itu-r
#region 0.*
oid_0:
if (index == values.Length) return "/ITU-T";
switch (values[index++])
{
case 0: goto oid_0_0;
case 2: return "/ITU-T/Administration";
case 3: return "/ITU-T/Network-Operator";
case 4: return "/ITU-T/Identified-Organization";
case 5: return "/ITU-R/R-Recommendation";
case 9: return "/ITU-T/Data";
default: return $"/ITU-T/{values[index - 1]}";
};
// recommendation
#region 0.0.*
oid_0_0:
if (index == values.Length) return "/ITU-T/Recommendation";
switch (values[index++])
{
case 1: return "/ITU-T/Recommendation/A";
case 2: return "/ITU-T/Recommendation/B";
case 3: return "/ITU-T/Recommendation/C";
case 4: return "/ITU-T/Recommendation/D";
case 5: return "/ITU-T/Recommendation/E";
case 6: return "/ITU-T/Recommendation/F";
case 7: return "/ITU-T/Recommendation/G";
case 8: return "/ITU-T/Recommendation/H";
case 9: return "/ITU-T/Recommendation/I";
case 10: return "/ITU-T/Recommendation/J";
case 11: return "/ITU-T/Recommendation/K";
case 12: return "/ITU-T/Recommendation/L";
case 13: return "/ITU-T/Recommendation/M";
case 14: return "/ITU-T/Recommendation/N";
case 15: return "/ITU-T/Recommendation/O";
case 16: return "/ITU-T/Recommendation/P";
case 17: return "/ITU-T/Recommendation/Q";
case 18: return "/ITU-T/Recommendation/R";
case 19: return "/ITU-T/Recommendation/S";
case 20: return "/ITU-T/Recommendation/T";
case 21: return "/ITU-T/Recommendation/U";
case 22: return "/ITU-T/Recommendation/V";
case 24: return "/ITU-T/Recommendation/X";
case 25: return "/ITU-T/Recommendation/Y";
case 26: return "/ITU-T/Recommendation/Z";
default: return $"/ITU-T/Recommendation/{values[index - 1]}";
}
#endregion
#endregion
// iso
#region 1.*
oid_1:
if (index == values.Length) return "/ISO";
switch (values[index++])
{
case 0: return "/ISO/Standard";
case 1: return "/ISO/Registration-Authority";
case 2: goto oid_1_2;
case 3: return "/ISO/Identified-Organization";
default: return $"/ISO/{values[index - 1]}";
}
// member-body
#region 1.2.*
oid_1_2:
if (index == values.Length) return "/ISO/Member-Body";
switch (values[index++])
{
case 36: return "/ISO/Member-Body/AU";
case 40: return "/ISO/Member-Body/AT";
case 56: return "/ISO/Member-Body/BE";
case 124: return "/ISO/Member-Body/CA";
case 156: return "/ISO/Member-Body/CN";
case 203: return "/ISO/Member-Body/CZ";
case 208: return "/ISO/Member-Body/DK";
case 246: return "/ISO/Member-Body/FI";
case 250: return "/ISO/Member-Body/FR";
case 276: return "/ISO/Member-Body/DE";
case 300: return "/ISO/Member-Body/GR";
case 344: return "/ISO/Member-Body/HK";
case 372: return "/ISO/Member-Body/IE";
case 392: return "/ISO/Member-Body/JP";
case 398: return "/ISO/Member-Body/KZ";
case 410: return "/ISO/Member-Body/KR";
case 498: return "/ISO/Member-Body/MD";
case 528: return "/ISO/Member-Body/NL";
case 566: return "/ISO/Member-Body/NG";
case 578: return "/ISO/Member-Body/NO";
case 616: return "/ISO/Member-Body/PL";
case 643: return "/ISO/Member-Body/RU";
case 702: return "/ISO/Member-Body/SG";
case 752: return "/ISO/Member-Body/SE";
case 804: return "/ISO/Member-Body/UA";
case 826: return "/ISO/Member-Body/GB";
case 840: return "/ISO/Member-Body/US";
default: return $"/ISO/Member-Body/{values[index - 1]}";
}
#endregion
#endregion
// joint-iso-itu-t, joint-iso-ccitt
#region 2.*
oid_2:
if (index == values.Length) return "/Joint-ISO-ITU-T";
switch (values[index++])
{
case 1: return "/ASN.1";
case 16: goto oid_2_16;
case 17: return "/Joint-ISO-ITU-T/Registration-Procedures";
case 23: return "/Joint-ISO-ITU-T/International-Organizations";
case 25: goto oid_2_25;
case 27: return "/Tag-Based";
case 28: return "/Joint-ISO-ITU-T/ITS";
case 41: return "/BIP";
case 42: goto oid_2_42;
case 48: goto oid_2_48;
case 49: goto oid_2_49;
case 50: return "/OIDResolutionSystem";
case 51: return "/GS1";
case 52: return "/Joint-ISO-ITU-T/UAV";
case 999: return "/Joint-ISO-ITU-T/Example";
default: return $"/Joint-ISO-ITU-T/{values[index - 1]}";
}
// country
#region 2.16.*
oid_2_16:
if (index == values.Length) return "/Country";
switch (values[index++])
{
case 4: return "/Country/AF";
case 8: return "/Country/AL";
case 12: return "/Country/DZ";
case 20: return "/Country/AD";
case 24: return "/Country/AO";
case 28: return "/Country/AG";
case 31: return "/Country/AZ";
case 32: return "/Country/AR";
case 36: return "/Country/AU";
case 40: return "/Country/AT";
case 44: return "/Country/BS";
case 48: return "/Country/BH";
case 50: return "/Country/BD";
case 51: return "/Country/AM";
case 52: return "/Country/BB";
case 56: return "/Country/BE";
case 60: return "/Country/BM";
case 64: return "/Country/BT";
case 68: return "/Country/BO";
case 70: return "/Country/BA";
case 72: return "/Country/BW";
case 76: return "/Country/BR";
case 84: return "/Country/BZ";
case 90: return "/Country/SB";
case 96: return "/Country/BN";
case 100: return "/Country/BG";
case 104: return "/Country/MM";
case 108: return "/Country/BI";
case 112: return "/Country/BY";
case 116: return "/Country/KH";
case 120: return "/Country/CM";
case 124: return "/Country/CA";
case 132: return "/Country/CV";
case 140: return "/Country/CF";
case 144: return "/Country/LK";
case 148: return "/Country/TD";
case 152: return "/Country/CL";
case 156: return "/Country/CN";
case 158: return "/Country/TW";
case 170: return "/Country/CO";
case 174: return "/Country/KM";
case 178: return "/Country/CG";
case 180: return "/Country/CD";
case 188: return "/Country/CR";
case 191: return "/Country/HR";
case 192: return "/Country/CU";
case 196: return "/Country/CY";
case 203: return "/Country/CZ";
case 204: return "/Country/BJ";
case 208: return "/Country/DK";
case 212: return "/Country/DM";
case 214: return "/Country/DO";
case 218: return "/Country/EC";
case 222: return "/Country/SV";
case 226: return "/Country/GQ";
case 231: return "/Country/ET";
case 232: return "/Country/ER";
case 233: return "/Country/EE";
case 242: return "/Country/FJ";
case 246: return "/Country/FI";
case 250: return "/Country/FR";
case 262: return "/Country/DJ";
case 266: return "/Country/GA";
case 268: return "/Country/GE";
case 270: return "/Country/GM";
case 275: return "/Country/PS";
case 276: return "/Country/DE";
case 288: return "/Country/GH";
case 296: return "/Country/KI";
case 300: return "/Country/GR";
case 308: return "/Country/GD";
case 320: return "/Country/GT";
case 324: return "/Country/GN";
case 328: return "/Country/GY";
case 332: return "/Country/HT";
case 336: return "/Country/VA";
case 340: return "/Country/HN";
case 344: return "/Country/HK";
case 348: return "/Country/HU";
case 352: return "/Country/IS";
case 356: return "/Country/IN";
case 360: return "/Country/ID";
case 364: return "/Country/IR";
case 368: return "/Country/IQ";
case 372: return "/Country/IE";
case 376: return "/Country/IL";
case 380: return "/Country/IT";
case 384: return "/Country/CI";
case 388: return "/Country/JM";
case 392: return "/Country/JP";
case 398: return "/Country/KZ";
case 400: return "/Country/JO";
case 404: return "/Country/KE";
case 408: return "/Country/KP";
case 410: return "/Country/KR";
case 414: return "/Country/KW";
case 417: return "/Country/KG";
case 418: return "/Country/LA";
case 422: return "/Country/LB";
case 426: return "/Country/LS";
case 428: return "/Country/LV";
case 430: return "/Country/LR";
case 434: return "/Country/LY";
case 438: return "/Country/LI";
case 440: return "/Country/LT";
case 442: return "/Country/LU";
case 450: return "/Country/MG";
case 454: return "/Country/MW";
case 458: return "/Country/MY";
case 462: return "/Country/MV";
case 466: return "/Country/ML";
case 470: return "/Country/MT";
case 478: return "/Country/MR";
case 480: return "/Country/MU";
case 484: return "/Country/MX";
case 492: return "/Country/MC";
case 496: return "/Country/MN";
case 498: return "/Country/MD";
case 499: return "/Country/ME";
case 504: return "/Country/MA";
case 508: return "/Country/MZ";
case 512: return "/Country/OM";
case 516: return "/Country/NA";
case 520: return "/Country/NR";
case 524: return "/Country/NP";
case 528: return "/Country/NL";
case 530: return "/Country/AN";
case 548: return "/Country/VU";
case 554: return "/Country/NZ";
case 558: return "/Country/NI";
case 562: return "/Country/NE";
case 566: return "/Country/NG";
case 578: return "/Country/NO";
case 583: return "/Country/FM";
case 584: return "/Country/MH";
case 585: return "/Country/PW";
case 586: return "/Country/PK";
case 591: return "/Country/PA";
case 598: return "/Country/PG";
case 600: return "/Country/PY";
case 604: return "/Country/PE";
case 608: return "/Country/PH";
case 616: return "/Country/PL";
case 620: return "/Country/PT";
case 624: return "/Country/GW";
case 626: return "/Country/TL";
case 634: return "/Country/QA";
case 642: return "/Country/RO";
case 643: return "/Country/RU";
case 646: return "/Country/RW";
case 659: return "/Country/KN";
case 662: return "/Country/LC";
case 670: return "/Country/VC";
case 674: return "/Country/SM";
case 678: return "/Country/ST";
case 682: return "/Country/SA";
case 686: return "/Country/SN";
case 688: return "/Country/RS";
case 690: return "/Country/SC";
case 694: return "/Country/SL";
case 702: return "/Country/SG";
case 703: return "/Country/SK";
case 704: return "/Country/VN";
case 705: return "/Country/SI";
case 706: return "/Country/SO";
case 710: return "/Country/ZA";
case 716: return "/Country/ZW";
case 724: return "/Country/ES";
case 728: return "/Country/SS";
case 729: return "/Country/SD";
case 740: return "/Country/SR";
case 748: return "/Country/SZ";
case 752: return "/Country/SE";
case 756: return "/Country/CH";
case 760: return "/Country/SY";
case 762: return "/Country/TJ";
case 764: return "/Country/TH";
case 768: return "/Country/TG";
case 776: return "/Country/TO";
case 780: return "/Country/TT";
case 784: return "/Country/AE";
case 788: return "/Country/TN";
case 792: return "/Country/TR";
case 795: return "/Country/TM";
case 798: return "/Country/TV";
case 800: return "/Country/UG";
case 804: return "/Country/UA";
case 807: return "/Country/MK";
case 818: return "/Country/EG";
case 826: return "/Country/GB";
case 834: return "/Country/TZ";
case 840: return "/Country/US";
case 854: return "/Country/BF";
case 858: return "/Country/UY";
case 860: return "/Country/UZ";
case 862: return "/Country/VE";
case 882: return "/Country/WS";
case 887: return "/Country/YE";
case 894: return "/Country/ZM";
default: return $"/Country/{values[index - 1]}";
}
#endregion
// uuid [TODO: Requires 128-bit values]
#region 2.25.*
oid_2_25:
if (index == values.Length) return "/Joint-ISO-ITU-T/UUID";
switch (values[index++])
{
case 0: return "/Joint-ISO-ITU-T/UUID/00000000-0000-0000-0000-000000000000";
//case 288786655511405443130567505384701230: return "/Joint-ISO-ITU-T/UUID/00379e48-0a2b-1085-b288-0002a5d5fd2e";
//case 987895962269883002155146617097157934: return "/Joint-ISO-ITU-T/UUID/00be4308-0c89-1085-8ea0-0002a5d5fd2e";
//case 1858228783942312576083372383319475483: return "/Joint-ISO-ITU-T/UUID/0165e1c0-a655-11e0-95b8-0002a5d5c51b";
//case 2474299330026746002885628159579243803: return "/Joint-ISO-ITU-T/UUID/01dc8860-25fb-11da-82b2-0002a5d5c51b";
//case 3263645701162998421821186056373271854: return "/Joint-ISO-ITU-T/UUID/02748e28-08c4-1085-b21d-0002a5d5fd2e";
//case 3325839809379844461264382260940242222: return "/Joint-ISO-ITU-T/UUID/02808890-0ad8-1085-9bdf-0002a5d5fd2e";
// TODO: Left off at http://www.oid-info.com/get/2.25.3664154270495270126161055518190585115
default: return $"/Joint-ISO-ITU-T/UUID/{values[index - 1]}";
}
#endregion
// telebiometrics
#region 2.42.*
oid_2_42:
if (index == values.Length) return "/Telebiometrics";
switch (values[index++])
{
case 0: goto oid_2_42_0;
case 1: goto oid_2_42_1;
case 2: goto oid_2_42_2;
case 3: goto oid_2_42_3;
default: return $"/Telebiometrics/{values[index - 1]}";
}
// modules
#region 2.42.0.*
oid_2_42_0:
if (index == values.Length) return "/Telebiometrics/Modules";
switch (values[index++])
{
case 0: goto oid_2_42_0_0;
default: return $"/Telebiometrics/Modules/{values[index - 1]}";
}
// main
#region 2.42.0.0.*
oid_2_42_0_0:
if (index == values.Length) return "/Telebiometrics/Modules/Main_Module";
switch (values[index++])
{
case 1: return "/Telebiometrics/Modules/Main_Module/Version1";
default: return $"/Telebiometrics/Modules/Main_Module/{values[index - 1]}";
}
#endregion
#endregion
// tmm
#region 2.42.1.*
oid_2_42_1:
if (index == values.Length) return "/Telebiometrics/TMM";
switch (values[index++])
{
case 0: goto oid_2_42_1_0;
case 1: goto oid_2_42_1_1;
case 2: goto oid_2_42_1_2;
case 3: goto oid_2_42_1_3;
case 4: return "/Telebiometrics/TMM/Practitioners";
default: return $"/Telebiometrics/TMM/{values[index - 1]}";
}
// modules
#region 2.42.1.0.*
oid_2_42_1_0:
if (index == values.Length) return "/Telebiometrics/TMM/Modules";
switch (values[index++])
{
case 0: goto oid_2_42_1_0_0;
default: return $"/Telebiometrics/TMM/Modules/{values[index - 1]}";
}
// main
#region 2.42.1.0.0.*
oid_2_42_1_0_0:
if (index == values.Length) return "/Telebiometrics/TMM/Modules/Main";
switch (values[index++])
{
case 0: return "/Telebiometrics/TMM/Modules/Main/First_Version";
default: return $"/Telebiometrics/TMM/Modules/Main/{values[index - 1]}";
}
#endregion
#endregion
// measures, metric
#region 2.42.1.1.*
oid_2_42_1_1:
if (index == values.Length) return "/Telebiometrics/TMM/Measures";
switch (values[index++])
{
case 1: goto oid_2_42_1_1_1;
case 2: return "/Telebiometrics/TMM/Measures/Units";
case 3: return "/Telebiometrics/TMM/Measures/Symbols";
case 4: return "/Telebiometrics/TMM/Measures/Conditions";
case 5: goto oid_2_42_1_1_5;
default: return $"/Telebiometrics/TMM/Measures/{values[index - 1]}";
}
// quantities
#region 2.42.1.1.1.*
oid_2_42_1_1_1:
if (index == values.Length) return "/Telebiometrics/TMM/Measures/Quantities";
switch (values[index++])
{
case 1: return "/Telebiometrics/TMM/Measures/Quantities/Physics";
case 2: return "/Telebiometrics/TMM/Measures/Quantities/Chemistry";
case 3: return "/Telebiometrics/TMM/Measures/Quantities/Biology";
case 4: return "/Telebiometrics/TMM/Measures/Quantities/Culturology";
case 5: return "/Telebiometrics/TMM/Measures/Quantities/Psychology";
default: return $"/Telebiometrics/TMM/Measures/Quantities/{values[index - 1]}";
}
#endregion
// methods
#region 2.42.1.1.5.*
oid_2_42_1_1_5:
if (index == values.Length) return "/Telebiometrics/TMM/Measures/Methods";
switch (values[index++])
{
case 1: return "/Telebiometrics/TMM/Measures/Methods/Physics";
case 2: return "/Telebiometrics/TMM/Measures/Methods/Chemistry";
case 3: return "/Telebiometrics/TMM/Measures/Methods/Biology";
case 4: return "/Telebiometrics/TMM/Measures/Methods/Culturology";
case 5: return "/Telebiometrics/TMM/Measures/Methods/Psychology";
default: return $"/Telebiometrics/TMM/Measures/Methods/{values[index - 1]}";
}
#endregion
#endregion
// fields-of-study, scientific
#region 2.42.1.2.*
oid_2_42_1_2:
if (index == values.Length) return "/Telebiometrics/TMM/Fields_of_Study";
switch (values[index++])
{
case 1: return "/Telebiometrics/TMM/Fields_of_Study/Physics";
case 2: return "/Telebiometrics/TMM/Fields_of_Study/Chemistry";
case 3: return "/Telebiometrics/TMM/Fields_of_Study/Biology";
case 4: return "/Telebiometrics/TMM/Fields_of_Study/Culturology";
case 5: return "/Telebiometrics/TMM/Fields_of_Study/Psychology";
default: return $"/Telebiometrics/TMM/Fields_of_Study/{values[index - 1]}";
}
#endregion
// modalities, sensory
#region 2.42.1.3.*
oid_2_42_1_3:
if (index == values.Length) return "/Telebiometrics/TMM/Modalities";
switch (values[index++])
{
case 1: return "/Telebiometrics/TMM/Modalities/Tango";
case 2: return "/Telebiometrics/TMM/Modalities/Video";
case 3: return "/Telebiometrics/TMM/Modalities/Audio";
case 4: return "/Telebiometrics/TMM/Modalities/Chemo";
case 5: return "/Telebiometrics/TMM/Modalities/Radio";
case 6: return "/Telebiometrics/TMM/Modalities/Calor";
case 7: return "/Telebiometrics/TMM/Modalities/Electro";
default: return $"/Telebiometrics/TMM/Modalities/{values[index - 1]}";
}
#endregion
#endregion
// human-physiology
#region 2.42.2.*
oid_2_42_2:
if (index == values.Length) return "/Telebiometrics/Human_Physiology";
switch (values[index++])
{
case 0: goto oid_2_42_2_0;
case 1: goto oid_2_42_2_1;
case 2: return "/Telebiometrics/Human_Physiology/Symbol_Combinations";
default: return $"/Telebiometrics/Human_Physiology/{values[index - 1]}";
}
// modules
#region 2.42.2.0.*
oid_2_42_2_0:
if (index == values.Length) return "/Telebiometrics/Human_Physiology/Modules";
switch (values[index++])
{
case 0: goto oid_2_42_2_0_0;
default: return $"/Telebiometrics/Human_Physiology/Modules/{values[index - 1]}";
}
// main
#region 2.42.2.0.0.*
oid_2_42_2_0_0:
if (index == values.Length) return "/Telebiometrics/Human_Physiology/Modules/Main_Module";
switch (values[index++])
{
case 0: return "/Telebiometrics/Human_Physiology/Modules/Main_Module/First_Version";
default: return $"/Telebiometrics/Human_Physiology/Modules/Main_Module/{values[index - 1]}";
}
#endregion
#endregion
// symbols
#region 2.42.2.1.*
oid_2_42_2_1:
if (index == values.Length) return "/Telebiometrics/Human_Physiology/Symbols";
switch (values[index++])
{
case 1: return "/Telebiometrics/Human_Physiology/Symbols/Tango_in";
case 2: return "/Telebiometrics/Human_Physiology/Symbols/Video_in";
case 3: return "/Telebiometrics/Human_Physiology/Symbols/Audio_in";
case 4: return "/Telebiometrics/Human_Physiology/Symbols/Chemo_in";
case 5: return "/Telebiometrics/Human_Physiology/Symbols/Radio_in";
case 6: return "/Telebiometrics/Human_Physiology/Symbols/Calor_in";
case 7: return "/Telebiometrics/Human_Physiology/Symbols/Tango_out";
case 8: return "/Telebiometrics/Human_Physiology/Symbols/Video_out";
case 9: return "/Telebiometrics/Human_Physiology/Symbols/Audio_out";
case 10: return "/Telebiometrics/Human_Physiology/Symbols/Chemo_out";
case 11: return "/Telebiometrics/Human_Physiology/Symbols/Radio_out";
case 12: return "/Telebiometrics/Human_Physiology/Symbols/Calor_out";
case 13: return "/Telebiometrics/Human_Physiology/Symbols/Safe";
case 14: return "/Telebiometrics/Human_Physiology/Symbols/Threshold";
default: return $"/Telebiometrics/Human_Physiology/Symbols/{values[index - 1]}";
}
#endregion
#endregion
// obj-cat, telehealth, e-health-protocol, th
#region 2.42.3.*
oid_2_42_3:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol";
switch (values[index++])
{
case 0: goto oid_2_42_3_0;
case 1: return "/Telebiometrics/E_Health_Protocol/[Patient schemes]";
case 2: return "/Telebiometrics/E_Health_Protocol/[Medical staff schemes]";
case 3: return "/Telebiometrics/E_Health_Protocol/[Observer schemes]";
case 4: return "/Telebiometrics/E_Health_Protocol/[Pharmaceutical schemes]";
case 5: return "/Telebiometrics/E_Health_Protocol/[Laboratory schemes]";
case 6: return "/Telebiometrics/E_Health_Protocol/[Drug manufacturer schemes]";
case 7: return "/Telebiometrics/E_Health_Protocol/[Medical device schemes]";
case 8: return "/Telebiometrics/E_Health_Protocol/[Medical software schemes]";
case 9: return "/Telebiometrics/E_Health_Protocol/[Medical insurance schemes]";
case 10: return "/Telebiometrics/E_Health_Protocol/[Medical record schemes]";
default: return $"/Telebiometrics/E_Health_Protocol/{values[index - 1]}";
}
// obj-cat, telehealth, e-health-protocol, th
#region 2.42.3.0.*
oid_2_42_3_0:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules";
switch (values[index++])
{
case 0: goto oid_2_42_3_0_0;
case 1: goto oid_2_42_3_0_1;
case 2: goto oid_2_42_3_0_2;
case 3: goto oid_2_42_3_0_3;
case 4: goto oid_2_42_3_0_4;
case 5: goto oid_2_42_3_0_5;
default: return $"/Telebiometrics/E_Health_Protocol/Modules/{values[index - 1]}";
}
// identification
#region 2.42.3.0.0.*
oid_2_42_3_0_0:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Identification";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Identification/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Identification/{values[index - 1]}";
}
#endregion
// set-up
#region 2.42.3.0.1.*
oid_2_42_3_0_1:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Setup";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Setup/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Setup/{values[index - 1]}";
}
#endregion
// send-and-ack
#region 2.42.3.0.2.*
oid_2_42_3_0_2:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Send-and-ack";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Send-and-ack/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Send-and-ack/{values[index - 1]}";
}
#endregion
// command-response
#region 2.42.3.0.3.*
oid_2_42_3_0_3:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Command-response";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Command-response/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Command-response/{values[index - 1]}";
}
#endregion
// quantity-and-units
#region 2.42.3.0.4.*
oid_2_42_3_0_4:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Quantities_And_Units";
switch (values[index++])
{
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Quantities_And_Units/Version1";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Quantities_And_Units/{values[index - 1]}";
}
#endregion
// examples
#region 2.42.3.0.5.*
oid_2_42_3_0_5:
if (index == values.Length) return "/Telebiometrics/E_Health_Protocol/Modules/Examples";
switch (values[index++])
{
case 0: return "/Telebiometrics/E_Health_Protocol/Modules/Examples/Command_Response";
case 1: return "/Telebiometrics/E_Health_Protocol/Modules/Examples/Data_Message";
default: return $"/Telebiometrics/E_Health_Protocol/Modules/Examples/{values[index - 1]}";
}
#endregion
#endregion
#endregion
#endregion
// cybersecurity
#region 2.48.*
oid_2_48:
if (index == values.Length) return "/Cybersecurity";
switch (values[index++])
{
case 1: return "/Cybersecurity/Country";
case 2: return "/Cybersecurity/International-Org";
default: return $"/Cybersecurity/{values[index - 1]}";
}
#endregion
// alerting
#region 2.49.*
oid_2_49:
if (index == values.Length) return "/Alerting";
switch (values[index++])
{
case 0: return "/Alerting/WMO";
default: return $"/Alerting/{values[index - 1]}";
}
#endregion
#endregion
}
}
}

View File

@@ -1,5 +1,18 @@
# SabreTools.ASN1
[![Build and Test](https://github.com/SabreTools/SabreTools.ASN1/actions/workflows/build_and_test.yml/badge.svg)](https://github.com/SabreTools/SabreTools.ASN1/actions/workflows/build_and_test.yml)
**NOTICE:** This library has been deprecated. All functionality formerly in this library is in [SabreTools.Serialization](https://github.com/SabreTools/SabreTools.Serialization) as of version 1.9.6.
This library comprises of code to parse Abstract Syntax Notation One (ASN.1) codes and output them into various formats. This also performs some rudimentary validation based on the chains that are input.
**Note:** This code is known to be incomplete and will be added to over time.
Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTools.ASN1).
## Releases
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/SabreTools.ASN1/releases)
For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/SabreTools.ASN1/releases/rolling)

View File

@@ -0,0 +1,61 @@
using System;
using System.IO;
using Xunit;
namespace SabreTools.ASN1.Test
{
public class AbstractSyntaxNotationOneTests
{
[Fact]
public void Parse_EmptyArray_Throws()
{
byte[] data = [];
Assert.Throws<InvalidDataException>(() => AbstractSyntaxNotationOne.Parse(data, 0));
}
[Fact]
public void Parse_ValidArrayNegativeIndex_Throws()
{
byte[] data = [0x00];
Assert.Throws<IndexOutOfRangeException>(() => AbstractSyntaxNotationOne.Parse(data, -1));
}
[Fact]
public void Parse_ValidArrayOverIndex_Throws()
{
byte[] data = [0x00];
Assert.Throws<IndexOutOfRangeException>(() => AbstractSyntaxNotationOne.Parse(data, 10));
}
[Fact]
public void Parse_ValidMinimalArray()
{
byte[] data = [0x00];
var tlvs = AbstractSyntaxNotationOne.Parse(data, 0);
var tlv = Assert.Single(tlvs);
Assert.Equal(ASN1Type.V_ASN1_EOC, tlv.Type);
Assert.Equal(default, tlv.Length);
Assert.Null(tlv.Value);
}
[Fact]
public void Parse_EmptyStream_Throws()
{
Stream data = new MemoryStream([], 0, 0, false, false);
Assert.Throws<InvalidDataException>(() => AbstractSyntaxNotationOne.Parse(data));
}
[Fact]
public void Parse_ValidMinimalStream()
{
Stream data = new MemoryStream([0x00]);
var tlvs = AbstractSyntaxNotationOne.Parse(data);
var tlv = Assert.Single(tlvs);
Assert.Equal(ASN1Type.V_ASN1_EOC, tlv.Type);
Assert.Equal(default, tlv.Length);
Assert.Null(tlv.Value);
}
}
}

View File

@@ -0,0 +1,110 @@
using Xunit;
namespace SabreTools.ASN1.Test
{
// These tests are known to be incomplete due to the sheer number
// of possible OIDs that exist. The tests below are a minimal
// representation of functionality to guarantee proper behavior
// not necessarily absolute outputs
public class ObjectIdentifierTests
{
#region ASN.1
[Fact]
public void ASN1Notation_AlwaysNull()
{
ulong[]? values = null;
string? actual = ObjectIdentifier.ParseOIDToASN1Notation(values);
Assert.Null(actual);
}
#endregion
#region Dot Notation
[Fact]
public void DotNotation_NullValues_Null()
{
ulong[]? values = null;
string? actual = ObjectIdentifier.ParseOIDToDotNotation(values);
Assert.Null(actual);
}
[Fact]
public void DotNotation_EmptyValues_Null()
{
ulong[]? values = [];
string? actual = ObjectIdentifier.ParseOIDToDotNotation(values);
Assert.Null(actual);
}
[Fact]
public void DotNotation_Values_Formatted()
{
string expected = "0.1.2.3";
ulong[]? values = [0, 1, 2, 3];
string? actual = ObjectIdentifier.ParseOIDToDotNotation(values);
Assert.Equal(expected, actual);
}
#endregion
#region Modified OID-IRI
[Fact]
public void ModifiedOIDIRI_NullValues_Null()
{
ulong[]? values = null;
string? actual = ObjectIdentifier.ParseOIDToModifiedOIDIRI(values);
Assert.Null(actual);
}
[Fact]
public void ModifiedOIDIRI_EmptyValues_Null()
{
ulong[]? values = [];
string? actual = ObjectIdentifier.ParseOIDToModifiedOIDIRI(values);
Assert.Null(actual);
}
[Fact]
public void ModifiedOIDIRI_Values_Formatted()
{
string expected = "/ITU-T/[question]/2/3";
ulong[]? values = [0, 1, 2, 3];
string? actual = ObjectIdentifier.ParseOIDToModifiedOIDIRI(values);
Assert.Equal(expected, actual);
}
#endregion
#region OID-IRI
[Fact]
public void OIDIRI_NullValues_Null()
{
ulong[]? values = null;
string? actual = ObjectIdentifier.ParseOIDToOIDIRINotation(values);
Assert.Null(actual);
}
[Fact]
public void OIDIRI_EmptyValues_Null()
{
ulong[]? values = [];
string? actual = ObjectIdentifier.ParseOIDToOIDIRINotation(values);
Assert.Null(actual);
}
[Fact]
public void OIDIRI_Values_Formatted()
{
string expected = "/ITU-T/1/2/3";
ulong[]? values = [0, 1, 2, 3];
string? actual = ObjectIdentifier.ParseOIDToOIDIRINotation(values);
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
<NoWarn>NU1903</NoWarn>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.ASN1\SabreTools.ASN1.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,322 @@
using System;
using System.IO;
using Xunit;
namespace SabreTools.ASN1.Test
{
public class TypeLengthValueTests
{
#region Construction
[Fact]
public void Constructor_EmptyArray_Throws()
{
int index = 0;
byte[] data = [];
Assert.Throws<InvalidDataException>(() => new TypeLengthValue(data, ref index));
}
[Fact]
public void Constructor_ValidArrayNegativeIndex_Throws()
{
int index = -1;
byte[] data = [0x00];
Assert.Throws<IndexOutOfRangeException>(() => new TypeLengthValue(data, ref index));
}
[Fact]
public void Constructor_ValidArrayOverIndex_Throws()
{
int index = 10;
byte[] data = [0x00];
Assert.Throws<IndexOutOfRangeException>(() => new TypeLengthValue(data, ref index));
}
[Fact]
public void Constructor_ValidMinimalArray()
{
int index = 0;
byte[] data = [0x00];
var tlv = new TypeLengthValue(data, ref index);
Assert.Equal(ASN1Type.V_ASN1_EOC, tlv.Type);
Assert.Equal(default, tlv.Length);
Assert.Null(tlv.Value);
}
[Fact]
public void Constructor_EmptyStream_Throws()
{
Stream data = new MemoryStream([], 0, 0, false, false);
Assert.Throws<InvalidDataException>(() => new TypeLengthValue(data));
}
[Fact]
public void Constructor_ValidMinimalStream()
{
Stream data = new MemoryStream([0x00]);
var tlv = new TypeLengthValue(data);
Assert.Equal(ASN1Type.V_ASN1_EOC, tlv.Type);
Assert.Equal(default, tlv.Length);
Assert.Null(tlv.Value);
}
[Fact]
public void Constructor_ValidBoolean()
{
Stream data = new MemoryStream([0x01, 0x01, 0x01]);
var tlv = new TypeLengthValue(data);
Assert.Equal(ASN1Type.V_ASN1_BOOLEAN, tlv.Type);
Assert.Equal(1UL, tlv.Length);
Assert.NotNull(tlv.Value);
byte[]? valueAsArray = tlv.Value as byte[];
Assert.NotNull(valueAsArray);
byte actual = Assert.Single(valueAsArray);
Assert.Equal(0x01, actual);
}
[Theory]
[InlineData(new byte[] { 0x26, 0x81, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x82, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x83, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x84, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x85, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
[InlineData(new byte[] { 0x26, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x01, 0x01 })]
public void Constructor_ComplexValue(byte[] arr)
{
Stream data = new MemoryStream(arr);
var tlv = new TypeLengthValue(data);
Assert.Equal(ASN1Type.V_ASN1_CONSTRUCTED | ASN1Type.V_ASN1_OBJECT, tlv.Type);
Assert.Equal(3UL, tlv.Length);
Assert.NotNull(tlv.Value);
TypeLengthValue[]? valueAsArray = tlv.Value as TypeLengthValue[];
Assert.NotNull(valueAsArray);
TypeLengthValue actual = Assert.Single(valueAsArray);
Assert.Equal(ASN1Type.V_ASN1_BOOLEAN, actual.Type);
Assert.Equal(1UL, actual.Length);
Assert.NotNull(actual.Value);
}
[Theory]
[InlineData(new byte[] { 0x26, 0x80 })]
[InlineData(new byte[] { 0x26, 0x89 })]
public void Constructor_ComplexValueInvalidLength_Throws(byte[] arr)
{
Stream data = new MemoryStream(arr);
Assert.Throws<InvalidOperationException>(() => new TypeLengthValue(data));
}
#endregion
#region Formatting
[Fact]
public void Format_EOC()
{
string expected = "Type: V_ASN1_EOC";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_EOC, 0, null);
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ZeroLength()
{
string expected = "Type: V_ASN1_NULL, Length: 0";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_NULL, 0, null);
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidConstructed()
{
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 1, Value: [INVALID DATA TYPE]";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, 1, (object?)false);
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidConstructed()
{
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 3, Value:\n Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
var boolTlv = new TypeLengthValue(ASN1Type.V_ASN1_BOOLEAN, 1, new byte[] { 0x01 });
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, 3, new TypeLengthValue[] { boolTlv });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidDataType()
{
string expected = "Type: V_ASN1_OBJECT, Length: 1, Value: [INVALID DATA TYPE]";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT, 1, (object?)false);
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidLength()
{
string expected = "Type: V_ASN1_NULL, Length: 1, Value: [NO DATA]";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_NULL, 1, Array.Empty<byte>());
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidBooleanLength()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 2 [Expected length of 1], Value: True";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BOOLEAN, 2, new byte[] { 0x01 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidBooleanArrayLength()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 1 [Expected value length of 1], Value: True";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BOOLEAN, 1, new byte[] { 0x01, 0x00 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidBoolean()
{
string expected = "Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BOOLEAN, 1, new byte[] { 0x01 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidInteger()
{
string expected = "Type: V_ASN1_INTEGER, Length: 1, Value: 1";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_INTEGER, 1, new byte[] { 0x01 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidBitString_NoBits()
{
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 0 unused bits";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BIT_STRING, 1, new byte[] { 0x00 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidBitString_Bits()
{
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 1 unused bits: 01";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BIT_STRING, 1, new byte[] { 0x01, 0x01 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidOctetString()
{
string expected = "Type: V_ASN1_OCTET_STRING, Length: 1, Value: 01";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OCTET_STRING, 1, new byte[] { 0x01 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidObject()
{
string expected = "Type: V_ASN1_OBJECT, Length: 3, Value: 0.1.2.3 (/ITU-T/1/2/3)";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT, 3, new byte[] { 0x01, 0x02, 0x03 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidUTF8String()
{
string expected = "Type: V_ASN1_UTF8STRING, Length: 3, Value: ABC";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_UTF8STRING, 3, new byte[] { 0x41, 0x42, 0x43 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidPrintableString()
{
string expected = "Type: V_ASN1_PRINTABLESTRING, Length: 3, Value: ABC";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_PRINTABLESTRING, 3, new byte[] { 0x41, 0x42, 0x43 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidTeletexString()
{
string expected = "Type: V_ASN1_TELETEXSTRING, Length: 3, Value: ABC";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_TELETEXSTRING, 3, new byte[] { 0x41, 0x42, 0x43 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidIA5String()
{
string expected = "Type: V_ASN1_IA5STRING, Length: 3, Value: ABC";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_IA5STRING, 3, new byte[] { 0x41, 0x42, 0x43 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_InvalidUTCTime()
{
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: ABC";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_UTCTIME, 3, new byte[] { 0x41, 0x42, 0x43 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidUTCTime()
{
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: 1980-01-01 00:00:00";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_UTCTIME, 3, new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidBmpString()
{
string expected = "Type: V_ASN1_BMPSTRING, Length: 6, Value: ABC";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_BMPSTRING, 6, new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
[Fact]
public void Format_ValidUnformatted()
{
string expected = "Type: V_ASN1_OBJECT_DESCRIPTOR, Length: 1, Value: 01";
var tlv = new TypeLengthValue(ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, 1, new byte[] { 0x01 });
string actual = tlv.Format();
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -1,32 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.2.0</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
<Description>Serialization and deserialization helpers for various types</Description>
<Copyright>Copyright (c) Matt Nadareski 2022-2023</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/SabreTools/SabreTools.ASN1</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>asn asn1 dot oid</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath=""/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.IO" Version="1.2.0" />
</ItemGroup>
</Project>

View File

@@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.ASN1", "SabreTools.ASN1.csproj", "{88EA40DD-E313-479C-94EA-0AB948DB4720}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.ASN1", "SabreTools.ASN1\SabreTools.ASN1.csproj", "{88EA40DD-E313-479C-94EA-0AB948DB4720}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.ASN1.Test", "SabreTools.ASN1.Test\SabreTools.ASN1.Test.csproj", "{5C740920-9505-4B58-AC17-2967559F6AE7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -18,5 +20,9 @@ Global
{88EA40DD-E313-479C-94EA-0AB948DB4720}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88EA40DD-E313-479C-94EA-0AB948DB4720}.Release|Any CPU.ActiveCfg = Release|Any CPU
{88EA40DD-E313-479C-94EA-0AB948DB4720}.Release|Any CPU.Build.0 = Release|Any CPU
{5C740920-9505-4B58-AC17-2967559F6AE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5C740920-9505-4B58-AC17-2967559F6AE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5C740920-9505-4B58-AC17-2967559F6AE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5C740920-9505-4B58-AC17-2967559F6AE7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace SabreTools.ASN1
{
/// <summary>
/// ASN.1 Parser
/// </summary>
public static class AbstractSyntaxNotationOne
{
/// <summary>
/// Parse a byte array into a DER-encoded ASN.1 structure
/// </summary>
/// <param name="data">Byte array representing the data</param>
/// <param name="pointer">Current pointer into the data</param>
public static List<TypeLengthValue> Parse(byte[] data, int pointer)
{
// If the data is invalid
if (data.Length == 0)
throw new InvalidDataException(nameof(data));
if (pointer < 0 || pointer >= data.Length)
throw new IndexOutOfRangeException(nameof(pointer));
using var stream = new MemoryStream(data);
stream.Seek(pointer, SeekOrigin.Begin);
return Parse(stream);
}
/// <summary>
/// Parse a stream into a DER-encoded ASN.1 structure
/// </summary>
/// <param name="data">Stream representing the data</param>
public static List<TypeLengthValue> Parse(Stream data)
{
// If the data is invalid
if (data.Length == 0 || !data.CanRead)
throw new InvalidDataException(nameof(data));
if (data.Position < 0 || data.Position >= data.Length)
throw new IndexOutOfRangeException(nameof(data));
// Create the output list to return
var topLevelValues = new List<TypeLengthValue>();
// Loop through the data and return all top-level values
while (data.Position < data.Length)
{
var topLevelValue = new TypeLengthValue(data);
topLevelValues.Add(topLevelValue);
}
return topLevelValues;
}
}
}

View File

@@ -8,17 +8,6 @@ namespace SabreTools.ASN1
[Flags]
public enum ASN1Type : byte
{
#region Modifiers
V_ASN1_UNIVERSAL = 0x00,
V_ASN1_PRIMITIVE_TAG = 0x1F,
V_ASN1_CONSTRUCTED = 0x20,
V_ASN1_APPLICATION = 0x40,
V_ASN1_CONTEXT_SPECIFIC = 0x80,
V_ASN1_PRIVATE = 0xC0,
#endregion
#region Types
V_ASN1_EOC = 0x00,
@@ -51,5 +40,19 @@ namespace SabreTools.ASN1
V_ASN1_BMPSTRING = 0x1E,
#endregion
#region Modifiers
// Commented out because it is the default
// and can interfere with V_ASN1_EOC
// V_ASN1_UNIVERSAL = 0x00,
V_ASN1_PRIMITIVE_TAG = 0x1F,
V_ASN1_CONSTRUCTED = 0x20,
V_ASN1_APPLICATION = 0x40,
V_ASN1_CONTEXT_SPECIFIC = 0x80,
V_ASN1_PRIVATE = 0xC0,
#endregion
}
}

View File

@@ -9,13 +9,12 @@ namespace SabreTools.ASN1
/// Parse an OID in separated-value notation into ASN.1 notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>ASN.1 formatted string, if possible</returns>
/// <remarks>
public static string? ParseOIDToASN1Notation(ulong[]? values, ref int index)
public static string? ParseOIDToASN1Notation(ulong[]? values)
{
// TODO: Once the modified OID-IRI formatting is done, make an ASN.1 notation version
return null;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,907 @@
using System;
using System.Text;
namespace SabreTools.ASN1
{
/// <summary>
/// Methods related to Object Identifiers (OID) and OID-IRI formatting
/// </summary>
public static partial class ObjectIdentifier
{
/// <summary>
/// Parse an OID in separated-value notation into OID-IRI notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>OID-IRI formatted string, if possible</returns>
/// <see href="http://www.oid-info.com/index.htm"/>
public static string? ParseOIDToOIDIRINotation(ulong[]? values)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
// Set the initial index
int index = 0;
// Get a string builder for the path
var nameBuilder = new StringBuilder();
// Try to parse the standard value
string? standard = ParseOIDToOIDIRINotation(values, ref index);
if (standard == null)
return null;
// Add the standard value to the output
nameBuilder.Append(standard);
// If we have no more items
if (index == values.Length)
return nameBuilder.ToString();
// Add trailing items as just values
nameBuilder.Append("/");
// Get the remaining values in a new array
var remainingValues = new ulong[values.Length - index];
Array.Copy(values, index, remainingValues, 0, remainingValues.Length);
// Convert the values and append to the builder
var stringValues = Array.ConvertAll(remainingValues, v => v.ToString());
nameBuilder.Append(string.Join("/", stringValues));
// Create and return the string
return nameBuilder.ToString();
}
/// <summary>
/// Parse an OID in separated-value notation into OID-IRI notation
/// </summary>
/// <param name="values">List of values to check against</param>
/// <param name="index">Current index into the list</param>
/// <returns>OID-IRI formatted string, if possible</returns>
/// <see href="http://www.oid-info.com/index.htm"/>
private static string? ParseOIDToOIDIRINotation(ulong[]? values, ref int index)
{
// If we have an invalid set of values, we can't do anything
if (values == null || values.Length == 0)
return null;
// If we have an invalid index, we can't do anything
if (index < 0 || index >= values.Length)
return null;
#region Start
var oidPath = string.Empty;
switch (values[index++])
{
case 0: goto oid_0;
case 1: goto oid_1;
case 2: goto oid_2;
default: return oidPath;
}
#endregion
// itu-t, ccitt, itu-r
#region 0.*
oid_0:
oidPath += "/ITU-T";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_0_0;
case 2: return $"{oidPath}/Administration";
case 3: return $"{oidPath}/Network-Operator";
case 4: return $"{oidPath}/Identified-Organization";
case 5: return "/ITU-R/R-Recommendation";
case 9: return $"{oidPath}/Data";
default: return $"{oidPath}/{values[index - 1]}";
}
;
// recommendation
#region 0.0.*
oid_0_0:
oidPath += "/Recommendation";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/A",
2 => $"{oidPath}/B",
3 => $"{oidPath}/C",
4 => $"{oidPath}/D",
5 => $"{oidPath}/E",
6 => $"{oidPath}/F",
7 => $"{oidPath}/G",
8 => $"{oidPath}/H",
9 => $"{oidPath}/I",
10 => $"{oidPath}/J",
11 => $"{oidPath}/K",
12 => $"{oidPath}/L",
13 => $"{oidPath}/M",
14 => $"{oidPath}/N",
15 => $"{oidPath}/O",
16 => $"{oidPath}/P",
17 => $"{oidPath}/Q",
18 => $"{oidPath}/R",
19 => $"{oidPath}/S",
20 => $"{oidPath}/T",
21 => $"{oidPath}/U",
22 => $"{oidPath}/V",
24 => $"{oidPath}/X",
25 => $"{oidPath}/Y",
26 => $"{oidPath}/Z",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// iso
#region 1.*
oid_1:
oidPath += "/ISO";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: return $"{oidPath}/Standard";
case 1: return $"{oidPath}/Registration-Authority";
case 2: goto oid_1_2;
case 3: return $"{oidPath}/Identified-Organization";
default: return $"{oidPath}/{values[index - 1]}";
}
// member-body
#region 1.2.*
oid_1_2:
oidPath += "/Member-Body";
if (index == values.Length) return oidPath;
return values[index++] switch
{
36 => $"{oidPath}/AU",
40 => $"{oidPath}/AT",
56 => $"{oidPath}/BE",
124 => $"{oidPath}/CA",
156 => $"{oidPath}/CN",
203 => $"{oidPath}/CZ",
208 => $"{oidPath}/DK",
246 => $"{oidPath}/FI",
250 => $"{oidPath}/FR",
276 => $"{oidPath}/DE",
300 => $"{oidPath}/GR",
344 => $"{oidPath}/HK",
372 => $"{oidPath}/IE",
392 => $"{oidPath}/JP",
398 => $"{oidPath}/KZ",
410 => $"{oidPath}/KR",
498 => $"{oidPath}/MD",
528 => $"{oidPath}/NL",
566 => $"{oidPath}/NG",
578 => $"{oidPath}/NO",
616 => $"{oidPath}/PL",
643 => $"{oidPath}/RU",
702 => $"{oidPath}/SG",
752 => $"{oidPath}/SE",
804 => $"{oidPath}/UA",
826 => $"{oidPath}/GB",
840 => $"{oidPath}/US",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// joint-iso-itu-t, joint-iso-ccitt
#region 2.*
oid_2:
oidPath += "/Joint-ISO-ITU-T";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 1: return "/ASN.1";
case 16: oidPath = string.Empty; goto oid_2_16;
case 17: return $"{oidPath}/Registration-Procedures";
case 23: return $"{oidPath}/International-Organizations";
case 25: goto oid_2_25;
case 27: return "/Tag-Based";
case 28: return $"{oidPath}/ITS";
case 41: return "/BIP";
case 42: oidPath = string.Empty; goto oid_2_42;
case 48: oidPath = string.Empty; goto oid_2_48;
case 49: oidPath = string.Empty; goto oid_2_49;
case 50: return "/OIDResolutionSystem";
case 51: return "/GS1";
case 52: return $"{oidPath}/UAV";
case 999: return $"{oidPath}/Example";
default: return $"{oidPath}/{values[index - 1]}";
}
// country
#region 2.16.*
oid_2_16:
oidPath += "/Country";
if (index == values.Length) return oidPath;
return values[index++] switch
{
4 => $"{oidPath}AF",
8 => $"{oidPath}AL",
12 => $"{oidPath}DZ",
20 => $"{oidPath}AD",
24 => $"{oidPath}AO",
28 => $"{oidPath}AG",
31 => $"{oidPath}AZ",
32 => $"{oidPath}AR",
36 => $"{oidPath}AU",
40 => $"{oidPath}AT",
44 => $"{oidPath}BS",
48 => $"{oidPath}BH",
50 => $"{oidPath}BD",
51 => $"{oidPath}AM",
52 => $"{oidPath}BB",
56 => $"{oidPath}BE",
60 => $"{oidPath}BM",
64 => $"{oidPath}BT",
68 => $"{oidPath}BO",
70 => $"{oidPath}BA",
72 => $"{oidPath}BW",
76 => $"{oidPath}BR",
84 => $"{oidPath}BZ",
90 => $"{oidPath}SB",
96 => $"{oidPath}BN",
100 => $"{oidPath}BG",
104 => $"{oidPath}MM",
108 => $"{oidPath}BI",
112 => $"{oidPath}BY",
116 => $"{oidPath}KH",
120 => $"{oidPath}CM",
124 => $"{oidPath}CA",
132 => $"{oidPath}CV",
140 => $"{oidPath}CF",
144 => $"{oidPath}LK",
148 => $"{oidPath}TD",
152 => $"{oidPath}CL",
156 => $"{oidPath}CN",
158 => $"{oidPath}TW",
170 => $"{oidPath}CO",
174 => $"{oidPath}KM",
178 => $"{oidPath}CG",
180 => $"{oidPath}CD",
188 => $"{oidPath}CR",
191 => $"{oidPath}HR",
192 => $"{oidPath}CU",
196 => $"{oidPath}CY",
203 => $"{oidPath}CZ",
204 => $"{oidPath}BJ",
208 => $"{oidPath}DK",
212 => $"{oidPath}DM",
214 => $"{oidPath}DO",
218 => $"{oidPath}EC",
222 => $"{oidPath}SV",
226 => $"{oidPath}GQ",
231 => $"{oidPath}ET",
232 => $"{oidPath}ER",
233 => $"{oidPath}EE",
242 => $"{oidPath}FJ",
246 => $"{oidPath}FI",
250 => $"{oidPath}FR",
262 => $"{oidPath}DJ",
266 => $"{oidPath}GA",
268 => $"{oidPath}GE",
270 => $"{oidPath}GM",
275 => $"{oidPath}PS",
276 => $"{oidPath}DE",
288 => $"{oidPath}GH",
296 => $"{oidPath}KI",
300 => $"{oidPath}GR",
308 => $"{oidPath}GD",
320 => $"{oidPath}GT",
324 => $"{oidPath}GN",
328 => $"{oidPath}GY",
332 => $"{oidPath}HT",
336 => $"{oidPath}VA",
340 => $"{oidPath}HN",
344 => $"{oidPath}HK",
348 => $"{oidPath}HU",
352 => $"{oidPath}IS",
356 => $"{oidPath}IN",
360 => $"{oidPath}ID",
364 => $"{oidPath}IR",
368 => $"{oidPath}IQ",
372 => $"{oidPath}IE",
376 => $"{oidPath}IL",
380 => $"{oidPath}IT",
384 => $"{oidPath}CI",
388 => $"{oidPath}JM",
392 => $"{oidPath}JP",
398 => $"{oidPath}KZ",
400 => $"{oidPath}JO",
404 => $"{oidPath}KE",
408 => $"{oidPath}KP",
410 => $"{oidPath}KR",
414 => $"{oidPath}KW",
417 => $"{oidPath}KG",
418 => $"{oidPath}LA",
422 => $"{oidPath}LB",
426 => $"{oidPath}LS",
428 => $"{oidPath}LV",
430 => $"{oidPath}LR",
434 => $"{oidPath}LY",
438 => $"{oidPath}LI",
440 => $"{oidPath}LT",
442 => $"{oidPath}LU",
450 => $"{oidPath}MG",
454 => $"{oidPath}MW",
458 => $"{oidPath}MY",
462 => $"{oidPath}MV",
466 => $"{oidPath}ML",
470 => $"{oidPath}MT",
478 => $"{oidPath}MR",
480 => $"{oidPath}MU",
484 => $"{oidPath}MX",
492 => $"{oidPath}MC",
496 => $"{oidPath}MN",
498 => $"{oidPath}MD",
499 => $"{oidPath}ME",
504 => $"{oidPath}MA",
508 => $"{oidPath}MZ",
512 => $"{oidPath}OM",
516 => $"{oidPath}NA",
520 => $"{oidPath}NR",
524 => $"{oidPath}NP",
528 => $"{oidPath}NL",
530 => $"{oidPath}AN",
548 => $"{oidPath}VU",
554 => $"{oidPath}NZ",
558 => $"{oidPath}NI",
562 => $"{oidPath}NE",
566 => $"{oidPath}NG",
578 => $"{oidPath}NO",
583 => $"{oidPath}FM",
584 => $"{oidPath}MH",
585 => $"{oidPath}PW",
586 => $"{oidPath}PK",
591 => $"{oidPath}PA",
598 => $"{oidPath}PG",
600 => $"{oidPath}PY",
604 => $"{oidPath}PE",
608 => $"{oidPath}PH",
616 => $"{oidPath}PL",
620 => $"{oidPath}PT",
624 => $"{oidPath}GW",
626 => $"{oidPath}TL",
634 => $"{oidPath}QA",
642 => $"{oidPath}RO",
643 => $"{oidPath}RU",
646 => $"{oidPath}RW",
659 => $"{oidPath}KN",
662 => $"{oidPath}LC",
670 => $"{oidPath}VC",
674 => $"{oidPath}SM",
678 => $"{oidPath}ST",
682 => $"{oidPath}SA",
686 => $"{oidPath}SN",
688 => $"{oidPath}RS",
690 => $"{oidPath}SC",
694 => $"{oidPath}SL",
702 => $"{oidPath}SG",
703 => $"{oidPath}SK",
704 => $"{oidPath}VN",
705 => $"{oidPath}SI",
706 => $"{oidPath}SO",
710 => $"{oidPath}ZA",
716 => $"{oidPath}ZW",
724 => $"{oidPath}ES",
728 => $"{oidPath}SS",
729 => $"{oidPath}SD",
740 => $"{oidPath}SR",
748 => $"{oidPath}SZ",
752 => $"{oidPath}SE",
756 => $"{oidPath}CH",
760 => $"{oidPath}SY",
762 => $"{oidPath}TJ",
764 => $"{oidPath}TH",
768 => $"{oidPath}TG",
776 => $"{oidPath}TO",
780 => $"{oidPath}TT",
784 => $"{oidPath}AE",
788 => $"{oidPath}TN",
792 => $"{oidPath}TR",
795 => $"{oidPath}TM",
798 => $"{oidPath}TV",
800 => $"{oidPath}UG",
804 => $"{oidPath}UA",
807 => $"{oidPath}MK",
818 => $"{oidPath}EG",
826 => $"{oidPath}GB",
834 => $"{oidPath}TZ",
840 => $"{oidPath}US",
854 => $"{oidPath}BF",
858 => $"{oidPath}UY",
860 => $"{oidPath}UZ",
862 => $"{oidPath}VE",
882 => $"{oidPath}WS",
887 => $"{oidPath}YE",
894 => $"{oidPath}ZM",
_ => $"{oidPath}{values[index - 1]}",
};
#endregion
// uuid [TODO: Requires 128-bit values]
#region 2.25.*
oid_2_25:
oidPath += "/UUID";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/00000000-0000-0000-0000-000000000000",
//case 288786655511405443130567505384701230: return $"{oidPath}/00379e48-0a2b-1085-b288-0002a5d5fd2e";
//case 987895962269883002155146617097157934: return $"{oidPath}/00be4308-0c89-1085-8ea0-0002a5d5fd2e";
//case 1858228783942312576083372383319475483: return $"{oidPath}/0165e1c0-a655-11e0-95b8-0002a5d5c51b";
//case 2474299330026746002885628159579243803: return $"{oidPath}/01dc8860-25fb-11da-82b2-0002a5d5c51b";
//case 3263645701162998421821186056373271854: return $"{oidPath}/02748e28-08c4-1085-b21d-0002a5d5fd2e";
//case 3325839809379844461264382260940242222: return $"{oidPath}/02808890-0ad8-1085-9bdf-0002a5d5fd2e";
// TODO: Left off at http://www.oid-info.com/get/2.25.3664154270495270126161055518190585115
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// telebiometrics
#region 2.42.*
oid_2_42:
oidPath += "/Telebiometrics";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_0;
case 1: goto oid_2_42_1;
case 2: goto oid_2_42_2;
case 3: goto oid_2_42_3;
default: return $"{oidPath}/{values[index - 1]}";
}
// modules
#region 2.42.0.*
oid_2_42_0:
oidPath += "/Modules";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_0_0;
default: return $"{oidPath}/{values[index - 1]}";
}
// main
#region 2.42.0.0.*
oid_2_42_0_0:
oidPath += "/Main_Module";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// tmm
#region 2.42.1.*
oid_2_42_1:
oidPath += "/TMM";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_1_0;
case 1: goto oid_2_42_1_1;
case 2: goto oid_2_42_1_2;
case 3: goto oid_2_42_1_3;
case 4: return $"{oidPath}/Practitioners";
default: return $"{oidPath}/{values[index - 1]}";
}
// modules
#region 2.42.1.0.*
oid_2_42_1_0:
oidPath += "/Modules";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_1_0_0;
default: return $"{oidPath}/{values[index - 1]}";
}
// main
#region 2.42.1.0.0.*
oid_2_42_1_0_0:
oidPath += "/Main";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/First_Version",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// measures, metric
#region 2.42.1.1.*
oid_2_42_1_1:
oidPath += "/Measures";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 1: goto oid_2_42_1_1_1;
case 2: return $"{oidPath}/Units";
case 3: return $"{oidPath}";
case 4: return $"{oidPath}/Conditions";
case 5: goto oid_2_42_1_1_5;
default: return $"{oidPath}/{values[index - 1]}";
}
// quantities
#region 2.42.1.1.1.*
oid_2_42_1_1_1:
oidPath += "/Quantities";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Physics",
2 => $"{oidPath}/Chemistry",
3 => $"{oidPath}/Biology",
4 => $"{oidPath}/Culturology",
5 => $"{oidPath}/Psychology",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// methods
#region 2.42.1.1.5.*
oid_2_42_1_1_5:
oidPath += "/Methods";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Physics",
2 => $"{oidPath}/Chemistry",
3 => $"{oidPath}/Biology",
4 => $"{oidPath}/Culturology",
5 => $"{oidPath}/Psychology",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// fields-of-study, scientific
#region 2.42.1.2.*
oid_2_42_1_2:
oidPath += "/Fields_of_Study";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Physics",
2 => $"{oidPath}/Chemistry",
3 => $"{oidPath}/Biology",
4 => $"{oidPath}/Culturology",
5 => $"{oidPath}/Psychology",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// modalities, sensory
#region 2.42.1.3.*
oid_2_42_1_3:
oidPath += "/Modalities";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Tango",
2 => $"{oidPath}/Video",
3 => $"{oidPath}/Audio",
4 => $"{oidPath}/Chemo",
5 => $"{oidPath}/Radio",
6 => $"{oidPath}/Calor",
7 => $"{oidPath}/Electro",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// human-physiology
#region 2.42.2.*
oid_2_42_2:
oidPath += "/Human_Physiology";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_2_0;
case 1: goto oid_2_42_2_1;
case 2: return $"{oidPath}/Symbol_Combinations";
default: return $"{oidPath}/{values[index - 1]}";
}
// modules
#region 2.42.2.0.*
oid_2_42_2_0:
oidPath += "/Modules";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_2_0_0;
default: return $"{oidPath}/{values[index - 1]}";
}
// main
#region 2.42.2.0.0.*
oid_2_42_2_0_0:
oidPath += "/Main_Module";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/First_Version",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// symbols
#region 2.42.2.1.*
oid_2_42_2_1:
oidPath += "/Symbols";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Tango_in",
2 => $"{oidPath}/Video_in",
3 => $"{oidPath}/Audio_in",
4 => $"{oidPath}/Chemo_in",
5 => $"{oidPath}/Radio_in",
6 => $"{oidPath}/Calor_in",
7 => $"{oidPath}/Tango_out",
8 => $"{oidPath}/Video_out",
9 => $"{oidPath}/Audio_out",
10 => $"{oidPath}/Chemo_out",
11 => $"{oidPath}/Radio_out",
12 => $"{oidPath}/Calor_out",
13 => $"{oidPath}/Safe",
14 => $"{oidPath}/Threshold",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
// obj-cat, telehealth, e-health-protocol, th
#region 2.42.3.*
oid_2_42_3:
oidPath += "/E_Health_Protocol";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_3_0;
case 1: return $"{oidPath}/[Patient schemes]";
case 2: return $"{oidPath}/[Medical staff schemes]";
case 3: return $"{oidPath}/[Observer schemes]";
case 4: return $"{oidPath}/[Pharmaceutical schemes]";
case 5: return $"{oidPath}/[Laboratory schemes]";
case 6: return $"{oidPath}/[Drug manufacturer schemes]";
case 7: return $"{oidPath}/[Medical device schemes]";
case 8: return $"{oidPath}/[Medical software schemes]";
case 9: return $"{oidPath}/[Medical insurance schemes]";
case 10: return $"{oidPath}/[Medical record schemes]";
default: return $"{oidPath}/{values[index - 1]}";
}
// obj-cat, telehealth, e-health-protocol, th
#region 2.42.3.0.*
oid_2_42_3_0:
oidPath += "/Modules";
if (index == values.Length) return oidPath;
switch (values[index++])
{
case 0: goto oid_2_42_3_0_0;
case 1: goto oid_2_42_3_0_1;
case 2: goto oid_2_42_3_0_2;
case 3: goto oid_2_42_3_0_3;
case 4: goto oid_2_42_3_0_4;
case 5: goto oid_2_42_3_0_5;
default: return $"{oidPath}/{values[index - 1]}";
}
// identification
#region 2.42.3.0.0.*
oid_2_42_3_0_0:
oidPath += "/Identification";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// set-up
#region 2.42.3.0.1.*
oid_2_42_3_0_1:
oidPath += "/Setup";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// send-and-ack
#region 2.42.3.0.2.*
oid_2_42_3_0_2:
oidPath += "/Send-and-ack";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// command-response
#region 2.42.3.0.3.*
oid_2_42_3_0_3:
oidPath += "/Command-response";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// quantity-and-units
#region 2.42.3.0.4.*
oid_2_42_3_0_4:
oidPath += "/Quantities_And_Units";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Version1",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// examples
#region 2.42.3.0.5.*
oid_2_42_3_0_5:
oidPath += "/Examples";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/Command_Response",
1 => $"{oidPath}/Data_Message",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
#endregion
#endregion
// cybersecurity
#region 2.48.*
oid_2_48:
oidPath += "/Cybersecurity";
if (index == values.Length) return oidPath;
return values[index++] switch
{
1 => $"{oidPath}/Country",
2 => $"{oidPath}/International-Org",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
// alerting
#region 2.49.*
oid_2_49:
oidPath += "/Alerting";
if (index == values.Length) return oidPath;
return values[index++] switch
{
0 => $"{oidPath}/WMO",
_ => $"{oidPath}/{values[index - 1]}",
};
#endregion
#endregion
}
}
}

View File

@@ -24,7 +24,7 @@ namespace SabreTools.ASN1
int firstNode = Math.DivRem(data[0], 40, out int secondNode);
// Create a list for all nodes
List<ulong> nodes = new List<ulong> { (ulong)firstNode, (ulong)secondNode };
List<ulong> nodes = [(ulong)firstNode, (ulong)secondNode];
// All other nodes are encoded uniquely
int offset = 1;
@@ -65,7 +65,7 @@ namespace SabreTools.ASN1
nodes.Add(dotValue);
}
return nodes.ToArray();
return [.. nodes];
}
}
}

View File

@@ -1,3 +1,5 @@
using System;
namespace SabreTools.ASN1
{
/// <summary>
@@ -16,7 +18,8 @@ namespace SabreTools.ASN1
if (values == null || values.Length == 0)
return null;
return string.Join(".", values);
var stringValues = Array.ConvertAll(values, v => v.ToString());
return string.Join(".", [.. stringValues]);
}
}
}
}

View File

@@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<IncludeSymbols>true</IncludeSymbols>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.6.4</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
<Description>Serialization and deserialization helpers for various types</Description>
<Copyright>Copyright (c) Matt Nadareski 2022-2024</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/SabreTools/SabreTools.ASN1</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>asn asn1 dot oid</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="SabreTools.ASN1.Test" />
</ItemGroup>
<ItemGroup>
<None Include="../README.md" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="NetLegacySupport.Numerics" Version="1.0.1" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`))" />
<PackageReference Include="SabreTools.IO" Version="1.7.5" />
</ItemGroup>
</Project>

View File

@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Numerics;
using System.Text;
using SabreTools.IO;
using SabreTools.IO.Extensions;
namespace SabreTools.ASN1
{
@@ -27,6 +27,16 @@ namespace SabreTools.ASN1
/// </summary>
public object? Value { get; private set; }
/// <summary>
/// Manual constructor
/// </summary>
public TypeLengthValue(ASN1Type type, ulong length, object? value)
{
Type = type;
Length = length;
Value = value;
}
/// <summary>
/// Read from the source data array at an index
/// </summary>
@@ -34,34 +44,26 @@ namespace SabreTools.ASN1
/// <param name="index">Index within the array to read at</param>
public TypeLengthValue(byte[] data, ref int index)
{
// Get the type and modifiers
this.Type = (ASN1Type)data[index++];
// If the data is invalid
if (data.Length == 0)
throw new InvalidDataException(nameof(data));
if (index < 0 || index >= data.Length)
throw new IndexOutOfRangeException(nameof(index));
// If we have an end indicator, we just return
if (this.Type == ASN1Type.V_ASN1_EOC)
return;
using var stream = new MemoryStream(data);
stream.Seek(index, SeekOrigin.Begin);
if (!Parse(stream))
throw new InvalidDataException(nameof(data));
}
// Get the length of the value
this.Length = ReadLength(data, ref index);
// Read the value
if (this.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
{
var valueList = new List<TypeLengthValue>();
int currentIndex = index;
while (index < currentIndex + (int)this.Length)
{
valueList.Add(new TypeLengthValue(data, ref index));
}
this.Value = valueList.ToArray();
}
else
{
// TODO: Get more granular based on type
this.Value = data.ReadBytes(ref index, (int)this.Length);
}
/// <summary>
/// Read from the source data stream
/// </summary>
/// <param name="data">Stream representing data to read</param>
public TypeLengthValue(Stream data)
{
if (!Parse(data))
throw new InvalidDataException(nameof(data));
}
/// <summary>
@@ -72,30 +74,29 @@ namespace SabreTools.ASN1
public string Format(int paddingLevel = 0)
{
// Create the left-padding string
string padding = new string(' ', paddingLevel);
// If we have an invalid item
if (this.Type == 0)
return $"{padding}UNKNOWN TYPE";
string padding = new(' ', paddingLevel);
// Create the string builder
StringBuilder formatBuilder = new StringBuilder();
var formatBuilder = new StringBuilder();
// Append the type
formatBuilder.Append($"{padding}Type: {this.Type}");
if (this.Type == ASN1Type.V_ASN1_EOC)
formatBuilder.Append($"{padding}Type: {Type}");
if (Type == ASN1Type.V_ASN1_EOC)
return formatBuilder.ToString();
// Append the length
formatBuilder.Append($", Length: {this.Length}");
if (this.Length == 0)
formatBuilder.Append($", Length: {Length}");
if (Length == 0)
return formatBuilder.ToString();
// If we have a constructed type
if (this.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
#if NET20 || NET35
if ((Type & ASN1Type.V_ASN1_CONSTRUCTED) != 0)
#else
if (Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
#endif
{
var valueAsObjectArray = this.Value as TypeLengthValue[];
if (valueAsObjectArray == null)
if (Value is not TypeLengthValue[] valueAsObjectArray)
{
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
return formatBuilder.ToString();
@@ -113,29 +114,35 @@ namespace SabreTools.ASN1
}
// Get the value as a byte array
byte[]? valueAsByteArray = this.Value as byte[];
if (valueAsByteArray == null)
if (Value is not byte[] valueAsByteArray)
{
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
return formatBuilder.ToString();
}
else if (valueAsByteArray.Length == 0)
{
formatBuilder.Append(", Value: [NO DATA]");
return formatBuilder.ToString();
}
// If we have a primitive type
switch (this.Type)
switch (Type)
{
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-boolean"/>
case ASN1Type.V_ASN1_BOOLEAN:
if (this.Length > 1 || valueAsByteArray.Length > 1)
if (Length > 1)
formatBuilder.Append($" [Expected length of 1]");
else if (valueAsByteArray.Length > 1)
formatBuilder.Append($" [Expected value length of 1]");
bool booleanValue = valueAsByteArray[0] == 0x00 ? false : true;
bool booleanValue = valueAsByteArray[0] != 0x00;
formatBuilder.Append($", Value: {booleanValue}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-integer"/>
case ASN1Type.V_ASN1_INTEGER:
Array.Reverse(valueAsByteArray);
BigInteger integerValue = new BigInteger(valueAsByteArray);
var integerValue = new BigInteger(valueAsByteArray);
formatBuilder.Append($", Value: {integerValue}");
break;
@@ -143,7 +150,10 @@ namespace SabreTools.ASN1
case ASN1Type.V_ASN1_BIT_STRING:
// TODO: Read into a BitArray and print that out instead?
int unusedBits = valueAsByteArray[0];
formatBuilder.Append($", Value with {unusedBits} unused bits: {BitConverter.ToString(valueAsByteArray.Skip(1).ToArray()).Replace('-', ' ')}");
if (unusedBits == 0)
formatBuilder.Append($", Value with {unusedBits} unused bits");
else
formatBuilder.Append($", Value with {unusedBits} unused bits: {BitConverter.ToString(valueAsByteArray, 1).Replace('-', ' ')}");
break;
/// <see href="https://learn.microsoft.com/en-us/windows/win32/seccertenroll/about-octet-string"/>
@@ -155,7 +165,7 @@ namespace SabreTools.ASN1
/// <see cref="http://snmpsharpnet.com/index.php/2009/03/02/ber-encoding-and-decoding-oid-values/"/>
case ASN1Type.V_ASN1_OBJECT:
// Derive array of values
ulong[] objectNodes = ObjectIdentifier.ParseDERIntoArray(valueAsByteArray, this.Length);
ulong[] objectNodes = ObjectIdentifier.ParseDERIntoArray(valueAsByteArray, Length);
// Append the dot and modified OID-IRI notations
string? dotNotationString = ObjectIdentifier.ParseOIDToDotNotation(objectNodes);
@@ -186,7 +196,7 @@ namespace SabreTools.ASN1
case ASN1Type.V_ASN1_UTCTIME:
string utctimeString = Encoding.ASCII.GetString(valueAsByteArray);
if (DateTime.TryParse(utctimeString, out DateTime utctimeDateTime))
formatBuilder.Append($", Value: {utctimeDateTime}");
formatBuilder.Append($", Value: {utctimeDateTime:yyyy-MM-dd HH:mm:ss}");
else
formatBuilder.Append($", Value: {utctimeString}");
break;
@@ -197,11 +207,7 @@ namespace SabreTools.ASN1
break;
default:
#if NET40 || NET452
formatBuilder.Append($", Value (Unknown Format): {BitConverter.ToString(this.Value as byte[] ?? new byte[0]).Replace('-', ' ')}");
#else
formatBuilder.Append($", Value (Unknown Format): {BitConverter.ToString(this.Value as byte[] ?? Array.Empty<byte>()).Replace('-', ' ')}");
#endif
formatBuilder.Append($", Value: {BitConverter.ToString(valueAsByteArray).Replace('-', ' ')}");
break;
}
@@ -209,20 +215,70 @@ namespace SabreTools.ASN1
return formatBuilder.ToString();
}
/// <summary>
/// Parse a stream into TLV data
/// </summary>
/// <param name="data">Stream representing data to read</param>
/// <returns>Indication if parsing was successful</returns>
private bool Parse(Stream data)
{
// If the data is invalid
if (data.Length == 0 || !data.CanRead)
return false;
if (data.Position < 0 || data.Position >= data.Length)
throw new IndexOutOfRangeException(nameof(data));
// Get the type and modifiers
Type = (ASN1Type)data.ReadByteValue();
// If we have an end indicator, we just return
if (Type == ASN1Type.V_ASN1_EOC)
return true;
// Get the length of the value
Length = ReadLength(data);
// Read the value
#if NET20 || NET35
if ((Type & ASN1Type.V_ASN1_CONSTRUCTED) != 0)
#else
if (Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
#endif
{
var valueList = new List<TypeLengthValue>();
long currentIndex = data.Position;
while (data.Position < currentIndex + (long)Length)
{
valueList.Add(new TypeLengthValue(data));
}
Value = valueList.ToArray();
}
else
{
// TODO: Get more granular based on type
Value = data.ReadBytes((int)Length);
}
return true;
}
/// <summary>
/// Reads the length field for a type
/// </summary>
/// <param name="data">Byte array representing data to read</param>
/// <param name="index">Index within the array to read at</param>
/// <param name="data">Stream representing data to read</param>
/// <returns>The length value read from the array</returns>
private static ulong ReadLength(byte[] data, ref int index)
private static ulong ReadLength(Stream data)
{
// If we have invalid data, throw an exception
if (data == null || index < 0 && index >= data.Length)
throw new ArgumentException();
// If the data is invalid
if (data.Length == 0 || !data.CanRead)
throw new InvalidDataException(nameof(data));
if (data.Position < 0 || data.Position >= data.Length)
throw new IndexOutOfRangeException(nameof(data));
// Read the first byte, assuming it's the length
byte length = data[index++];
byte length = data.ReadByteValue();
// If the bit 7 is not set, then use the value as it is
if ((length & 0x80) == 0)
@@ -230,36 +286,48 @@ namespace SabreTools.ASN1
// Otherwise, use the value as the number of remaining bytes to read
int bytesToRead = length & ~0x80;
byte[]? bytesRead = data.ReadBytes(ref index, bytesToRead);
if (bytesRead == null)
throw new InvalidOperationException();
// TODO: Write extensions to read big-endian
// Reverse the bytes to be in big-endian order
Array.Reverse(bytesRead);
switch (bytesRead.Length)
// Assemble the length based on byte count
ulong fullLength = 0;
switch (bytesToRead)
{
case 1:
return bytesRead[0];
case 2:
return BitConverter.ToUInt16(bytesRead, 0);
case 3:
Array.Resize(ref bytesRead, 4);
case 8:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 7;
case 7:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 6;
case 6:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 5;
case 5:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 4;
case 4:
return BitConverter.ToUInt32(bytesRead, 0);
case 5:
case 6:
case 7:
Array.Resize(ref bytesRead, 8);
goto case 8;
case 8:
return BitConverter.ToUInt64(bytesRead, 0);
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 3;
case 3:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 2;
case 2:
fullLength |= data.ReadByteValue();
fullLength <<= 8;
goto case 1;
case 1:
fullLength |= data.ReadByteValue();
break;
default:
throw new InvalidOperationException();
}
return fullLength;
}
}
}

36
publish-nix.sh Executable file
View File

@@ -0,0 +1,36 @@
#! /bin/bash
# This batch file assumes the following:
# - .NET 9.0 (or newer) SDK is installed and in PATH
#
# If any of these are not satisfied, the operation may fail
# in an unpredictable way and result in an incomplete output.
# Optional parameters
NO_BUILD=false
while getopts "b" OPTION
do
case $OPTION in
b)
NO_BUILD=true
;;
*)
echo "Invalid option provided"
exit 1
;;
esac
done
# Set the current directory as a variable
BUILD_FOLDER=$PWD
# Only build if requested
if [ $NO_BUILD = false ]
then
# Restore Nuget packages for all builds
echo "Restoring Nuget packages"
dotnet restore
# Create Nuget Package
dotnet pack SabreTools.ASN1/SabreTools.ASN1.csproj --output $BUILD_FOLDER
fi

26
publish-win.ps1 Normal file
View File

@@ -0,0 +1,26 @@
# This batch file assumes the following:
# - .NET 9.0 (or newer) SDK is installed and in PATH
#
# If any of these are not satisfied, the operation may fail
# in an unpredictable way and result in an incomplete output.
# Optional parameters
param(
[Parameter(Mandatory = $false)]
[Alias("NoBuild")]
[switch]$NO_BUILD
)
# Set the current directory as a variable
$BUILD_FOLDER = $PSScriptRoot
# Only build if requested
if (!$NO_BUILD.IsPresent)
{
# Restore Nuget packages for all builds
Write-Host "Restoring Nuget packages"
dotnet restore
# Create Nuget Package
dotnet pack SabreTools.ASN1\SabreTools.ASN1.csproj --output $BUILD_FOLDER
}