Change FeedbackSize on Rijndael wrappers to delegate to implementation.
authorKevin Jones <kevin@vcsjones.com>
Thu, 7 Jan 2021 22:44:07 +0000 (17:44 -0500)
committerGitHub <noreply@github.com>
Thu, 7 Jan 2021 22:44:07 +0000 (14:44 -0800)
The FeedbackSize on RijndaelManaged and RijndaelImplementation were not
accurately reflecting the feedback size that the actual implementation
was using. The FeedbackSize would report 128, however the implementation
defaults to 8, so CFB8 is actually what was being used.

Likewise, the setter for FeedbackSize on the Rijndael types had no
effect. The implementation's default feedback size of 8 would always
be used.

src/libraries/System.Security.Cryptography.Algorithms/src/Internal/Cryptography/RijndaelImplementation.cs
src/libraries/System.Security.Cryptography.Algorithms/src/System/Security/Cryptography/RijndaelManaged.cs
src/libraries/System.Security.Cryptography.Algorithms/tests/RijndaelTests.cs

index c4afcf0..a91a886 100644 (file)
@@ -23,6 +23,7 @@ namespace Internal.Cryptography
 
             // This class wraps Aes
             _impl = Aes.Create();
+            _impl.FeedbackSize = 128;
         }
 
         public override int BlockSize
@@ -42,6 +43,12 @@ namespace Internal.Cryptography
             }
         }
 
+        public override int FeedbackSize
+        {
+            get => _impl.FeedbackSize;
+            set => _impl.FeedbackSize = value;
+        }
+
         public override byte[] IV
         {
             get { return _impl.IV; }
index 876d175..8eda1cf 100644 (file)
@@ -19,6 +19,7 @@ namespace System.Security.Cryptography
 
             // This class wraps Aes
             _impl = Aes.Create();
+            _impl.FeedbackSize = 128;
         }
 
         public override int BlockSize
@@ -38,6 +39,12 @@ namespace System.Security.Cryptography
             }
         }
 
+        public override int FeedbackSize
+        {
+            get => _impl.FeedbackSize;
+            set => _impl.FeedbackSize = value;
+        }
+
         public override byte[] IV
         {
             get { return _impl.IV; }
index f2c922f..5fe1c8d 100644 (file)
@@ -25,6 +25,7 @@ namespace System.Security.Cryptography.Encryption.Rijndael.Tests
                 Assert.Equal(128, alg.LegalBlockSizes[0].MinSize);
                 Assert.Equal(128, alg.LegalBlockSizes[0].MaxSize);
                 Assert.Equal(128, alg.BlockSize);
+                Assert.Equal(128, alg.FeedbackSize);
 
                 // Different exception since we have different supported BlockSizes than desktop
                 Assert.Throws<PlatformNotSupportedException>(() => alg.BlockSize = 192);
@@ -32,6 +33,7 @@ namespace System.Security.Cryptography.Encryption.Rijndael.Tests
 
                 // Normal exception for rest
                 Assert.Throws<CryptographicException>(() => alg.BlockSize = 111);
+                Assert.Throws<CryptographicException>(() => alg.FeedbackSize = 15);
 
                 Assert.Equal(CipherMode.CBC, alg.Mode);
                 Assert.Equal(PaddingMode.PKCS7, alg.Padding);
@@ -169,6 +171,9 @@ namespace System.Security.Cryptography.Encryption.Rijndael.Tests
 
                 alg.Padding = PaddingMode.PKCS7;
                 Assert.Equal(PaddingMode.PKCS7, alg.Padding);
+
+                alg.FeedbackSize = 8;
+                Assert.Equal(8, alg.FeedbackSize);
             }
 
             using (var alg = Rijndael.Create())
@@ -291,6 +296,51 @@ namespace System.Security.Cryptography.Encryption.Rijndael.Tests
             Assert.Equal(ExpectedOutput, decrypted);
         }
 
+        [Theory]
+        [InlineData(128)]
+        [InlineData(8)]
+        [InlineData(null)]
+        public static void CfbFeedbackSizeIsRespected(int? feedbackSize)
+        {
+            // Windows 7 CFB only supports CFB8.
+            if (PlatformDetection.IsWindows7 && feedbackSize != 8)
+                return;
+
+            void Test(Rijndael alg)
+            {
+                alg.Mode = CipherMode.CFB;
+
+                if (feedbackSize == null)
+                {
+                    feedbackSize = alg.FeedbackSize;
+                }
+                else
+                {
+                    alg.FeedbackSize = feedbackSize.Value;
+                }
+
+                int feedbackSizeBytes = feedbackSize.Value / 8;
+                byte[] input = new byte[feedbackSizeBytes + 1];
+
+                using ICryptoTransform transform = alg.CreateEncryptor();
+
+                byte[] output = transform.TransformFinalBlock(input, 0, input.Length);
+                int expectedOutputSize = (input.Length / feedbackSizeBytes) * feedbackSizeBytes + feedbackSizeBytes;
+
+                Assert.Equal(expectedOutputSize, output.Length);
+            }
+
+            using (Rijndael alg = new RijndaelManaged())
+            {
+                Test(alg);
+            }
+
+            using (Rijndael alg = Rijndael.Create())
+            {
+                Test(alg);
+            }
+        }
+
         private class RijndaelLegalSizesBreaker : RijndaelMinimal
         {
             public RijndaelLegalSizesBreaker()