Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Add ECDsa span-based methods #23252

Merged
merged 1 commit into from
Aug 15, 2017
Merged
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 @@ -154,19 +154,20 @@ internal static byte[] RsaEncrypt(
RSAEncryptionPadding padding)
{
return ExecuteTransform(
(out SafeCFDataHandle encrypted, out SafeCFErrorHandle error) =>
data,
(ReadOnlySpan<byte> source, out SafeCFDataHandle encrypted, out SafeCFErrorHandle error) =>
{
if (padding == RSAEncryptionPadding.Pkcs1)
{
return RsaEncryptPkcs(publicKey, data, data.Length, out encrypted, out error);
return RsaEncryptPkcs(publicKey, source, source.Length, out encrypted, out error);
}

Debug.Assert(padding.Mode == RSAEncryptionPaddingMode.Oaep);

return RsaEncryptOaep(
publicKey,
data,
data.Length,
source,
source.Length,
PalAlgorithmFromAlgorithmName(padding.OaepHashAlgorithm),
out encrypted,
out error);
Expand Down Expand Up @@ -199,19 +200,20 @@ internal static byte[] RsaDecrypt(
RSAEncryptionPadding padding)
{
return ExecuteTransform(
(out SafeCFDataHandle decrypted, out SafeCFErrorHandle error) =>
data,
(ReadOnlySpan<byte> source, out SafeCFDataHandle decrypted, out SafeCFErrorHandle error) =>
{
if (padding == RSAEncryptionPadding.Pkcs1)
{
return RsaDecryptPkcs(privateKey, data, data.Length, out decrypted, out error);
return RsaDecryptPkcs(privateKey, source, source.Length, out decrypted, out error);
}

Debug.Assert(padding.Mode == RSAEncryptionPaddingMode.Oaep);

return RsaDecryptOaep(
privateKey,
data,
data.Length,
source,
source.Length,
PalAlgorithmFromAlgorithmName(padding.OaepHashAlgorithm),
out decrypted,
out error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,24 @@ private static extern int AppleCryptoNative_SecKeyImportEphemeral(
out SafeSecKeyRefHandle ppKeyOut,
out int pOSStatus);

private static unsafe int AppleCryptoNative_GenerateSignature(
SafeSecKeyRefHandle privateKey,
ReadOnlySpan<byte> pbDataHash,
int cbDataHash,
out SafeCFDataHandle pSignatureOut,
out SafeCFErrorHandle pErrorOut)
{
fixed (byte* pbDataHashPtr = &pbDataHash.DangerousGetPinnableReference())
{
return AppleCryptoNative_GenerateSignature(
privateKey, pbDataHashPtr, cbDataHash, out pSignatureOut, out pErrorOut);
}
}

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_GenerateSignature(
private static extern unsafe int AppleCryptoNative_GenerateSignature(
SafeSecKeyRefHandle privateKey,
byte[] pbDataHash,
byte* pbDataHash,
int cbDataHash,
out SafeCFDataHandle pSignatureOut,
out SafeCFErrorHandle pErrorOut);
Expand Down Expand Up @@ -106,18 +120,17 @@ private static extern unsafe int AppleCryptoNative_VerifySignatureWithHashAlgori
[DllImport(Libraries.AppleCryptoNative)]
private static extern ulong AppleCryptoNative_SecKeyGetSimpleKeySizeInBytes(SafeSecKeyRefHandle publicKey);

private delegate int SecKeyTransform(out SafeCFDataHandle data, out SafeCFErrorHandle error);
private delegate int SecKeySpanTransform(ReadOnlySpan<byte> source, out SafeCFDataHandle outputHandle, out SafeCFErrorHandle errorHandle);
private delegate int SecKeyTransform(ReadOnlySpan<byte> source, out SafeCFDataHandle outputHandle, out SafeCFErrorHandle errorHandle);

private static byte[] ExecuteTransform(SecKeyTransform transform)
private static byte[] ExecuteTransform(ReadOnlySpan<byte> source, SecKeyTransform transform)
{
const int Success = 1;
const int kErrorSeeError = -2;

SafeCFDataHandle data;
SafeCFErrorHandle error;

int ret = transform(out data, out error);
int ret = transform(source, out data, out error);

using (error)
using (data)
Expand All @@ -141,7 +154,7 @@ private static bool TryExecuteTransform(
ReadOnlySpan<byte> source,
Span<byte> destination,
out int bytesWritten,
SecKeySpanTransform transform)
SecKeyTransform transform)
{
SafeCFDataHandle outputHandle;
SafeCFErrorHandle errorHandle;
Expand Down Expand Up @@ -204,36 +217,36 @@ internal static SafeSecKeyRefHandle ImportEphemeralKey(byte[] keyBlob, bool hasP
throw new CryptographicException();
}

internal static byte[] GenerateSignature(SafeSecKeyRefHandle privateKey, byte[] dataHash)
internal static byte[] GenerateSignature(SafeSecKeyRefHandle privateKey, ReadOnlySpan<byte> dataHash)
{
Debug.Assert(privateKey != null, "privateKey != null");
Debug.Assert(dataHash != null, "dataHash != null");

return ExecuteTransform(
(out SafeCFDataHandle signature, out SafeCFErrorHandle error) =>
dataHash,
(ReadOnlySpan<byte> source, out SafeCFDataHandle signature, out SafeCFErrorHandle error) =>
AppleCryptoNative_GenerateSignature(
privateKey,
dataHash,
dataHash.Length,
source,
source.Length,
out signature,
out error));
}

internal static byte[] GenerateSignature(
SafeSecKeyRefHandle privateKey,
byte[] dataHash,
ReadOnlySpan<byte> dataHash,
PAL_HashAlgorithm hashAlgorithm)
{
Debug.Assert(privateKey != null, "privateKey != null");
Debug.Assert(dataHash != null, "dataHash != null");
Debug.Assert(hashAlgorithm != PAL_HashAlgorithm.Unknown, "hashAlgorithm != PAL_HashAlgorithm.Unknown");

return ExecuteTransform(
(out SafeCFDataHandle signature, out SafeCFErrorHandle error) =>
dataHash,
(ReadOnlySpan<byte> source, out SafeCFDataHandle signature, out SafeCFErrorHandle error) =>
AppleCryptoNative_GenerateSignatureWithHashAlgorithm(
privateKey,
dataHash,
dataHash.Length,
source,
source.Length,
hashAlgorithm,
out signature,
out error));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,35 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

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

internal static partial class Interop
{
internal static partial class Crypto
{
internal static unsafe bool EcDsaSign(ReadOnlySpan<byte> dgst, int dlen, Span<byte> sig, [In, Out] ref int siglen, SafeEcKeyHandle ecKey)
{
fixed (byte* dgstPtr = &dgst.DangerousGetPinnableReference())
fixed (byte* sigPtr = &sig.DangerousGetPinnableReference())
{
return EcDsaSign(dgstPtr, dlen, sigPtr, ref siglen, ecKey);
}
}

[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcDsaSign")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool EcDsaSign([In] byte[] dgst, int dlen, [Out] byte[] sig, [In, Out] ref int siglen, SafeEcKeyHandle ecKey);
private static extern unsafe bool EcDsaSign([In] byte* dgst, int dlen, [Out] byte* sig, [In, Out] ref int siglen, SafeEcKeyHandle ecKey);

internal static unsafe int EcDsaVerify(ReadOnlySpan<byte> dgst, int dgst_len, ReadOnlySpan<byte> sigbuf, int sig_len, SafeEcKeyHandle ecKey)
{
fixed (byte* dgstPtr = &dgst.DangerousGetPinnableReference())
fixed (byte* sigbufPtr = &sigbuf.DangerousGetPinnableReference())
{
return EcDsaVerify(dgstPtr, dgst_len, sigbufPtr, sig_len, ecKey);
}
}

/*-
* returns
Expand All @@ -20,7 +39,7 @@ internal static partial class Crypto
* -1: error
*/
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcDsaVerify")]
internal static extern int EcDsaVerify([In] byte[] dgst, int dgst_len, [In] byte[] sigbuf, int sig_len, SafeEcKeyHandle ecKey);
private static extern unsafe int EcDsaVerify(byte* dgst, int dgst_len, byte* sigbuf, int sig_len, SafeEcKeyHandle ecKey);

// returns the maximum length of a DER encoded ECDSA signature created with this key.
[DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EcDsaSize")]
Expand Down
15 changes: 7 additions & 8 deletions src/Common/src/System/Security/Cryptography/ECDsaCng.HashData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ internal static partial class ECDsaImplementation
#endif
public sealed partial class ECDsaCng : ECDsa
{
protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
{
return CngCommon.HashData(data, offset, count, hashAlgorithm);
}
protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) =>
CngCommon.HashData(data, offset, count, hashAlgorithm);

protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
{
return CngCommon.HashData(data, hashAlgorithm);
}
protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) =>
CngCommon.HashData(data, hashAlgorithm);

protected override bool TryHashData(ReadOnlySpan<byte> source, Span<byte> destination, HashAlgorithmName hashAlgorithm, out int bytesWritten) =>
CngCommon.TryHashData(source, destination, hashAlgorithm, out bytesWritten);
}
#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
}
Expand Down
21 changes: 15 additions & 6 deletions src/Common/src/System/Security/Cryptography/ECDsaCng.SignVerify.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ public override byte[] SignHash(byte[] hash)
}
}

public override unsafe bool TrySignHash(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
Copy link
Member

Choose a reason for hiding this comment

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

What here (and at VerifyHash) requires the unsafe keyword?

Copy link
Member Author

Choose a reason for hiding this comment

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

The null argument to TrySignHash is a void*.

{
using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
{
return keyHandle.TrySignHash(source, destination, AsymmetricPaddingMode.None, null, out bytesWritten);
}
}

/// <summary>
/// Verifies that alleged signature of a hash is, in fact, a valid signature of that hash.
/// </summary>
Expand All @@ -61,13 +69,14 @@ public override bool VerifyHash(byte[] hash, byte[] signature)
if (signature == null)
throw new ArgumentNullException(nameof(signature));

unsafe
return VerifyHash((ReadOnlySpan<byte>)hash, (ReadOnlySpan<byte>)signature);
}

public override unsafe bool VerifyHash(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature)
{
using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
{
using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle())
{
bool verified = keyHandle.VerifyHash(hash, signature, AsymmetricPaddingMode.None, null);
return verified;
}
return keyHandle.VerifyHash(hash, signature, AsymmetricPaddingMode.None, null);
}
}
}
Expand Down
59 changes: 49 additions & 10 deletions src/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Buffers;
using System.IO;
using Internal.Cryptography;
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.IO;

namespace System.Security.Cryptography
{
Expand Down Expand Up @@ -89,13 +89,53 @@ public override byte[] SignHash(byte[] hash)
return converted;
}

public override bool TrySignHash(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
{
SafeEcKeyHandle key = _key.Value;

byte[] converted;
int signatureLength = Interop.Crypto.EcDsaSize(key);
byte[] signature = ArrayPool<byte>.Shared.Rent(signatureLength);
try
{
if (!Interop.Crypto.EcDsaSign(source, source.Length, new Span<byte>(signature, 0, signatureLength), ref signatureLength, key))
{
throw Interop.Crypto.CreateOpenSslCryptographicException();
}

converted = AsymmetricAlgorithmHelpers.ConvertDerToIeee1363(signature, 0, signatureLength, KeySize);
}
finally
{
Array.Clear(signature, 0, signatureLength);
ArrayPool<byte>.Shared.Return(signature);
}

if (converted.Length <= destination.Length)
{
new ReadOnlySpan<byte>(converted).CopyTo(destination);
bytesWritten = converted.Length;
return true;
}
else
{
bytesWritten = 0;
return false;
}
}

public override bool VerifyHash(byte[] hash, byte[] signature)
{
if (hash == null)
throw new ArgumentNullException(nameof(hash));
if (signature == null)
throw new ArgumentNullException(nameof(signature));

return VerifyHash((ReadOnlySpan<byte>)hash, (ReadOnlySpan<byte>)signature);
}

public override bool VerifyHash(ReadOnlySpan<byte> hash, ReadOnlySpan<byte> signature)
{
// The signature format for .NET is r.Concat(s). Each of r and s are of length BitsToBytes(KeySize), even
// when they would have leading zeroes. If it's the correct size, then we need to encode it from
// r.Concat(s) to SEQUENCE(INTEGER(r), INTEGER(s)), because that's the format that OpenSSL expects.
Expand All @@ -113,15 +153,14 @@ public override bool VerifyHash(byte[] hash, byte[] signature)
return verifyResult == 1;
}

protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
{
return AsymmetricAlgorithmHelpers.HashData(data, offset, count, hashAlgorithm);
}
protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) =>
AsymmetricAlgorithmHelpers.HashData(data, offset, count, hashAlgorithm);

protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
{
return AsymmetricAlgorithmHelpers.HashData(data, hashAlgorithm);
}
protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) =>
AsymmetricAlgorithmHelpers.HashData(data, hashAlgorithm);

protected override bool TryHashData(ReadOnlySpan<byte> source, Span<byte> destination, HashAlgorithmName hashAlgorithm, out int bytesWritten) =>
AsymmetricAlgorithmHelpers.TryHashData(source, destination, hashAlgorithm, out bytesWritten);

protected override void Dispose(bool disposing)
{
Expand Down
Loading