Skip to content

Commit

Permalink
Use data keys on macOS for RSA, EC cryptography
Browse files Browse the repository at this point in the history
  • Loading branch information
filipnavara committed Apr 29, 2021
1 parent 6a5bc53 commit 04d2618
Show file tree
Hide file tree
Showing 15 changed files with 500 additions and 733 deletions.
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

0 comments on commit 04d2618

Please sign in to comment.