Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,30 @@

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class BCrypt
{
[LibraryImport(Libraries.BCrypt)]
internal static unsafe partial NTSTATUS BCryptGenerateSymmetricKey(
internal static partial NTSTATUS BCryptGenerateSymmetricKey(
SafeBCryptAlgorithmHandle hAlgorithm,
out SafeBCryptKeyHandle phKey,
IntPtr pbKeyObject,
int cbKeyObject,
byte* pbSecret,
[MarshalUsing(typeof(NotNullReadOnlySpanMarshaller<,>))] ReadOnlySpan<byte> pbSecret,

Check failure on line 19 in src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build linux-x64 debug Libraries_WithPackages)

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs#L19

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs(19,14): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'MarshalUsingAttribute' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 19 in src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build linux-x64 debug Libraries_WithPackages)

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs#L19

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs(19,14): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'MarshalUsing' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 19 in src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs#L19

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs(19,14): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'MarshalUsingAttribute' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 19 in src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs#L19

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs(19,14): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'MarshalUsing' could not be found (are you missing a using directive or an assembly reference?)
int cbSecret,
uint dwFlags);

[LibraryImport(Libraries.BCrypt)]
internal static unsafe partial NTSTATUS BCryptGenerateSymmetricKey(
internal static partial NTSTATUS BCryptGenerateSymmetricKey(
nuint hAlgorithm,
out SafeBCryptKeyHandle phKey,
IntPtr pbKeyObject,
int cbKeyObject,
byte* pbSecret,
[MarshalUsing(typeof(NotNullReadOnlySpanMarshaller<,>))] ReadOnlySpan<byte> pbSecret,

Check failure on line 29 in src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build linux-x64 debug Libraries_WithPackages)

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs#L29

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs(29,14): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'MarshalUsingAttribute' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 29 in src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build linux-x64 debug Libraries_WithPackages)

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs#L29

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs(29,14): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'MarshalUsing' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 29 in src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs#L29

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs(29,14): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'MarshalUsingAttribute' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 29 in src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs#L29

src/libraries/Common/src/Interop/Windows/BCrypt/Interop.BCryptGenerateSymmetricKey.cs(29,14): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'MarshalUsing' could not be found (are you missing a using directive or an assembly reference?)
int cbSecret,
uint dwFlags);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;

namespace System.Runtime.InteropServices.Marshalling
{
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.ManagedToUnmanagedIn, typeof(NotNullReadOnlySpanMarshaller<,>.ManagedToUnmanagedIn))]

Check failure on line 11 in src/libraries/Common/src/System/Runtime/InteropServices/NotNullReadOnlySpanMarshaller.cs

View check run for this annotation

Azure Pipelines / runtime-dev-innerloop (Build linux-x64 debug Libraries_WithPackages)

src/libraries/Common/src/System/Runtime/InteropServices/NotNullReadOnlySpanMarshaller.cs#L11

src/libraries/Common/src/System/Runtime/InteropServices/NotNullReadOnlySpanMarshaller.cs(11,6): error CS0246: (NETCORE_ENGINEERING_TELEMETRY=Build) The type or namespace name 'CustomMarshallerAttribute' could not be found (are you missing a using directive or an assembly reference?)
[CustomMarshaller(typeof(ReadOnlySpan<>), MarshalMode.ManagedToUnmanagedOut, typeof(NotNullReadOnlySpanMarshaller<,>.ManagedToUnmanagedOut))]
Copy link
Member

Choose a reason for hiding this comment

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

I doubt that you will ever use (or want to use) the ManagedToUnmanagedOut variant in libraries. You can certainly add it, but it is effectively dead code.

[ContiguousCollectionMarshaller]
internal static unsafe class NotNullReadOnlySpanMarshaller<T, TUnmanagedElement>
where TUnmanagedElement : unmanaged
{
public ref struct ManagedToUnmanagedIn
{
/// <summary>
/// Gets the size of the caller-allocated buffer to allocate.
/// </summary>
// We'll keep the buffer size at a maximum of 512 bytes to avoid overflowing the stack.
public static int BufferSize => 0x200 / sizeof(TUnmanagedElement);

private ReadOnlySpan<T> _managedArray;
private TUnmanagedElement* _allocatedMemory;
private Span<TUnmanagedElement> _span;

public void FromManaged(ReadOnlySpan<T> managed, Span<TUnmanagedElement> buffer)
{
_managedArray = managed;

if (managed.Length <= buffer.Length)
{
_span = buffer[0..managed.Length];
}
else
{
int bufferSize = checked(managed.Length * sizeof(TUnmanagedElement));
_allocatedMemory = (TUnmanagedElement*)NativeMemory.Alloc((nuint)bufferSize);
_span = new Span<TUnmanagedElement>(_allocatedMemory, managed.Length);
}
}

public ReadOnlySpan<T> GetManagedValuesSource() => _managedArray;

public Span<TUnmanagedElement> GetUnmanagedValuesDestination() => _span;

public ref TUnmanagedElement GetPinnableReference() => ref MemoryMarshal.GetReference(_span);

public TUnmanagedElement* ToUnmanaged()
{
// Unsafe.AsPointer is safe since buffer must be pinned
return (TUnmanagedElement*)Unsafe.AsPointer(ref GetPinnableReference());
}

public void Free()
{
NativeMemory.Free(_allocatedMemory);
}

public static ref T GetPinnableReference(ReadOnlySpan<T> managed)
{
if (Unsafe.IsNullRef(ref MemoryMarshal.GetReference(managed)) && managed.IsEmpty)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (Unsafe.IsNullRef(ref MemoryMarshal.GetReference(managed)) && managed.IsEmpty)
if (managed.IsEmpty)

This can be simpler

Copy link
Member

Choose a reason for hiding this comment

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

I had the same thought as Jan, then wondered if it was valueable to have the zero-length slice of some other array report a different pointer than the empty array, and didn't leave a comment. (His comment has "forced" me to).

I can't think of anywhere in the crypto use cases where the pointer is used as a lookup (and if it was we'd have to do a lot more pinning), so normalizing all emptys to the same non-null is probably fine.

{
return ref MemoryMarshal.GetArrayDataReference(Array.Empty<T>());
}
else
{
return ref MemoryMarshal.GetReference(managed);
}
}
}

public struct ManagedToUnmanagedOut
{
private TUnmanagedElement* _unmanagedArray;
private T[]? _managedValues;

public void FromUnmanaged(TUnmanagedElement* unmanaged)
{
_unmanagedArray = unmanaged;
}

public ReadOnlySpan<T> ToManaged()
{
return new ReadOnlySpan<T>(_managedValues!);
}

public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements)
{
return new ReadOnlySpan<TUnmanagedElement>(_unmanagedArray, numElements);
}

public Span<T> GetManagedValuesDestination(int numElements)
{
_managedValues = new T[numElements];
return _managedValues;
}

public void Free()
{
Marshal.FreeCoTaskMem((IntPtr)_unmanagedArray);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ internal static void DeriveBytesOneShot(
}
}

private static unsafe SafeBCryptKeyHandle CreateSymmetricKey(byte* symmetricKey, int symmetricKeyLength)
private static SafeBCryptKeyHandle CreateSymmetricKey(ReadOnlySpan<byte> symmetricKey)
{
NTSTATUS generateKeyStatus;
SafeBCryptKeyHandle keyHandle;
Expand All @@ -141,7 +141,7 @@ private static unsafe SafeBCryptKeyHandle CreateSymmetricKey(byte* symmetricKey,
pbKeyObject: IntPtr.Zero,
cbKeyObject: 0,
symmetricKey,
symmetricKeyLength,
symmetricKey.Length,
dwFlags: 0);
}
else
Expand All @@ -152,7 +152,7 @@ private static unsafe SafeBCryptKeyHandle CreateSymmetricKey(byte* symmetricKey,
pbKeyObject: IntPtr.Zero,
cbKeyObject: 0,
symmetricKey,
symmetricKeyLength,
symmetricKey.Length,
dwFlags: 0);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETCoreApp'">
<Compile Include="$(CommonPath)System\Runtime\InteropServices\NotNullReadOnlySpanMarshaller.cs"
Link="Common\System\Runtime\InteropServices\NotNullReadOnlySpanMarshaller.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeBCryptAlgorithmHandle.cs"
Link="Microsoft\Win32\SafeHandles\SafeBCryptAlgorithmHandle.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeBCryptHandle.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ internal unsafe SP800108HmacCounterKdfImplementationCng(ReadOnlySpan<byte> key,

scoped ReadOnlySpan<byte> symmetricKeyMaterial;
scoped Span<byte> clearSpan = default;
int symmetricKeyMaterialLength;
int hashAlgorithmBlockSize = GetHashBlockSize(hashAlgorithm.Name);

if (key.Length > hashAlgorithmBlockSize)
Expand All @@ -28,26 +27,15 @@ internal unsafe SP800108HmacCounterKdfImplementationCng(ReadOnlySpan<byte> key,
}

symmetricKeyMaterial = clearSpan;
symmetricKeyMaterialLength = symmetricKeyMaterial.Length;
}
else if (!key.IsEmpty)
{
symmetricKeyMaterial = key;
symmetricKeyMaterialLength = key.Length;
}
else
{
// CNG requires a non-null pointer even when the length is zero.
symmetricKeyMaterial = [0];
symmetricKeyMaterialLength = 0;
symmetricKeyMaterial = key;
}

try
{
fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial)
{
_keyHandle = CreateSymmetricKey(pSymmetricKeyMaterial, symmetricKeyMaterialLength);
}
_keyHandle = CreateSymmetricKey(symmetricKeyMaterial);
}
finally
{
Expand All @@ -65,33 +53,21 @@ internal unsafe SP800108HmacCounterKdfImplementationCng(byte[] key, HashAlgorith

scoped ReadOnlySpan<byte> symmetricKeyMaterial;
scoped Span<byte> clearSpan = default;
int symmetricKeyMaterialLength;
int hashAlgorithmBlockSize = GetHashBlockSize(hashAlgorithm.Name);

if (key.Length > hashAlgorithmBlockSize)
{
clearSpan = HashOneShot(hashAlgorithm, key);
symmetricKeyMaterial = clearSpan;
symmetricKeyMaterialLength = symmetricKeyMaterial.Length;
}
else if (key.Length > 0)
{
symmetricKeyMaterial = key;
symmetricKeyMaterialLength = key.Length;
}
else
{
// CNG requires a non-null pointer even when the length is zero.
symmetricKeyMaterial = [0];
symmetricKeyMaterialLength = 0;
symmetricKeyMaterial = key;
}

try
{
fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial)
{
_keyHandle = CreateSymmetricKey(pSymmetricKeyMaterial, symmetricKeyMaterialLength);
}
_keyHandle = CreateSymmetricKey(symmetricKeyMaterial);
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,8 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'windows'">
<Compile Include="$(CommonPath)System\Runtime\InteropServices\NotNullReadOnlySpanMarshaller.cs"
Link="Common\System\Runtime\InteropServices\NotNullReadOnlySpanMarshaller.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.CryptAcquireContext.cs"
Link="Common\Interop\Windows\Advapi32\Interop.CryptAcquireContext.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Advapi32\Interop.CryptAcquireContext_IntPtr.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,20 +55,11 @@ private static unsafe void FillKeyDerivation(
// stackalloc 0 to let compiler know this cannot escape.
scoped Span<byte> clearSpan;
scoped ReadOnlySpan<byte> symmetricKeyMaterial;
int symmetricKeyMaterialLength;

if (password.IsEmpty)
{
// CNG won't accept a null pointer for the password.
symmetricKeyMaterial = [0];
symmetricKeyMaterialLength = 0;
clearSpan = default;
}
else if (password.Length <= hashBlockSizeBytes)
if (password.Length <= hashBlockSizeBytes)
{
// Password is small enough to use as-is.
symmetricKeyMaterial = password;
symmetricKeyMaterialLength = password.Length;
clearSpan = default;
}
else
Expand Down Expand Up @@ -108,26 +99,20 @@ private static unsafe void FillKeyDerivation(

clearSpan = hashBuffer.Slice(0, hashBufferSize);
symmetricKeyMaterial = clearSpan;
symmetricKeyMaterialLength = hashBufferSize;
}

Debug.Assert(symmetricKeyMaterial.Length > 0);

NTSTATUS generateKeyStatus;

if (Interop.BCrypt.PseudoHandlesSupported)
{
fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial)
{
generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
(nuint)BCryptAlgPseudoHandle.BCRYPT_PBKDF2_ALG_HANDLE,
out keyHandle,
pbKeyObject: IntPtr.Zero,
cbKeyObject: 0,
pSymmetricKeyMaterial,
symmetricKeyMaterialLength,
dwFlags: 0);
}
generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
(nuint)BCryptAlgPseudoHandle.BCRYPT_PBKDF2_ALG_HANDLE,
out keyHandle,
pbKeyObject: IntPtr.Zero,
cbKeyObject: 0,
symmetricKeyMaterial,
symmetricKeyMaterial.Length,
dwFlags: 0);
}
else
{
Expand All @@ -151,17 +136,14 @@ private static unsafe void FillKeyDerivation(
Interlocked.CompareExchange(ref s_pbkdf2AlgorithmHandle, pbkdf2AlgorithmHandle, null);
}

fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial)
{
generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
s_pbkdf2AlgorithmHandle,
out keyHandle,
pbKeyObject: IntPtr.Zero,
cbKeyObject: 0,
pSymmetricKeyMaterial,
symmetricKeyMaterialLength,
dwFlags: 0);
}
generateKeyStatus = Interop.BCrypt.BCryptGenerateSymmetricKey(
s_pbkdf2AlgorithmHandle,
out keyHandle,
pbKeyObject: IntPtr.Zero,
cbKeyObject: 0,
symmetricKeyMaterial,
symmetricKeyMaterial.Length,
dwFlags: 0);
}

CryptographicOperations.ZeroMemory(clearSpan);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,29 @@ namespace System.Security.Cryptography
{
internal sealed partial class SP800108HmacCounterKdfImplementationCng
{
internal unsafe SP800108HmacCounterKdfImplementationCng(ReadOnlySpan<byte> key, HashAlgorithmName hashAlgorithm)
internal SP800108HmacCounterKdfImplementationCng(ReadOnlySpan<byte> key, HashAlgorithmName hashAlgorithm)
{
Debug.Assert(hashAlgorithm.Name is not null);

scoped ReadOnlySpan<byte> symmetricKeyMaterial;
scoped Span<byte> clearSpan = default;
int symmetricKeyMaterialLength;
int hashAlgorithmBlockSize = GetHashBlockSize(hashAlgorithm.Name);

if (key.Length > hashAlgorithmBlockSize)
{
Span<byte> buffer = stackalloc byte[512 / 8]; // Largest supported digest is SHA512.
symmetricKeyMaterialLength = HashOneShot(hashAlgorithm, key, buffer);
int symmetricKeyMaterialLength = HashOneShot(hashAlgorithm, key, buffer);
clearSpan = buffer.Slice(0, symmetricKeyMaterialLength);
symmetricKeyMaterial = clearSpan;
}
else if (!key.IsEmpty)
{
symmetricKeyMaterial = key;
symmetricKeyMaterialLength = key.Length;
}
else
{
// CNG requires a non-null pointer even when the length is zero.
symmetricKeyMaterial = [0];
symmetricKeyMaterialLength = 0;
symmetricKeyMaterial = key;
}

try
{
fixed (byte* pSymmetricKeyMaterial = symmetricKeyMaterial)
{
_keyHandle = CreateSymmetricKey(pSymmetricKeyMaterial, symmetricKeyMaterialLength);
}
_keyHandle = CreateSymmetricKey(symmetricKeyMaterial);
}
finally
{
Expand Down
Loading
Loading