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

[iOS] Implement DSA, RSA, EC key import/export #51926

Merged
merged 5 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
@@ -0,0 +1,67 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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 void EncodeToUncompressedAnsiX963Key(
ReadOnlySpan<byte> x,
ReadOnlySpan<byte> y,
ReadOnlySpan<byte> d,
Span<byte> 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));
d.CopyTo(destination.Slice(1 + x.Length + y.Length));
}

public static void DecodeFromUncompressedAnsiX963Key(
ReadOnlySpan<byte> ansiKey,
bool hasPrivateKey,
out ECParameters ret)
{
ret = default;

const byte UncompressedKeyPrefix = 0x04;
if (ansiKey.Length < 1 || ansiKey[0] != UncompressedKeyPrefix)
throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);

int fieldCount = hasPrivateKey ? 3 : 2;
int fieldSize = (ansiKey.Length - 1) / fieldCount;

if (ansiKey.Length != 1 + fieldSize * fieldCount)
throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);

ret.Q = new ECPoint {
X = ansiKey.Slice(1, fieldSize).ToArray(),
Y = ansiKey.Slice(1 + fieldSize, fieldSize).ToArray()
};

if (hasPrivateKey)
{
ret.D = ansiKey.Slice(1 + fieldSize + fieldSize, fieldSize).ToArray();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private static extern int AppleCryptoNative_SecKeychainEnumerateIdentities(
out SafeCFArrayHandle matches,
out int pOSStatus);

internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeKeychainItemHandle item)
private static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeHandle item)
{
bool addedRef = false;

Expand All @@ -85,6 +85,12 @@ internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeKeychainItemH
}
}

internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeKeychainItemHandle item)
=> SecKeychainItemCopyKeychain((SafeHandle)item);

internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeSecKeyRefHandle item)
=> SecKeychainItemCopyKeychain((SafeHandle)item);

internal static SafeKeychainHandle SecKeychainItemCopyKeychain(IntPtr item)
{
SafeKeychainHandle keychain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,6 @@ internal static partial class AppleCrypto
private const int kErrorSeeError = -2;
private const int kPlatformNotSupported = -5;

private static int AppleCryptoNative_SecKeyImportEphemeral(
ReadOnlySpan<byte> pbKeyBlob,
int isPrivateKey,
out SafeSecKeyRefHandle ppKeyOut,
out int pOSStatus) =>
AppleCryptoNative_SecKeyImportEphemeral(
ref MemoryMarshal.GetReference(pbKeyBlob),
pbKeyBlob.Length,
isPrivateKey,
out ppKeyOut,
out pOSStatus);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_SecKeyImportEphemeral(
ref byte pbKeyBlob,
int cbKeyBlob,
int isPrivateKey,
out SafeSecKeyRefHandle ppKeyOut,
out int pOSStatus);

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

Expand Down Expand Up @@ -102,40 +82,27 @@ internal static int GetSimpleKeySizeInBits(SafeSecKeyRefHandle publicKey)
return (int)(keySizeInBytes * 8);
}
}

internal static SafeSecKeyRefHandle ImportEphemeralKey(ReadOnlySpan<byte> keyBlob, bool hasPrivateKey)
{
Debug.Assert(keyBlob != null);

SafeSecKeyRefHandle keyHandle;
int osStatus;

int ret = AppleCryptoNative_SecKeyImportEphemeral(
keyBlob,
hasPrivateKey ? 1 : 0,
out keyHandle,
out osStatus);

if (ret == 1 && !keyHandle.IsInvalid)
{
return keyHandle;
}

if (ret == 0)
{
throw CreateExceptionForOSStatus(osStatus);
}

Debug.Fail($"SecKeyImportEphemeral returned {ret}");
throw new CryptographicException();
}
}
}

namespace System.Security.Cryptography.Apple
{
internal sealed class SafeSecKeyRefHandle : SafeKeychainItemHandle
internal sealed class SafeSecKeyRefHandle : SafeHandle
{
public SafeSecKeyRefHandle()
: base(IntPtr.Zero, ownsHandle: true)
{
}

protected override bool ReleaseHandle()
{
Interop.CoreFoundation.CFRelease(handle);
SetHandle(IntPtr.Zero);
return true;
}

public override bool IsInvalid => handle == IntPtr.Zero;

protected override void Dispose(bool disposing)
{
if (disposing && SafeHandleCache<SafeSecKeyRefHandle>.IsCachedInvalidHandle(this))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.Apple;
using Microsoft.Win32.SafeHandles;

internal static partial class Interop
{
internal static partial class AppleCrypto
{
internal enum PAL_KeyAlgorithm : uint
{
Unknown = 0,
EC = 1,
RSA = 2,
}

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 byte[] SecKeyCopyExternalRepresentation(
SafeSecKeyRefHandle key)
{
int result = AppleCryptoNative_SecKeyCopyExternalRepresentation(
key,
out SafeCFDataHandle data,
out SafeCFErrorHandle errorHandle);

using (errorHandle)
using (data)
{
switch (result)
{
case kSuccess:
return CoreFoundation.CFGetData(data);
case kErrorSeeError:
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,53 @@ internal static partial class AppleCrypto
{
private static readonly SafeCreateHandle s_nullExportString = new SafeCreateHandle();

private static int AppleCryptoNative_SecKeyImportEphemeral(
ReadOnlySpan<byte> pbKeyBlob,
int isPrivateKey,
out SafeSecKeyRefHandle ppKeyOut,
out int pOSStatus) =>
AppleCryptoNative_SecKeyImportEphemeral(
ref MemoryMarshal.GetReference(pbKeyBlob),
pbKeyBlob.Length,
isPrivateKey,
out ppKeyOut,
out pOSStatus);

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_SecKeyImportEphemeral(
ref byte pbKeyBlob,
int cbKeyBlob,
int isPrivateKey,
out SafeSecKeyRefHandle ppKeyOut,
out int pOSStatus);

internal static SafeSecKeyRefHandle ImportEphemeralKey(ReadOnlySpan<byte> keyBlob, bool hasPrivateKey)
{
Debug.Assert(keyBlob != null);

SafeSecKeyRefHandle keyHandle;
int osStatus;

int ret = AppleCryptoNative_SecKeyImportEphemeral(
keyBlob,
hasPrivateKey ? 1 : 0,
out keyHandle,
out osStatus);

if (ret == 1 && !keyHandle.IsInvalid)
{
return keyHandle;
}

if (ret == 0)
{
throw CreateExceptionForOSStatus(osStatus);
}

Debug.Fail($"SecKeyImportEphemeral returned {ret}");
throw new CryptographicException();
}

[DllImport(Libraries.AppleCryptoNative)]
private static extern int AppleCryptoNative_SecKeyExport(
SafeSecKeyRefHandle? key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentit
SafeSecKeyRefHandle key;
int osStatus = AppleCryptoNative_X509CopyPrivateKeyFromIdentity(identity, out key);

SafeTemporaryKeychainHandle.TrackItem(key);
//SafeTemporaryKeychainHandle.TrackItem(key);

if (osStatus != 0)
{
Expand All @@ -354,7 +354,7 @@ internal static SafeSecKeyRefHandle X509GetPublicKey(SafeSecCertificateHandle ce
int osStatus;
int ret = AppleCryptoNative_X509GetPublicKey(cert, out publicKey, out osStatus);

SafeTemporaryKeychainHandle.TrackItem(publicKey);
//SafeTemporaryKeychainHandle.TrackItem(publicKey);

if (ret == 1)
{
Expand Down
Loading