Skip to content

Conversation

@vcsjones
Copy link
Member

This PR does:

  1. Change DecodeObjectNoThrow to operate off of ReadOnlySpan<byte> instead of byte[]. This lets us save a few allocations since the input is usually from a DATA_BLOB.
  2. Change DecodeObjectNoThrow to accept and return TState and TResult. This means the callback functions can be fully static and not capture state, thus making them easier to extract in to static-local functions.
  3. Various cleanup. Bring the formatting up to be more consistent with our style guidelines. Braces where appropriate, new lines between control blocks, etc.

@ghost ghost assigned vcsjones May 17, 2022
@ghost ghost added the area-System.Security label May 17, 2022
@ghost
Copy link

ghost commented May 17, 2022

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

Issue Details

This PR does:

  1. Change DecodeObjectNoThrow to operate off of ReadOnlySpan<byte> instead of byte[]. This lets us save a few allocations since the input is usually from a DATA_BLOB.
  2. Change DecodeObjectNoThrow to accept and return TState and TResult. This means the callback functions can be fully static and not capture state, thus making them easier to extract in to static-local functions.
  3. Various cleanup. Bring the formatting up to be more consistent with our style guidelines. Braces where appropriate, new lines between control blocks, etc.
Author: vcsjones
Assignees: vcsjones
Labels:

area-System.Security

Milestone: -

if ((uint)cb > MaxStackAllocSize)
{
throw Marshal.GetLastWin32Error().ToCryptographicException();
decoded = new byte[cb];
Copy link
Member

Choose a reason for hiding this comment

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

Is it possible cb is negative?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's extremely unlikely as the data we are dealing with here is typically a dozen to a few hundred bytes. If it overflows to negative then the array-new will give a runtime exception, but I don't anticipate it would ever happen unless someone was doing something extremely nonsensical.

Copy link
Member

Choose a reason for hiding this comment

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

And in case it's ambiguous, Win32 calls it DWORD, so there's not "negative", just "really, really big"

return false;
}

byte[] actual = new byte[cbData];
Copy link
Member

Choose a reason for hiding this comment

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

We could probably do some improvement here.

  1. If cbData != keyIdentifier.Length return false without the second dip.
  2. If cbData <= some cap, stackalloc.

cbData'll be 20 99% of the time, so even a stackalloc-128 probably eliminates all allocations from this routine.

Copy link
Member

Choose a reason for hiding this comment

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

And we could do even better if we did the stackalloc first.

const int MaxStackAlloc = 64;

if (keyIdentifier.Length <= MaxStackAlloc)
{
    Span<byte> actualKeyId = stackalloc byte[keyIdentifier.Length];

    if (!Interop.Crypt32.CertGetCertificateContextProperty(pCertContext, Interop.Crypt32.CertContextPropId.CERT_KEY_IDENTIFIER_PROP_ID, actualKeyId, ref cbData))
    {
        return false;
    }

    return actualKeyId.Slice(0, cbData).SequenceEquals(keyIdentifier);
}
else
{
    // the current code, more or less.
}

Copy link
Member Author

Choose a reason for hiding this comment

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

If cbData != keyIdentifier.Length return false without the second dip.

I'm a little nervous of that since some CAPI APIs I've seen, on the "give null and I'll give you a length" give you a size that is at least big enough to hold the result, while the second call with the real buffer gives the "real" length.

Agree we can put some things on the stack though.

Copy link
Member Author

Choose a reason for hiding this comment

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

stackalloc byte[keyIdentifier.Length];

ಠ_ಠ

Copy link
Member

Choose a reason for hiding this comment

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

OK,

Span<byte> actualKeyId = stackalloc byte[64];

if (keyIdentifier.Length <= actualKeyId.Length)
{
   ...
}

😄

@vcsjones vcsjones merged commit 5b1fd50 into dotnet:main May 18, 2022
@vcsjones vcsjones deleted the find-pal-span branch May 18, 2022 03:05
@ghost ghost locked as resolved and limited conversation to collaborators Jun 17, 2022
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.

3 participants