Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ChaCha20Poly1305 skeleton #52030

Merged
merged 10 commits into from
May 5, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Security.Cryptography;
using System.Threading;
using Internal.NativeCrypto;

namespace Internal.Cryptography
{
internal static class AeadBCryptHandles
{
private static SafeAlgorithmHandle? s_aesCcm;
private static SafeAlgorithmHandle? s_aesGcm;
private static SafeAlgorithmHandle? s_chaCha20Poly1305;

internal static SafeAlgorithmHandle AesCcm => GetCachedAlgorithmHandle(ref s_aesCcm, Cng.BCRYPT_AES_ALGORITHM, Cng.BCRYPT_CHAIN_MODE_CCM);
internal static SafeAlgorithmHandle AesGcm => GetCachedAlgorithmHandle(ref s_aesGcm, Cng.BCRYPT_AES_ALGORITHM, Cng.BCRYPT_CHAIN_MODE_GCM);

internal static bool IsChaCha20Poly1305Supported { get; } = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 20142);
internal static SafeAlgorithmHandle ChaCha20Poly1305 => GetCachedAlgorithmHandle(ref s_chaCha20Poly1305, Cng.BCRYPT_CHACHA20_POLY1305_ALGORITHM);

private static SafeAlgorithmHandle GetCachedAlgorithmHandle(ref SafeAlgorithmHandle? handle, string algId, string? chainingMode = null)
{
// Do we already have a handle to this algorithm?
SafeAlgorithmHandle? existingHandle = Volatile.Read(ref handle);
if (existingHandle != null) { return existingHandle; }
GrabYourPitchforks marked this conversation as resolved.
Show resolved Hide resolved

// No cached handle exists; create a new handle. It's ok if multiple threads call
// this concurrently. Only one handle will "win" and the rest will be destroyed.
SafeAlgorithmHandle newHandle = Cng.BCryptOpenAlgorithmProvider(algId, null, Cng.OpenAlgorithmProviderFlags.NONE);
if (chainingMode != null)
{
newHandle.SetCipherMode(chainingMode);
}

existingHandle = Interlocked.CompareExchange(ref handle, newHandle, null);
if (existingHandle != null)
{
newHandle.Dispose();
return existingHandle;
}
else
{
return newHandle;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Security.Cryptography;
using System.Threading;
using Internal.NativeCrypto;

namespace Internal.Cryptography
{
internal static class BCryptAeadHandleCache
{
private static SafeAlgorithmHandle? s_aesCcm;
private static SafeAlgorithmHandle? s_aesGcm;
private static SafeAlgorithmHandle? s_chaCha20Poly1305;

internal static SafeAlgorithmHandle AesCcm => GetCachedAlgorithmHandle(ref s_aesCcm, Cng.BCRYPT_AES_ALGORITHM, Cng.BCRYPT_CHAIN_MODE_CCM);
internal static SafeAlgorithmHandle AesGcm => GetCachedAlgorithmHandle(ref s_aesGcm, Cng.BCRYPT_AES_ALGORITHM, Cng.BCRYPT_CHAIN_MODE_GCM);

internal static bool IsChaCha20Poly1305Supported { get; } = OperatingSystem.IsWindowsVersionAtLeast(10, 0, 20142);
internal static SafeAlgorithmHandle ChaCha20Poly1305 => GetCachedAlgorithmHandle(ref s_chaCha20Poly1305, Cng.BCRYPT_CHACHA20_POLY1305_ALGORITHM);

private static SafeAlgorithmHandle GetCachedAlgorithmHandle(ref SafeAlgorithmHandle? handle, string algId, string? chainingMode = null)
{
// Do we already have a handle to this algorithm?
SafeAlgorithmHandle? existingHandle = Volatile.Read(ref handle);
if (existingHandle != null)
{
return existingHandle;
}

// No cached handle exists; create a new handle. It's ok if multiple threads call
// this concurrently. Only one handle will "win" and the rest will be destroyed.
SafeAlgorithmHandle newHandle = Cng.BCryptOpenAlgorithmProvider(algId, null, Cng.OpenAlgorithmProviderFlags.NONE);
if (chainingMode != null)
{
newHandle.SetCipherMode(chainingMode);
}

existingHandle = Interlocked.CompareExchange(ref handle, newHandle, null);
if (existingHandle != null)
{
newHandle.Dispose();
return existingHandle;
}
else
{
return newHandle;
}
}
}
}
1 change: 1 addition & 0 deletions src/libraries/Common/src/Interop/Windows/BCrypt/Cng.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public enum OpenAlgorithmProviderFlags : int

public const string BCRYPT_3DES_ALGORITHM = "3DES";
public const string BCRYPT_AES_ALGORITHM = "AES";
public const string BCRYPT_CHACHA20_POLY1305_ALGORITHM = "CHACHA20_POLY1305";
public const string BCRYPT_DES_ALGORITHM = "DES";
public const string BCRYPT_RC2_ALGORITHM = "RC2";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public sealed partial class AesCcm : System.IDisposable
{
public AesCcm(byte[] key) { }
public AesCcm(System.ReadOnlySpan<byte> key) { }
public static bool IsSupported { get { throw null; } }
public static System.Security.Cryptography.KeySizes NonceByteSizes { get { throw null; } }
public static System.Security.Cryptography.KeySizes TagByteSizes { get { throw null; } }
public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[]? associatedData = null) { }
Expand All @@ -32,6 +33,7 @@ public sealed partial class AesGcm : System.IDisposable
{
public AesGcm(byte[] key) { }
public AesGcm(System.ReadOnlySpan<byte> key) { }
public static bool IsSupported { get { throw null; } }
public static System.Security.Cryptography.KeySizes NonceByteSizes { get { throw null; } }
public static System.Security.Cryptography.KeySizes TagByteSizes { get { throw null; } }
public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[]? associatedData = null) { }
Expand Down Expand Up @@ -97,6 +99,18 @@ protected AsymmetricSignatureFormatter() { }
public abstract void SetHashAlgorithm(string strName);
public abstract void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key);
}
[System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")]
public sealed partial class ChaCha20Poly1305 : System.IDisposable
{
public ChaCha20Poly1305(byte[] key) { }
public ChaCha20Poly1305(System.ReadOnlySpan<byte> key) { }
public static bool IsSupported { get { throw null; } }
public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[]? associatedData = null) { }
public void Decrypt(System.ReadOnlySpan<byte> nonce, System.ReadOnlySpan<byte> ciphertext, System.ReadOnlySpan<byte> tag, System.Span<byte> plaintext, System.ReadOnlySpan<byte> associatedData = default(System.ReadOnlySpan<byte>)) { }
public void Dispose() { }
public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[]? associatedData = null) { }
public void Encrypt(System.ReadOnlySpan<byte> nonce, System.ReadOnlySpan<byte> plaintext, System.Span<byte> ciphertext, System.Span<byte> tag, System.ReadOnlySpan<byte> associatedData = default(System.ReadOnlySpan<byte>)) { }
}
public partial class CryptoConfig
{
public CryptoConfig() { }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
M:System.Security.Cryptography.AesCcm.IsSupported
M:System.Security.Cryptography.AesGcm.IsSupported
M:System.Security.Cryptography.ChaCha20Poly1305.IsSupported
T:System.Security.Cryptography.CryptoConfig
T:System.Security.Cryptography.RandomNumberGenerator
T:System.Security.Cryptography.IncrementalHash
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DefineConstants>$(DefineConstants);INTERNAL_ASYMMETRIC_IMPLEMENTATIONS</DefineConstants>
Expand Down Expand Up @@ -27,6 +27,7 @@
<Compile Include="Internal\Cryptography\RC2Implementation.cs" />
<Compile Include="Internal\Cryptography\RijndaelImplementation.cs" />
<Compile Include="Internal\Cryptography\TripleDesImplementation.cs" />
<Compile Include="System\Security\Cryptography\AeadCommon.cs" />
<Compile Include="System\Security\Cryptography\Aes.cs" />
<Compile Include="System\Security\Cryptography\AesAEAD.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.cs" />
Expand All @@ -36,6 +37,7 @@
<Compile Include="System\Security\Cryptography\AsymmetricKeyExchangeFormatter.cs" />
<Compile Include="System\Security\Cryptography\AsymmetricSignatureDeformatter.cs" />
<Compile Include="System\Security\Cryptography\AsymmetricSignatureFormatter.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.cs" />
<Compile Include="System\Security\Cryptography\CryptoConfig.cs" />
<Compile Include="System\Security\Cryptography\DeriveBytes.cs" />
<Compile Include="System\Security\Cryptography\DES.cs" />
Expand Down Expand Up @@ -276,9 +278,10 @@
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<Compile Include="System\Security\Cryptography\AeadCommon.Windows.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.Windows.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.Windows.cs" />
<Compile Include="System\Security\Cryptography\AesAEAD.Windows.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.Windows.cs" />
<Compile Include="System\Security\Cryptography\CngKeyLite.cs" />
<Compile Include="System\Security\Cryptography\CngPkcs8.cs" />
<Compile Include="System\Security\Cryptography\DSACng.cs" />
Expand All @@ -304,6 +307,8 @@
Link="Common\Interop\Windows\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\AesBCryptModes.cs"
Link="Common\Interop\Windows\BCrypt\AesBCryptModes.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\BCryptAeadHandleCache.cs"
Link="Common\Interop\Windows\BCrypt\BCryptAeadHandleCache.cs" />
<Compile Include="$(CommonPath)Interop\Windows\BCrypt\Cng.cs"
Link="Common\Interop\Windows\BCrypt\Cng.cs" />
<Compile Include="$(CommonPath)\Interop\Windows\BCrypt\Interop.BCryptImportKey.cs"
Expand Down Expand Up @@ -583,6 +588,7 @@
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Cipher.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.Unix.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.Unix.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.NotSupported.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true' and '$(UseAndroidCrypto)' != 'true' and '$(UseAppleCrypto)' != 'true'">
<Compile Include="Internal\Cryptography\DesImplementation.Unix.cs" />
Expand Down Expand Up @@ -673,6 +679,7 @@
<Compile Include="Internal\Cryptography\RC2Implementation.Android.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.Android.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.Android.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\ECDiffieHellman.Create.Android.cs" />
<Compile Include="System\Security\Cryptography\ECDsa.Create.Android.cs" />
<Compile Include="System\Security\Cryptography\RSA.Create.Android.cs" />
Expand All @@ -693,6 +700,9 @@
<Compile Include="Internal\Cryptography\RandomNumberGeneratorImplementation.cs" />
<Compile Include="Internal\Cryptography\RandomNumberGeneratorImplementation.Browser.cs" />
<Compile Include="Internal\Cryptography\SHAHashProvider.Browser.cs" />
<Compile Include="System\Security\Cryptography\AesCcm.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\AesGcm.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\ChaCha20Poly1305.NotSupported.cs" />
<Compile Include="System\Security\Cryptography\CryptoConfig.Browser.cs" />
<Compile Include="System\Security\Cryptography\RandomNumberGenerator.cs" />
<Compile Include="System\Security\Cryptography\IncrementalHash.cs" />
Expand All @@ -711,6 +721,7 @@
<Reference Include="System.Formats.Asn1" />
<Reference Include="System.Memory" />
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
<Reference Include="System.Runtime.Extensions" />
<Reference Include="System.Runtime.InteropServices" />
<Reference Include="System.Runtime.InteropServices.RuntimeInformation" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,27 @@
using Internal.NativeCrypto;
using static Interop.BCrypt;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

namespace System.Security.Cryptography
{
internal static partial class AesAEAD
internal static partial class AeadCommon
{
public static unsafe void Encrypt(
SafeAlgorithmHandle algorithm,
SafeKeyHandle keyHandle,
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> associatedData,
ReadOnlySpan<byte> plaintext,
Span<byte> ciphertext,
Span<byte> tag)
{
fixed (byte* plaintextBytes = plaintext)
fixed (byte* nonceBytes = nonce)
fixed (byte* ciphertextBytes = ciphertext)
fixed (byte* tagBytes = tag)
fixed (byte* associatedDataBytes = associatedData)
// bcrypt sometimes misbehaves when given nullptr buffers; ensure non-nullptr
fixed (byte* plaintextBytes = &GetNonNullPinnableReference(plaintext))
fixed (byte* nonceBytes = &GetNonNullPinnableReference(nonce))
fixed (byte* ciphertextBytes = &GetNonNullPinnableReference(ciphertext))
fixed (byte* tagBytes = &GetNonNullPinnableReference(tag))
fixed (byte* associatedDataBytes = &GetNonNullPinnableReference(associatedData))
{
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Create();
authInfo.pbNonce = nonceBytes;
Expand Down Expand Up @@ -55,7 +57,6 @@ public static unsafe void Encrypt(
}

public static unsafe void Decrypt(
SafeAlgorithmHandle algorithm,
SafeKeyHandle keyHandle,
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> associatedData,
Expand All @@ -64,11 +65,12 @@ public static unsafe void Decrypt(
Span<byte> plaintext,
bool clearPlaintextOnFailure)
{
fixed (byte* plaintextBytes = plaintext)
fixed (byte* nonceBytes = nonce)
fixed (byte* ciphertextBytes = ciphertext)
fixed (byte* tagBytes = tag)
fixed (byte* associatedDataBytes = associatedData)
// bcrypt sometimes misbehaves when given nullptr buffers; ensure non-nullptr
fixed (byte* plaintextBytes = &GetNonNullPinnableReference(plaintext))
fixed (byte* nonceBytes = &GetNonNullPinnableReference(nonce))
fixed (byte* ciphertextBytes = &GetNonNullPinnableReference(ciphertext))
fixed (byte* tagBytes = &GetNonNullPinnableReference(tag))
fixed (byte* associatedDataBytes = &GetNonNullPinnableReference(associatedData))
{
BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Create();
authInfo.pbNonce = nonceBytes;
Expand Down Expand Up @@ -108,5 +110,15 @@ public static unsafe void Decrypt(
}
}
}

// Implementations below based on internal MemoryMarshal.GetNonNullPinnableReference methods.

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe ref readonly byte GetNonNullPinnableReference(ReadOnlySpan<byte> buffer)
=> ref buffer.Length != 0 ? ref MemoryMarshal.GetReference(buffer) : ref Unsafe.AsRef<byte>((void*)1);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe ref byte GetNonNullPinnableReference(Span<byte> buffer)
=> ref buffer.Length != 0 ? ref MemoryMarshal.GetReference(buffer) : ref Unsafe.AsRef<byte>((void*)1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.Cryptography;

namespace System.Security.Cryptography
{
internal static partial class AeadCommon
{
public static void CheckArgumentsForNull(
byte[] nonce,
byte[] plaintext,
byte[] ciphertext,
byte[] tag)
{
if (nonce == null)
throw new ArgumentNullException(nameof(nonce));

if (plaintext == null)
throw new ArgumentNullException(nameof(plaintext));

if (ciphertext == null)
throw new ArgumentNullException(nameof(ciphertext));

if (tag == null)
throw new ArgumentNullException(nameof(tag));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,12 @@ namespace System.Security.Cryptography
{
internal static partial class AesAEAD
{
public static void CheckKeySize(int keySizeInBits)
public static void CheckKeySize(int keySizeInBytes)
{
if (keySizeInBits != 128 && keySizeInBits != 192 && keySizeInBits != 256)
if (keySizeInBytes != (128 / 8) && keySizeInBytes != (192 / 8) && keySizeInBytes != (256 / 8))
{
throw new CryptographicException(SR.Cryptography_InvalidKeySize);
}
}

public static void CheckArgumentsForNull(
byte[] nonce,
byte[] plaintext,
byte[] ciphertext,
byte[] tag)
{
if (nonce == null)
throw new ArgumentNullException(nameof(nonce));

if (plaintext == null)
throw new ArgumentNullException(nameof(plaintext));

if (ciphertext == null)
throw new ArgumentNullException(nameof(ciphertext));

if (tag == null)
throw new ArgumentNullException(nameof(tag));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ private void ImportKey(ReadOnlySpan<byte> key)
_key = key.ToArray();
}

private void EncryptInternal(
private void EncryptCore(
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> plaintext,
Span<byte> ciphertext,
Expand Down Expand Up @@ -97,7 +97,7 @@ private void EncryptInternal(
}
}

private void DecryptInternal(
private void DecryptCore(
ReadOnlySpan<byte> nonce,
ReadOnlySpan<byte> ciphertext,
ReadOnlySpan<byte> tag,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Security.Cryptography
{
public partial class AesCcm
{
public static bool IsSupported => false;
}
}
Loading