Skip to content

Implement ChaCha20Poly1305 with CryptoKit on macOS #76317

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

Merged
merged 9 commits into from
Oct 1, 2022
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
2 changes: 1 addition & 1 deletion eng/native/configurecompiler.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ if (CLR_CMAKE_HOST_UNIX)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0")
add_compile_options(-arch arm64)
elseif(CLR_CMAKE_HOST_ARCH_AMD64)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")
add_compile_options(-arch x86_64)
else()
clr_unknown_arch()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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;

internal static partial class Interop
{
internal static partial class AppleCrypto
{
internal static unsafe void ChaCha20Poly1305Encrypt(
ReadOnlySpan<byte> key,
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> plaintext,
Span<byte> ciphertext,
Span<byte> tag,
ReadOnlySpan<byte> aad)
{
fixed (byte* keyPtr = key)
fixed (byte* noncePtr = nonce)
fixed (byte* plaintextPtr = plaintext)
fixed (byte* ciphertextPtr = ciphertext)
fixed (byte* tagPtr = tag)
fixed (byte* aadPtr = aad)
{
const int Success = 1;
int result = AppleCryptoNative_ChaCha20Poly1305Encrypt(
keyPtr, key.Length,
noncePtr, nonce.Length,
plaintextPtr, plaintext.Length,
ciphertextPtr, ciphertext.Length,
tagPtr, tag.Length,
aadPtr, aad.Length);

if (result != Success)
{
Debug.Assert(result == 0);
CryptographicOperations.ZeroMemory(ciphertext);
CryptographicOperations.ZeroMemory(tag);
throw new CryptographicException();
}
}
}

internal static unsafe void ChaCha20Poly1305Decrypt(
ReadOnlySpan<byte> key,
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> ciphertext,
ReadOnlySpan<byte> tag,
Span<byte> plaintext,
ReadOnlySpan<byte> aad)
{
fixed (byte* keyPtr = key)
fixed (byte* noncePtr = nonce)
fixed (byte* ciphertextPtr = ciphertext)
fixed (byte* tagPtr = tag)
fixed (byte* plaintextPtr = plaintext)
fixed (byte* aadPtr = aad)
{
const int Success = 1;
const int AuthTagMismatch = -1;
int result = AppleCryptoNative_ChaCha20Poly1305Decrypt(
keyPtr, key.Length,
noncePtr, nonce.Length,
ciphertextPtr, ciphertext.Length,
tagPtr, tag.Length,
plaintextPtr, plaintext.Length,
aadPtr, aad.Length);

if (result != Success)
{
CryptographicOperations.ZeroMemory(plaintext);

if (result == AuthTagMismatch)
{
throw new AuthenticationTagMismatchException();
}
else
{
Debug.Assert(result == 0);
throw new CryptographicException();
}
}
}
}

[LibraryImport(Libraries.AppleCryptoNative)]
private static unsafe partial int AppleCryptoNative_ChaCha20Poly1305Encrypt(
byte* keyPtr,
int keyLength,
byte* noncePtr,
int nonceLength,
byte* plaintextPtr,
int plaintextLength,
byte* ciphertextPtr,
int ciphertextLength,
byte* tagPtr,
int tagLength,
byte* aadPtr,
int aadLength);

[LibraryImport(Libraries.AppleCryptoNative)]
private static unsafe partial int AppleCryptoNative_ChaCha20Poly1305Decrypt(
byte* keyPtr,
int keyLength,
byte* noncePtr,
int nonceLength,
byte* ciphertextPtr,
int ciphertextLength,
byte* tagPtr,
int tagLength,
byte* plaintextPtr,
int plaintextLength,
byte* aadPtr,
int aadLength);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -788,6 +788,7 @@
<Compile Include="System\Security\Cryptography\CapiHelper.DSA.Shared.cs" />
<Compile Include="System\Security\Cryptography\CapiHelper.Shared.cs" />
<Compile Include="System\Security\Cryptography\CapiHelper.Unix.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\Cng.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\CspKeyContainerInfo.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\DESCryptoServiceProvider.Unix.cs" />
Expand Down Expand Up @@ -868,7 +869,6 @@
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Cipher.cs" />
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs"
Link="Common\Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.OpenSsl.cs" />
</ItemGroup>
Expand Down Expand Up @@ -1257,6 +1257,8 @@
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Rsa.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Security.Cryptography.Native\Interop.OpenSslVersion.cs"
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.OpenSslVersion.cs" />
<Compile Include="$(CommonPath)Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Aead.cs"
Link="Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Aead.cs" />
<Compile Include="$(CommonPath)Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Keychain.macOS.cs"
Link="Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Keychain.macOS.cs" />
<Compile Include="$(CommonPath)Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Pbkdf2.cs"
Expand Down Expand Up @@ -1289,6 +1291,7 @@
Link="Common\System\Security\Cryptography\RSASecurityTransforms.macOS.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\RSAOpenSsl.cs"
Link="Common\System\Security\Cryptography\RSAOpenSsl.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.macOS.cs" />
<Compile Include="System\Security\Cryptography\DSA.Create.SecurityTransforms.cs" />
<Compile Include="System\Security\Cryptography\DSACryptoServiceProvider.Unix.cs" />
<Compile Include="System\Security\Cryptography\DSAOpenSsl.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace System.Security.Cryptography
{
public sealed partial class ChaCha20Poly1305
{
// CryptoKit added ChaCha20Poly1305 in macOS 10.15, which is our minimum target for macOS.
public static bool IsSupported => true;
private byte[]? _key;

[MemberNotNull(nameof(_key))]
private void ImportKey(ReadOnlySpan<byte> key)
{
// We should only be calling this in the constructor, so there shouldn't be a previous key.
Debug.Assert(_key is null);

// Pin the array on the POH so that the GC doesn't move it around to allow zeroing to be more effective.
_key = GC.AllocateArray<byte>(key.Length, pinned: true);
key.CopyTo(_key);
}

private void EncryptCore(
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> plaintext,
Span<byte> ciphertext,
Span<byte> tag,
ReadOnlySpan<byte> associatedData = default)
{
CheckDisposed();
Interop.AppleCrypto.ChaCha20Poly1305Encrypt(
_key,
nonce,
plaintext,
ciphertext,
tag,
associatedData);
}

private void DecryptCore(
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> ciphertext,
ReadOnlySpan<byte> tag,
Span<byte> plaintext,
ReadOnlySpan<byte> associatedData = default)
{
CheckDisposed();
Interop.AppleCrypto.ChaCha20Poly1305Decrypt(
_key,
nonce,
ciphertext,
tag,
plaintext,
associatedData);
}

public void Dispose()
{
CryptographicOperations.ZeroMemory(_key);
_key = null;
}

[MemberNotNull(nameof(_key))]
private void CheckDisposed()
{
ObjectDisposedException.ThrowIf(_key is null, this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -457,8 +457,12 @@ public static void CheckIsSupported()
// OpenSSL is present, and a high enough version,
// but the distro build options turned off ChaCha/Poly.
}
else if (PlatformDetection.OpenSslPresentOnSystem &&
(PlatformDetection.IsOSX || PlatformDetection.IsOpenSslSupported))
else if (PlatformDetection.IsOSX)
{
// CryptoKit is supported on macOS 10.15+, which is our minimum target.
expectedIsSupported = true;
}
else if (PlatformDetection.OpenSslPresentOnSystem && PlatformDetection.IsOpenSslSupported)
{
const int OpenSslChaChaMinimumVersion = 0x1_01_00_00_F; //major_minor_fix_patch_status
expectedIsSupported = SafeEvpPKeyHandle.OpenSslVersion >= OpenSslChaChaMinimumVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ if (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVO
else()
set(NATIVECRYPTO_SOURCES
${NATIVECRYPTO_SOURCES}
pal_swiftbindings.o
pal_keychain_macos.c
pal_keyderivation_macos.c
pal_seckey_macos.c
Expand All @@ -38,6 +39,24 @@ else()
)
endif()

# As of CMake 3.20.2 support for Swift only works with the Ninja and XCode
# generators so we cannot rely on it. Even with the Ninja generator it doesn't
# work in combination with other languages within the same library.
if (NOT SWIFT_COMPILER_TARGET AND CLR_CMAKE_TARGET_OSX)
set(SWIFT_PLATFORM "macosx")
set(SWIFT_PLATFORM_SUFFIX "")
set(SWIFT_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET})
set(SWIFT_COMPILER_TARGET "${CMAKE_OSX_ARCHITECTURES}-apple-${SWIFT_PLATFORM}${SWIFT_DEPLOYMENT_TARGET}${SWIFT_PLATFORM_SUFFIX}")
endif()

add_custom_command(
OUTPUT pal_swiftbindings.o
COMMAND xcrun swiftc -emit-object -static -parse-as-library -runtime-compatibility-version none -sdk ${CMAKE_OSX_SYSROOT} -target ${SWIFT_COMPILER_TARGET} ${CMAKE_CURRENT_SOURCE_DIR}/pal_swiftbindings.swift -o pal_swiftbindings.o
MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/pal_swiftbindings.swift
COMMENT "Compiling Swift file pal_swiftbindings.swift"
)
set_source_files_properties(pal_swiftbindings.o PROPERTIES EXTERNAL_OBJECT true GENERATED true)

if (CLR_CMAKE_TARGET_MACCATALYST)
add_definitions(-DTARGET_MACCATALYST)
endif()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "pal_seckey_macos.h"
#include "pal_signverify.h"
#include "pal_ssl.h"
#include "pal_swiftbindings.h"
#include "pal_symmetric.h"
#include "pal_trust_macos.h"
#include "pal_x509.h"
Expand All @@ -25,6 +26,8 @@

static const Entry s_cryptoAppleNative[] =
{
DllImportEntry(AppleCryptoNative_ChaCha20Poly1305Encrypt)
DllImportEntry(AppleCryptoNative_ChaCha20Poly1305Decrypt)
DllImportEntry(AppleCryptoNative_DigestFree)
DllImportEntry(AppleCryptoNative_DigestCreate)
DllImportEntry(AppleCryptoNative_DigestUpdate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ macro(append_extra_cryptography_apple_libs NativeLibsExtra)
find_library(SECURITY_LIBRARY Security)

list(APPEND ${NativeLibsExtra} ${COREFOUNDATION_LIBRARY} ${SECURITY_LIBRARY})

if (CLR_CMAKE_TARGET_OSX)
find_library(CRYPTOKIT_LIBRARY CryptoKit)

list(APPEND ${NativeLibsExtra} ${CRYPTOKIT_LIBRARY} -L/usr/lib/swift -lobjc -lswiftCore -lswiftFoundation)
endif()
endmacro()
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma once

#include "pal_types.h"
#include "pal_compiler.h"

PALEXPORT int32_t AppleCryptoNative_ChaCha20Poly1305Encrypt(
uint8_t* keyPtr,
int32_t keyLength,
uint8_t* noncePtr,
int32_t nonceLength,
uint8_t* plaintextPtr,
int32_t plaintextLength,
uint8_t* ciphertextBuffer,
int32_t ciphertextBufferLength,
uint8_t* tagBuffer,
int32_t tagBufferLength,
uint8_t* aadPtr,
int32_t aadLength);

PALEXPORT int32_t AppleCryptoNative_ChaCha20Poly1305Decrypt(
uint8_t* keyPtr,
int32_t keyLength,
uint8_t* noncePtr,
int32_t nonceLength,
uint8_t* ciphertextPtr,
int32_t ciphertextLength,
uint8_t* tagPtr,
int32_t tagLength,
uint8_t* plaintextBuffer,
int32_t plaintextBufferLength,
uint8_t* aadPtr,
int32_t aadLength);
Loading