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 62102ac0a5939..edadae0ea60ec 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 @@ -87,6 +87,82 @@ internal static unsafe void ChaCha20Poly1305Decrypt( } } + internal static unsafe void AesGcmEncrypt( + ReadOnlySpan key, + ReadOnlySpan nonce, + ReadOnlySpan plaintext, + Span ciphertext, + Span tag, + ReadOnlySpan 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_AesGcmEncrypt( + 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 AesGcmDecrypt( + ReadOnlySpan key, + ReadOnlySpan nonce, + ReadOnlySpan ciphertext, + ReadOnlySpan tag, + Span plaintext, + ReadOnlySpan 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_AesGcmDecrypt( + 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, @@ -116,5 +192,35 @@ private static unsafe partial int AppleCryptoNative_ChaCha20Poly1305Decrypt( int plaintextLength, byte* aadPtr, int aadLength); + + [LibraryImport(Libraries.AppleCryptoNative)] + 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); + + [LibraryImport(Libraries.AppleCryptoNative)] + 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); } } 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 dadeb106d6613..99e10abbe0595 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -783,6 +783,7 @@ Link="Common\System\Text\UrlBase64Encoding.cs" /> + @@ -870,7 +871,6 @@ - + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Android.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Android.cs index 471338a8e8a76..48f10541a5070 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Android.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Android.cs @@ -12,6 +12,7 @@ public sealed partial class AesGcm private SafeEvpCipherCtxHandle _ctxHandle; public static bool IsSupported => true; + public static KeySizes TagByteSizes { get; } = new KeySizes(12, 16, 1); [MemberNotNull(nameof(_ctxHandle))] private void ImportKey(ReadOnlySpan key) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.NotSupported.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.NotSupported.cs index afa639420be8e..4fdf9a9d8e0df 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.NotSupported.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.NotSupported.cs @@ -8,6 +8,7 @@ namespace System.Security.Cryptography public partial class AesGcm { public static bool IsSupported => false; + public static KeySizes TagByteSizes { get; } = new KeySizes(12, 16, 1); #pragma warning disable CA1822, IDE0060 private void ImportKey(ReadOnlySpan key) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.OpenSsl.cs index f11936951bc9d..05ce035370201 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.OpenSsl.cs @@ -12,6 +12,7 @@ public sealed partial class AesGcm private SafeEvpCipherCtxHandle _ctxHandle; public static bool IsSupported { get; } = Interop.OpenSslNoInit.OpenSslIsAvailable; + public static KeySizes TagByteSizes { get; } = new KeySizes(12, 16, 1); [MemberNotNull(nameof(_ctxHandle))] private void ImportKey(ReadOnlySpan key) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Windows.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Windows.cs index f6326f42653f3..305fccb16188e 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Windows.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Windows.cs @@ -12,6 +12,7 @@ public partial class AesGcm private SafeKeyHandle _keyHandle; public static bool IsSupported => true; + public static KeySizes TagByteSizes { get; } = new KeySizes(12, 16, 1); [MemberNotNull(nameof(_keyHandle))] private void ImportKey(ReadOnlySpan key) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs index 02c7e6945eb90..104d280452482 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.cs @@ -13,7 +13,6 @@ public sealed partial class AesGcm : IDisposable { private const int NonceSize = 12; public static KeySizes NonceByteSizes { get; } = new KeySizes(NonceSize, NonceSize, 1); - public static KeySizes TagByteSizes { get; } = new KeySizes(12, 16, 1); public AesGcm(ReadOnlySpan key) { diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.macOS.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.macOS.cs new file mode 100644 index 0000000000000..5b5b2638164f7 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.macOS.cs @@ -0,0 +1,77 @@ +// 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; +using Microsoft.Win32.SafeHandles; + +namespace System.Security.Cryptography +{ + public sealed partial class AesGcm + { + private byte[]? _key; + + // CryptoKit added AES.GCM in macOS 10.15, which is our minimum target for macOS. + public static bool IsSupported => true; + + // CryptoKit only supports 16 byte tags. + public static KeySizes TagByteSizes { get; } = new KeySizes(16, 16, 1); + + [MemberNotNull(nameof(_key))] + private void ImportKey(ReadOnlySpan 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(key.Length, pinned: true); + key.CopyTo(_key); + } + + private void EncryptCore( + ReadOnlySpan nonce, + ReadOnlySpan plaintext, + Span ciphertext, + Span tag, + ReadOnlySpan associatedData) + { + CheckDisposed(); + Interop.AppleCrypto.AesGcmEncrypt( + _key, + nonce, + plaintext, + ciphertext, + tag, + associatedData); + } + + private void DecryptCore( + ReadOnlySpan nonce, + ReadOnlySpan ciphertext, + ReadOnlySpan tag, + Span plaintext, + ReadOnlySpan associatedData) + { + CheckDisposed(); + Interop.AppleCrypto.AesGcmDecrypt( + _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); + } + } +} diff --git a/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs b/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs index 0da0f45b31a2d..741b28020281f 100644 --- a/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs @@ -11,6 +11,8 @@ namespace System.Security.Cryptography.Tests [ConditionalClass(typeof(AesGcm), nameof(AesGcm.IsSupported))] public class AesGcmTests : CommonAEADTests { + private const int CryptoKitSupportedTagSizeInBytes = 16; + [Theory] [ActiveIssue("https://github.com/dotnet/runtime/issues/51332", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] [MemberData(nameof(EncryptTamperAADDecryptTestInputs))] @@ -152,7 +154,9 @@ public static void TwoEncryptionsAndDecryptionsUsingOneInstance() byte[] nonce2 = "8ba10892e8b87d031196bf99".HexToByteArray(); byte[] expectedCiphertext1 = "f1af1fb2d4485cc536d618475d52ff".HexToByteArray(); - byte[] expectedTag1 = "5ab65624c46b8160f34e81f5".HexToByteArray(); + byte[] expectedTag1 = PlatformDetection.IsOSX ? + "5ab65624c46b8160f34e81f51fee6cd9".HexToByteArray() : + "5ab65624c46b8160f34e81f5".HexToByteArray(); byte[] expectedCiphertext2 = ( "217bed01446d731a372a2b30ac7fcd73aed7c946d9171ae9c00b1c589ca73ba2" + @@ -327,13 +331,29 @@ public static void AesGcmNistTests(AEADTest testCase) { byte[] ciphertext = new byte[testCase.Plaintext.Length]; byte[] tag = new byte[testCase.Tag.Length]; - aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); - Assert.Equal(testCase.Ciphertext, ciphertext); - Assert.Equal(testCase.Tag, tag); - byte[] plaintext = new byte[testCase.Plaintext.Length]; - aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData); - Assert.Equal(testCase.Plaintext, plaintext); + if (PlatformDetection.IsOSX && testCase.Tag.Length != CryptoKitSupportedTagSizeInBytes) + { + Assert.Throws("tag", () => + { + aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); + }); + Assert.Throws("tag", () => + { + byte[] plaintext = new byte[testCase.Plaintext.Length]; + aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, testCase.Ciphertext, testCase.AssociatedData); + }); + } + else + { + aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); + Assert.Equal(testCase.Ciphertext, ciphertext); + Assert.Equal(testCase.Tag, tag); + + byte[] plaintext = new byte[testCase.Plaintext.Length]; + aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData); + Assert.Equal(testCase.Plaintext, plaintext); + } } } @@ -344,19 +364,33 @@ public static void AesGcmNistTestsTamperTag(AEADTest testCase) { using (var aesGcm = new AesGcm(testCase.Key)) { - byte[] ciphertext = new byte[testCase.Plaintext.Length]; - byte[] tag = new byte[testCase.Tag.Length]; - aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); - Assert.Equal(testCase.Ciphertext, ciphertext); - Assert.Equal(testCase.Tag, tag); - - tag[0] ^= 1; - - byte[] plaintext = new byte[testCase.Plaintext.Length]; - RandomNumberGenerator.Fill(plaintext); - Assert.Throws( - () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); - Assert.Equal(new byte[plaintext.Length], plaintext); + if (PlatformDetection.IsOSX && testCase.Tag.Length != CryptoKitSupportedTagSizeInBytes) + { + byte[] plaintext = new byte[testCase.Plaintext.Length]; + byte[] tamperedTag = testCase.Tag.AsSpan().ToArray(); + tamperedTag[0] ^= 1; + + Assert.Throws("tag", () => + { + aesGcm.Decrypt(testCase.Nonce, testCase.Ciphertext, tamperedTag, plaintext, testCase.AssociatedData); + }); + } + else + { + byte[] ciphertext = new byte[testCase.Plaintext.Length]; + byte[] tag = new byte[testCase.Tag.Length]; + aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); + Assert.Equal(testCase.Ciphertext, ciphertext); + Assert.Equal(testCase.Tag, tag); + + tag[0] ^= 1; + + byte[] plaintext = new byte[testCase.Plaintext.Length]; + RandomNumberGenerator.Fill(plaintext); + Assert.Throws( + () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); + Assert.Equal(new byte[plaintext.Length], plaintext); + } } } @@ -367,19 +401,30 @@ public static void AesGcmNistTestsTamperCiphertext(AEADTest testCase) { using (var aesGcm = new AesGcm(testCase.Key)) { - byte[] ciphertext = new byte[testCase.Plaintext.Length]; - byte[] tag = new byte[testCase.Tag.Length]; - aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); - Assert.Equal(testCase.Ciphertext, ciphertext); - Assert.Equal(testCase.Tag, tag); - - ciphertext[0] ^= 1; - - byte[] plaintext = new byte[testCase.Plaintext.Length]; - RandomNumberGenerator.Fill(plaintext); - Assert.Throws( - () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); - Assert.Equal(new byte[plaintext.Length], plaintext); + if (PlatformDetection.IsOSX && testCase.Tag.Length != CryptoKitSupportedTagSizeInBytes) + { + byte[] tamperedCiphertext = testCase.Ciphertext.AsSpan().ToArray(); + tamperedCiphertext[0] ^= 1; + Assert.Throws("tag", () => + { + aesGcm.Decrypt(testCase.Nonce, tamperedCiphertext, testCase.Tag, testCase.Plaintext, testCase.AssociatedData); + }); + } + else + { + byte[] ciphertext = new byte[testCase.Plaintext.Length]; + byte[] tag = new byte[testCase.Tag.Length]; + aesGcm.Encrypt(testCase.Nonce, testCase.Plaintext, ciphertext, tag, testCase.AssociatedData); + Assert.Equal(testCase.Ciphertext, ciphertext); + Assert.Equal(testCase.Tag, tag); + + ciphertext[0] ^= 1; + + byte[] plaintext = RandomNumberGenerator.GetBytes(testCase.Plaintext.Length); + Assert.Throws( + () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); + AssertExtensions.FilledWith(0, plaintext); + } } } @@ -889,7 +934,7 @@ public static void CheckIsSupported() if (PlatformDetection.IsOSX) { - expectedIsSupported = PlatformDetection.OpenSslPresentOnSystem; + expectedIsSupported = true; } else if (PlatformDetection.UsesMobileAppleCrypto) { 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 fb1c504db94d0..08fdb438e8849 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c +++ b/src/native/libs/System.Security.Cryptography.Native.Apple/entrypoints.c @@ -26,6 +26,8 @@ static const Entry s_cryptoAppleNative[] = { + DllImportEntry(AppleCryptoNative_AesGcmEncrypt) + DllImportEntry(AppleCryptoNative_AesGcmDecrypt) DllImportEntry(AppleCryptoNative_ChaCha20Poly1305Encrypt) DllImportEntry(AppleCryptoNative_ChaCha20Poly1305Decrypt) DllImportEntry(AppleCryptoNative_DigestFree) 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 42e3c22e7dad6..9fd0f1ea0ab31 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 @@ -33,3 +33,31 @@ PALEXPORT int32_t AppleCryptoNative_ChaCha20Poly1305Decrypt( int32_t plaintextBufferLength, uint8_t* aadPtr, int32_t aadLength); + +PALEXPORT int32_t AppleCryptoNative_AesGcmEncrypt( + 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_AesGcmDecrypt( + 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); 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 7d54df744b1f7..7b04d52504fa2 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 @@ -55,8 +55,7 @@ public func AppleCryptoNative_ChaCha20Poly1305Decrypt( plaintextBufferLength: Int32, aadPtr: UnsafeMutableRawPointer, aadLength: Int32 -) -> 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) @@ -68,12 +67,94 @@ public func AppleCryptoNative_ChaCha20Poly1305Decrypt( return 0 } - guard let sealedBoxRestored = try? ChaChaPoly.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag) else { + guard let sealedBox = try? ChaChaPoly.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag) else { return 0 } do { - let result = try ChaChaPoly.open(sealedBoxRestored, using: symmetricKey, authenticating: aad) + let result = try ChaChaPoly.open(sealedBox, using: symmetricKey, authenticating: aad) + + assert(plaintextBufferLength >= result.count) + result.copyBytes(to: plaintextBuffer, count: result.count) + return 1 + } + catch CryptoKitError.authenticationFailure { + return -1 + } + catch { + return 0 + } +} + +@_cdecl("AppleCryptoNative_AesGcmEncrypt") +public func AppleCryptoNative_AesGcmEncrypt( + 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) + + guard let nonce = try? AES.GCM.Nonce(data: nonceData) else { + return 0 + } + + guard let result = try? AES.GCM.seal(plaintext, using: symmetricKey, nonce: nonce, authenticating: aad) else { + return 0 + } + + assert(ciphertextBufferLength >= result.ciphertext.count) + assert(tagBufferLength >= result.tag.count) + + result.ciphertext.copyBytes(to: ciphertextBuffer, count: result.ciphertext.count) + result.tag.copyBytes(to: tagBuffer, count: result.tag.count) + return 1 + } + +@_cdecl("AppleCryptoNative_AesGcmDecrypt") +public func AppleCryptoNative_AesGcmDecrypt( + keyPtr: UnsafeMutableRawPointer, + keyLength: Int32, + noncePtr: UnsafeMutableRawPointer, + nonceLength: Int32, + ciphertextPtr: UnsafeMutableRawPointer, + ciphertextLength: Int32, + tagPtr: UnsafeMutableRawPointer, + tagLength: Int32, + plaintextBuffer: UnsafeMutablePointer, + 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? AES.GCM.Nonce(data: nonceData) else { + return 0 + } + + guard let sealedBox = try? AES.GCM.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag) else { + return 0 + } + + do { + let result = try AES.GCM.open(sealedBox, using: symmetricKey, authenticating: aad) assert(plaintextBufferLength >= result.count) result.copyBytes(to: plaintextBuffer, count: result.count)