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

Add experimental Swift bindings for CryptoKit #108318

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
274 changes: 274 additions & 0 deletions src/libraries/Common/src/Interop/OSX/Swift.Runtime/CryptoKit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
// 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.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Swift;

#pragma warning disable CS3016 // Arrays as attribute arguments are not CLS Compliant
#pragma warning disable SYSLIB1051

namespace Swift.Runtime
{
/// <summary>
/// Represents ChaChaPoly in C#.
/// </summary>
internal unsafe partial struct ChaChaPoly
{
/// <summary>
/// Represents Nonce in C#.
/// </summary>
internal sealed unsafe partial class Nonce
{
private const int _payloadSize = 16;
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
internal void* payload;
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

internal Nonce()
{
payload = Marshal.AllocHGlobal(_payloadSize).ToPointer();
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(payload);
CryptoKit.PInvoke_ChaChaPoly_Nonce_Init(swiftIndirectResult);
}

internal Nonce(Data data)
{
payload = Marshal.AllocHGlobal(_payloadSize).ToPointer();
SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(payload);
void* dataPtr = &data;

CryptoKit.PInvoke_ChaChaPoly_Nonce_Init2(swiftIndirectResult, dataPtr, data.Metadata(), default(DataProtocol).WitnessTable(data), out SwiftError error);

if (error.Value != null) // Should throw when nonce is not 12 bytes
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
{
throw new Exception();
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
}
}

~Nonce()
{
Marshal.FreeHGlobal(new IntPtr(payload));
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// <summary>
/// Represents SealedBox in C#.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 16)]
internal unsafe partial struct SealedBox
{
internal Data combined;
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
internal readonly Data Ciphertext { get => CryptoKit.PInvoke_ChaChaPoly_SealedBox_GetCiphertext(this); }
internal readonly Data Tag { get => CryptoKit.PInvoke_ChaChaPoly_SealedBox_GetTag(this); }

internal SealedBox(ChaChaPoly.Nonce nonce, Data ciphertext, Data tag)
{
void* tagPtr = &tag;
void* ciphertextPtr = &ciphertext;
this = CryptoKit.PInvoke_ChaChaPoly_SealedBox_Init(nonce.payload, ciphertextPtr, tagPtr, ciphertext.Metadata(), tag.Metadata(), default(DataProtocol).WitnessTable(ciphertext), default(DataProtocol).WitnessTable(tag), out SwiftError error);
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

if (error.Value != null)
{
throw new Exception();
}
}
}
}

/// <summary>
/// Represents AesGcm in C#.
/// </summary>
internal unsafe partial struct AesGcm
{
/// <summary>
/// Represents Nonce in C#.
/// </summary>
internal sealed unsafe partial class Nonce
{
private const int _payloadSize = 16;
internal void* payload;

internal Nonce()
{
payload = Marshal.AllocHGlobal(_payloadSize).ToPointer();
SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(payload);
CryptoKit.PInvoke_AesGcm_Nonce_Init(swiftIndirectResult);
}

internal Nonce(Data data)
{
payload = Marshal.AllocHGlobal(_payloadSize).ToPointer();
SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(payload);
void* dataPtr = &data;
CryptoKit.PInvoke_AesGcm_Nonce_Init2(swiftIndirectResult, dataPtr, data.Metadata(), default(DataProtocol).WitnessTable(data), out SwiftError error);

if (error.Value != null) // Should throw when nonce is not 12 bytes
{
throw new Exception();
}
}

~Nonce()
{
Marshal.FreeHGlobal(new IntPtr(payload));
}
}

/// <summary>
/// Represents SealedBox in C#.
/// </summary>
internal sealed unsafe partial class SealedBox
{
private const int _payloadSize = 24;
internal void* payload;
public Data Ciphertext { get => CryptoKit.PInvoke_AesGcm_SealedBox_GetCiphertext(new SwiftSelf(payload)); }
public Data Tag { get => CryptoKit.PInvoke_AesGcm_SealedBox_GetTag(new SwiftSelf(payload)); }
internal SealedBox(AesGcm.Nonce nonce, Data ciphertext, Data tag)
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
{
payload = Marshal.AllocHGlobal(_payloadSize).ToPointer();
SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(payload);
void* tagPtr = &tag;
void* ciphertextPtr = &ciphertext;

CryptoKit.PInvoke_AesGcm_SealedBox_Init(swiftIndirectResult, nonce.payload, ciphertextPtr, tagPtr, ciphertext.Metadata(), tag.Metadata(), default(DataProtocol).WitnessTable(ciphertext), default(DataProtocol).WitnessTable(tag), out SwiftError error);

if (error.Value != null)
{
throw new Exception();
}
}

internal SealedBox()
{
payload = Marshal.AllocHGlobal(_payloadSize).ToPointer();
}

~SealedBox()
{
Marshal.FreeHGlobal(new IntPtr(payload));
}
}
}

/// <summary>
/// Represents SymmetricKey in C#.
/// </summary>
internal sealed unsafe partial class SymmetricKey
{
private const int _payloadSize = 8;
internal void* payload;

internal SymmetricKey(SymmetricKeySize symmetricKeySize)
{
payload = Marshal.AllocHGlobal(_payloadSize).ToPointer();
SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(payload);
CryptoKit.PInvoke_SymmetricKey_Init(swiftIndirectResult, &symmetricKeySize);
}

internal SymmetricKey(Data data)
{
payload = Marshal.AllocHGlobal(_payloadSize).ToPointer();
SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(payload);
void* dataPtr = &data;
CryptoKit.PInvoke_SymmetricKey_Init2(swiftIndirectResult, dataPtr, data.Metadata(), default(ContiguousBytes).WitnessTable(data));
}

~SymmetricKey()
{
Marshal.FreeHGlobal(new IntPtr(payload));
}
}

/// <summary>
/// Represents SymmetricKeySize in C#.
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 8)]
internal unsafe partial struct SymmetricKeySize
{
internal const string AppleCryptoKit = "/System/Library/Frameworks/CryptoKit.framework/CryptoKit";
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
internal nint bitCount;

[LibraryImport(AppleCryptoKit, EntryPoint = "$s9CryptoKit16SymmetricKeySizeV8bitCountACSi_tcfC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_init(SwiftIndirectResult result, nint bitCount);
internal SymmetricKeySize(nint bitCount)
{
SymmetricKeySize instance;
PInvoke_init(new SwiftIndirectResult(&instance), bitCount);
this = instance;
}
}

/// <summary>
/// Swift CryptoKit PInvoke methods in C#.
/// </summary>
internal static partial class CryptoKit
{
internal const string Path = "/System/Library/Frameworks/CryptoKit.framework/CryptoKit";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know if CryptoKit.framework will move over time or will live in different locations on different platforms. Generally speaking, what you want to do is build the library referencing CryptoKit.framework and then rely on a post-build step to run install_name_tool to set paths that point to the appropriate location for the platform.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolute paths are used in this setup: https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Interop/OSX/Interop.Libraries.cs

Can a mangled name differ per architecture? There are test failures:

System.EntryPointNotFoundException : Unable to find an entry point named '$s10Foundation4DataV5bytes5countACSV_SitcfC' in shared library '/System/Library/Frameworks/Foundation.framework/Foundation'


[LibraryImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO5NonceVAEycfC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_ChaChaPoly_Nonce_Init(SwiftIndirectResult result);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO5NonceV4dataAEx_tKc10Foundation12DataProtocolRzlufC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_ChaChaPoly_Nonce_Init2(SwiftIndirectResult result, void* data, void* metadata, void* witnessTable, out SwiftError error);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO9SealedBoxV10ciphertext10Foundation4DataVvg")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial Data PInvoke_ChaChaPoly_SealedBox_GetCiphertext(ChaChaPoly.SealedBox sealedBox);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO9SealedBoxV3tag10Foundation4DataVvg")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial Data PInvoke_ChaChaPoly_SealedBox_GetTag(ChaChaPoly.SealedBox sealedBox);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO9SealedBoxV5nonce10ciphertext3tagAeC5NonceV_xq_tKc10Foundation12DataProtocolRzAkLR_r0_lufC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial ChaChaPoly.SealedBox PInvoke_ChaChaPoly_SealedBox_Init(void* nonce, void* ciphertext, void* tag, void* metadata1, void* metadata2, void* witnessTable1, void*witnessTable2, out SwiftError error);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO5NonceVAGycfC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_AesGcm_Nonce_Init(SwiftIndirectResult result);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO5NonceV4dataAGx_tKc10Foundation12DataProtocolRzlufC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_AesGcm_Nonce_Init2(SwiftIndirectResult result, void* data, void* metadata, void* witnessTable, out SwiftError error);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO9SealedBoxV10ciphertext10Foundation4DataVvg")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial Data PInvoke_AesGcm_SealedBox_GetCiphertext(SwiftSelf sealedBox);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO9SealedBoxV3tag10Foundation4DataVvg")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial Data PInvoke_AesGcm_SealedBox_GetTag(SwiftSelf sealedBox);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO9SealedBoxV5nonce10ciphertext3tagAgE5NonceV_xq_tKc10Foundation12DataProtocolRzAmNR_r0_lufC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_AesGcm_SealedBox_Init(SwiftIndirectResult result, void* nonce, void* ciphertext, void* tag, void* metadata1, void* metadata2, void* witnessTable1, void*witnessTable2, out SwiftError error);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit12SymmetricKeyV4sizeAcA0cD4SizeV_tcfC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_SymmetricKey_Init(SwiftIndirectResult result, SymmetricKeySize* symmetricKeySize);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit12SymmetricKeyV4dataACx_tc10Foundation15ContiguousBytesRzlufC")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_SymmetricKey_Init2(SwiftIndirectResult result, void* data, void* md, void* pwt);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO4seal_5using5nonce14authenticatingAC9SealedBoxVx_AA12SymmetricKeyVAC5NonceVSgq_tK10Foundation12DataProtocolRzAoPR_r0_lFZ")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial ChaChaPoly.SealedBox PInvoke_ChaChaPoly_Seal(void* messagePtr, void* key, void* nonce, void* authenticatedData, void* metadata1, void* metadata2, void* witnessTable1, void* witnessTable2, out SwiftError error);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO4open_5using14authenticating10Foundation4DataVAC9SealedBoxV_AA12SymmetricKeyVxtKAG0I8ProtocolRzlFZ")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial Data PInvoke_ChaChaPoly_Open(ChaChaPoly.SealedBox sealedBox, void* key, void* authenticatedData, void* metadata, void* witnessTable, out SwiftError error);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO4seal_5using5nonce14authenticatingAE9SealedBoxVx_AA12SymmetricKeyVAE5NonceVSgq_tK10Foundation12DataProtocolRzAqRR_r0_lFZ")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial void PInvoke_AesGcm_Seal(SwiftIndirectResult result, void* messagePtr, void* key, void* nonce, void* authenticatedData, void* metadata1, void* metadata2, void* witnessTable1, void* witnessTable2, out SwiftError error);

[LibraryImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO4open_5using14authenticating10Foundation4DataVAE9SealedBoxV_AA12SymmetricKeyVxtKAI0I8ProtocolRzlFZ")]
[UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])]
internal static unsafe partial Data PInvoke_AesGcm_Open(void* sealedBox, void* key, void* authenticatedData, void* metadata, void* witnessTable, out SwiftError error);
}
}
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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.CompilerServices;
using System.Runtime.InteropServices;

namespace Swift.Runtime
{
// <summary>
// Represents dynamic library loader in C#.
// </summary>
internal static partial class DynamicLibraryLoader
{
public const int RTLD_LAZY = 0x1;
public const int RTLD_NOW = 0x2;

[LibraryImport("libdl.dylib")]
internal static partial IntPtr dlopen([MarshalAs(UnmanagedType.LPStr)] string path, int mode);

[LibraryImport("libdl.dylib")]
internal static partial IntPtr dlsym(IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string symbol);

[LibraryImport("libdl.dylib")]
internal static partial int dlclose(IntPtr handle);

[LibraryImport("libdl.dylib")]
internal static partial IntPtr dlerror();
}
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading