From 43dbffaf4779f4111b2adebd93024ea6bfac73d9 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 4 Jun 2024 10:03:47 -0700 Subject: [PATCH] Convert our CryptoKit bindings to use Swift structs at the boundary (#102583) * Try converting our CryptoKit bindings to use more Swift types. Also refactor our cryptokit bindings to reduce duplication. * Fix build on macos * Remove target-typed new and other style changes * Don't pass down null with UnsafeBufferPointer * Call copyBytes with a pointer because everything else seems to fail some assert for some reason I can't figure out. * Make copies and use copyBytes(to: buffer) again. * Fix generic value types condition * Put SwiftError parameter first to try working around Mono register allocation bug * Comma --- .../OSX/Swift.Runtime/UnsafeBufferPointer.cs | 39 +++ .../Interop.Aead.cs | 216 +++++++------ .../src/System.Security.Cryptography.csproj | 2 + src/mono/mono/metadata/marshal.c | 8 +- .../CMakeLists.txt | 2 +- .../entrypoints.c | 1 + .../pal_swiftbindings.h | 1 + .../pal_swiftbindings.swift | 286 +++++++++--------- 8 files changed, 300 insertions(+), 255 deletions(-) create mode 100644 src/libraries/Common/src/Interop/OSX/Swift.Runtime/UnsafeBufferPointer.cs diff --git a/src/libraries/Common/src/Interop/OSX/Swift.Runtime/UnsafeBufferPointer.cs b/src/libraries/Common/src/Interop/OSX/Swift.Runtime/UnsafeBufferPointer.cs new file mode 100644 index 0000000000000..ec54fb705a836 --- /dev/null +++ b/src/libraries/Common/src/Interop/OSX/Swift.Runtime/UnsafeBufferPointer.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Swift.Runtime +{ + // + // Represents Swift UnsafeBufferPointer in C#. + // + internal 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#. + // + internal 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; + } +} diff --git a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Aead.cs b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Aead.cs index 2f405e0bfd0da..3e7583694b1ca 100644 --- a/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Aead.cs +++ b/src/libraries/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Aead.cs @@ -5,8 +5,10 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; using System.Security.Cryptography; using System.Security.Cryptography.Apple; +using Swift.Runtime; #pragma warning disable CS3016 // Arrays as attribute arguments are not CLS Compliant @@ -14,6 +16,17 @@ internal static partial class Interop { internal static partial class AppleCrypto { + private static byte NullSentinel; + + // CryptoKit doesn't do well with a null pointer for the buffer data, + // so provide a sentinel pointer instead. + private static ref readonly byte GetSwiftRef(ReadOnlySpan b) + { + return ref (b.Length == 0 + ? ref NullSentinel + : ref MemoryMarshal.GetReference(b)); + } + internal static unsafe void ChaCha20Poly1305Encrypt( ReadOnlySpan key, ReadOnlySpan nonce, @@ -24,23 +37,22 @@ internal static unsafe void ChaCha20Poly1305Encrypt( { fixed (byte* keyPtr = key) fixed (byte* noncePtr = nonce) - fixed (byte* plaintextPtr = plaintext) - fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* plaintextPtr = &GetSwiftRef(plaintext)) + fixed (byte* ciphertextPtr = &GetSwiftRef(ciphertext)) fixed (byte* tagPtr = tag) - fixed (byte* aadPtr = aad) + fixed (byte* aadPtr = &GetSwiftRef(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) + AppleCryptoNative_ChaCha20Poly1305Encrypt( + out SwiftError error, + new UnsafeBufferPointer(keyPtr, key.Length), + new UnsafeBufferPointer(noncePtr, nonce.Length), + new UnsafeBufferPointer(plaintextPtr, plaintext.Length), + new UnsafeMutableBufferPointer(ciphertextPtr, ciphertext.Length), + new UnsafeMutableBufferPointer(tagPtr, tag.Length), + new UnsafeBufferPointer(aadPtr, aad.Length)); + + if (error.Value != null) { - Debug.Assert(result == 0); CryptographicOperations.ZeroMemory(ciphertext); CryptographicOperations.ZeroMemory(tag); throw new CryptographicException(); @@ -58,32 +70,30 @@ internal static unsafe void ChaCha20Poly1305Decrypt( { fixed (byte* keyPtr = key) fixed (byte* noncePtr = nonce) - fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* ciphertextPtr = &GetSwiftRef(ciphertext)) fixed (byte* tagPtr = tag) - fixed (byte* plaintextPtr = plaintext) - fixed (byte* aadPtr = aad) + fixed (byte* plaintextPtr = &GetSwiftRef(plaintext)) + fixed (byte* aadPtr = &GetSwiftRef(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) + AppleCryptoNative_ChaCha20Poly1305Decrypt( + out SwiftError error, + new UnsafeBufferPointer(keyPtr, key.Length), + new UnsafeBufferPointer(noncePtr, nonce.Length), + new UnsafeBufferPointer(ciphertextPtr, ciphertext.Length), + new UnsafeBufferPointer(tagPtr, tag.Length), + new UnsafeMutableBufferPointer(plaintextPtr, plaintext.Length), + new UnsafeBufferPointer(aadPtr, aad.Length)); + + if (error.Value != null) { CryptographicOperations.ZeroMemory(plaintext); - if (result == AuthTagMismatch) + if (AppleCryptoNative_IsAuthenticationFailure(error.Value)) { throw new AuthenticationTagMismatchException(); } else { - Debug.Assert(result == 0); throw new CryptographicException(); } } @@ -100,23 +110,22 @@ internal static unsafe void AesGcmEncrypt( { fixed (byte* keyPtr = key) fixed (byte* noncePtr = nonce) - fixed (byte* plaintextPtr = plaintext) - fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* plaintextPtr = &GetSwiftRef(plaintext)) + fixed (byte* ciphertextPtr = &GetSwiftRef(ciphertext)) fixed (byte* tagPtr = tag) - fixed (byte* aadPtr = aad) + fixed (byte* aadPtr = &GetSwiftRef(aad)) { - const int Success = 1; - int result = AppleCryptoNative_AesGcmEncrypt( - keyPtr, key.Length, - noncePtr, nonce.Length, - plaintextPtr, plaintext.Length, - ciphertextPtr, ciphertext.Length, - tagPtr, tag.Length, - aadPtr, aad.Length); - - if (result != Success) + AppleCryptoNative_AesGcmEncrypt( + out SwiftError error, + new UnsafeBufferPointer(keyPtr, key.Length), + new UnsafeBufferPointer(noncePtr, nonce.Length), + new UnsafeBufferPointer(plaintextPtr, plaintext.Length), + new UnsafeMutableBufferPointer(ciphertextPtr, ciphertext.Length), + new UnsafeMutableBufferPointer(tagPtr, tag.Length), + new UnsafeBufferPointer(aadPtr, aad.Length)); + + if (error.Value != null) { - Debug.Assert(result == 0); CryptographicOperations.ZeroMemory(ciphertext); CryptographicOperations.ZeroMemory(tag); throw new CryptographicException(); @@ -134,32 +143,30 @@ internal static unsafe void AesGcmDecrypt( { fixed (byte* keyPtr = key) fixed (byte* noncePtr = nonce) - fixed (byte* ciphertextPtr = ciphertext) + fixed (byte* ciphertextPtr = &GetSwiftRef(ciphertext)) fixed (byte* tagPtr = tag) - fixed (byte* plaintextPtr = plaintext) - fixed (byte* aadPtr = aad) + fixed (byte* plaintextPtr = &GetSwiftRef(plaintext)) + fixed (byte* aadPtr = &GetSwiftRef(aad)) { - const int Success = 1; - const int AuthTagMismatch = -1; - int result = AppleCryptoNative_AesGcmDecrypt( - keyPtr, key.Length, - noncePtr, nonce.Length, - ciphertextPtr, ciphertext.Length, - tagPtr, tag.Length, - plaintextPtr, plaintext.Length, - aadPtr, aad.Length); - - if (result != Success) + AppleCryptoNative_AesGcmDecrypt( + out SwiftError error, + new UnsafeBufferPointer(keyPtr, key.Length), + new UnsafeBufferPointer(noncePtr, nonce.Length), + new UnsafeBufferPointer(ciphertextPtr, ciphertext.Length), + new UnsafeBufferPointer(tagPtr, tag.Length), + new UnsafeMutableBufferPointer(plaintextPtr, plaintext.Length), + new UnsafeBufferPointer(aadPtr, aad.Length)); + + if (error.Value != null) { CryptographicOperations.ZeroMemory(plaintext); - if (result == AuthTagMismatch) + if (AppleCryptoNative_IsAuthenticationFailure(error.Value)) { throw new AuthenticationTagMismatchException(); } else { - Debug.Assert(result == 0); throw new CryptographicException(); } } @@ -168,66 +175,51 @@ internal static unsafe void AesGcmDecrypt( [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - 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); + private static unsafe partial void AppleCryptoNative_ChaCha20Poly1305Encrypt( + out SwiftError error, + UnsafeBufferPointer key, + UnsafeBufferPointer nonce, + UnsafeBufferPointer plaintext, + UnsafeMutableBufferPointer ciphertext, + UnsafeMutableBufferPointer tag, + UnsafeBufferPointer aad); [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - 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); + private static unsafe partial void AppleCryptoNative_ChaCha20Poly1305Decrypt( + out SwiftError error, + UnsafeBufferPointer key, + UnsafeBufferPointer nonce, + UnsafeBufferPointer ciphertext, + UnsafeBufferPointer tag, + UnsafeMutableBufferPointer plaintext, + UnsafeBufferPointer aad); [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial int AppleCryptoNative_AesGcmEncrypt( - byte* keyPtr, - int keyLength, - byte* noncePtr, - int nonceLength, - byte* plaintextPtr, - int plaintextLength, - byte* ciphertextPtr, - int ciphertextLength, - byte* tagPtr, - int tagLength, - byte* aadPtr, - int aadLength); + private static unsafe partial void AppleCryptoNative_AesGcmEncrypt( + out SwiftError error, + UnsafeBufferPointer key, + UnsafeBufferPointer nonce, + UnsafeBufferPointer plaintext, + UnsafeMutableBufferPointer ciphertext, + UnsafeMutableBufferPointer tag, + UnsafeBufferPointer aad); [LibraryImport(Libraries.AppleCryptoNative)] [UnmanagedCallConv(CallConvs = [ typeof(CallConvSwift) ])] - private static unsafe partial int AppleCryptoNative_AesGcmDecrypt( - byte* keyPtr, - int keyLength, - byte* noncePtr, - int nonceLength, - byte* ciphertextPtr, - int ciphertextLength, - byte* tagPtr, - int tagLength, - byte* plaintextPtr, - int plaintextLength, - byte* aadPtr, - int aadLength); + private static unsafe partial void AppleCryptoNative_AesGcmDecrypt( + out SwiftError error, + UnsafeBufferPointer key, + UnsafeBufferPointer nonce, + UnsafeBufferPointer ciphertext, + UnsafeBufferPointer tag, + UnsafeMutableBufferPointer plaintext, + UnsafeBufferPointer aad); + + [LibraryImport(Libraries.AppleCryptoNative)] + [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvSwift) })] + [return: MarshalAs(UnmanagedType.U1)] + private static unsafe partial bool AppleCryptoNative_IsAuthenticationFailure(void* error); } } diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index ae5affcbb971a..4a7bd04e14eec 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -1210,6 +1210,8 @@ Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EvpPkey.Rsa.cs" /> + type == MONO_TYPE_GENERICINST && !mono_type_generic_inst_is_valuetype (type)) { - lowering.by_reference = TRUE; - return lowering; + if (type->type == MONO_TYPE_GENERICINST) { + if (!mono_type_generic_inst_is_valuetype (type)) { + lowering.by_reference = TRUE; + return lowering; + } } else if (type->type != MONO_TYPE_VALUETYPE && !mono_type_is_primitive(type)) { lowering.by_reference = TRUE; return lowering; diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/CMakeLists.txt b/src/native/libs/System.Security.Cryptography.Native.Apple/CMakeLists.txt index b847f5c3cd68b..a50ade7e86317 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/CMakeLists.txt +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/CMakeLists.txt @@ -51,7 +51,7 @@ 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 + COMMAND xcrun swiftc -emit-object -static -parse-as-library -enable-library-evolution -g -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" ) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c index 099fb34394711..41395f63b201f 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -48,6 +48,7 @@ static const Entry s_cryptoAppleNative[] = DllImportEntry(AppleCryptoNative_HmacFinal) DllImportEntry(AppleCryptoNative_HmacCurrent) DllImportEntry(AppleCryptoNative_HmacOneShot) + DllImportEntry(AppleCryptoNative_IsAuthenticationFailure) DllImportEntry(AppleCryptoNative_SecKeychainItemCopyKeychain) DllImportEntry(AppleCryptoNative_SecKeychainCopyDefault) DllImportEntry(AppleCryptoNative_SecKeychainCreate) diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h index fd90fd0ad8216..058c94869b57d 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.h @@ -10,3 +10,4 @@ EXTERN_C void* AppleCryptoNative_ChaCha20Poly1305Encrypt; EXTERN_C void* AppleCryptoNative_ChaCha20Poly1305Decrypt; EXTERN_C void* AppleCryptoNative_AesGcmEncrypt; EXTERN_C void* AppleCryptoNative_AesGcmDecrypt; +EXTERN_C void* AppleCryptoNative_IsAuthenticationFailure; diff --git a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift index 2a50deefb52bc..92d461886730b 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/pal_swiftbindings.swift @@ -4,166 +4,174 @@ import CryptoKit import Foundation -@_silgen_name("AppleCryptoNative_ChaCha20Poly1305Encrypt") -public func AppleCryptoNative_ChaCha20Poly1305Encrypt( - keyPtr: UnsafeMutableRawPointer, - keyLength: Int32, - noncePtr: UnsafeMutableRawPointer, - nonceLength: Int32, - plaintextPtr: UnsafeMutableRawPointer, - plaintextLength: Int32, - ciphertextBuffer: UnsafeMutablePointer, - ciphertextBufferLength: Int32, - tagBuffer: UnsafeMutablePointer, - tagBufferLength: Int32, - aadPtr: UnsafeMutableRawPointer, - aadLength: Int32 - ) -> Int32 { - let nonceData = Data(bytesNoCopy: noncePtr, count: Int(nonceLength), deallocator: Data.Deallocator.none) - let key = Data(bytesNoCopy: keyPtr, count: Int(keyLength), deallocator: Data.Deallocator.none) - let plaintext = Data(bytesNoCopy: plaintextPtr, count: Int(plaintextLength), deallocator: Data.Deallocator.none) - let aad = Data(bytesNoCopy: aadPtr, count: Int(aadLength), deallocator: Data.Deallocator.none) - let symmetricKey = SymmetricKey(data: key) +protocol NonceProtocol { + init(data: D) throws where D : DataProtocol +} - guard let nonce = try? ChaChaPoly.Nonce(data: nonceData) else { - return 0 - } +protocol SealedBoxProtocol { + associatedtype Nonce : NonceProtocol - guard let result = try? ChaChaPoly.seal(plaintext, using: symmetricKey, nonce: nonce, authenticating: aad) else { - return 0 - } + var ciphertext: Data { get } + var tag: Data { get } - assert(ciphertextBufferLength >= result.ciphertext.count) - assert(tagBufferLength >= result.tag.count) + init( + nonce: Nonce, + ciphertext: C, + tag: T + ) throws where C : DataProtocol, T : DataProtocol +} - result.ciphertext.copyBytes(to: ciphertextBuffer, count: result.ciphertext.count) - result.tag.copyBytes(to: tagBuffer, count: result.tag.count) - return 1 - } +protocol AEADSymmetricAlgorithm { + associatedtype SealedBox : SealedBoxProtocol + + static func seal(_ plaintext: Plaintext, using key: SymmetricKey, nonce: SealedBox.Nonce?) throws -> SealedBox where Plaintext: DataProtocol + static func seal<Plaintext, AuthenticatedData>(_ plaintext: Plaintext, using key: SymmetricKey, nonce: SealedBox.Nonce?, authenticating additionalData: AuthenticatedData) throws -> SealedBox where Plaintext: DataProtocol, AuthenticatedData: DataProtocol + static func open<AuthenticatedData>(_ sealedBox: SealedBox, using key: SymmetricKey, authenticating additionalData: AuthenticatedData) throws -> Data where AuthenticatedData: DataProtocol + static func open(_ sealedBox: SealedBox, using key: SymmetricKey) throws -> Data +} + +extension AES.GCM.Nonce: NonceProtocol {} +extension AES.GCM.SealedBox: SealedBoxProtocol { + typealias Nonce = AES.GCM.Nonce +} +extension AES.GCM: AEADSymmetricAlgorithm {} + +extension ChaChaPoly.Nonce: NonceProtocol {} +extension ChaChaPoly.SealedBox: SealedBoxProtocol { + typealias Nonce = ChaChaPoly.Nonce +} +extension ChaChaPoly: AEADSymmetricAlgorithm {} + +func encrypt<Algorithm>( + _ algorithm: Algorithm.Type, + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeMutableBufferPointer<UInt8>, + tag: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8>) throws where Algorithm: AEADSymmetricAlgorithm { -@_silgen_name("AppleCryptoNative_ChaCha20Poly1305Decrypt") -public func AppleCryptoNative_ChaCha20Poly1305Decrypt( - keyPtr: UnsafeMutableRawPointer, - keyLength: Int32, - noncePtr: UnsafeMutableRawPointer, - nonceLength: Int32, - ciphertextPtr: UnsafeMutableRawPointer, - ciphertextLength: Int32, - tagPtr: UnsafeMutableRawPointer, - tagLength: Int32, - plaintextBuffer: UnsafeMutablePointer<UInt8>, - plaintextBufferLength: Int32, - aadPtr: UnsafeMutableRawPointer, - aadLength: Int32 -) -> Int32 { - let nonceData = Data(bytesNoCopy: noncePtr, count: Int(nonceLength), deallocator: Data.Deallocator.none) - let key = Data(bytesNoCopy: keyPtr, count: Int(keyLength), deallocator: Data.Deallocator.none) - let ciphertext = Data(bytesNoCopy: ciphertextPtr, count: Int(ciphertextLength), deallocator: Data.Deallocator.none) - let aad = Data(bytesNoCopy: aadPtr, count: Int(aadLength), deallocator: Data.Deallocator.none) - let tag = Data(bytesNoCopy: tagPtr, count: Int(tagLength), deallocator: Data.Deallocator.none) let symmetricKey = SymmetricKey(data: key) - guard let nonce = try? ChaChaPoly.Nonce(data: nonceData) else { - return 0 - } + let nonce = try Algorithm.SealedBox.Nonce(data: nonceData) - guard let sealedBox = try? ChaChaPoly.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag) else { - return 0 - } + let result = try Algorithm.seal(plaintext, using: symmetricKey, nonce: nonce, authenticating: aad) - do { - let result = try ChaChaPoly.open(sealedBox, using: symmetricKey, authenticating: aad) + // Copy results out of the SealedBox as the Data objects returned here are sometimes slices, + // which don't have a correct implementation of copyBytes. + // See https://github.com/apple/swift-foundation/issues/638 for more information. + let resultCiphertext = Data(result.ciphertext) + let resultTag = Data(result.tag) - assert(plaintextBufferLength >= result.count) - result.copyBytes(to: plaintextBuffer, count: result.count) - return 1 - } - catch CryptoKitError.authenticationFailure { - return -1 - } - catch { - return 0 - } + _ = resultCiphertext.copyBytes(to: cipherText) + _ = resultTag.copyBytes(to: tag) } -@_silgen_name("AppleCryptoNative_AesGcmEncrypt") -public func AppleCryptoNative_AesGcmEncrypt( - keyPtr: UnsafeMutableRawPointer, - keyLength: Int32, - noncePtr: UnsafeMutableRawPointer, - nonceLength: Int32, - plaintextPtr: UnsafeMutableRawPointer, - plaintextLength: Int32, - ciphertextBuffer: UnsafeMutablePointer<UInt8>, - ciphertextBufferLength: Int32, - tagBuffer: UnsafeMutablePointer<UInt8>, - tagBufferLength: Int32, - aadPtr: UnsafeMutableRawPointer, - aadLength: Int32 - ) -> Int32 { - let nonceData = Data(bytesNoCopy: noncePtr, count: Int(nonceLength), deallocator: Data.Deallocator.none) - let key = Data(bytesNoCopy: keyPtr, count: Int(keyLength), deallocator: Data.Deallocator.none) - let plaintext = Data(bytesNoCopy: plaintextPtr, count: Int(plaintextLength), deallocator: Data.Deallocator.none) - let aad = Data(bytesNoCopy: aadPtr, count: Int(aadLength), deallocator: Data.Deallocator.none) +func decrypt<Algorithm>( + _ algorithm: Algorithm.Type, + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeBufferPointer<UInt8>, + tag: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8>) throws where Algorithm: AEADSymmetricAlgorithm { + let symmetricKey = SymmetricKey(data: key) - guard let nonce = try? AES.GCM.Nonce(data: nonceData) else { - return 0 - } + let nonce = try Algorithm.SealedBox.Nonce(data: nonceData) - guard let result = try? AES.GCM.seal(plaintext, using: symmetricKey, nonce: nonce, authenticating: aad) else { - return 0 - } + let sealedBox = try Algorithm.SealedBox(nonce: nonce, ciphertext: cipherText, tag: tag) - assert(ciphertextBufferLength >= result.ciphertext.count) - assert(tagBufferLength >= result.tag.count) + let result = try Algorithm.open(sealedBox, using: symmetricKey, authenticating: aad) - result.ciphertext.copyBytes(to: ciphertextBuffer, count: result.ciphertext.count) - result.tag.copyBytes(to: tagBuffer, count: result.tag.count) - return 1 - } + _ = result.copyBytes(to: plaintext) +} -@_silgen_name("AppleCryptoNative_AesGcmDecrypt") -public func AppleCryptoNative_AesGcmDecrypt( - keyPtr: UnsafeMutableRawPointer, - keyLength: Int32, - noncePtr: UnsafeMutableRawPointer, - nonceLength: Int32, - ciphertextPtr: UnsafeMutableRawPointer, - ciphertextLength: Int32, - tagPtr: UnsafeMutableRawPointer, - tagLength: Int32, - plaintextBuffer: UnsafeMutablePointer<UInt8>, - plaintextBufferLength: Int32, - aadPtr: UnsafeMutableRawPointer, - aadLength: Int32 -) -> Int32 { - let nonceData = Data(bytesNoCopy: noncePtr, count: Int(nonceLength), deallocator: Data.Deallocator.none) - let key = Data(bytesNoCopy: keyPtr, count: Int(keyLength), deallocator: Data.Deallocator.none) - let ciphertext = Data(bytesNoCopy: ciphertextPtr, count: Int(ciphertextLength), deallocator: Data.Deallocator.none) - let aad = Data(bytesNoCopy: aadPtr, count: Int(aadLength), deallocator: Data.Deallocator.none) - let tag = Data(bytesNoCopy: tagPtr, count: Int(tagLength), deallocator: Data.Deallocator.none) - let symmetricKey = SymmetricKey(data: key) +@_silgen_name("AppleCryptoNative_ChaCha20Poly1305Encrypt") +public func AppleCryptoNative_ChaCha20Poly1305Encrypt( + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeMutableBufferPointer<UInt8>, + tag: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8> +) throws { + return try encrypt( + ChaChaPoly.self, + key: key, + nonceData: nonceData, + plaintext: plaintext, + cipherText: cipherText, + tag: tag, + aad: aad) + } - guard let nonce = try? AES.GCM.Nonce(data: nonceData) else { - return 0 - } +@_silgen_name("AppleCryptoNative_ChaCha20Poly1305Decrypt") +public func AppleCryptoNative_ChaCha20Poly1305Decrypt( + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeBufferPointer<UInt8>, + tag: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8> +) throws { + return try decrypt( + ChaChaPoly.self, + key: key, + nonceData: nonceData, + cipherText: cipherText, + tag: tag, + plaintext: plaintext, + aad: aad); +} - guard let sealedBox = try? AES.GCM.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag) else { - return 0 - } +@_silgen_name("AppleCryptoNative_AesGcmEncrypt") +public func AppleCryptoNative_AesGcmEncrypt( + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeMutableBufferPointer<UInt8>, + tag: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8> +) throws { + return try encrypt( + AES.GCM.self, + key: key, + nonceData: nonceData, + plaintext: plaintext, + cipherText: cipherText, + tag: tag, + aad: aad) + } - do { - let result = try AES.GCM.open(sealedBox, using: symmetricKey, authenticating: aad) +@_silgen_name("AppleCryptoNative_AesGcmDecrypt") +public func AppleCryptoNative_AesGcmDecrypt( + key: UnsafeBufferPointer<UInt8>, + nonceData: UnsafeBufferPointer<UInt8>, + cipherText: UnsafeBufferPointer<UInt8>, + tag: UnsafeBufferPointer<UInt8>, + plaintext: UnsafeMutableBufferPointer<UInt8>, + aad: UnsafeBufferPointer<UInt8> +) throws { + return try decrypt( + AES.GCM.self, + key: key, + nonceData: nonceData, + cipherText: cipherText, + tag: tag, + plaintext: plaintext, + aad: aad); +} - assert(plaintextBufferLength >= result.count) - result.copyBytes(to: plaintextBuffer, count: result.count) - return 1 - } - catch CryptoKitError.authenticationFailure { - return -1 - } - catch { - return 0 +@_silgen_name("AppleCryptoNative_IsAuthenticationFailure") +public func AppleCryptoNative_IsAuthenticationFailure(error: Error) -> Bool { + if let error = error as? CryptoKitError { + switch error { + case .authenticationFailure: + return true + default: + return false + } } + return false }