File: System\Security\Cryptography\RC2Implementation.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 Internal.Cryptography;
 
namespace System.Security.Cryptography
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "We are providing the implementation for RC2, not consuming it.")]
    internal sealed partial class RC2Implementation : RC2
    {
        private const int BitsPerByte = 8;
 
        public override int EffectiveKeySize
        {
            get
            {
                return KeySizeValue;
            }
            set
            {
                if (value != KeySizeValue)
                    throw new CryptographicUnexpectedOperationException(SR.Cryptography_RC2_EKSKS2);
            }
        }
 
        public override ICryptoTransform CreateDecryptor()
        {
            return CreateTransform(Key, IV, encrypting: false);
        }
 
        public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[]? rgbIV)
        {
            return CreateTransform(rgbKey, rgbIV.CloneByteArray(), encrypting: false);
        }
 
        public override ICryptoTransform CreateEncryptor()
        {
            return CreateTransform(Key, IV, encrypting: true);
        }
 
        public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[]? rgbIV)
        {
            return CreateTransform(rgbKey, rgbIV.CloneByteArray(), encrypting: true);
        }
 
        public override void GenerateIV()
        {
            IV = RandomNumberGenerator.GetBytes(BlockSize / BitsPerByte);
        }
 
        public sealed override void GenerateKey()
        {
            Key = RandomNumberGenerator.GetBytes(KeySize / BitsPerByte);
        }
 
        private UniversalCryptoTransform CreateTransform(byte[] rgbKey, byte[]? rgbIV, bool encrypting)
        {
            ArgumentNullException.ThrowIfNull(rgbKey);
 
            // note: rgbIV is guaranteed to be cloned before this method, so no need to clone it again
 
            if (!ValidKeySize(rgbKey.Length))
                throw new ArgumentException(SR.Cryptography_InvalidKeySize, nameof(rgbKey));
 
            if (rgbIV != null)
            {
                long ivSize = rgbIV.Length * (long)BitsPerByte;
                if (ivSize != BlockSize)
                    throw new ArgumentException(SR.Cryptography_InvalidIVSize, nameof(rgbIV));
            }
 
            if (Mode == CipherMode.CFB)
            {
                ValidateCFBFeedbackSize(FeedbackSize);
            }
 
            Debug.Assert(EffectiveKeySize == KeySize);
            return CreateTransformCore(Mode, Padding, rgbKey, rgbIV, BlockSize / BitsPerByte, FeedbackSize / BitsPerByte, GetPaddingSize(), encrypting);
        }
 
        protected override bool TryDecryptEcbCore(
            ReadOnlySpan<byte> ciphertext,
            Span<byte> destination,
            PaddingMode paddingMode,
            out int bytesWritten)
        {
            if (!ValidKeySize(Key.Length))
                throw new InvalidOperationException(SR.Cryptography_InvalidKeySize);
 
            Debug.Assert(EffectiveKeySize == KeySize);
            ILiteSymmetricCipher cipher = CreateLiteCipher(
                CipherMode.ECB,
                Key,
                iv: null,
                blockSize: BlockSize / BitsPerByte,
                paddingSize: BlockSize / BitsPerByte,
                encrypting: false);
 
            using (cipher)
            {
                return UniversalCryptoOneShot.OneShotDecrypt(cipher, paddingMode, ciphertext, destination, out bytesWritten);
            }
        }
 
        protected override bool TryEncryptEcbCore(
            ReadOnlySpan<byte> plaintext,
            Span<byte> destination,
            PaddingMode paddingMode,
            out int bytesWritten)
        {
            if (!ValidKeySize(Key.Length))
                throw new InvalidOperationException(SR.Cryptography_InvalidKeySize);
 
            Debug.Assert(EffectiveKeySize == KeySize);
            ILiteSymmetricCipher cipher = CreateLiteCipher(
                CipherMode.ECB,
                Key,
                iv: default,
                blockSize: BlockSize / BitsPerByte,
                paddingSize: BlockSize / BitsPerByte,
                encrypting: true);
 
            using (cipher)
            {
                return UniversalCryptoOneShot.OneShotEncrypt(cipher, paddingMode, plaintext, destination, out bytesWritten);
            }
        }
 
        protected override bool TryEncryptCbcCore(
            ReadOnlySpan<byte> plaintext,
            ReadOnlySpan<byte> iv,
            Span<byte> destination,
            PaddingMode paddingMode,
            out int bytesWritten)
        {
            if (!ValidKeySize(Key.Length))
                throw new InvalidOperationException(SR.Cryptography_InvalidKeySize);
 
            Debug.Assert(EffectiveKeySize == KeySize);
            ILiteSymmetricCipher cipher = CreateLiteCipher(
                CipherMode.CBC,
                Key,
                iv,
                blockSize: BlockSize / BitsPerByte,
                paddingSize: BlockSize / BitsPerByte,
                encrypting: true);
 
            using (cipher)
            {
                return UniversalCryptoOneShot.OneShotEncrypt(cipher, paddingMode, plaintext, destination, out bytesWritten);
            }
        }
 
        protected override bool TryDecryptCbcCore(
            ReadOnlySpan<byte> ciphertext,
            ReadOnlySpan<byte> iv,
            Span<byte> destination,
            PaddingMode paddingMode,
            out int bytesWritten)
        {
            if (!ValidKeySize(Key.Length))
                throw new InvalidOperationException(SR.Cryptography_InvalidKeySize);
 
            Debug.Assert(EffectiveKeySize == KeySize);
            ILiteSymmetricCipher cipher = CreateLiteCipher(
                CipherMode.CBC,
                Key,
                iv,
                blockSize: BlockSize / BitsPerByte,
                paddingSize: BlockSize / BitsPerByte,
                encrypting: false);
 
            using (cipher)
            {
                return UniversalCryptoOneShot.OneShotDecrypt(cipher, paddingMode, ciphertext, destination, out bytesWritten);
            }
        }
 
        protected override bool TryDecryptCfbCore(
            ReadOnlySpan<byte> ciphertext,
            ReadOnlySpan<byte> iv,
            Span<byte> destination,
            PaddingMode paddingMode,
            int feedbackSizeInBits,
            out int bytesWritten)
        {
            throw new CryptographicException(SR.Format(SR.Cryptography_CipherModeNotSupported, CipherMode.CFB));
        }
 
        protected override bool TryEncryptCfbCore(
            ReadOnlySpan<byte> plaintext,
            ReadOnlySpan<byte> iv,
            Span<byte> destination,
            PaddingMode paddingMode,
            int feedbackSizeInBits,
            out int bytesWritten)
        {
            throw new CryptographicException(SR.Format(SR.Cryptography_CipherModeNotSupported, CipherMode.CFB));
        }
 
        private static void ValidateCFBFeedbackSize(int feedback)
        {
            // CFB not supported at all
            throw new CryptographicException(SR.Format(SR.Cryptography_CipherModeFeedbackNotSupported, feedback, CipherMode.CFB));
        }
 
        private int GetPaddingSize()
        {
            return BlockSize / BitsPerByte;
        }
 
        private new bool ValidKeySize(int keySizeBytes)
        {
            if (keySizeBytes > (int.MaxValue / BitsPerByte))
            {
                return false;
            }
 
            int keySizeBits = keySizeBytes << 3;
            return keySizeBits.IsLegalSize(LegalKeySizes);
        }
    }
}