Skip to content

Conversation

@PranavSenthilnathan
Copy link
Member

When the list of OIDs is long (SLH-DSA and Composite ML-DSA), use a more efficient lookup instead of Array.IndexOf.

@PranavSenthilnathan PranavSenthilnathan self-assigned this Jul 19, 2025
Copilot AI review requested due to automatic review settings July 19, 2025 08:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR optimizes OID lookup performance by replacing Array.IndexOf with more efficient lookup mechanisms when dealing with long lists of known OIDs. The change introduces a new IStringLookup interface with implementations using SearchValues<string> for .NET 9+ and HashSet<string> for earlier versions, while maintaining StringLookupArray for short lists.

Key changes:

  • Introduces IStringLookup interface and optimized implementations for string lookups
  • Updates cryptographic classes to use the new lookup mechanism for OID validation
  • Conditionally uses SearchValues or HashSet based on target framework for better performance

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
KeyFormatHelper.cs Adds IStringLookup interface and implementations, updates method signatures to use the interface
KeyFormatHelper.Encrypted.cs Updates method signatures to accept IStringLookup instead of string arrays
SlhDsa.cs Replaces string array with StringLookup for 12 known OIDs
CompositeMLDsa.cs Replaces string array with StringLookup for 18 known OIDs
ECAlgorithm.cs Replaces string array with StringLookupArray for single OID
RSAKeyFormatHelper.cs Replaces string array with StringLookupArray for single OID
MLKem.cs Replaces string array with StringLookupArray for 3 OIDs
MLDsa.cs Replaces string array with StringLookupArray for 3 OIDs
EccKeyFormatHelper.cs Replaces string array with StringLookupArray for single OID
DSAKeyFormatHelper.cs Replaces string array with StringLookupArray for single OID


internal sealed class StringLookupArray(string[] backingArray) : IStringLookup
{
public bool Contains(string value) => Array.IndexOf(backingArray, value) >= 0;
Copy link

Copilot AI Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The StringLookupArray class stores the backing array but doesn't make a defensive copy. Consider making a copy of the input array to prevent external modifications that could affect lookup behavior.

Suggested change
public bool Contains(string value) => Array.IndexOf(backingArray, value) >= 0;
private readonly string[] _backingArray = (string[])backingArray.Clone();
public bool Contains(string value) => Array.IndexOf(_backingArray, value) >= 0;

Copilot uses AI. Check for mistakes.
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

@vcsjones
Copy link
Member

Are there any benchmarks showing this is a worthwhile change?

In the grand scheme of things, the OID lookups are done during PKCS#8 and SPKI import, so I would generally expect these to be only a small fraction of the actual key processing.

Comment on lines +220 to +222
internal sealed class StringLookup(SearchValues<string> backingSearchValues) : IStringLookup
{
public bool Contains(string value) => backingSearchValues.Contains(value);
Copy link
Member

@MihaZupan MihaZupan Jul 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite the name, SearchValues is not beneficial in this case.
SearchValues<string>.Contains(string) is currently backed by a HashSet<string>.Contains(string).

The only searches this is being used for are setOfValues.Contains(string), this is what HashSet/FrozenSet excel at.
You'd want to use SearchValues if you were looking for substrings within a text, i.e. someText.ContainsAny(setOfValues).

@PranavSenthilnathan
Copy link
Member Author

Are there any benchmarks showing this is a worthwhile change?

In the grand scheme of things, the OID lookups are done during PKCS#8 and SPKI import, so I would generally expect these to be only a small fraction of the actual key processing.

I haven't run benchmarks but I expect the same.

Regardless of perf, I think we should still get rid of the list for SLH-DSA and Composite ML-DSA and use SlhDsaAlgorithm.GetAlgorithmFromOid/CompositeMLDsaAlgorithm.GetAlgorithmFromOid as the single source of truth instead of duplicating the OIDs.

Copy link
Member

@bartonjs bartonjs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like the kind of change that feels like a perf win, but is actually a loss. N isn't very big here, and the affected methods run very few times in a real world program.

The comment suggesting that we stop using a duplicate list and just use an existing lookup function sounds reasonable; but is not a priority unless the change itself is trivially small.

@PranavSenthilnathan
Copy link
Member Author

Closing since there's no significant perf benefit.

@github-actions github-actions bot locked and limited conversation to collaborators Aug 21, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants