File: System\Security\Cryptography\AesGcm.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography)
// 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.Runtime.Versioning;
using Internal.Cryptography;
 
namespace System.Security.Cryptography
{
    [UnsupportedOSPlatform("browser")]
    [UnsupportedOSPlatform("ios")]
    [UnsupportedOSPlatform("tvos")]
    [SupportedOSPlatform("ios13.0")]
    [SupportedOSPlatform("tvos13.0")]
    public sealed partial class AesGcm : IDisposable
    {
        private const int NonceSize = 12;
        public static KeySizes NonceByteSizes { get; } = new KeySizes(NonceSize, NonceSize, 1);
 
        [Obsolete(Obsoletions.AesGcmTagConstructorMessage, DiagnosticId = Obsoletions.AesGcmTagConstructorDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public AesGcm(ReadOnlySpan<byte> key)
        {
            ThrowIfNotSupported();
 
            AesAEAD.CheckKeySize(key.Length);
            ImportKey(key);
        }
 
        [Obsolete(Obsoletions.AesGcmTagConstructorMessage, DiagnosticId = Obsoletions.AesGcmTagConstructorDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public AesGcm(byte[] key)
            : this(new ReadOnlySpan<byte>(key ?? throw new ArgumentNullException(nameof(key))))
        {
        }
 
        /// <summary>
        ///   Initializes a new instance of the <see cref="AesGcm" /> class with a provided key and required tag size.
        /// </summary>
        /// <param name="key">The secret key to use for this instance.</param>
        /// <param name="tagSizeInBytes">The size of the tag, in bytes, that encryption and decryption must use.</param>
        /// <exception cref="CryptographicException">
        ///   The <paramref name="key" /> parameter length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   The <paramref name="tagSizeInBytes" /> parameter is an unsupported tag size indicated by
        ///   <see cref="TagByteSizes" />.
        /// </exception>
        /// <exception cref="PlatformNotSupportedException">
        ///   The current platform does not support AES-GCM.
        /// </exception>
        /// <remarks>
        ///   The <paramref name="tagSizeInBytes" /> parameter is used to indicate that the tag parameter in <c>Encrypt</c>
        ///   or <c>Decrypt</c> must be exactly this size. Indicating the required tag size prevents issues where callers
        ///   of <c>Decrypt</c> may supply a tag as input and that input is truncated to an unexpected size.
        /// </remarks>
        public AesGcm(ReadOnlySpan<byte> key, int tagSizeInBytes)
        {
            ThrowIfNotSupported();
 
            AesAEAD.CheckKeySize(key.Length);
 
            if (!tagSizeInBytes.IsLegalSize(TagByteSizes))
            {
                throw new ArgumentException(SR.Cryptography_InvalidTagLength, nameof(tagSizeInBytes));
            }
 
            TagSizeInBytes = tagSizeInBytes;
            ImportKey(key);
        }
 
        /// <summary>
        ///   Initializes a new instance of the <see cref="AesGcm" /> class with a provided key and required tag size.
        /// </summary>
        /// <param name="key">The secret key to use for this instance.</param>
        /// <param name="tagSizeInBytes">The size of the tag, in bytes, that encryption and decryption must use.</param>
        /// <exception cref="ArgumentNullException">The <paramref name="key" /> parameter is null.</exception>
        /// <exception cref="CryptographicException">
        ///   The <paramref name="key" /> parameter length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
        /// </exception>
        /// <exception cref="ArgumentException">
        ///   The <paramref name="tagSizeInBytes" /> parameter is an unsupported tag size indicated by
        ///   <see cref="TagByteSizes" />.
        /// </exception>
        /// <exception cref="PlatformNotSupportedException">
        ///   The current platform does not support AES-GCM.
        /// </exception>
        /// <remarks>
        ///   The <paramref name="tagSizeInBytes" /> parameter is used to indicate that the tag parameter in <c>Encrypt</c>
        ///   or <c>Decrypt</c> must be exactly this size. Indicating the required tag size prevents issues where callers
        ///   of <c>Decrypt</c> may supply a tag as input and that input is truncated to an unexpected size.
        /// </remarks>
        public AesGcm(byte[] key, int tagSizeInBytes)
            : this(new ReadOnlySpan<byte>(key ?? throw new ArgumentNullException(nameof(key))), tagSizeInBytes)
        {
        }
 
        /// <summary>
        /// Gets the size of the tag, in bytes.
        /// </summary>
        /// <value>
        /// The size of the tag that must be used for encryption or decryption, or <see langword="null" /> if the
        /// tag size is unspecified.
        /// </value>
        public int? TagSizeInBytes { get; }
 
        public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[]? associatedData = null)
        {
            ArgumentNullException.ThrowIfNull(nonce);
            ArgumentNullException.ThrowIfNull(plaintext);
            ArgumentNullException.ThrowIfNull(ciphertext);
            ArgumentNullException.ThrowIfNull(tag);
 
            Encrypt((ReadOnlySpan<byte>)nonce, plaintext, ciphertext, tag, associatedData);
        }
 
        public void Encrypt(
            ReadOnlySpan<byte> nonce,
            ReadOnlySpan<byte> plaintext,
            Span<byte> ciphertext,
            Span<byte> tag,
            ReadOnlySpan<byte> associatedData = default)
        {
            CheckParameters(plaintext, ciphertext, nonce, tag);
            EncryptCore(nonce, plaintext, ciphertext, tag, associatedData);
        }
 
        public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[]? associatedData = null)
        {
            ArgumentNullException.ThrowIfNull(nonce);
            ArgumentNullException.ThrowIfNull(ciphertext);
            ArgumentNullException.ThrowIfNull(tag);
            ArgumentNullException.ThrowIfNull(plaintext);
 
            Decrypt((ReadOnlySpan<byte>)nonce, ciphertext, tag, plaintext, associatedData);
        }
 
        public void Decrypt(
            ReadOnlySpan<byte> nonce,
            ReadOnlySpan<byte> ciphertext,
            ReadOnlySpan<byte> tag,
            Span<byte> plaintext,
            ReadOnlySpan<byte> associatedData = default)
        {
            CheckParameters(plaintext, ciphertext, nonce, tag);
            DecryptCore(nonce, ciphertext, tag, plaintext, associatedData);
        }
 
        private void CheckParameters(
            ReadOnlySpan<byte> plaintext,
            ReadOnlySpan<byte> ciphertext,
            ReadOnlySpan<byte> nonce,
            ReadOnlySpan<byte> tag)
        {
            if (plaintext.Length != ciphertext.Length)
                throw new ArgumentException(SR.Cryptography_PlaintextCiphertextLengthMismatch);
 
            if (!nonce.Length.IsLegalSize(NonceByteSizes))
                throw new ArgumentException(SR.Cryptography_InvalidNonceLength, nameof(nonce));
 
            if (TagSizeInBytes is int tagSizeInBytes)
            {
                // constructor promise
                Debug.Assert(tagSizeInBytes.IsLegalSize(TagByteSizes));
 
                if (tag.Length != tagSizeInBytes)
                {
                    throw new ArgumentException(SR.Format(SR.Cryptography_IncorrectTagLength, tagSizeInBytes), nameof(tag));
                }
            }
            else if (!tag.Length.IsLegalSize(TagByteSizes))
            {
                throw new ArgumentException(SR.Cryptography_InvalidTagLength, nameof(tag));
            }
        }
 
        private static void ThrowIfNotSupported()
        {
            if (!IsSupported)
            {
                throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(AesGcm)));
            }
        }
    }
}