Skip to content

Commit

Permalink
[iOS] Implement DSA, RSA, EC key import/export (#51926)
Browse files Browse the repository at this point in the history
  • Loading branch information
filipnavara committed Apr 29, 2021
1 parent 1a18528 commit 6a5bc53
Show file tree
Hide file tree
Showing 38 changed files with 1,535 additions and 784 deletions.
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

0 comments on commit 6a5bc53

Please sign in to comment.