Skip to content

Commit

Permalink
Optimize JSON.
Browse files Browse the repository at this point in the history
  • Loading branch information
kingcean committed Aug 12, 2024
1 parent 06d28e5 commit 713e992
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 134 deletions.
10 changes: 1 addition & 9 deletions Core/Data/Result/ConciseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,7 @@ public ConciseModel(IConciseModel copy)
Description = copy.Description;
ImageUri = copy.ImageUri;
var keywords = copy.Keywords;
if (keywords != null)
{
Keywords = new();
foreach (var keyword in keywords)
{
Keywords.Add(keyword);
}
}

if (keywords != null) Keywords = [.. keywords];
if (copy is not ConciseModel model) return;
Raw = model.Raw;
Tag = model.Tag;
Expand Down
234 changes: 114 additions & 120 deletions Core/Security/Cryptography/RSAParametersConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ public static class RSAParametersConvert
/// <summary>
/// Encoded OID sequence for PKCS #1 RSA encryption szOID_RSA_RSA = "1.2.840.113549.1.1.1".
/// </summary>
private static readonly byte[] seqOID = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
private static readonly byte[] seqOID = [0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00];

/// <summary>
/// PEM versions.
/// </summary>
private static readonly byte[] verPem = new byte[] { 0x02, 0x01, 0x00 };
private static readonly byte[] verPem = [0x02, 0x01, 0x00];

/// <summary>
/// Parses the parameters from OpenSSL RSA key (PEM Base64) or the RSA parameters XML string.
Expand Down Expand Up @@ -108,130 +108,126 @@ public static class RSAParametersConvert
key = string.Join(string.Empty, lines.Skip(1).Take(lines.Length - 2));
}

using (var stream = new MemoryStream(Convert.FromBase64String(key)))
using var stream = new MemoryStream(Convert.FromBase64String(key));
using var reader = new BinaryReader(stream);
if (isPrivate)
{
using (var reader = new BinaryReader(stream))
var twoBytes = reader.ReadUInt16();
if (twoBytes == 0x8130) reader.ReadByte();
else if (twoBytes == 0x8230) reader.ReadInt16();
else return null;
if (!AreSame(reader, verPem)) return null;

// For PKCS8.
if (reader.BaseStream.Position + seqOID.Length < reader.BaseStream.Length)
{
if (isPrivate)
var isPkcs8 = true;
var i = 0;
for (; i < seqOID.Length; i++)
{
var twoBytes = reader.ReadUInt16();
if (twoBytes == 0x8130) reader.ReadByte();
else if (twoBytes == 0x8230) reader.ReadInt16();
else return null;
if (!AreSame(reader, verPem)) return null;
if (seqOID[i] == reader.ReadByte()) continue;
isPkcs8 = false;
break;
}

// For PKCS8.
if (reader.BaseStream.Position + seqOID.Length < reader.BaseStream.Length)
{
var isPkcs8 = true;
var i = 0;
for (; i < seqOID.Length; i++)
{
if (seqOID[i] == reader.ReadByte()) continue;
isPkcs8 = false;
break;
}

if (isPkcs8)
{
GetIntegerSize(reader, 0x04);
GetIntegerSize(reader, 0x30);
if (!AreSame(reader, verPem)) return null;
}
else
{
i++;
reader.BaseStream.Seek(-i, SeekOrigin.Current);
}
}

return new RSAParameters
{
Modulus = reader.ReadBytes(GetIntegerSize(reader)),
Exponent = reader.ReadBytes(GetIntegerSize(reader)),
D = reader.ReadBytes(GetIntegerSize(reader)),
P = reader.ReadBytes(GetIntegerSize(reader)),
Q = reader.ReadBytes(GetIntegerSize(reader)),
DP = reader.ReadBytes(GetIntegerSize(reader)),
DQ = reader.ReadBytes(GetIntegerSize(reader)),
InverseQ = reader.ReadBytes(GetIntegerSize(reader))
};
if (isPkcs8)
{
GetIntegerSize(reader, 0x04);
GetIntegerSize(reader, 0x30);
if (!AreSame(reader, verPem)) return null;
}
else
{
var twoBytes = reader.ReadUInt16();
if (twoBytes == 0x8130) // Data read as little endian order (actual data order for Sequence is 30 81).
reader.ReadByte(); // Advance 1 byte.
else if (twoBytes == 0x8230)
reader.ReadInt16(); // Advance 2 bytes.
else
return null;

var seq = reader.ReadBytes(15); // Read the Sequence OID.
if (!ListExtensions.Equals(seq, seqOID)) // Make sure Sequence for OID is correct.
return null;

twoBytes = reader.ReadUInt16();
if (twoBytes == 0x8103) // Data read as little endian order (actual data order for Bit String is 03 81).
reader.ReadByte(); // Advance 1 byte.
else if (twoBytes == 0x8203)
reader.ReadInt16(); // Advance 2 bytes.
else
return null;

var testByte = reader.ReadByte();
if (testByte != 0x00) // Expect null byte next.
return null;

twoBytes = reader.ReadUInt16();
if (twoBytes == 0x8130) // Data read as little endian order (actual data order for Sequence is 30 81).
reader.ReadByte(); // Advance 1 byte.
else if (twoBytes == 0x8230)
reader.ReadInt16(); // Advance 2 bytes.
else
return null;

twoBytes = reader.ReadUInt16();
byte lowByte = 0x00;
byte highByte = 0x00;

if (twoBytes == 0x8102) // Data read as little endian order (actual data order for Integer is 02 81).
{
lowByte = reader.ReadByte(); // Read next bytes which is bytes in modulus.
}
else if (twoBytes == 0x8202)
{
highByte = reader.ReadByte(); // Advance 2 bytes.
lowByte = reader.ReadByte();
}
else
{
return null;
}

byte[] modInt = { lowByte, highByte, 0x00, 0x00 }; // Reverse byte order since asn.1 key uses big endian order.
var modsize = BitConverter.ToInt32(modInt, 0);
var firstByte = reader.PeekChar();
if (firstByte == 0x00)
{ // Don't include it if the first byte (highest order) of modulus is zero.
reader.ReadByte(); // Skip this null byte.
modsize -= 1; // Reduce modulus buffer size by 1.
}

var modulus = reader.ReadBytes(modsize); // Read the modulus bytes.
if (reader.ReadByte() != 0x02) // Expect an Integer for the exponent data.
return null;
var expBytes = (int)reader.ReadByte(); // Should only need one byte for actual exponent data (for all useful values).
var exponent = reader.ReadBytes(expBytes);

// Create RSACryptoServiceProvider instance and initialize with public key.
return new RSAParameters
{
Modulus = modulus,
Exponent = exponent
};
i++;
reader.BaseStream.Seek(-i, SeekOrigin.Current);
}
}

return new RSAParameters
{
Modulus = reader.ReadBytes(GetIntegerSize(reader)),
Exponent = reader.ReadBytes(GetIntegerSize(reader)),
D = reader.ReadBytes(GetIntegerSize(reader)),
P = reader.ReadBytes(GetIntegerSize(reader)),
Q = reader.ReadBytes(GetIntegerSize(reader)),
DP = reader.ReadBytes(GetIntegerSize(reader)),
DQ = reader.ReadBytes(GetIntegerSize(reader)),
InverseQ = reader.ReadBytes(GetIntegerSize(reader))
};
}
else
{
var twoBytes = reader.ReadUInt16();
if (twoBytes == 0x8130) // Data read as little endian order (actual data order for Sequence is 30 81).
reader.ReadByte(); // Advance 1 byte.
else if (twoBytes == 0x8230)
reader.ReadInt16(); // Advance 2 bytes.
else
return null;

var seq = reader.ReadBytes(15); // Read the Sequence OID.
if (!ListExtensions.Equals(seq, seqOID)) // Make sure Sequence for OID is correct.
return null;

twoBytes = reader.ReadUInt16();
if (twoBytes == 0x8103) // Data read as little endian order (actual data order for Bit String is 03 81).
reader.ReadByte(); // Advance 1 byte.
else if (twoBytes == 0x8203)
reader.ReadInt16(); // Advance 2 bytes.
else
return null;

var testByte = reader.ReadByte();
if (testByte != 0x00) // Expect null byte next.
return null;

twoBytes = reader.ReadUInt16();
if (twoBytes == 0x8130) // Data read as little endian order (actual data order for Sequence is 30 81).
reader.ReadByte(); // Advance 1 byte.
else if (twoBytes == 0x8230)
reader.ReadInt16(); // Advance 2 bytes.
else
return null;

twoBytes = reader.ReadUInt16();
byte lowByte = 0x00;
byte highByte = 0x00;

if (twoBytes == 0x8102) // Data read as little endian order (actual data order for Integer is 02 81).
{
lowByte = reader.ReadByte(); // Read next bytes which is bytes in modulus.
}
else if (twoBytes == 0x8202)
{
highByte = reader.ReadByte(); // Advance 2 bytes.
lowByte = reader.ReadByte();
}
else
{
return null;
}

byte[] modInt = { lowByte, highByte, 0x00, 0x00 }; // Reverse byte order since asn.1 key uses big endian order.
var modsize = BitConverter.ToInt32(modInt, 0);
var firstByte = reader.PeekChar();
if (firstByte == 0x00)
{ // Don't include it if the first byte (highest order) of modulus is zero.
reader.ReadByte(); // Skip this null byte.
modsize -= 1; // Reduce modulus buffer size by 1.
}

var modulus = reader.ReadBytes(modsize); // Read the modulus bytes.
if (reader.ReadByte() != 0x02) // Expect an Integer for the exponent data.
return null;
var expBytes = (int)reader.ReadByte(); // Should only need one byte for actual exponent data (for all useful values).
var exponent = reader.ReadBytes(expBytes);

// Create RSACryptoServiceProvider instance and initialize with public key.
return new RSAParameters
{
Modulus = modulus,
Exponent = exponent
};
}
}

Expand Down Expand Up @@ -532,7 +528,5 @@ private static XmlElement AppendChildWithText(XmlDocument doc, string name, stri
}

private static XmlElement AppendChildWithBase64(XmlDocument doc, string name, byte[] value)
{
return AppendChildWithText(doc, name, value != null ? Convert.ToBase64String(value) : null);
}
=> AppendChildWithText(doc, name, value != null ? Convert.ToBase64String(value) : null);
}
65 changes: 60 additions & 5 deletions Core/Text/Json/ObjectNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2579,7 +2579,7 @@ public List<JsonObjectNode> TryGetObjectListValue(string key, bool ignoreNotMatc
/// Tries to get the value of the specific property.
/// </summary>
/// <param name="key">The property key.</param>
/// <param name="subKey">The sub-key of the previous property.</param>
/// <param name="subKey">The sub-key of the parent property.</param>
/// <param name="keyPath">The additional property key path.</param>
/// <returns>The value.</returns>
public JsonObjectNode TryGetObjectValue(string key, string subKey, params string[] keyPath)
Expand All @@ -2596,6 +2596,28 @@ public JsonObjectNode TryGetObjectValue(string key, string subKey, params string
return json;
}

/// <summary>
/// Tries to get the value of the specific property.
/// </summary>
/// <param name="key">The property key.</param>
/// <param name="subKey">The sub-index of the parent property.</param>
/// <param name="keyPath">The additional property key path.</param>
/// <returns>The value.</returns>
public JsonObjectNode TryGetObjectValue(string key, int subKey, params string[] keyPath)
{
var arr = TryGetArrayValue(key);
var json = arr is null
? TryGetObjectValue(key)?.TryGetObjectValue(subKey.ToString("g"))
: arr.TryGetObjectValue(subKey);
foreach (var k in keyPath)
{
if (json is null) return null;
json = TryGetObjectValueByProperty(json, k);
}

return json;
}

/// <summary>
/// Tries to get the value of the specific property.
/// </summary>
Expand Down Expand Up @@ -6254,7 +6276,7 @@ public bool Remove(KeyValuePair<string, IJsonValueNode> item)
/// </summary>
public void Clear()
{
var keys = store.Keys;
var keys = store.Keys.ToList();
store.Clear();
if (PropertyChanged == null) return;
foreach (var key in keys)
Expand Down Expand Up @@ -7040,7 +7062,8 @@ IEnumerator IEnumerable.GetEnumerator()
/// </summary>
/// <returns>The value of the element as a date time.</returns>
/// <exception cref="InvalidOperationException">The value kind is not expected.</exception>
DateTime IJsonDataNode.GetDateTime() => throw new InvalidOperationException("Expect a date time but it is an object.");
DateTime IJsonDataNode.GetDateTime()
=> TryGetDateTime() ?? throw new InvalidOperationException("Expect a date time but it is an object.");

/// <summary>
/// Gets the value of the element as a number.
Expand Down Expand Up @@ -7123,8 +7146,9 @@ bool IJsonDataNode.TryGetBoolean(out bool result)
/// <returns>true if the kind is the one expected; otherwise, false.</returns>
bool IJsonDataNode.TryGetDateTime(out DateTime result)
{
result = WebFormat.ParseDate(0);
return false;
var t = TryGetDateTime();
result = t ?? WebFormat.ParseDate(0);
return t.HasValue;
}

/// <summary>
Expand Down Expand Up @@ -7227,6 +7251,37 @@ bool IJsonDataNode.TryGetGuid(out Guid result)
/// <returns>The property keys.</returns>
IEnumerable<string> IJsonContainerNode.GetKeys() => Keys;

/// <summary>
/// Tries to get the value of the element as a date time.
/// </summary>
/// <returns>The result.</returns>
private DateTime? TryGetDateTime()
{
var year = TryGetInt32Value("year");
if (!year.HasValue) return null;
var month = TryGetInt32Value("month") ?? 0;
if (month < 1 || month > 12) return null;
var day = TryGetInt32Value("day") ?? 0;
if (day < 1 || day > 31) return null;
var hour = TryGetInt32Value("hour") ?? 0;
if (hour < 0 || hour > 23) return null;
var minute = TryGetInt32Value("minute") ?? 0;
if (minute < 0 || minute > 59) return null;
var second = TryGetInt32Value("second") ?? 0;
if (second < 0 || second > 59) return null;
var millisecond = TryGetInt32Value("millisecond") ?? 0;
if (millisecond < 0 || millisecond > 1000) return null;
var offset = TryGetStringTrimmedValue("offset")?.ToLowerInvariant() ?? string.Empty;
var kind = offset switch
{
"" or "utc" or "false" or "z" or "0" or "0:00" or "00:00" => DateTimeKind.Utc,
"local" => DateTimeKind.Local,
_ => DateTimeKind.Unspecified
};
if (kind == DateTimeKind.Unspecified) return null;
return new(year.Value, month, day, hour, minute, second, millisecond, kind);
}

private void AddProperty(string key, IJsonDataNode value)
{
store.Add(key, value);
Expand Down

0 comments on commit 713e992

Please sign in to comment.