diff --git a/src/Swift.Bindings/tests/CryptoKit/CryptoKit.Source.cs b/src/Swift.Bindings/tests/CryptoKit/CryptoKit.Source.cs new file mode 100644 index 000000000000..264fcdad91c7 --- /dev/null +++ b/src/Swift.Bindings/tests/CryptoKit/CryptoKit.Source.cs @@ -0,0 +1,490 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; +using System.Security.Cryptography; + +namespace Test +{ + /// + /// Represents ChaChaPoly in C#. + /// + public unsafe struct ChaChaPoly + { + /// + /// Represents Nonce in C#. + /// + public sealed unsafe class Nonce : IDisposable, ISwiftObject + { + private static nuint PayloadSize = (nuint)((Runtime.ValueWitnessTable*)Test.Runtime.GetValueWitnessTable(Metadata))->Size; + + private readonly void* _payload; + + private bool _disposed = false; + + public Nonce() + { + _payload = NativeMemory.Alloc(PayloadSize); + SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(_payload); + CryptoKit.PInvoke_ChaChaPoly_Nonce_Init(swiftIndirectResult); + } + + public Nonce(Data data) + { + _payload = NativeMemory.Alloc(PayloadSize); + SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(_payload); + + void* metadata = Test.Runtime.GetMetadata(data); + void* conformanceDescriptor = IDataProtocol.GetConformanceDescriptor; + void* witnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, metadata, null); + + CryptoKit.PInvoke_ChaChaPoly_Nonce_Init2(swiftIndirectResult, &data, metadata, witnessTable, out SwiftError error); + + if (error.Value != null) + { + NativeMemory.Free(_payload); + throw new CryptographicException(); + } + } + + public void* Payload => _payload; + + public static void* Metadata => CryptoKit.PInvoke_ChaChaPoly_Nonce_GetMetadata(); + + public void Dispose() + { + if (!_disposed) + { + NativeMemory.Free(_payload); + _disposed = true; + GC.SuppressFinalize(this); + } + } + + ~Nonce() + { + NativeMemory.Free(_payload); + } + } + + /// + /// Represents SealedBox in C#. + /// + [StructLayout(LayoutKind.Sequential, Size = 16)] + public unsafe struct SealedBox + { + private readonly Data _combined; + + public SealedBox(ChaChaPoly.Nonce nonce, Data ciphertext, Data tag) + { + void* ciphertextMetadata = Test.Runtime.GetMetadata(ciphertext); + void* tagMetadata = Test.Runtime.GetMetadata(tag); + void* conformanceDescriptor = IDataProtocol.GetConformanceDescriptor; + void* ciphertextWitnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, ciphertextMetadata, null); + void* tagWitnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, tagMetadata, null); + + this = CryptoKit.PInvoke_ChaChaPoly_SealedBox_Init( + nonce.Payload, + &ciphertext, + &tag, + ciphertextMetadata, + tagMetadata, + ciphertextWitnessTable, + tagWitnessTable, + out SwiftError error); + + if (error.Value != null) + { + throw new CryptographicException(); + } + } + + public Data Ciphertext => CryptoKit.PInvoke_ChaChaPoly_SealedBox_GetCiphertext(this); + + public Data Tag => CryptoKit.PInvoke_ChaChaPoly_SealedBox_GetTag(this); + } + + /// + /// Encrypts the plaintext using the key, nonce, and authenticated data. + /// + public static unsafe SealedBox seal(Plaintext plaintext, SymmetricKey key, Nonce nonce, AuthenticateData aad, out SwiftError error) where Plaintext : unmanaged, ISwiftObject where AuthenticateData : unmanaged, ISwiftObject { + void* plaintextMetadata = Test.Runtime.GetMetadata(plaintext); + void* aadMetadata = Test.Runtime.GetMetadata(aad); + void* conformanceDescriptor = IDataProtocol.GetConformanceDescriptor; + void* plaintextWitnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, plaintextMetadata, null); + void* aadWitnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, aadMetadata, null); + + SealedBox sealedBox = CryptoKit.PInvoke_ChaChaPoly_Seal( + &plaintext, + key.Payload, + nonce.Payload, + &aad, + plaintextMetadata, + aadMetadata, + plaintextWitnessTable, + aadWitnessTable, + out error); + + return sealedBox; + } + + /// + /// Decrypts the sealed box using the key and authenticated data. + /// + public static unsafe Data open(SealedBox sealedBox, SymmetricKey key, AuthenticateData aad, out SwiftError error) where AuthenticateData : unmanaged, ISwiftObject { + void* metadata = Test.Runtime.GetMetadata(aad); + void* conformanceDescriptor = IDataProtocol.GetConformanceDescriptor; + void* witnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, metadata, null); + + Data data = CryptoKit.PInvoke_ChaChaPoly_Open( + sealedBox, + key.Payload, + &aad, + metadata, + witnessTable, + out error); + + return data; + } + } + + /// + /// Represents AesGcm in C#. + /// + public unsafe struct AesGcm + { + /// + /// Represents Nonce in C#. + /// + public sealed unsafe class Nonce : IDisposable, ISwiftObject + { + private static nuint PayloadSize = (nuint)((Runtime.ValueWitnessTable*)Test.Runtime.GetValueWitnessTable(Metadata))->Size; + + private readonly void* _payload; + + private bool _disposed = false; + + public Nonce() + { + _payload = NativeMemory.Alloc(PayloadSize); + SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(_payload); + CryptoKit.PInvoke_AesGcm_Nonce_Init(swiftIndirectResult); + } + + public Nonce(Data data) + { + _payload = NativeMemory.Alloc(PayloadSize); + SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(_payload); + + void* metadata = Test.Runtime.GetMetadata(data); + void* conformanceDescriptor = IDataProtocol.GetConformanceDescriptor; + void* witnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, metadata, null); + + CryptoKit.PInvoke_AesGcm_Nonce_Init2(swiftIndirectResult, &data, metadata, witnessTable, out SwiftError error); + + if (error.Value != null) + { + NativeMemory.Free(_payload); + throw new CryptographicException(); + } + } + + public void* Payload => _payload; + + public static void* Metadata => CryptoKit.PInvoke_AesGcm_Nonce_GetMetadata(); + + public void Dispose() + { + if (!_disposed) + { + NativeMemory.Free(_payload); + _disposed = true; + GC.SuppressFinalize(this); + } + } + + ~Nonce() + { + NativeMemory.Free(_payload); + } + } + + /// + /// Represents SealedBox in C#. + /// + public sealed unsafe class SealedBox : IDisposable, ISwiftObject + { + private static nuint PayloadSize = (nuint)((Runtime.ValueWitnessTable*)Test.Runtime.GetValueWitnessTable(Metadata))->Size; + + private readonly void* _payload; + + private bool _disposed = false; + + public SealedBox() + { + _payload = NativeMemory.Alloc(PayloadSize); + } + + public SealedBox(AesGcm.Nonce nonce, Data ciphertext, Data tag) + { + _payload = NativeMemory.Alloc(PayloadSize); + SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(_payload); + + void* ciphertextMetadata = Test.Runtime.GetMetadata(ciphertext); + void* tagMetadata = Test.Runtime.GetMetadata(tag); + void* conformanceDescriptor = IDataProtocol.GetConformanceDescriptor; + void* ciphertextWitnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, ciphertextMetadata, null); + void* tagWitnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, tagMetadata, null); + + CryptoKit.PInvoke_AesGcm_SealedBox_Init( + swiftIndirectResult, + nonce.Payload, + &ciphertext, + &tag, + ciphertextMetadata, + tagMetadata, + ciphertextWitnessTable, + tagWitnessTable, + out SwiftError error); + + if (error.Value != null) + { + NativeMemory.Free(_payload); + throw new CryptographicException(); + } + } + + public void* Payload => _payload; + + public static void* Metadata => CryptoKit.PInvoke_AesGcm_SealedBox_GetMetadata(); + + public Data Ciphertext => CryptoKit.PInvoke_AesGcm_SealedBox_GetCiphertext(new SwiftSelf(_payload)); + + public Data Tag => CryptoKit.PInvoke_AesGcm_SealedBox_GetTag(new SwiftSelf(_payload)); + + public void Dispose() + { + if (!_disposed) + { + NativeMemory.Free(_payload); + _disposed = true; + GC.SuppressFinalize(this); + } + } + + ~SealedBox() + { + NativeMemory.Free(_payload); + } + } + + /// + /// Encrypts the plaintext using the key, nonce, and authenticated data. + /// + public static unsafe SealedBox seal(Plaintext plaintext, SymmetricKey key, Nonce nonce, AuthenticateData aad, out SwiftError error) where Plaintext : unmanaged, ISwiftObject where AuthenticateData : unmanaged, ISwiftObject { + AesGcm.SealedBox sealedBox = new AesGcm.SealedBox(); + SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(sealedBox.Payload); + + void* plaintextMetadata = Test.Runtime.GetMetadata(plaintext); + void* aadMetadata = Test.Runtime.GetMetadata(aad); + void* conformanceDescriptor = IDataProtocol.GetConformanceDescriptor; + void* plaintextWitnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, plaintextMetadata, null); + void* aadWitnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, aadMetadata, null); + + CryptoKit.PInvoke_AesGcm_Seal( + swiftIndirectResult, + &plaintext, + key.Payload, + nonce.Payload, + &aad, + plaintextMetadata, + aadMetadata, + plaintextWitnessTable, + aadWitnessTable, + out error); + + return sealedBox; + } + + /// + /// Decrypts the sealed box using the key and authenticated data. + /// + public static unsafe Data open(SealedBox sealedBox, SymmetricKey key, AuthenticateData aad, out SwiftError error) where AuthenticateData : unmanaged, ISwiftObject { + void* metadata = Test.Runtime.GetMetadata(aad); + void* conformanceDescriptor = IDataProtocol.GetConformanceDescriptor; + void* witnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, metadata, null); + + Data data = CryptoKit.PInvoke_AesGcm_Open( + sealedBox.Payload, + key.Payload, + &aad, + metadata, + witnessTable, + out error); + + return data; + } + } + + /// + /// Represents SymmetricKey in C#. + /// + public sealed unsafe class SymmetricKey : IDisposable, ISwiftObject + { + private static nuint PayloadSize = (nuint)((Runtime.ValueWitnessTable*)Test.Runtime.GetValueWitnessTable(Metadata))->Size; + + public readonly void* _payload; + + private bool _disposed = false; + + public SymmetricKey(SymmetricKeySize symmetricKeySize) + { + _payload = NativeMemory.Alloc(PayloadSize); + SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(_payload); + CryptoKit.PInvoke_SymmetricKey_Init(swiftIndirectResult, &symmetricKeySize); + } + + public SymmetricKey(Data data) + { + _payload = NativeMemory.Alloc(PayloadSize); + SwiftIndirectResult swiftIndirectResult = new SwiftIndirectResult(_payload); + + void* metadata = Test.Runtime.GetMetadata(data); + void* conformanceDescriptor = IContiguousBytes.GetConformanceDescriptor; + void* witnessTable = Foundation.PInvoke_Swift_GetWitnessTable(conformanceDescriptor, metadata, null); + + CryptoKit.PInvoke_SymmetricKey_Init2(swiftIndirectResult, &data, metadata, witnessTable); + } + + public void* Payload => _payload; + + public static void* Metadata => CryptoKit.PInvoke_SymmetricKey_GetMetadata(); + + public void Dispose() + { + if (!_disposed) + { + NativeMemory.Free(_payload); + _disposed = true; + GC.SuppressFinalize(this); + } + } + + ~SymmetricKey() + { + NativeMemory.Free(_payload); + } + } + + /// + /// Represents SymmetricKeySize in C#. + /// + [StructLayout(LayoutKind.Sequential, Size = 8)] + public unsafe struct SymmetricKeySize + { + private readonly nint _bitCount; + + public SymmetricKeySize(nint bitCount) + { + SymmetricKeySize instance; + CryptoKit.PInvoke_init(new SwiftIndirectResult(&instance), bitCount); + this = instance; + } + } + + /// + /// Swift CryptoKit PInvoke methods in C#. + /// + public static class CryptoKit + { + public const string Path = "/System/Library/Frameworks/CryptoKit.framework/CryptoKit"; + + [DllImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO5NonceVAEycfC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_ChaChaPoly_Nonce_Init(SwiftIndirectResult result); + + [DllImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO5NonceV4dataAEx_tKc10Foundation12DataProtocolRzlufC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_ChaChaPoly_Nonce_Init2(SwiftIndirectResult result, void* data, void* metadata, void* witnessTable, out SwiftError error); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO5NonceVMa")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void* PInvoke_ChaChaPoly_Nonce_GetMetadata(); + + [DllImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO9SealedBoxV10ciphertext10Foundation4DataVvg")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern Data PInvoke_ChaChaPoly_SealedBox_GetCiphertext(ChaChaPoly.SealedBox sealedBox); + + [DllImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO9SealedBoxV3tag10Foundation4DataVvg")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern Data PInvoke_ChaChaPoly_SealedBox_GetTag(ChaChaPoly.SealedBox sealedBox); + + [DllImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO9SealedBoxV5nonce10ciphertext3tagAeC5NonceV_xq_tKc10Foundation12DataProtocolRzAkLR_r0_lufC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern ChaChaPoly.SealedBox PInvoke_ChaChaPoly_SealedBox_Init(void* nonce, void* ciphertext, void* tag, void* ciphertextMetadata, void* tagMetadata, void* ciphertextWitnessTable, void* tagWitnessTable, out SwiftError error); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO5NonceVAGycfC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_AesGcm_Nonce_Init(SwiftIndirectResult result); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO5NonceV4dataAGx_tKc10Foundation12DataProtocolRzlufC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_AesGcm_Nonce_Init2(SwiftIndirectResult result, void* data, void* metadata, void* witnessTable, out SwiftError error); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO5NonceVMa")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void* PInvoke_AesGcm_Nonce_GetMetadata(); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO9SealedBoxVMa")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void* PInvoke_AesGcm_SealedBox_GetMetadata(); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO9SealedBoxV10ciphertext10Foundation4DataVvg")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern Data PInvoke_AesGcm_SealedBox_GetCiphertext(SwiftSelf sealedBox); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO9SealedBoxV3tag10Foundation4DataVvg")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern Data PInvoke_AesGcm_SealedBox_GetTag(SwiftSelf sealedBox); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO9SealedBoxV5nonce10ciphertext3tagAgE5NonceV_xq_tKc10Foundation12DataProtocolRzAmNR_r0_lufC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_AesGcm_SealedBox_Init(SwiftIndirectResult result, void* nonce, void* ciphertext, void* tag, void* ciphertextMetadata, void* tagMetadata, void* ciphertextWitnessTable, void* tagWitnessTable, out SwiftError error); + + [DllImport(Path, EntryPoint = "$s9CryptoKit12SymmetricKeyV4sizeAcA0cD4SizeV_tcfC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_SymmetricKey_Init(SwiftIndirectResult result, SymmetricKeySize* symmetricKeySize); + + [DllImport(Path, EntryPoint = "$s9CryptoKit12SymmetricKeyV4dataACx_tc10Foundation15ContiguousBytesRzlufC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_SymmetricKey_Init2(SwiftIndirectResult result, void* data, void* metadata, void* witnessTable); + + [DllImport(Path, EntryPoint = "$s9CryptoKit12SymmetricKeyVMa")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void* PInvoke_SymmetricKey_GetMetadata(); + + [DllImport(Path, EntryPoint = "$s9CryptoKit16SymmetricKeySizeV8bitCountACSi_tcfC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_init(SwiftIndirectResult result, nint bitCount); + + [DllImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO4seal_5using5nonce14authenticatingAC9SealedBoxVx_AA12SymmetricKeyVAC5NonceVSgq_tK10Foundation12DataProtocolRzAoPR_r0_lFZ")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern ChaChaPoly.SealedBox PInvoke_ChaChaPoly_Seal(void* plaintext, void* key, void* nonce, void* aad, void* plaintextMetadata, void* aadMetadata, void* plaintextWitnessTable, void* aadWitnessTable, out SwiftError error); + + [DllImport(Path, EntryPoint = "$s9CryptoKit03ChaC4PolyO4open_5using14authenticating10Foundation4DataVAC9SealedBoxV_AA12SymmetricKeyVxtKAG0I8ProtocolRzlFZ")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern Data PInvoke_ChaChaPoly_Open(ChaChaPoly.SealedBox sealedBox, void* key, void* aad, void* metadata, void* witnessTable, out SwiftError error); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO4seal_5using5nonce14authenticatingAE9SealedBoxVx_AA12SymmetricKeyVAE5NonceVSgq_tK10Foundation12DataProtocolRzAqRR_r0_lFZ")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_AesGcm_Seal(SwiftIndirectResult result, void* plaintext, void* key, void* nonce, void* aad, void* plaintextMetadata, void* aadMetadata, void* plaintextWitnessTable, void* aadWitnessTable, out SwiftError error); + + [DllImport(Path, EntryPoint = "$s9CryptoKit3AESO3GCMO4open_5using14authenticating10Foundation4DataVAE9SealedBoxV_AA12SymmetricKeyVxtKAI0I8ProtocolRzlFZ")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern Data PInvoke_AesGcm_Open(void* sealedBox, void* key, void* aad, void* metadata, void* witnessTable, out SwiftError error); + } +} diff --git a/src/Swift.Bindings/tests/CryptoKit/CryptoKitTests.Source.cs b/src/Swift.Bindings/tests/CryptoKit/CryptoKitTests.Source.cs new file mode 100644 index 000000000000..e8ae308ecb65 --- /dev/null +++ b/src/Swift.Bindings/tests/CryptoKit/CryptoKitTests.Source.cs @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; +using System.Security.Cryptography; +using System.Diagnostics; +using AesGcm = Test.AesGcm; + +namespace Test +{ + public class MainClass + { + public static unsafe void AesGcmEncrypt( + ReadOnlySpan key, + ReadOnlySpan nonce, + ReadOnlySpan plaintext, + Span ciphertext, + Span tag, + ReadOnlySpan aad) + { + fixed (void* keyPtr = key) + fixed (void* noncePtr = nonce) + fixed (void* plaintextPtr = plaintext) + fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* tagPtr = tag) + fixed (void* aadPtr = aad) + { + Data symmetricKeyData = new Data(keyPtr, key.Length); + SymmetricKey symmetricKey = new SymmetricKey(symmetricKeyData); + + Data nonceData = new Data(noncePtr, nonce.Length); + AesGcm.Nonce aesGcmNonce = new AesGcm.Nonce(nonceData); + + Data plaintextData = new Data(plaintextPtr, plaintext.Length); + Data aadData = new Data(aadPtr, aad.Length); + + AesGcm.SealedBox sealedBox = AesGcm.seal( + plaintextData, + symmetricKey, + aesGcmNonce, + aadData, + out SwiftError error); + + if (error.Value != null) + { + sealedBox.Dispose(); + aesGcmNonce.Dispose(); + symmetricKey.Dispose(); + + throw new CryptographicException(); + } + + Data resultCiphertext = sealedBox.Ciphertext; + Data resultTag = sealedBox.Tag; + + resultCiphertext.CopyBytes(ciphertextPtr, resultCiphertext.Count); + resultTag.CopyBytes(tagPtr, resultTag.Count); + } + } + + public static unsafe void AesGcmDecrypt( + ReadOnlySpan key, + ReadOnlySpan nonce, + ReadOnlySpan ciphertext, + ReadOnlySpan tag, + Span plaintext, + ReadOnlySpan aad) + { + fixed (void* keyPtr = key) + fixed (void* noncePtr = nonce) + fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* tagPtr = tag) + fixed (byte* plaintextPtr = plaintext) + fixed (void* aadPtr = aad) + { + Data symmetricKeyData = new Data(keyPtr, key.Length); + SymmetricKey symmetricKey = new SymmetricKey(symmetricKeyData); + + Data nonceData = new Data(noncePtr, nonce.Length); + AesGcm.Nonce aesGcmNonce = new AesGcm.Nonce(nonceData); + + Data ciphertextData = new Data(ciphertextPtr, ciphertext.Length); + Data tagData = new Data(tagPtr, tag.Length); + Data aadData = new Data(aadPtr, aad.Length); + + AesGcm.SealedBox sealedBox = new AesGcm.SealedBox(aesGcmNonce, ciphertextData, tagData); + + Data data = AesGcm.open( + sealedBox, + symmetricKey, + aadData, + out SwiftError error); + + if (error.Value != null) + { + sealedBox.Dispose(); + aesGcmNonce.Dispose(); + symmetricKey.Dispose(); + + throw new CryptographicException(); + } + + data.CopyBytes(plaintextPtr, data.Count); + } + } + + public static unsafe void ChaCha20Poly1305Encrypt( + ReadOnlySpan key, + ReadOnlySpan nonce, + ReadOnlySpan plaintext, + Span ciphertext, + Span tag, + ReadOnlySpan aad) + { + fixed (void* keyPtr = key) + fixed (void* noncePtr = nonce) + fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* tagPtr = tag) + fixed (byte* plaintextPtr = plaintext) + fixed (void* aadPtr = aad) + { + Data symmetricKeyData = new Data(keyPtr, key.Length); + SymmetricKey symmetricKey = new SymmetricKey(symmetricKeyData); + + Data nonceData = new Data(noncePtr, nonce.Length); + ChaChaPoly.Nonce chaChaPolyNonce = new ChaChaPoly.Nonce(nonceData); + + Data plaintextData = new Data(plaintextPtr, plaintext.Length); + Data aadData = new Data(aadPtr, aad.Length); + + ChaChaPoly.SealedBox sealedBox = ChaChaPoly.seal( + plaintextData, + symmetricKey, + chaChaPolyNonce, + aadData, + out SwiftError error); + + if (error.Value != null) + { + chaChaPolyNonce.Dispose(); + symmetricKey.Dispose(); + + throw new CryptographicException(); + } + + Data resultCiphertext = sealedBox.Ciphertext; + Data resultTag = sealedBox.Tag; + + resultCiphertext.CopyBytes(ciphertextPtr, resultCiphertext.Count); + resultTag.CopyBytes(tagPtr, resultTag.Count); + } + } + + public static unsafe void ChaCha20Poly1305Decrypt( + ReadOnlySpan key, + ReadOnlySpan nonce, + ReadOnlySpan ciphertext, + ReadOnlySpan tag, + Span plaintext, + ReadOnlySpan aad) + { + fixed (void* keyPtr = key) + fixed (void* noncePtr = nonce) + fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* tagPtr = tag) + fixed (byte* plaintextPtr = plaintext) + fixed (void* aadPtr = aad) + { + Data symmetricKeyData = new Data(keyPtr, key.Length); + SymmetricKey symmetricKey = new SymmetricKey(symmetricKeyData); + + Data nonceData = new Data(noncePtr, nonce.Length); + ChaChaPoly.Nonce chaChaPolyNonce = new ChaChaPoly.Nonce(nonceData); + + Data ciphertextData = new Data(ciphertextPtr, ciphertext.Length); + Data tagData = new Data(tagPtr, tag.Length); + Data aadData = new Data(aadPtr, aad.Length); + + ChaChaPoly.SealedBox sealedBox = new ChaChaPoly.SealedBox(chaChaPolyNonce, ciphertextData, tagData); + + Data data = ChaChaPoly.open( + sealedBox, + symmetricKey, + aadData, + out SwiftError error); + + if (error.Value != null) + { + chaChaPolyNonce.Dispose(); + symmetricKey.Dispose(); + + CryptographicOperations.ZeroMemory(plaintext); + throw new CryptographicException(); + } + + data.CopyBytes(plaintextPtr, data.Count); + } + } + + private const int KeySizeInBytes = 256 / 8; + private const int NonceSizeInBytes = 96 / 8; + private const int TagSizeInBytes = 128 / 8; + public static int Main(string[] args) + { + const int dataLength = 35; + byte[] plaintext = Enumerable.Range(1, dataLength).Select(x => (byte)x).ToArray(); + byte[] ciphertext = new byte[dataLength]; + byte[] key = RandomNumberGenerator.GetBytes(KeySizeInBytes); + byte[] nonce = RandomNumberGenerator.GetBytes(NonceSizeInBytes); + byte[] tag = new byte[TagSizeInBytes]; + byte[] decrypted = new byte[dataLength]; + + ChaCha20Poly1305Encrypt(key, nonce, plaintext, ciphertext, tag, default); + ChaCha20Poly1305Decrypt(key, nonce, ciphertext, tag, decrypted, default); + + if (!plaintext.SequenceEqual(decrypted)) + { + Console.WriteLine("ChaChaPoly decryption failed"); + return 1; + } + else + { + Console.WriteLine("ChaChaPoly decryption succeeded"); + } + + decrypted = new byte[dataLength]; + AesGcmEncrypt(key, nonce, plaintext, ciphertext, tag, default); + + AesGcmDecrypt(key, nonce, ciphertext, tag, decrypted, default); + + if (!plaintext.SequenceEqual(decrypted)) + { + Console.WriteLine("AES-GCM decryption failed"); + return 1; + } + else + { + Console.WriteLine("AES-GCM decryption succeeded"); + } + + return 0; + } + } +} diff --git a/src/Swift.Bindings/tests/CryptoKit/CryptoKitTests.cs b/src/Swift.Bindings/tests/CryptoKit/CryptoKitTests.cs new file mode 100644 index 000000000000..48dfcf164e47 --- /dev/null +++ b/src/Swift.Bindings/tests/CryptoKit/CryptoKitTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Xunit; + +namespace BindingsGeneration.Tests +{ + public class CryptoKitTests: IClassFixture + { + private readonly TestFixture _fixture; + private static string _assemblyPath; + + public CryptoKitTests(TestFixture fixture) + { + _fixture = fixture; + } + + public class TestFixture + { + static TestFixture() + { + InitializeResources(); + } + + private static void InitializeResources() + { + _assemblyPath = TestsHelper.Compile( + new string [] { "CryptoKit/*.cs" }, + new string [] { }, + new string [] { "System.Security.Cryptography" }); + } + } + + [Fact] + public static void TestUnsafePointerCryptoKit() + { + int result = (int)TestsHelper.Execute(_assemblyPath, "Test.MainClass", "Main", new object [] { new string[0] }); + Assert.Equal(0, result); + } + } +} diff --git a/src/Swift.Bindings/tests/CryptoKit/Foundation.Source.cs b/src/Swift.Bindings/tests/CryptoKit/Foundation.Source.cs new file mode 100644 index 000000000000..b8048aee29a2 --- /dev/null +++ b/src/Swift.Bindings/tests/CryptoKit/Foundation.Source.cs @@ -0,0 +1,274 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Reflection; + +namespace Test +{ + /// + /// Represents a Swift type in C#. + /// + public unsafe interface ISwiftObject + { + public static abstract void* Metadata { get; } + } + + // + // Represents Swift UnsafePointer in C#. + // + public readonly unsafe struct UnsafePointer where T : unmanaged + { + private readonly T* _pointee; + public UnsafePointer(T* pointee) + { + this._pointee = pointee; + } + + public T* Pointee => _pointee; + + public static implicit operator T*(UnsafePointer pointer) => pointer.Pointee; + + public static implicit operator UnsafePointer(T* pointee) => new(pointee); + } + + // + // Represents Swift UnsafeMutablePointer in C#. + // + public readonly unsafe struct UnsafeMutablePointer where T : unmanaged + { + private readonly T* _pointee; + public UnsafeMutablePointer(T* pointee) + { + _pointee = pointee; + } + + public T* Pointee => _pointee; + + public static implicit operator T*(UnsafeMutablePointer pointer) => pointer.Pointee; + + public static implicit operator UnsafeMutablePointer(T* pointee) => new(pointee); + } + + // + // Represents Swift UnsafeRawPointer in C#. + // + public readonly unsafe struct UnsafeRawPointer + { + private readonly void* _pointee; + public UnsafeRawPointer(void* pointee) + { + _pointee = pointee; + } + + public void* Pointee => _pointee; + + public static implicit operator void*(UnsafeRawPointer pointer) => pointer.Pointee; + + public static implicit operator UnsafeRawPointer(void* pointee) => new(pointee); + } + + // + // Represents Swift UnsafeMutableRawPointer in C#. + // + public readonly unsafe struct UnsafeMutableRawPointer + { + private readonly void* _pointee; + public UnsafeMutableRawPointer(void* pointee) + { + _pointee = pointee; + } + + public void* Pointee => _pointee; + + public static implicit operator void*(UnsafeMutableRawPointer pointer) => pointer.Pointee; + + public static implicit operator UnsafeMutableRawPointer(void* pointee) => new(pointee); + } + + // + // Represents Swift UnsafeBufferPointer in C#. + // + public readonly unsafe struct UnsafeBufferPointer where T : unmanaged + { + private readonly T* _baseAddress; + private readonly nint _count; + public UnsafeBufferPointer(T* baseAddress, nint count) + { + _baseAddress = baseAddress; + _count = count; + } + + public T* BaseAddress => _baseAddress; + public nint Count => _count; + } + + // + // Represents Swift UnsafeMutableBufferPointer in C#. + // + public readonly unsafe struct UnsafeMutableBufferPointer where T : unmanaged + { + private readonly T* _baseAddress; + private readonly nint _count; + public UnsafeMutableBufferPointer(T* baseAddress, nint count) + { + _baseAddress = baseAddress; + _count = count; + } + + public T* BaseAddress => _baseAddress; + public nint Count => _count; + } + + // + // Represents Swift Foundation.Data in C#. + // + [StructLayout(LayoutKind.Sequential, Size = 16)] + [InlineArray(16)] + public unsafe struct Data : ISwiftObject + { + private byte _payload; + + public unsafe Data(UnsafeRawPointer pointer, nint count) + { + this = Foundation.PInvoke_Data_InitWithBytes(pointer, count); + } + + public byte Payload => _payload; + + public readonly nint Count => Foundation.PInvoke_Data_GetCount(this); + + public unsafe void CopyBytes(UnsafeMutablePointer buffer, nint count) + { + Foundation.PInvoke_Data_CopyBytes(buffer, count, this); + } + + public static void* Metadata => Foundation.PInvoke_Data_GetMetadata(); + } + + /// + /// Represents Swift Foundation.DataProtocol in C#. + /// + public unsafe interface IDataProtocol + { + public static void* GetConformanceDescriptor => Runtime.GetConformanceDescriptor("$s10Foundation4DataVAA0B8ProtocolAAMc"); + } + + /// + /// Represents Swift Foundation.ContiguousBytes in C#. + /// + public unsafe interface IContiguousBytes + { + public static void* GetConformanceDescriptor => Runtime.GetConformanceDescriptor("$s10Foundation4DataVAA15ContiguousBytesAAMc"); + } + + /// + /// Swift Foundation PInvoke methods in C#. + /// + public static class Foundation + { + public const string Path = "/System/Library/Frameworks/Foundation.framework/Foundation"; + + [DllImport(Path, EntryPoint = "$s10Foundation4DataV5bytes5countACSV_SitcfC")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern Data PInvoke_Data_InitWithBytes(UnsafeRawPointer pointer, nint count); + + [DllImport(Path, EntryPoint = "$s10Foundation4DataV5countSivg")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern nint PInvoke_Data_GetCount(Data data); + + [DllImport(Path, EntryPoint = "$s10Foundation4DataV9copyBytes2to5countySpys5UInt8VG_SitF")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void PInvoke_Data_CopyBytes(UnsafeMutablePointer buffer, nint count, Data data); + + [DllImport(Path, EntryPoint = "swift_getWitnessTable")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void* PInvoke_Swift_GetWitnessTable(void* conformanceDescriptor, void* typeMetadata, void* instantiationArgs); + + [DllImport(Path, EntryPoint = "$s10Foundation4DataVMa")] + [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] + public static unsafe extern void* PInvoke_Data_GetMetadata(); + } + + /// + /// Swift runtime helper methods in C#. + /// + public static class Runtime + { + /// + /// https://github.com/apple/swift/blob/main/include/swift/ABI/MetadataValues.h#L117 + /// + [Flags] + public enum ValueWitnessFlags + { + AlignmentMask = 0x0000FFFF, + IsNonPOD = 0x00010000, + IsNonInline = 0x00020000, + HasSpareBits = 0x00080000, + IsNonBitwiseTakable = 0x00100000, + HasEnumWitnesses = 0x00200000, + Incomplete = 0x00400000, + } + + /// + /// See https://github.com/apple/swift/blob/main/include/swift/ABI/ValueWitness.def + /// + [StructLayout (LayoutKind.Sequential)] + public ref struct ValueWitnessTable + { + public IntPtr InitializeBufferWithCopyOfBuffer; + public IntPtr Destroy; + public IntPtr InitWithCopy; + public IntPtr AssignWithCopy; + public IntPtr InitWithTake; + public IntPtr AssignWithTake; + public IntPtr GetEnumTagSinglePayload; + public IntPtr StoreEnumTagSinglePayload; + private IntPtr _Size; + private IntPtr _Stride; + public ValueWitnessFlags Flags; + public uint ExtraInhabitantCount; + public int Size => _Size.ToInt32(); + public int Stride => _Stride.ToInt32(); + public int Alignment => (int)((Flags & ValueWitnessFlags.AlignmentMask) + 1); + public bool IsNonPOD => Flags.HasFlag (ValueWitnessFlags.IsNonPOD); + public bool IsNonBitwiseTakable => Flags.HasFlag (ValueWitnessFlags.IsNonBitwiseTakable); + public bool HasExtraInhabitants => ExtraInhabitantCount != 0; + } + + public static unsafe void* GetMetadata(T type) where T: ISwiftObject + { + return T.Metadata; + } + + public static unsafe void* GetValueWitnessTable(void* metadata) + { + void* valueWitnessTable = (void*)Marshal.ReadIntPtr((IntPtr)metadata, -IntPtr.Size); + return valueWitnessTable; + } + + public static unsafe void* GetConformanceDescriptor(string symbol) + { + IntPtr handle = IntPtr.Zero; + try + { + handle = NativeLibrary.Load(Foundation.Path); + void* conformanceDescriptor = NativeLibrary.GetExport(handle, symbol).ToPointer(); + return conformanceDescriptor; + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to get conformance descriptor for symbol: {symbol}", ex); + } + finally + { + if (handle != IntPtr.Zero) + { + NativeLibrary.Free(handle); + } + } + } + } +} diff --git a/src/Swift.Bindings/tests/TestsHelper.cs b/src/Swift.Bindings/tests/TestsHelper.cs index 2896a5b00e27..50899b4781a1 100644 --- a/src/Swift.Bindings/tests/TestsHelper.cs +++ b/src/Swift.Bindings/tests/TestsHelper.cs @@ -26,6 +26,7 @@ public static string Compile(string[] filePaths, string[] sourceCodes, string[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Console).Assembly.Location), + MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location), MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location), MetadataReference.CreateFromFile(Assembly.Load("System.Runtime.InteropServices").Location), };