Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -78,6 +78,15 @@ internal static byte[] Consume(ReadOnlySpan<byte> blob, ref int offset, int coun
return value;
}

/// <summary>
/// Peel off the next "count" bytes in blob and copy them into the destination.
/// </summary>
internal static void Consume(ReadOnlySpan<byte> blob, ref int offset, int count, Span<byte> destination)
{
blob.Slice(offset, count).CopyTo(destination);
offset += count;
}

/// <summary>
/// Magic numbers identifying blob types
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ internal static partial class NCrypt
[LibraryImport(Interop.Libraries.NCrypt, StringMarshalling = StringMarshalling.Utf16)]
internal static partial ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, IntPtr pParameterList, ref byte pbOutput, int cbOutput, out int pcbResult, int dwFlags);

[LibraryImport(Interop.Libraries.NCrypt, StringMarshalling = StringMarshalling.Utf16)]
internal static partial ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, IntPtr pParameterList, Span<byte> pbOutput, int cbOutput, out int pcbResult, int dwFlags);

[LibraryImport(Interop.Libraries.NCrypt, StringMarshalling = StringMarshalling.Utf16)]
internal static partial ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, ref NCryptBufferDesc pParameterList, ref byte pbOutput, int cbOutput, out int pcbResult, int dwFlags);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,110 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Formats.Asn1;

namespace System.Security.Cryptography
{
internal static partial class AsymmetricAlgorithmHelpers
{
/// <summary>
/// Convert Ieee1363 format of (r, s) to Der format
/// </summary>
public static byte[] ConvertIeee1363ToDer(ReadOnlySpan<byte> input)
{
AsnWriter writer = WriteIeee1363ToDer(input);
return writer.Encode();
}

internal static bool TryConvertIeee1363ToDer(
ReadOnlySpan<byte> input,
Span<byte> destination,
out int bytesWritten)
{
AsnWriter writer = WriteIeee1363ToDer(input);
return writer.TryEncode(destination, out bytesWritten);
}

private static AsnWriter WriteIeee1363ToDer(ReadOnlySpan<byte> input)
{
Debug.Assert(input.Length % 2 == 0);
Debug.Assert(input.Length > 1);

// Input is (r, s), each of them exactly half of the array.
// Output is the DER encoded value of SEQUENCE(INTEGER(r), INTEGER(s)).
int halfLength = input.Length / 2;

AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
writer.PushSequence();
writer.WriteKeyParameterInteger(input.Slice(0, halfLength));
writer.WriteKeyParameterInteger(input.Slice(halfLength, halfLength));
writer.PopSequence();
return writer;
}

/// <summary>
/// Convert Der format of (r, s) to Ieee1363 format
/// </summary>
public static byte[] ConvertDerToIeee1363(ReadOnlySpan<byte> input, int fieldSizeBits)
{
int fieldSizeBytes = BitsToBytes(fieldSizeBits);
int encodedSize = 2 * fieldSizeBytes;
byte[] response = new byte[encodedSize];

ConvertDerToIeee1363(input, fieldSizeBits, response);
return response;
}

internal static int ConvertDerToIeee1363(ReadOnlySpan<byte> input, int fieldSizeBits, Span<byte> destination)
{
int fieldSizeBytes = BitsToBytes(fieldSizeBits);
int encodedSize = 2 * fieldSizeBytes;

Debug.Assert(destination.Length >= encodedSize);

try
{
AsnValueReader reader = new AsnValueReader(input, AsnEncodingRules.DER);
AsnValueReader sequenceReader = reader.ReadSequence();
reader.ThrowIfNotEmpty();
ReadOnlySpan<byte> rDer = sequenceReader.ReadIntegerBytes();
ReadOnlySpan<byte> sDer = sequenceReader.ReadIntegerBytes();
sequenceReader.ThrowIfNotEmpty();

CopySignatureField(rDer, destination.Slice(0, fieldSizeBytes));
CopySignatureField(sDer, destination.Slice(fieldSizeBytes, fieldSizeBytes));
return encodedSize;
}
catch (AsnContentException e)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
}
}

private static void CopySignatureField(ReadOnlySpan<byte> signatureField, Span<byte> response)
{
if (signatureField.Length > response.Length)
{
if (signatureField.Length != response.Length + 1 ||
signatureField[0] != 0 ||
signatureField[1] <= 0x7F)
{
// The only way this should be true is if the value required a zero-byte-pad.
Debug.Fail($"A signature field was longer ({signatureField.Length}) than expected ({response.Length})");
throw new CryptographicException();
}

signatureField = signatureField.Slice(1);
}

// If the field is too short then it needs to be prepended
// with zeroes in the response. Since the array was already
// zeroed out, just figure out where we need to start copying.
int writeOffset = response.Length - signatureField.Length;
response.Slice(0, writeOffset).Clear();
signatureField.CopyTo(response.Slice(writeOffset));
}

internal static int BitsToBytes(int bitLength)
{
int byteLength = (bitLength + 7) / 8;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@ internal static CryptographicException ToCryptographicException(this Interop.NCr
return ((int)errorCode).ToCryptographicException();
}

internal static SafeNCryptProviderHandle OpenStorageProvider(this CngProvider provider)
{
ErrorCode errorCode = Interop.NCrypt.NCryptOpenStorageProvider(
out SafeNCryptProviderHandle providerHandle,
provider.Provider,
0);

if (errorCode != ErrorCode.ERROR_SUCCESS)
{
providerHandle.Dispose();
throw errorCode.ToCryptographicException();
}

return providerHandle;
}

internal static void SetExportPolicy(this SafeNCryptKeyHandle keyHandle, CngExportPolicies exportPolicy)
{
unsafe
Expand Down Expand Up @@ -125,6 +141,32 @@ internal static void SetExportPolicy(this SafeNCryptKeyHandle keyHandle, CngExpo
}
}

internal delegate void ExportKeyBlobCallback(ReadOnlySpan<byte> blob);

internal static void ExportKeyBlob(
this SafeNCryptKeyHandle handle,
string blobType,
ExportKeyBlobCallback callback)
{
int numBytesNeeded;
ErrorCode errorCode = Interop.NCrypt.NCryptExportKey(handle, IntPtr.Zero, blobType, IntPtr.Zero, null, 0, out numBytesNeeded, 0);
if (errorCode != ErrorCode.ERROR_SUCCESS)
throw errorCode.ToCryptographicException();

byte[] buffer = new byte[numBytesNeeded];

using (PinAndClear.Track(buffer))
{
errorCode = Interop.NCrypt.NCryptExportKey(handle, IntPtr.Zero, blobType, IntPtr.Zero, buffer, buffer.Length, out numBytesNeeded, 0);
if (errorCode != ErrorCode.ERROR_SUCCESS)
throw errorCode.ToCryptographicException();

// The second call to NCryptExportKey should always return the same number of bytes as the first call,
// but we will slice the buffer just in case.
callback(buffer.AsSpan(0, numBytesNeeded));
}
}

internal static bool TryExportKeyBlob(
this SafeNCryptKeyHandle handle,
string blobType,
Expand Down
Loading
Loading