protected void OneShotRoundtripTest(byte[] plaintext, byte[] ciphertext, PaddingMode padding, CipherMode mode, int feedbackSize = 0) { using (SymmetricAlgorithm alg = CreateAlgorithm()) { int paddingSizeBytes = mode == CipherMode.CFB ? feedbackSize / 8 : alg.BlockSize / 8; alg.Key = Key; // Set the instance to use a different mode and padding than what will be used // in the one-shots to test that the one shot "wins". alg.FeedbackSize = 8; alg.Mode = mode == CipherMode.ECB ? CipherMode.CBC : CipherMode.ECB; alg.Padding = padding == PaddingMode.None ? PaddingMode.PKCS7 : PaddingMode.None; byte[] encrypted = mode switch { CipherMode.ECB => alg.EncryptEcb(plaintext, padding), CipherMode.CBC => alg.EncryptCbc(plaintext, IV, padding), CipherMode.CFB => alg.EncryptCfb(plaintext, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; byte[] decrypted = mode switch { CipherMode.ECB => alg.DecryptEcb(encrypted, padding), CipherMode.CBC => alg.DecryptCbc(encrypted, IV, padding), CipherMode.CFB => alg.DecryptCfb(encrypted, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; AssertPlaintexts(plaintext, decrypted, padding); AssertCiphertexts(encrypted, ciphertext, padding, paddingSizeBytes); decrypted = mode switch { CipherMode.ECB => alg.DecryptEcb(ciphertext, padding), CipherMode.CBC => alg.DecryptCbc(ciphertext, IV, padding), CipherMode.CFB => alg.DecryptCfb(ciphertext, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; encrypted = mode switch { CipherMode.ECB => alg.EncryptEcb(decrypted, padding), CipherMode.CBC => alg.EncryptCbc(decrypted, IV, padding), CipherMode.CFB => alg.EncryptCfb(decrypted, IV, padding, feedbackSize), _ => throw new NotImplementedException(), }; AssertPlaintexts(plaintext, decrypted, padding); AssertCiphertexts(ciphertext, encrypted, padding, paddingSizeBytes); } }
internal static void VerifyPersistedKey( string keyName, int plainBytesCount, Func <string, SymmetricAlgorithm> persistedFunc, Func <SymmetricAlgorithm> ephemeralFunc, CipherMode cipherMode, PaddingMode paddingMode, int feedbackSizeInBits) { byte[] plainBytes = GenerateRandom(plainBytesCount); using (SymmetricAlgorithm persisted = persistedFunc(keyName)) using (SymmetricAlgorithm ephemeral = ephemeralFunc()) { persisted.Mode = ephemeral.Mode = cipherMode; persisted.Padding = ephemeral.Padding = paddingMode; if (cipherMode == CipherMode.CFB) { persisted.FeedbackSize = ephemeral.FeedbackSize = feedbackSizeInBits; } ephemeral.Key = persisted.Key; ephemeral.GenerateIV(); persisted.IV = ephemeral.IV; using (ICryptoTransform persistedEncryptor = persisted.CreateEncryptor()) using (ICryptoTransform persistedDecryptor = persisted.CreateDecryptor()) using (ICryptoTransform ephemeralEncryptor = ephemeral.CreateEncryptor()) { Assert.True( persistedEncryptor.CanTransformMultipleBlocks, "Pre-condition: persistedEncryptor.CanTransformMultipleBlocks"); byte[] persistedEncrypted = persistedEncryptor.TransformFinalBlock(plainBytes, 0, plainBytesCount); byte[] ephemeralEncrypted = ephemeralEncryptor.TransformFinalBlock(plainBytes, 0, plainBytesCount); Assert.Equal(ephemeralEncrypted, persistedEncrypted); byte[] cipherBytes = persistedEncrypted; byte[] persistedDecrypted = persistedDecryptor.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length); byte[] expectedBytes = plainBytes; if (persistedDecrypted.Length > plainBytes.Length) { // This should only ever happen in Assert.Equal(PaddingMode.Zeros, paddingMode); expectedBytes = new byte[persistedDecrypted.Length]; Buffer.BlockCopy(plainBytes, 0, expectedBytes, 0, plainBytesCount); } Assert.Equal(expectedBytes, persistedDecrypted); } byte[] oneShotPersistedEncrypted = null; byte[] oneShotEphemeralEncrypted = null; byte[] oneShotPersistedDecrypted = null; if (cipherMode == CipherMode.ECB) { oneShotPersistedEncrypted = persisted.EncryptEcb(plainBytes, paddingMode); oneShotEphemeralEncrypted = ephemeral.EncryptEcb(plainBytes, paddingMode); oneShotPersistedDecrypted = persisted.DecryptEcb(oneShotEphemeralEncrypted, paddingMode); } else if (cipherMode == CipherMode.CBC) { oneShotPersistedEncrypted = persisted.EncryptCbc(plainBytes, persisted.IV, paddingMode); oneShotEphemeralEncrypted = ephemeral.EncryptCbc(plainBytes, ephemeral.IV, paddingMode); oneShotPersistedDecrypted = persisted.DecryptCbc(oneShotEphemeralEncrypted, persisted.IV, paddingMode); } else if (cipherMode == CipherMode.CFB) { oneShotPersistedEncrypted = persisted.EncryptCfb(plainBytes, persisted.IV, paddingMode, feedbackSizeInBits); oneShotEphemeralEncrypted = ephemeral.EncryptCfb(plainBytes, ephemeral.IV, paddingMode, feedbackSizeInBits); oneShotPersistedDecrypted = persisted.DecryptCfb(oneShotEphemeralEncrypted, persisted.IV, paddingMode, feedbackSizeInBits); } if (oneShotPersistedEncrypted is not null) { Assert.Equal(oneShotEphemeralEncrypted, oneShotPersistedEncrypted); if (paddingMode == PaddingMode.Zeros) { byte[] plainPadded = new byte[oneShotPersistedDecrypted.Length]; plainBytes.AsSpan().CopyTo(plainPadded); Assert.Equal(plainPadded, oneShotPersistedDecrypted); } else { Assert.Equal(plainBytes, oneShotPersistedDecrypted); } } } }