Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[macOS] Use data keys on macOS for RSA, EC cryptography #51976

Merged
merged 1 commit into from
May 3, 2021
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 @@ -35,14 +35,6 @@ private static extern int AppleCryptoNative_RsaVerificationPrimitive(
out SafeCFDataHandle pDataOut,
out SafeCFErrorHandle pErrorOut);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_RsaDecryptionPrimitive(
SafeSecKeyRefHandle privateKey,
ref byte pbData,
int cbData,
out SafeCFDataHandle pDataOut,
out SafeCFErrorHandle pErrorOut);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_RsaEncryptionPrimitive(
SafeSecKeyRefHandle publicKey,
Expand Down Expand Up @@ -270,22 +262,6 @@ private static bool ProcessPrimitiveResponse(
throw new CryptographicException();
}

internal static bool TryRsaDecryptionPrimitive(
SafeSecKeyRefHandle privateKey,
ReadOnlySpan<byte> source,
Span<byte> destination,
out int bytesWritten)
{
int returnValue = AppleCryptoNative_RsaDecryptionPrimitive(
privateKey,
ref MemoryMarshal.GetReference(source),
source.Length,
out SafeCFDataHandle cfData,
out SafeCFErrorHandle cfError);

return ProcessPrimitiveResponse(returnValue, cfData, cfError, destination, out bytesWritten);
}

internal static bool TryRsaEncryptionPrimitive(
SafeSecKeyRefHandle publicKey,
ReadOnlySpan<byte> source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ internal static partial class AppleCrypto
private const int kErrorSeeError = -2;
private const int kPlatformNotSupported = -5;

internal enum PAL_KeyAlgorithm : uint
{
Unknown = 0,
EC = 1,
RSA = 2,
}

[DllImport(Libraries.AppleCryptoNative)]
private static extern ulong AppleCryptoNative_SecKeyGetSimpleKeySizeInBytes(SafeSecKeyRefHandle publicKey);

Expand Down Expand Up @@ -82,6 +89,88 @@ internal static int GetSimpleKeySizeInBits(SafeSecKeyRefHandle publicKey)
return (int)(keySizeInBytes * 8);
}
}

internal static unsafe SafeSecKeyRefHandle CreateDataKey(
ReadOnlySpan<byte> keyData,
PAL_KeyAlgorithm keyAlgorithm,
bool isPublic)
{
fixed (byte* pKey = keyData)
{
int result = AppleCryptoNative_SecKeyCreateWithData(
pKey,
keyData.Length,
keyAlgorithm,
isPublic ? 1 : 0,
out SafeSecKeyRefHandle dataKey,
out SafeCFErrorHandle errorHandle);

using (errorHandle)
{
switch (result)
{
case kSuccess:
return dataKey;
case kErrorSeeError:
throw CreateExceptionForCFError(errorHandle);
default:
Debug.Fail($"SecKeyCreateWithData returned {result}");
throw new CryptographicException();
}
}
}
}

internal static bool TrySecKeyCopyExternalRepresentation(
SafeSecKeyRefHandle key,
out byte[] externalRepresentation)
{
const int errSecPassphraseRequired = -25260;

int result = AppleCryptoNative_SecKeyCopyExternalRepresentation(
key,
out SafeCFDataHandle data,
out SafeCFErrorHandle errorHandle);

using (errorHandle)
using (data)
{
switch (result)
{
case kSuccess:
externalRepresentation = CoreFoundation.CFGetData(data);
return true;
case kErrorSeeError:
if (Interop.CoreFoundation.GetErrorCode(errorHandle) == errSecPassphraseRequired)
{
externalRepresentation = Array.Empty<byte>();
return false;
}
throw CreateExceptionForCFError(errorHandle);
default:
Debug.Fail($"SecKeyCopyExternalRepresentation returned {result}");
throw new CryptographicException();
}
}
}

[DllImport(Libraries.AppleCryptoNative)]
private static unsafe extern int AppleCryptoNative_SecKeyCreateWithData(
byte* pKey,
int cbKey,
PAL_KeyAlgorithm keyAlgorithm,
int isPublic,
out SafeSecKeyRefHandle pDataKey,
out SafeCFErrorHandle pErrorOut);

[DllImport(Libraries.AppleCryptoNative)]
private static unsafe extern int AppleCryptoNative_SecKeyCopyExternalRepresentation(
SafeSecKeyRefHandle key,
out SafeCFDataHandle pDataOut,
out SafeCFErrorHandle pErrorOut);

[DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SecKeyCopyPublicKey")]
internal static unsafe extern SafeSecKeyRefHandle CopyPublicKey(SafeSecKeyRefHandle privateKey);
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,135 @@ private void SetKey(SecKeyPair keyPair)
current?.Dispose();
}

internal ECParameters ExportParameters(bool includePrivateParameters, int keySizeInBits)
{
SecKeyPair keys = GetOrGenerateKeys(keySizeInBits);

if (includePrivateParameters && keys.PrivateKey == null)
{
throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
}

bool gotKeyBlob = Interop.AppleCrypto.TrySecKeyCopyExternalRepresentation(
includePrivateParameters ? keys.PrivateKey! : keys.PublicKey,
out byte[] keyBlob);

if (!gotKeyBlob)
{
return ExportParametersFromLegacyKey(keys, includePrivateParameters);
}

try
{
AsymmetricAlgorithmHelpers.DecodeFromUncompressedAnsiX963Key(
keyBlob,
includePrivateParameters,
out ECParameters key);

switch (GetKeySize(keys))
{
case 256: key.Curve = ECCurve.NamedCurves.nistP256; break;
case 384: key.Curve = ECCurve.NamedCurves.nistP384; break;
case 521: key.Curve = ECCurve.NamedCurves.nistP521; break;
default:
Debug.Fail("Unsupported curve");
throw new CryptographicException();
}

return key;
}
finally
{
CryptographicOperations.ZeroMemory(keyBlob);
}
}

internal int ImportParameters(ECParameters parameters)
{
parameters.Validate();
ThrowIfDisposed();

if (!parameters.Curve.IsNamed)
{
throw new PlatformNotSupportedException(SR.Cryptography_ECC_NamedCurvesOnly);
}

switch (parameters.Curve.Oid.Value)
{
case Oids.secp256r1:
case Oids.secp384r1:
case Oids.secp521r1:
break;
default:
throw new PlatformNotSupportedException(
SR.Format(SR.Cryptography_CurveNotSupported, parameters.Curve.Oid.Value ?? parameters.Curve.Oid.FriendlyName));
}

if (parameters.Q.X == null || parameters.Q.Y == null)
{
ExtractPublicKeyFromPrivateKey(ref parameters);
}

bool isPrivateKey = parameters.D != null;
SecKeyPair newKeys;

if (isPrivateKey)
{
// Start with the private key, in case some of the private key fields don't
// match the public key fields and the system determines an integrity failure.
//
// Public import should go off without a hitch.
SafeSecKeyRefHandle privateKey = ImportKey(parameters);
SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.CopyPublicKey(privateKey);
newKeys = SecKeyPair.PublicPrivatePair(publicKey, privateKey);
}
else
{
SafeSecKeyRefHandle publicKey = ImportKey(parameters);
newKeys = SecKeyPair.PublicOnly(publicKey);
}

int size = GetKeySize(newKeys);
SetKey(newKeys);

return size;
}

private static int GetKeySize(SecKeyPair newKeys)
{
long size = Interop.AppleCrypto.EccGetKeySizeInBits(newKeys.PublicKey);
Debug.Assert(size == 256 || size == 384 || size == 521, $"Unknown keysize ({size})");
return (int)size;
}

private static SafeSecKeyRefHandle ImportKey(ECParameters parameters)
{
int fieldSize = parameters.Q!.X!.Length;

Debug.Assert(parameters.Q.Y != null && parameters.Q.Y.Length == fieldSize);
Debug.Assert(parameters.Q.X != null && parameters.Q.X.Length == fieldSize);

int keySize = 1 + fieldSize * (parameters.D != null ? 3 : 2);
byte[] dataKeyPool = CryptoPool.Rent(keySize);
Span<byte> dataKey = dataKeyPool.AsSpan(0, keySize);

try
{
AsymmetricAlgorithmHelpers.EncodeToUncompressedAnsiX963Key(
parameters.Q.X,
parameters.Q.Y,
parameters.D,
dataKey);

return Interop.AppleCrypto.CreateDataKey(
dataKey,
Interop.AppleCrypto.PAL_KeyAlgorithm.EC,
isPublic: parameters.D == null);
}
finally
{
CryptoPool.Return(dataKeyPool, keySize);
}
}
}
}
Loading