Skip to content

Commit 07a915d

Browse files
Improved JSON parsing
1 parent f82d7df commit 07a915d

14 files changed

Lines changed: 199 additions & 571 deletions

Src/DSInternals.Common.Test/KeyCredentialTester.cs

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -14,59 +14,10 @@ private static KeyCredential CreateSampleKey()
1414
{
1515
byte[] publicKey = "525341310008000003000000000100000000000000000000010001C1A78914457758B0B13C70C710C7F8548F3F9ED56AD4640B6E6A112655C98ECAC1CBD68A298F5686C08439428A97FE6FDF58D78EA481905182BAD684C2D9C5CDE1CDE34AA19742E8BBF58B953EAC4C562FCF598CC176B02DBE9FFFEF5937A65815C236F92892F7E511A1FEDD5483CB33F1EA715D68106180DED2432A293367114A6E325E62F93F73D7ECE4B6A2BCDB829D95C8645C3073B94BA7CB7515CD29042F0967201C6E24A77821E92A6C756DF79841ACBAAE11D90CA03B9FCD24EF9E304B5D35248A7BD70557399960277058AE3E99C7C7E2284858B7BF8B08CDD286964186A50A7FCBCC6A24F00FEE5B9698BBD3B1AEAD0CE81FEA461C0ABD716843A5".HexToBinary();
1616
Guid deviceId = Guid.Parse("47f577e3-d2d0-4a0a-8aca-e0501098bde4");
17-
DateTime creationTime = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
17+
DateTime creationTime = new(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
1818
return new KeyCredential(publicKey, deviceId, DummyDN, creationTime);
1919
}
2020

21-
[TestMethod]
22-
public void KeyCredential_RoundTrip_DoubleQuoted()
23-
{
24-
KeyCredential key = CreateSampleKey();
25-
string json = key.ToJson();
26-
KeyCredential result = KeyCredential.ParseJson(json);
27-
Assert.IsNotNull(result);
28-
Assert.AreEqual(key.Identifier, result.Identifier);
29-
Assert.AreEqual(key.DeviceId, result.DeviceId);
30-
Assert.AreEqual(key.CreationTime, result.CreationTime);
31-
}
32-
33-
[TestMethod]
34-
public void KeyCredential_Deserialize_SingleQuoted_Input()
35-
{
36-
KeyCredential key = CreateSampleKey();
37-
string json = key.ToJson().Replace('"', '\'');
38-
KeyCredential result = KeyCredential.ParseJson(json);
39-
Assert.IsNotNull(result);
40-
Assert.AreEqual(key.DeviceId, result.DeviceId);
41-
}
42-
43-
[TestMethod]
44-
public void KeyCredential_Deserialize_TrailingComma_And_Comments()
45-
{
46-
KeyCredential key = CreateSampleKey();
47-
string json = key.ToJson();
48-
string jsonWithComment = json.Insert(1, "\n // comment\n");
49-
jsonWithComment = jsonWithComment.Substring(0, jsonWithComment.Length - 1) + ",\n}";
50-
KeyCredential result = KeyCredential.ParseJson(jsonWithComment);
51-
Assert.IsNotNull(result);
52-
Assert.AreEqual(key.DeviceId, result.DeviceId);
53-
}
54-
55-
[TestMethod]
56-
public void Parse_SingleQuoted_WithEscapedApostrophe_Works()
57-
{
58-
var jsonSingleQuoted = "{ 'OwnerDN':'CN=O\\'Connor,DC=contoso,DC=com', 'IsComputerKey': false }";
59-
var obj = KeyCredential.ParseJson(jsonSingleQuoted);
60-
Assert.IsNotNull(obj);
61-
}
62-
63-
[TestMethod]
64-
public void Parse_BadJson_StillThrows()
65-
{
66-
var bad = "{ \"OwnerDN\": \"CN=User,DC=contoso,DC=com\" ";
67-
Assert.ThrowsExactly<JsonException>(() => _ = KeyCredential.ParseJson(bad));
68-
}
69-
7021
[TestMethod]
7122
public void KeyCredential_Parse_NonMFAKey()
7223
{
@@ -500,7 +451,7 @@ public void KeyCredential_Parse_DeviceKey_TPM()
500451
Assert.AreEqual(KeyFlags.None, key.CustomKeyInfo.Flags);
501452
Assert.AreEqual("OoYGWyO8xBkLx3x2d0QltMav9+41lY0sGxKIMPF9if0=", key.Identifier);
502453
Assert.AreEqual(key.LastLogonTime, key.CreationTime);
503-
Assert.AreEqual("2019-08-02T18:11:37.5665512Z", key.CreationTime.ToUniversalTime().ToString("o"));
454+
Assert.AreEqual("2019-08-02T18:11:37.5665512Z", key.CreationTime.Value.ToUniversalTime().ToString("o"));
504455
Assert.AreEqual(0x304, key.RawKeyMaterial.Length);
505456

506457
// Serialize
@@ -542,7 +493,7 @@ public void KeyCredential_Parse_Impacket()
542493
Assert.AreEqual(KeySource.AD, key.Source);
543494

544495
// Bug in Impacket causes a time skew of 1600 years:
545-
Assert.AreEqual(3625, key.CreationTime.Year);
496+
Assert.AreEqual(3625, key.CreationTime.Value.Year);
546497

547498
// Serialize
548499
byte[] serialized = key.ToByteArray();
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using DSInternals.Common.Data;
2+
3+
namespace DSInternals.Common.Test;
4+
5+
/// <summary>
6+
/// Contains tests for parsing the key material corresponding to KEY_USAGE_FIDO.
7+
/// </summary>
8+
[TestClass]
9+
public class KeyMaterialFidoTester
10+
{
11+
[TestMethod]
12+
public void FidoKeyMaterial_Parse_FIDO_Input1()
13+
{
14+
string encodedKeyMaterial = "eyJ2ZXJzaW9uIjoxLCJhdXRoRGF0YSI6Ik5XeWUxS0NUSWJscFh4NnZrWUlEOGJWZmFKMm1IN3lXR0V3VmZkcG9ESUhGQUFBQUdjdHBTQjZQOTBBNWsrd0tKeW1oVktnQUVCckljaURTekdqanNLcmRTelZJdElHbEFRSURKaUFCSVZnZ29uTkNlY2EwZE5LTzlaWXBGdjlvMCtlZ0VGQ1ZTeXJ0UmN1NndrMStoT2dpV0NDNWE3MFI0ZlNFZ0ZpNWxnQTRBVVBmN1Y0Q2p5VWcvb1VWTFEzem5kZnc1YUZyYUcxaFl5MXpaV055WlhUMSIsIng1YyI6WyJNSUlDdlRDQ0FhV2dBd0lCQWdJRUdLeEd3REFOQmdrcWhraUc5dzBCQVFzRkFEQXVNU3d3S2dZRFZRUURFeU5aZFdKcFkyOGdWVEpHSUZKdmIzUWdRMEVnVTJWeWFXRnNJRFExTnpJd01EWXpNVEFnRncweE5EQTRNREV3TURBd01EQmFHQTh5TURVd01Ea3dOREF3TURBd01Gb3diakVMTUFrR0ExVUVCaE1DVTBVeEVqQVFCZ05WQkFvTUNWbDFZbWxqYnlCQlFqRWlNQ0FHQTFVRUN3d1pRWFYwYUdWdWRHbGpZWFJ2Y2lCQmRIUmxjM1JoZEdsdmJqRW5NQ1VHQTFVRUF3d2VXWFZpYVdOdklGVXlSaUJGUlNCVFpYSnBZV3dnTkRFek9UUXpORGc0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFZWVvN0xIeEpjQkJpSXd6U1ArdGc1U2t4Y2RTRDhRQytoWjFyRDRPWEF3RzFSczNVYnMvSzQrUHpENEhwN1dLOUpvMU1IcjAzczd5K2txakNydXRPT3FOc01Hb3dJZ1lKS3dZQkJBR0N4QW9DQkJVeExqTXVOaTR4TGpRdU1TNDBNVFE0TWk0eExqY3dFd1lMS3dZQkJBR0M1UndDQVFFRUJBTUNCU0F3SVFZTEt3WUJCQUdDNVJ3QkFRUUVFZ1FReTJsSUhvLzNRRG1UN0FvbkthRlVxREFNQmdOVkhSTUJBZjhFQWpBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ1huUU9YMkdENEx1RmRNUng1YnJyN0l2cW40SVRadXJUR0c3dFg4K2Ewd1lwSU43aGNQRTdiNUlORDlOYWwyYkhPMm9yaC90U1JLU0Z6Qlk1ZTRjdmRhOXJBZFZmR29PalRhQ1c2Rlo1L3RhMk0ydmdFaG96NURvOGZpdW9Yd0JhMVhDcDYxSmZJbFB0eDExUFhtNXBJUzJ3M2JYSTdtWTB1SFVNR3Z4QXp0YTc0ektYTHNsYUxhU1FpYlNLaldLdDloK1NzWHk0SkdxY1ZlZk9sYVFsSmZYTDFUZ2E2d2NPMFFUdTZYcStVdzdaUE5QbnJwQnJMYXVLRGQyMDJSbE40U1A3b2hMM2Q5Ykc2VjVoVXovM091c05FQlpVbjVXM1ZtUGoxWm5GYXZrTUIzUmtSTU9hNThNWkFPUkpUNGltQVB6cnZKMHZ0djk0L3k3MUM2dFo1Il0sImRpc3BsYXlOYW1lIjoiWXViaUtleSA1In0=";
15+
byte[] keyMaterialBytes = Convert.FromBase64String(encodedKeyMaterial);
16+
17+
// Parse the FIDO key
18+
var keyMaterial = KeyMaterialFido.ParseJson(keyMaterialBytes);
19+
20+
// Check all fields
21+
Assert.IsNotNull(keyMaterial);
22+
Assert.AreEqual(1, keyMaterial.Version);
23+
Assert.AreEqual(1, keyMaterial.AttestationCertificatesRaw?.Length);
24+
Assert.AreEqual("YubiKey 5", keyMaterial.DisplayName);
25+
Assert.AreEqual("e7d092ba192fdbbb2f36552832d616126971a269", keyMaterial.AttestationCertificates[0].Thumbprint.ToLowerInvariant());
26+
Assert.AreEqual("cb69481e-8ff7-4039-93ec-0a2729a154a8", keyMaterial.AuthenticatorData?.AttestedCredentialData.AaGuid.ToString());
27+
}
28+
29+
[TestMethod]
30+
public void FidoKeyMaterial_Parse_FIDO_Input2()
31+
{
32+
string encodedKeyMaterial = "eyJ2ZXJzaW9uIjoxLCJhdXRoRGF0YSI6Ik5XeWUxS0NUSWJscFh4NnZrWUlEOGJWZmFKMm1IN3lXR0V3VmZkcG9ESUhGQUFBRmRoTGUxMFZMN1VmVXE2cm5FL1VkWTVNQUlKVW96bENOMTFMWmFFOFF0SFhWU2JUeXltVEVNaWxpcTA0RjFtblJwaC9YcFFFQ0F5WWdBU0ZZSVAzWms5Vm5URUpONlQ4VWgxMHRPcUpmdDRicW1leVJUUzhvNkJQK2prYkNJbGdnYjFnR0dNMnM3T2dDMTNQdkZBVTJ1UDZJcVVGWWdiaGxQc3diUzRDK1FoV2hhMmh0WVdNdGMyVmpjbVYwOVE9PSIsIng1YyI6WyJNSUlDUXpDQ0FlbWdBd0lCQWdJUUhmSzFXbEhjUzJpRm85bWVhWC90RmpBS0JnZ3Foa2pPUFFRREFqQkpNUXN3Q1FZRFZRUUdFd0pWVXpFZE1Cc0dBMVVFQ2d3VVJtVnBkR2xoYmlCVVpXTm9ibTlzYjJkcFpYTXhHekFaQmdOVkJBTU1Fa1psYVhScFlXNGdSa2xFVHlCRFFTQXdNekFnRncweE9ERXlNalV3TURBd01EQmFHQTh5TURNek1USXlOREl6TlRrMU9Wb3djREVMTUFrR0ExVUVCaE1DVlZNeEhUQWJCZ05WQkFvTUZFWmxhWFJwWVc0Z1ZHVmphRzV2Ykc5bmFXVnpNU0l3SUFZRFZRUUxEQmxCZFhSb1pXNTBhV05oZEc5eUlFRjBkR1Z6ZEdGMGFXOXVNUjR3SEFZRFZRUUREQlZHVkNCQ2FXOVFZWE56SUVaSlJFOHlJREEwTnpBd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTNjJoSWJ5ZW5IOVdQbnpZSGVoYUJSM0M3cXN3b21aa2FQekd5VWxGUmlKSU1vM3VJVGVJbUZPRmZOY0R1T3pvcTF3Y1hYR1RtRXRFdHhGMndvOW5va280R0pNSUdHTUIwR0ExVWREZ1FXQkJTQkkxWG9MRFkxL0hKYWJhK1czMm54aHhwM1dqQWZCZ05WSFNNRUdEQVdnQlJCdC94TmRjcU8wcDhzMHhlYnpZTlJpbm5ZcVRBTUJnTlZIUk1CQWY4RUFqQUFNQk1HQ3lzR0FRUUJndVVjQWdFQkJBUURBZ1J3TUNFR0N5c0dBUVFCZ3VVY0FRRUVCQklFRUJMZTEwVkw3VWZVcTZybkUvVWRZNU13Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUk2R1NWaTEwcjY3M3VxdHNvKzJvQjZmNVM1Z0UwZmY0NHQzTmNRK1ROOU5BaUFDL1NDUCtlS3cxQm5tY1NnYnhjUXBZdVdqQlBNVkRmcWVnOHBibU9kSEt3PT0iLCJvQWpxMkNlTkEyL2d5NW14YzV0Vll4NVJ4RWFMektEMWxhMDdhZ3RmR28wPSJdLCJkaXNwbGF5TmFtZSI6IkZlaXRpYW4gQWxsLUluLVBhc3MifQ==";
33+
byte[] keyMaterialBytes = Convert.FromBase64String(encodedKeyMaterial);
34+
35+
// Parse the FIDO key and check all fields
36+
var keyMaterial = KeyMaterialFido.ParseJson(keyMaterialBytes);
37+
Assert.AreEqual("Feitian All-In-Pass", keyMaterial.DisplayName);
38+
Assert.AreEqual("9e3808651ecfa53162772e9d2bc62bfe568350a9", keyMaterial.AttestationCertificates[0].Thumbprint.ToLowerInvariant());
39+
Assert.AreEqual("12ded745-4bed-47d4-abaa-e713f51d6393", keyMaterial.AuthenticatorData.AttestedCredentialData.AaGuid.ToString());
40+
}
41+
42+
[TestMethod]
43+
public void FidoKeyMaterial_Parse_Null()
44+
{
45+
Assert.IsNull(KeyMaterialFido.ParseJson((string)null));
46+
}
47+
48+
[TestMethod]
49+
public void FidoKeyMaterial_Parse_Empty_String()
50+
{
51+
Assert.IsNull(KeyMaterialFido.ParseJson(String.Empty));
52+
}
53+
54+
[TestMethod]
55+
public void FidoKeyMaterial_Parse_Empty_Array()
56+
{
57+
Assert.IsNull(KeyMaterialFido.ParseJson([]));
58+
}
59+
}

Src/DSInternals.Common.Test/LapsTester.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public void LAPS_Parse_Encrypted2()
4949
public void LAPS_Parse_Cleartext()
5050
{
5151
byte[] binaryLapsPassword = "7b226e223a2241646d696e6973747261746f72222c2274223a22316438313631623431633431636465222c2270223a2241366133233725656221353762653461344239355a343333393462613935366465363965356438393735227d".HexToBinary();
52-
var rawLapsPassword = LapsClearTextPassword.Parse(new ReadOnlySpan<byte>(binaryLapsPassword), false);
52+
var rawLapsPassword = LapsClearTextPassword.Parse(new ReadOnlySpan<byte>(binaryLapsPassword), isUtf16: false);
5353
LapsPasswordInformation laps = new LapsPasswordInformation("CONTOSO-PC1", rawLapsPassword, DateTime.Now);
5454

5555
Assert.AreEqual("Administrator", laps.Account);

0 commit comments

Comments
 (0)