<commit_before>﻿using System;
using System.Security.Cryptography;
using System.Threading;

namespace Kirkin.Security.Cryptography
{
    internal static class Aes256Cbc
    {
        internal const int KeySizeInBytes = 32;
        internal const int BlockSizeInBytes = 16;

        private static readonly ThreadLocal<Aes> s_aes256_cbc_pkcs7 = new ThreadLocal<Aes>(() =>
        {
            Aes aes = CryptoFactories.AesFactory();

            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            return aes;
        });

        /// <summary>
        /// Shared thread local AES256 CBC PKCS7 instance.
        /// </summary>
        internal static Aes AES256_CBC_PKCS7 => s_aes256_cbc_pkcs7.Value;

        /// <summary>
        /// Generates a random 256-bit key which can be used by an <see cref="Aes256CbcAlgorithm"/> instance.
        /// </summary>
        public static byte[] GenerateKey() => CryptoRandom.GetRandomBytes(KeySizeInBytes);

        internal static int EncryptBytes(ArraySegment<byte> plaintext, byte[] key, byte[] iv, byte[] output, int outputOffset)
        {
            using (ICryptoTransform transform = AES256_CBC_PKCS7.CreateEncryptor(key, iv))
            {
                if (!transform.CanTransformMultipleBlocks) {
                    throw new NotSupportedException("AES encryptor does not support multi-block transforms.");
                }

                int blockCount = plaintext.Count / BlockSizeInBytes + 1;
                int bytesWritten = 0;

                if (blockCount > 1)
                {
                    int count = (blockCount - 1) * BlockSizeInBytes;

                    bytesWritten += transform.TransformBlock(plaintext.Array, plaintext.Offset, count, output, outputOffset);
                }

                int finalBlockIndex = plaintext.Offset + (blockCount - 1) * BlockSizeInBytes;
                byte[] finalBlock = transform.TransformFinalBlock(plaintext.Array, finalBlockIndex, plaintext.Offset + plaintext.Count - finalBlockIndex);

                Array.Copy(finalBlock, 0, output, outputOffset + bytesWritten, finalBlock.Length);

                bytesWritten += finalBlock.Length;

                return bytesWritten;
            }
        }

        internal static int DecryptBytes(ArraySegment<byte> ciphertext, byte[] key, byte[] iv, byte[] output, int outputOffset)
        {
            using (ICryptoTransform transform = AES256_CBC_PKCS7.CreateDecryptor(key, iv))
            {
                if (!transform.CanTransformMultipleBlocks) {
                    throw new NotSupportedException("AES encryptor does not support multi-block transforms.");
                }

                int blockCount = ciphertext.Count / BlockSizeInBytes;
                int bytesWritten = 0;

                if (blockCount > 1)
                {
                    int count = (blockCount - 1) * BlockSizeInBytes;

                    bytesWritten += transform.TransformBlock(ciphertext.Array, ciphertext.Offset, count, output, outputOffset);
                }

                int finalBlockIndex = ciphertext.Offset + (blockCount - 1) * BlockSizeInBytes;
                byte[] finalBlock = transform.TransformFinalBlock(ciphertext.Array, finalBlockIndex, BlockSizeInBytes);

                Array.Copy(finalBlock, 0, output, outputOffset + bytesWritten, finalBlock.Length);

                bytesWritten += finalBlock.Length;

                return bytesWritten;
            }
        }
    }
}<commit_msg>Aes256Cbc refactoring<commit_after>﻿using System;
using System.Security.Cryptography;
using System.Threading;

namespace Kirkin.Security.Cryptography
{
    internal static class Aes256Cbc
    {
        internal const int KeySizeInBytes = 32;
        internal const int BlockSizeInBytes = 16;

        private static readonly ThreadLocal<Aes> s_aes256_cbc_pkcs7 = new ThreadLocal<Aes>(() =>
        {
            Aes aes = CryptoFactories.AesFactory();

            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            return aes;
        });

        /// <summary>
        /// Shared thread local AES256 CBC PKCS7 instance.
        /// </summary>
        internal static Aes AES256_CBC_PKCS7 => s_aes256_cbc_pkcs7.Value;

        /// <summary>
        /// Generates a random 256-bit key which can be used by an <see cref="Aes256CbcAlgorithm"/> instance.
        /// </summary>
        public static byte[] GenerateKey() => CryptoRandom.GetRandomBytes(KeySizeInBytes);

        internal static int EncryptBytes(in ArraySegment<byte> plaintext, byte[] key, byte[] iv, byte[] output, int outputOffset)
        {
            using (ICryptoTransform transform = AES256_CBC_PKCS7.CreateEncryptor(key, iv)) {
                return ApplyTransform(plaintext, transform, output, outputOffset);
            }
        }

        internal static int DecryptBytes(in ArraySegment<byte> ciphertext, byte[] key, byte[] iv, byte[] output, int outputOffset)
        {
            using (ICryptoTransform transform = AES256_CBC_PKCS7.CreateDecryptor(key, iv)) {
                return ApplyTransform(ciphertext, transform, output, outputOffset);
            }
        }

        private static int ApplyTransform(in ArraySegment<byte> input, ICryptoTransform transform, byte[] output, int outputOffset)
        {
            if (!transform.CanTransformMultipleBlocks) {
                throw new NotSupportedException("AES encryptor does not support multi-block transforms.");
            }

            int blockCount = input.Count / BlockSizeInBytes;

            if (input.Count % BlockSizeInBytes != 0) {
                blockCount++;
            }

            int bytesWritten = 0;

            if (blockCount > 1)
            {
                int count = (blockCount - 1) * BlockSizeInBytes;

                bytesWritten += transform.TransformBlock(input.Array, input.Offset, count, output, outputOffset);
            }

            int finalBlockIndex = input.Offset + (blockCount - 1) * BlockSizeInBytes;
            byte[] finalBlock = transform.TransformFinalBlock(input.Array, finalBlockIndex, input.Offset + input.Count - finalBlockIndex);

            Array.Copy(finalBlock, 0, output, outputOffset + bytesWritten, finalBlock.Length);

            bytesWritten += finalBlock.Length;

            return bytesWritten;
        }
    }
}
{'fraction_non_alphanumeric': 0.08864526233359436, 'fraction_numerical': 0.015035238841033673, 'mean_word_length': 2.1181640625, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7657683', 'n_tokens_mistral': 1799, 'n_tokens_neox': 1601, 'n_words': 412}