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

Implement AES-GCM with CryptoKit on macOS #76490

Merged
merged 10 commits into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,82 @@ internal static partial class AppleCrypto
}
}

internal static unsafe void AesGcmEncrypt(
vcsjones marked this conversation as resolved.
Show resolved Hide resolved
ReadOnlySpan<byte> key,
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> plaintext,
Span<byte> ciphertext,
Span<byte> tag,
ReadOnlySpan<byte> aad)
{
fixed (byte* keyPtr = key)
fixed (byte* noncePtr = nonce)
fixed (byte* plaintextPtr = plaintext)
fixed (byte* ciphertextPtr = ciphertext)
fixed (byte* tagPtr = tag)
fixed (byte* aadPtr = aad)
{
const int Success = 1;
int result = AppleCryptoNative_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<byte> key,
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> ciphertext,
ReadOnlySpan<byte> tag,
Span<byte> plaintext,
ReadOnlySpan<byte> aad)
{
fixed (byte* keyPtr = key)
fixed (byte* noncePtr = nonce)
fixed (byte* ciphertextPtr = ciphertext)
fixed (byte* tagPtr = tag)
fixed (byte* plaintextPtr = plaintext)
fixed (byte* aadPtr = aad)
{
const int Success = 1;
const int AuthTagMismatch = -1;
int result = AppleCryptoNative_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,
Expand Down Expand Up @@ -116,5 +192,35 @@ internal static partial class AppleCrypto
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@
Link="Common\System\Text\UrlBase64Encoding.cs" />
<Compile Include="$(CommonPath)System\Text\ValueUtf8Converter.cs"
Link="Common\System\Text\ValueUtf8Converter.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\AesImplementation.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\AsnFormatter.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\CapiHelper.DSA.Shared.cs" />
Expand Down Expand Up @@ -870,7 +871,6 @@
<Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs"
Link="Common\Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.OpenSsl.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.OpenSsl.cs" />
</ItemGroup>
<ItemGroup Condition="'$(UseAndroidCrypto)' == 'true'">
<Compile Include="$(CommonPath)Interop\Android\Interop.JObjectLifetime.cs"
Expand Down Expand Up @@ -1292,6 +1292,7 @@
Link="Common\System\Security\Cryptography\RSASecurityTransforms.macOS.cs" />
<Compile Include="$(CommonPath)System\Security\Cryptography\RSAOpenSsl.cs"
Link="Common\System\Security\Cryptography\RSAOpenSsl.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.macOS.cs" />
vcsjones marked this conversation as resolved.
Show resolved Hide resolved
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.macOS.cs" />
<Compile Include="System\Security\Cryptography\DSA.Create.SecurityTransforms.cs" />
<Compile Include="System\Security\Cryptography\DSACryptoServiceProvider.Unix.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
vcsjones marked this conversation as resolved.
Show resolved Hide resolved

#pragma warning disable CA1822
private void ImportKey(ReadOnlySpan<byte> key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte> key)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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<byte> key)
{
// We should only be calling this in the constructor, so there shouldn't be a previous key.
Debug.Assert(_key is null);

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

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

private void DecryptCore(
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> ciphertext,
ReadOnlySpan<byte> tag,
Span<byte> plaintext,
ReadOnlySpan<byte> 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);
}
}
}