From ad615e6cc756437537d6d0641851266ccd371b29 Mon Sep 17 00:00:00 2001 From: Kevin Jones Date: Wed, 3 Jun 2020 17:33:59 -0400 Subject: [PATCH] Use key services and data keys where possible for faster EC operations. --- .../AsymmetricAlgorithmHelpers.Ansi.cs | 53 +++++ .../Interop.KeyServices.cs | 210 +++++++++++++++++ .../Cryptography/ECDsaSecurityTransforms.cs | 59 ++++- .../Cryptography/EccSecurityTransforms.cs | 130 +++++++++-- .../Security/Cryptography/SecKeyPair.cs | 32 ++- .../CMakeLists.txt | 1 + .../pal_keyservices.c | 219 ++++++++++++++++++ .../pal_keyservices.h | 57 +++++ ...em.Security.Cryptography.Algorithms.csproj | 4 + ...urity.Cryptography.X509Certificates.csproj | 6 +- 10 files changed, 741 insertions(+), 30 deletions(-) create mode 100644 src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Ansi.cs create mode 100644 src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.KeyServices.cs create mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keyservices.c create mode 100644 src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keyservices.h diff --git a/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Ansi.cs b/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Ansi.cs new file mode 100644 index 0000000000000..177c00947bd35 --- /dev/null +++ b/src/libraries/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.Ansi.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// 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.Diagnostics; +using System.Security.Cryptography; + +namespace Internal.Cryptography +{ + internal static partial class AsymmetricAlgorithmHelpers + { + // Encodes a EC key as an uncompressed set of concatenated scalars, + // optionally including the private key. To omit the private parameter, + // "d" must have a length of zero. + public static (Range PublicKey, Range? PrivateKey) EncodeToUncompressedAnsiX963Key( + ReadOnlySpan x, + ReadOnlySpan y, + ReadOnlySpan d, + Span destination) + { + const byte UncompressedKeyPrefix = 0x04; + if (x.Length != y.Length || (d.Length > 0 && d.Length != y.Length)) + throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); + + int size = 1 + x.Length + y.Length + d.Length; // 0x04 || X || Y { || D } + + if (destination.Length < size) + { + Debug.Fail("destination.Length < size"); + throw new CryptographicException(); + } + + destination[0] = UncompressedKeyPrefix; + x.CopyTo(destination.Slice(1)); + y.CopyTo(destination.Slice(1 + x.Length)); + Range publicKey = 0..(1 + x.Length + y.Length); + Range? privateKey; + + if (d.Length > 0) + { + d.CopyTo(destination[publicKey.End..]); + privateKey = 0..size; + } + else + { + privateKey = null; + } + + return (publicKey, privateKey); + } + } +} diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.KeyServices.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.KeyServices.cs new file mode 100644 index 0000000000000..8b2e6bce9a496 --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.KeyServices.cs @@ -0,0 +1,210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using Microsoft.Win32.SafeHandles; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Security.Cryptography.Apple; + +internal static partial class Interop +{ + internal static partial class AppleCrypto + { + internal enum PAL_KeyAlgorithm : uint + { + Unknown = 0, + EC = 1, + RSA = 2, + } + + internal enum PAL_SignatureAlgorithm : uint + { + Unknown = 0, + RsaPkcs1 = 1, + EC = 2, + DSA = 3 + } + + internal static unsafe SafeSecKeyRefHandle CreateDataKey( + ReadOnlySpan keyData, + int keySizeInBits, + PAL_KeyAlgorithm keyAlgorithm, + bool isPublic) + { + fixed (byte* pKey = keyData) + { + int result = AppleCryptoNative_CreateDataKey( + pKey, + keyData.Length, + keySizeInBits, + keyAlgorithm, + isPublic ? 1 : 0, + out SafeSecKeyRefHandle dataKey, + out SafeCFErrorHandle errorHandle); + + using (errorHandle) + { + const int Success = 1; + const int kErrorSeeError = -2; + + return result switch + { + Success => dataKey, + kErrorSeeError => throw CreateExceptionForCFError(errorHandle), + _ => throw new CryptographicException { HResult = result } + }; + } + } + } + + internal static bool KeyServicesVerifySignature( + SafeSecKeyRefHandle publicKey, + ReadOnlySpan dataHash, + ReadOnlySpan signature, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + bool digest) + { + const int Valid = 1; + const int Invalid = 0; + const int kErrorSeeError = -2; + + int result = AppleCryptoNative_SecKeyVerifySignature( + publicKey, + dataHash, + signature, + hashAlgorithm, + signatureAlgorithm, + digest, + out SafeCFErrorHandle errorHandle); + + using (errorHandle) + { + return result switch + { + Valid => true, + Invalid => false, + kErrorSeeError => throw CreateExceptionForCFError(errorHandle), + _ => throw new CryptographicException { HResult = result } + }; + } + } + + internal static byte[] KeyServicesCreateSignature( + SafeSecKeyRefHandle privateKey, + ReadOnlySpan dataHash, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + bool digest) + { + const int Success = 1; + const int kErrorSeeError = -2; + + int result = AppleCryptoNative_SecKeyCreateSignature( + privateKey, + dataHash, + hashAlgorithm, + signatureAlgorithm, + digest, + out SafeCFDataHandle signature, + out SafeCFErrorHandle errorHandle); + + using (errorHandle) + using (signature) + { + return result switch + { + Success => CoreFoundation.CFGetData(signature), + kErrorSeeError => throw CreateExceptionForCFError(errorHandle), + _ => throw new CryptographicException { HResult = result } + }; + } + } + + [DllImport(Libraries.AppleCryptoNative)] + private static unsafe extern int AppleCryptoNative_CreateDataKey( + byte* pKey, + int cbKey, + int keySizeInBits, + PAL_KeyAlgorithm keyAlgorithm, + int isPublic, + out SafeSecKeyRefHandle pDataKey, + out SafeCFErrorHandle pErrorOut); + + private static unsafe int AppleCryptoNative_SecKeyVerifySignature( + SafeSecKeyRefHandle publicKey, + ReadOnlySpan dataHash, + ReadOnlySpan signature, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + bool digest, + out SafeCFErrorHandle pErrorOut) + { + fixed (byte* pDataHash = dataHash) + fixed (byte* pSignature = signature) + { + return AppleCryptoNative_SecKeyVerifySignature( + publicKey, + pDataHash, + dataHash.Length, + pSignature, + signature.Length, + hashAlgorithm, + signatureAlgorithm, + digest ? 1 : 0, + out pErrorOut); + } + } + + [DllImport(Libraries.AppleCryptoNative)] + private static unsafe extern int AppleCryptoNative_SecKeyVerifySignature( + SafeSecKeyRefHandle publicKey, + byte* pbDataHash, + int cbDataHash, + byte* pbSignature, + int cbSignature, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + int digest, + out SafeCFErrorHandle pErrorOut); + + private static unsafe int AppleCryptoNative_SecKeyCreateSignature( + SafeSecKeyRefHandle privateKey, + ReadOnlySpan dataHash, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + bool digest, + out SafeCFDataHandle pSignatureOut, + out SafeCFErrorHandle pErrorOut) + { + fixed (byte* pDataHash = dataHash) + { + return AppleCryptoNative_SecKeyCreateSignature( + privateKey, + pDataHash, + dataHash.Length, + hashAlgorithm, + signatureAlgorithm, + digest ? 1 : 0, + out pSignatureOut, + out pErrorOut); + } + } + + [DllImport(Libraries.AppleCryptoNative)] + private static unsafe extern int AppleCryptoNative_SecKeyCreateSignature( + SafeSecKeyRefHandle privateKey, + byte* pbDataHash, + int cbDataHash, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + int digest, + out SafeCFDataHandle pSignatureOut, + out SafeCFErrorHandle pErrorOut); + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs index da68d61d336cb..cb2d582bd6a38 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Diagnostics; using System.Security.Cryptography.Apple; using Internal.Cryptography; @@ -107,7 +108,22 @@ public override byte[] SignHash(byte[] hash) throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); } - byte[] derFormatSignature = Interop.AppleCrypto.GenerateSignature(keys.PrivateKey, hash); + byte[] derFormatSignature; + + if (keys.PrivateDataKey != null) + { + derFormatSignature = Interop.AppleCrypto.KeyServicesCreateSignature( + keys.PrivateDataKey, + hash, + Interop.AppleCrypto.PAL_HashAlgorithm.Unknown, + Interop.AppleCrypto.PAL_SignatureAlgorithm.EC, + digest: true); + } + else + { + derFormatSignature = Interop.AppleCrypto.GenerateSignature(keys.PrivateKey, hash); + } + byte[] ieeeFormatSignature = AsymmetricAlgorithmHelpers.ConvertDerToIeee1363( derFormatSignature.AsSpan(0, derFormatSignature.Length), KeySize); @@ -123,7 +139,22 @@ public override bool TrySignHash(ReadOnlySpan source, Span destinati throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey); } - byte[] derFormatSignature = Interop.AppleCrypto.GenerateSignature(keys.PrivateKey, source); + byte[] derFormatSignature; + + if (keys.PrivateDataKey != null) + { + derFormatSignature = Interop.AppleCrypto.KeyServicesCreateSignature( + keys.PrivateDataKey, + source, + Interop.AppleCrypto.PAL_HashAlgorithm.Unknown, + Interop.AppleCrypto.PAL_SignatureAlgorithm.EC, + digest: true); + } + else + { + derFormatSignature = Interop.AppleCrypto.GenerateSignature(keys.PrivateKey, source); + } + byte[] ieeeFormatSignature = AsymmetricAlgorithmHelpers.ConvertDerToIeee1363( derFormatSignature.AsSpan(0, derFormatSignature.Length), KeySize); @@ -165,10 +196,26 @@ public override bool VerifyHash(ReadOnlySpan hash, ReadOnlySpan sign return false; } - return Interop.AppleCrypto.VerifySignature( - GetKeys().PublicKey, - hash, - AsymmetricAlgorithmHelpers.ConvertIeee1363ToDer(signature)); + SecKeyPair keys = GetKeys(); + byte[] formattedSignature = AsymmetricAlgorithmHelpers.ConvertIeee1363ToDer(signature); + + if (keys.PublicDataKey != null) + { + return Interop.AppleCrypto.KeyServicesVerifySignature( + keys.PublicDataKey, + hash, + formattedSignature, + Interop.AppleCrypto.PAL_HashAlgorithm.Unknown, + Interop.AppleCrypto.PAL_SignatureAlgorithm.EC, + digest: true); + } + else + { + return Interop.AppleCrypto.VerifySignature( + keys.PublicKey, + hash, + formattedSignature); + } } protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) => diff --git a/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs b/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs index 3eba157bdeb4e..c3b1ea9df5e5e 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/EccSecurityTransforms.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. #nullable enable + +using Internal.Cryptography; using System.Buffers; using System.Diagnostics; using System.Formats.Asn1; @@ -74,7 +76,16 @@ private SecKeyPair GenerateKey(int keySizeInBits) Interop.AppleCrypto.EccGenerateKey(keySizeInBits, out publicKey, out privateKey); - SecKeyPair newPair = SecKeyPair.PublicPrivatePair(publicKey, privateKey); + CreateDataKey( + privateKey, + keySizeInBits, + isPrivate: true, + out SafeSecKeyRefHandle publicDataKeyHandle, + out SafeSecKeyRefHandle? privateDataKeyHandle); + Debug.Assert(privateDataKeyHandle != null); + + SecKeyPair newPair = SecKeyPair.PublicPrivatePair(publicKey, privateKey, publicDataKeyHandle, privateDataKeyHandle); + SetKey(newPair); return newPair; } @@ -103,7 +114,7 @@ internal SecKeyPair GetOrGenerateKeys(int keySizeInBits) internal int SetKeyAndGetSize(SecKeyPair keyPair) { - int size = GetKeySize(keyPair); + int size = GetKeySize(keyPair.PublicKey); SetKey(keyPair); return size; } @@ -128,20 +139,14 @@ internal static ECParameters ExportPublicParametersFromPrivateKey(SafeSecKeyRefH return key; } - internal ECParameters ExportParameters(bool includePrivateParameters, int keySizeInBits) + private static ECParameters ExportParameters(SafeSecKeyRefHandle keyHandle, bool includePrivateParameters, int keySizeInBits) { // Apple requires all private keys to be exported encrypted, but since we're trying to export // as parsed structures we will need to decrypt it for the user. const string ExportPassword = "DotnetExportPassphrase"; - SecKeyPair keys = GetOrGenerateKeys(keySizeInBits); - - if (includePrivateParameters && keys.PrivateKey == null) - { - throw new CryptographicException(SR.Cryptography_OpenInvalidHandle); - } byte[] keyBlob = Interop.AppleCrypto.SecKeyExport( - includePrivateParameters ? keys.PrivateKey : keys.PublicKey, + keyHandle, exportPrivate: includePrivateParameters, password: ExportPassword); @@ -151,7 +156,7 @@ internal ECParameters ExportParameters(bool includePrivateParameters, int keySiz { EccKeyFormatHelper.ReadSubjectPublicKeyInfo( keyBlob, - out int localRead, + out _, out ECParameters key); return key; } @@ -160,7 +165,7 @@ internal ECParameters ExportParameters(bool includePrivateParameters, int keySiz EccKeyFormatHelper.ReadEncryptedPkcs8( keyBlob, ExportPassword, - out int localRead, + out _, out ECParameters key); return key; } @@ -171,6 +176,18 @@ internal ECParameters ExportParameters(bool includePrivateParameters, int keySiz } } + internal ECParameters ExportParameters(bool includePrivateParameters, int keySizeInBits) + { + SecKeyPair keys = GetOrGenerateKeys(keySizeInBits); + + if (includePrivateParameters && keys.PrivateKey == null) + { + throw new CryptographicException(SR.Cryptography_OpenInvalidHandle); + } + + return ExportParameters(includePrivateParameters ? keys.PrivateKey! : keys.PublicKey, includePrivateParameters, keySizeInBits); + } + internal int ImportParameters(ECParameters parameters) { parameters.Validate(); @@ -179,6 +196,7 @@ internal int ImportParameters(ECParameters parameters) bool isPrivateKey = parameters.D != null; bool hasPublicParameters = parameters.Q.X != null && parameters.Q.Y != null; SecKeyPair newKeys; + int keySizeInBits; if (isPrivateKey) { @@ -211,23 +229,34 @@ internal int ImportParameters(ECParameters parameters) throw; } - newKeys = SecKeyPair.PublicPrivatePair(publicKey, privateKey); + keySizeInBits = GetKeySize(publicKey); + + CreateDataKey( + privateKey, + keySizeInBits, + isPrivate: true, + out SafeSecKeyRefHandle publicDataKeyHandle, + out SafeSecKeyRefHandle? privateDataKeyHandle); + + Debug.Assert(privateDataKeyHandle != null); + newKeys = SecKeyPair.PublicPrivatePair(publicKey, privateKey, publicDataKeyHandle, privateDataKeyHandle); } else { SafeSecKeyRefHandle publicKey = ImportKey(parameters); - newKeys = SecKeyPair.PublicOnly(publicKey); + keySizeInBits = GetKeySize(publicKey); + CreateDataKey(publicKey, keySizeInBits, isPrivate: false, out SafeSecKeyRefHandle publicDataKeyHandle, out _); + newKeys = SecKeyPair.PublicOnly(publicKey, publicDataKeyHandle); } - int size = GetKeySize(newKeys); SetKey(newKeys); - return size; + return keySizeInBits; } - private static int GetKeySize(SecKeyPair newKeys) + private static int GetKeySize(SafeSecKeyRefHandle publicKey) { - long size = Interop.AppleCrypto.EccGetKeySizeInBits(newKeys.PublicKey); + long size = Interop.AppleCrypto.EccGetKeySizeInBits(publicKey); Debug.Assert(size == 256 || size == 384 || size == 521, $"Unknown keysize ({size})"); return (int)size; } @@ -286,7 +315,7 @@ internal unsafe int ImportSubjectPublicKeyInfo( SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.ImportEphemeralKey(source.Slice(0, localRead), false); SecKeyPair newKeys = SecKeyPair.PublicOnly(publicKey); - int size = GetKeySize(newKeys); + int size = GetKeySize(newKeys.PublicKey); SetKey(newKeys); bytesRead = localRead; @@ -294,5 +323,68 @@ internal unsafe int ImportSubjectPublicKeyInfo( } } } + + private static void CreateDataKey( + SafeSecKeyRefHandle keyHandle, + int keySizeInBits, + bool isPrivate, + out SafeSecKeyRefHandle publicKeyHandle, + out SafeSecKeyRefHandle? privateKeyHandle) + { + ECParameters ecParameters = ExportParameters(keyHandle, isPrivate, keySizeInBits); + int fieldSize = AsymmetricAlgorithmHelpers.BitsToBytes(keySizeInBits); + + Debug.Assert(ecParameters.Q.Y != null && ecParameters.Q.Y.Length == fieldSize); + Debug.Assert(ecParameters.Q.X != null && ecParameters.Q.X.Length == fieldSize); + + int keySize = 1 + fieldSize * (isPrivate ? 3 : 2); + byte[] dataKeyPool = CryptoPool.Rent(keySize); + Span dataKey = dataKeyPool; + Range? privateKey = null; + Range publicKey; + + try + { + (publicKey, privateKey) = AsymmetricAlgorithmHelpers.EncodeToUncompressedAnsiX963Key( + ecParameters.Q.X, + ecParameters.Q.Y, + isPrivate ? ecParameters.D : default, + dataKey); + + publicKeyHandle = Interop.AppleCrypto.CreateDataKey( + dataKey[publicKey], + keySizeInBits, + Interop.AppleCrypto.PAL_KeyAlgorithm.EC, + isPublic: true); + + Debug.Assert(privateKey.HasValue == isPrivate, "privateKey.HasValue == isPrivate"); + + if (privateKey.HasValue) + { + privateKeyHandle = Interop.AppleCrypto.CreateDataKey( + dataKey[privateKey.Value], + keySizeInBits, + Interop.AppleCrypto.PAL_KeyAlgorithm.EC, + isPublic: false); + } + else + { + privateKeyHandle = null; + } + } + finally + { + if (privateKey.HasValue) + { + CryptographicOperations.ZeroMemory(dataKey[privateKey.Value]); + } + + CryptographicOperations.ZeroMemory(ecParameters.D); + + // We manually cleared out the private key bytes above if the + // key was private, we don't need to clear the buffer again + CryptoPool.Return(dataKeyPool, clearSize: 0); + } + } } } diff --git a/src/libraries/Common/src/System/Security/Cryptography/SecKeyPair.cs b/src/libraries/Common/src/System/Security/Cryptography/SecKeyPair.cs index 219a03ec4ee74..75a6eb634564d 100644 --- a/src/libraries/Common/src/System/Security/Cryptography/SecKeyPair.cs +++ b/src/libraries/Common/src/System/Security/Cryptography/SecKeyPair.cs @@ -11,11 +11,15 @@ internal sealed class SecKeyPair : IDisposable { internal SafeSecKeyRefHandle PublicKey { get; private set; } internal SafeSecKeyRefHandle? PrivateKey { get; private set; } + internal SafeSecKeyRefHandle? PublicDataKey { get; private set; } + internal SafeSecKeyRefHandle? PrivateDataKey { get; private set; } - private SecKeyPair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle? privateKey) + private SecKeyPair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle? privateKey, SafeSecKeyRefHandle? publicDataKey, SafeSecKeyRefHandle? privateDataKey) { PublicKey = publicKey; PrivateKey = privateKey; + PublicDataKey = publicDataKey; + PrivateDataKey = privateDataKey; } public void Dispose() @@ -24,6 +28,10 @@ public void Dispose() PrivateKey = null; PublicKey?.Dispose(); PublicKey = null!; + PublicDataKey?.Dispose(); + PublicDataKey = null!; + PrivateDataKey?.Dispose(); + PrivateDataKey = null!; } internal static SecKeyPair PublicPrivatePair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle privateKey) @@ -33,15 +41,31 @@ internal static SecKeyPair PublicPrivatePair(SafeSecKeyRefHandle publicKey, Safe if (privateKey == null || privateKey.IsInvalid) throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(privateKey)); - return new SecKeyPair(publicKey, privateKey); + return new SecKeyPair(publicKey, privateKey, null, null); } - internal static SecKeyPair PublicOnly(SafeSecKeyRefHandle publicKey) + internal static SecKeyPair PublicPrivatePair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle privateKey, SafeSecKeyRefHandle publicDataKey, SafeSecKeyRefHandle privateDataKey) { if (publicKey == null || publicKey.IsInvalid) throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(publicKey)); + if (privateKey == null || privateKey.IsInvalid) + throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(privateKey)); + if (publicDataKey == null || publicDataKey.IsInvalid) + throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(publicDataKey)); + if (privateDataKey == null || privateDataKey.IsInvalid) + throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(privateDataKey)); + + return new SecKeyPair(publicKey, privateKey, publicDataKey, privateDataKey); + } + + internal static SecKeyPair PublicOnly(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle? publicDataKey = null) + { + if (publicKey == null || publicKey.IsInvalid) + throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(publicKey)); + if (publicDataKey != null && publicDataKey.IsInvalid) + throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(publicDataKey)); - return new SecKeyPair(publicKey, null); + return new SecKeyPair(publicKey, null, publicDataKey, null); } } } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt index 5a424ab6eaf70..aacaaf62b73cc 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt @@ -9,6 +9,7 @@ set(NATIVECRYPTO_SOURCES pal_hmac.c pal_keyagree.c pal_keychain.c + pal_keyservices.c pal_random.c pal_rsa.c pal_sec.c diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keyservices.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keyservices.c new file mode 100644 index 0000000000000..58bc3c5ade966 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keyservices.c @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "pal_keyservices.h" +#include "pal_ecc.h" + +#if !defined(TARGET_IOS) && !defined(TARGET_TVOS) + +static CFStringRef GetSignatureAlgorithmIdentifier(PAL_HashAlgorithm hashAlgorithm, PAL_SignatureAlgorithm signatureAlgorithm, bool digest) +{ + if (signatureAlgorithm == PAL_SignatureAlgorithm_EC) + { + // ECDSA signatures are always based on digests. The managed implementation + // will always pre-hash data before getting here. + assert(digest); + return kSecKeyAlgorithmECDSASignatureDigestX962; + } + if (signatureAlgorithm == PAL_SignatureAlgorithm_RSA_Pkcs1) + { + if (digest) + { + switch (hashAlgorithm) + { + case PAL_SHA1: return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1; + case PAL_SHA256: return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256; + case PAL_SHA384: return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384; + case PAL_SHA512: return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512; + } + + } + else + { + switch (hashAlgorithm) + { + case PAL_SHA1: return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1; + case PAL_SHA256: return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256; + case PAL_SHA384: return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384; + case PAL_SHA512: return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512; + } + } + } + + assert(false); + return NULL; +} + +static CFStringRef GetKeyAlgorithmIdentifier(PAL_KeyAlgorithm keyAlgorithm) +{ + if (keyAlgorithm == PAL_KeyAlgorithm_EC) + return kSecAttrKeyTypeECSECPrimeRandom; + if (keyAlgorithm == PAL_KeyAlgorithm_RSA) + return kSecAttrKeyTypeRSA; + + return NULL; +} + +int32_t AppleCryptoNative_CreateDataKey(uint8_t* pKey, + int32_t cbKey, + int32_t keySizeInBits, + PAL_KeyAlgorithm keyAlgorithm, + int32_t isPublic, + SecKeyRef* pKeyOut, + CFErrorRef* pErrorOut) +{ + if (pErrorOut != NULL) + *pErrorOut = NULL; + + if (pKeyOut != NULL) + *pKeyOut = NULL; + + if (pKeyOut == NULL || pErrorOut == NULL || cbKey <= 0 || pKey == NULL) + return kErrorBadInput; + + CFMutableDictionaryRef dataAttributes = CFDictionaryCreateMutable( + kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if (dataAttributes == NULL) + { + return kErrorUnknownState; + } + + CFNumberRef cfKeySizeInBits = CFNumberCreate(NULL, kCFNumberIntType, &keySizeInBits); + + if (cfKeySizeInBits == NULL) + { + CFRelease(dataAttributes); + return kErrorUnknownState; + } + + CFStringRef keyClass = isPublic == 0 ? kSecAttrKeyClassPrivate : kSecAttrKeyClassPublic; + CFStringRef keyType = GetKeyAlgorithmIdentifier(keyAlgorithm); + + if (keyType == NULL) + { + CFRelease(dataAttributes); + CFRelease(cfKeySizeInBits); + return kErrorBadInput; + } + + CFDictionarySetValue(dataAttributes, kSecAttrKeySizeInBits, cfKeySizeInBits); + CFDictionarySetValue(dataAttributes, kSecAttrKeyType, keyType); + CFDictionarySetValue(dataAttributes, kSecAttrKeyClass, keyClass); + CFDataRef cfData = CFDataCreateWithBytesNoCopy(NULL, pKey, cbKey, kCFAllocatorNull); + + *pKeyOut = SecKeyCreateWithData(cfData, dataAttributes, pErrorOut); + int32_t ret = kErrorSeeError; + + if (*pKeyOut != NULL) + { + ret = 1; + } + + CFRelease(cfData); + CFRelease(cfKeySizeInBits); + CFRelease(dataAttributes); + return ret; +} + +int32_t AppleCryptoNative_SecKeyCreateSignature(SecKeyRef privateKey, + uint8_t* pbDataHash, + int32_t cbDataHash, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + int32_t digest, + CFDataRef* pSignatureOut, + CFErrorRef* pErrorOut) +{ + if (pErrorOut != NULL) + *pErrorOut = NULL; + + if (pSignatureOut != NULL) + *pSignatureOut = NULL; + + if (privateKey == NULL || pbDataHash == NULL || cbDataHash < 0 || + pErrorOut == NULL || pSignatureOut == NULL) + return kErrorBadInput; + + bool useDigest = digest != 0; + CFStringRef algorithm = GetSignatureAlgorithmIdentifier(hashAlgorithm, signatureAlgorithm, useDigest); + + if (algorithm == NULL) + return kErrorBadInput; + + CFDataRef dataHash = CFDataCreateWithBytesNoCopy(NULL, pbDataHash, cbDataHash, kCFAllocatorNull); + + if (dataHash == NULL) + { + return kErrorUnknownState; + } + + int32_t ret = kErrorSeeError; + + CFDataRef sig = SecKeyCreateSignature(privateKey, algorithm, dataHash, pErrorOut); + + if (sig != NULL) + { + CFRetain(sig); + *pSignatureOut = sig; + ret = 1; + } + + CFRelease(dataHash); + return ret; +} + +int32_t AppleCryptoNative_SecKeyVerifySignature(SecKeyRef publicKey, + uint8_t* pbDataHash, + int32_t cbDataHash, + uint8_t* pbSignature, + int32_t cbSignature, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + int digest, + CFErrorRef* pErrorOut) +{ + if (pErrorOut != NULL) + *pErrorOut = NULL; + + if (publicKey == NULL || pbDataHash == NULL || cbDataHash < 0 || pbSignature == NULL || cbSignature < 0 || + pErrorOut == NULL) + return kErrorBadInput; + + bool useDigest = digest != 0; + CFStringRef algorithm = GetSignatureAlgorithmIdentifier(hashAlgorithm, signatureAlgorithm, useDigest); + + if (algorithm == NULL) + return kErrorBadInput; + + CFDataRef dataHash = CFDataCreateWithBytesNoCopy(NULL, pbDataHash, cbDataHash, kCFAllocatorNull); + + if (dataHash == NULL) + return kErrorUnknownState; + + CFDataRef signature = CFDataCreateWithBytesNoCopy(NULL, pbSignature, cbSignature, kCFAllocatorNull); + + if (signature == NULL) + { + CFRelease(dataHash); + return kErrorUnknownState; + } + + int32_t ret = kErrorSeeError; + + if (SecKeyVerifySignature(publicKey, algorithm, dataHash, signature, pErrorOut)) + { + ret = 1; + } + else if (CFErrorGetCode(*pErrorOut) == errSecVerifyFailed) + { + ret = 0; + } + + CFRelease(dataHash); + CFRelease(signature); + + return ret; +} +#endif diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keyservices.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keyservices.h new file mode 100644 index 0000000000000..9c1449ea038e5 --- /dev/null +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keyservices.h @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#pragma once + +#include "pal_digest.h" +#include "pal_seckey.h" +#include "pal_compiler.h" + +#include + +#if !defined(TARGET_IOS) && !defined(TARGET_TVOS) +enum +{ + PAL_SignatureAlgorithm_Unknown = 0, + PAL_SignatureAlgorithm_RSA_Pkcs1 = 1, + PAL_SignatureAlgorithm_EC = 2, + PAL_SignatureAlgorithm_DSA = 3, +}; +typedef uint32_t PAL_SignatureAlgorithm; + +enum +{ + PAL_KeyAlgorithm_Unknown = 0, + PAL_KeyAlgorithm_EC = 1, + PAL_KeyAlgorithm_RSA = 2, +}; +typedef uint32_t PAL_KeyAlgorithm; + +PALEXPORT int32_t AppleCryptoNative_CreateDataKey(uint8_t* pKey, + int32_t cbKey, + int32_t keySizeInBits, + PAL_KeyAlgorithm keyAlgorithm, + int32_t isPublic, + SecKeyRef* pKeyOut, + CFErrorRef* pErrorOut); + +PALEXPORT int32_t AppleCryptoNative_SecKeyCreateSignature(SecKeyRef privateKey, + uint8_t* pbDataHash, + int32_t cbDataHash, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + int32_t digest, + CFDataRef* pSignatureOut, + CFErrorRef* pErrorOut); + +PALEXPORT int32_t AppleCryptoNative_SecKeyVerifySignature(SecKeyRef publicKey, + uint8_t* pbDataHash, + int32_t cbDataHash, + uint8_t* pbSignature, + int32_t cbSignature, + PAL_HashAlgorithm hashAlgorithm, + PAL_SignatureAlgorithm signatureAlgorithm, + int digest, + CFErrorRef* pErrorOut); +#endif diff --git a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj index 99f268a3034f6..ca93dec52b2b5 100644 --- a/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj +++ b/src/libraries/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj @@ -86,6 +86,8 @@ + + Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.manual.cs Common\System\Security\Cryptography\Asn1\X509ExtensionAsn.xml + + - +