public void Test_Encrypt_ReturnsNonZeroCiphertext(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var nonce  = XChaChaNonce.Generate();

                var message    = TestConstants.MessageBytes;
                var ciphertext = cipher.Encrypt(message, key, nonce);

                Assert.False(ciphertext.All(b => b == 0));
            }
        }
        public void Test_Decrypt_CanDecryptCiphertext(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher  = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var nonce   = XChaChaNonce.Generate();
                var message = TestConstants.MessageBytes;

                var ciphertext = cipher.Encrypt(message, key, nonce);
                var result     = cipher.Decrypt(ciphertext, key, nonce);

                Assert.Equal(message, result);
            }
        }
        public void Test_Encrypt_WithAdditionalData_ReturnsNonZeroOutput()
        {
            using (var key = XChaChaKey.Generate())
            {
                var aeadCipher     = new XChaChaAeadCipher();
                var nonce          = XChaChaNonce.Generate();
                var additionalData = Encoding.UTF8.GetBytes(DateTime.Now.ToString());

                var message    = RandomBytesGenerator.NextBytes(1024 * 1024);
                var ciphertext = aeadCipher.Encrypt(message, key, nonce, additionalData);

                Assert.False(ciphertext.All(b => b == 0));
            }
        }
        public void Test_Decrypt_ReturnsMessage(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var nonce  = XChaChaNonce.Generate();

                const int messageLength = 1024 * 1024;
                var       message       = RandomBytesGenerator.NextBytes(messageLength);
                var       ciphertext    = cipher.Encrypt(message, key, nonce);

                var result = cipher.Decrypt(ciphertext, key, nonce);
                Assert.Equal(message, result);
            }
        }
        public void Test_Encrypt_ProducesNonZeroOutput(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var nonce  = XChaChaNonce.Generate();

                var message    = TestConstants.MessageBytes;
                var ciphertext = new byte[cipher.GetCipherTextLength(message.Length)];

                cipher.Encrypt(message, ciphertext, key, nonce);

                Assert.False(ciphertext.All(b => b == 0));
            }
        }
        public void Test_Encrypt_NonceEmpty_ThrowsArgumentException(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var    cipher  = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var    message = TestConstants.MessageBytes;
                var    nonce   = new XChaChaNonce();
                Action action  = () =>
                {
                    cipher.Encrypt(message, key, nonce);
                };

                var exception = Assert.Throws <ArgumentException>(action);
                Assert.Equal("nonce is empty", exception.Message);
            }
        }
        public void Test_Decrypt_WithAdditionalData_Success()
        {
            using (var key = XChaChaKey.Generate())
            {
                var aeadCipher     = new XChaChaAeadCipher();
                var nonce          = XChaChaNonce.Generate();
                var additionalData = Encoding.UTF8.GetBytes(DateTime.Now.ToString());

                const int messageLength = 1024 * 1024;
                var       message       = RandomBytesGenerator.NextBytes(messageLength);
                var       ciphertext    = aeadCipher.Encrypt(message, key, nonce, additionalData);

                var result = aeadCipher.Decrypt(ciphertext, key, nonce, additionalData);
                Assert.Equal(message, result);
            }
        }
        public void Test_Encrypt_CiphertextBufferTooSmall_ThrowsArgumentException(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var    cipher     = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var    message    = TestConstants.MessageBytes;
                var    ciphertext = Array.Empty <byte>();
                var    nonce      = XChaChaNonce.Generate();
                Action action     = () =>
                {
                    cipher.Encrypt(message, ciphertext, key, nonce);
                };

                var exception = Assert.Throws <ArgumentException>(action);
                Assert.Equal("ciphertext buffer is not large enough", exception.Message);
            }
        }
        public void Test_TryDecrypt_Fails_ReturnsFalse(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var nonce  = XChaChaNonce.Generate();

                const int messageLength = 1024 * 1024;
                var       message       = RandomBytesGenerator.NextBytes(messageLength);
                var       ciphertext    = new byte[cipher.GetCipherTextLength(message.Length)];

                cipher.Encrypt(message, ciphertext, key, nonce);

                var wrongNonce = XChaChaNonce.Generate();
                var result     = cipher.TryDecrypt(ciphertext, ciphertext, key, wrongNonce);
                Assert.False(result);
            }
        }
        public void Test_Decrypt_MessageBufferTooSmall_ThrowsArgumentException(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher     = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var message    = Array.Empty <byte>();
                var ciphertext = new byte[cipher.GetCipherTextLength(1)];
                var nonce      = XChaChaNonce.Generate();

                Action action = () =>
                {
                    cipher.Decrypt(ciphertext, message, key, nonce);
                };

                var exception = Assert.Throws <ArgumentException>(action);
                Assert.Equal("message buffer is not large enough", exception.Message);
            }
        }
        public void Test_EncryptWithReturn_MaxMessageLengthExceeded_ThrowsException(Type cipherType)
        {
            unsafe
            {
                using (var key = XChaChaKey.Generate())
                {
                    var    cipher = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                    Action action = () =>
                    {
                        var message = new ReadOnlySpan <byte>(IntPtr.Zero.ToPointer(), int.MaxValue);
                        var nonce   = XChaChaNonce.Generate();
                        cipher.Encrypt(message, key, nonce);
                    };

                    var exception = Assert.Throws <ArgumentException>(action);
                    Assert.Equal("message is too long", exception.Message);
                }
            }
        }
        public void Test_TryDecrypt_CanDecryptCiphertext(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var nonce  = XChaChaNonce.Generate();

                const int messageLength = 1024 * 1024;
                var       message       = RandomBytesGenerator.NextBytes(messageLength);
                var       ciphertext    = new byte[cipher.GetCipherTextLength(message.Length)];

                cipher.Encrypt(message, ciphertext, key, nonce);

                var decryptedMessage = new byte[messageLength];
                var result           = cipher.TryDecrypt(ciphertext, decryptedMessage, key, nonce);
                Assert.True(result);
                Assert.Equal(message, decryptedMessage);
            }
        }
        public void Test_Decrypt_WrongNonce_ThrowsException(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher  = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var nonce   = XChaChaNonce.Generate();
                var message = TestConstants.MessageBytes;

                var    ciphertext = cipher.Encrypt(message, key, nonce);
                Action action     = () =>
                {
                    var wrongNonce = XChaChaNonce.Generate();
                    var result     = cipher.Decrypt(ciphertext, key, wrongNonce);
                };

                var exception = Assert.Throws <CryptographicException>(action);
                Assert.Equal("decryption failed", exception.Message);
            }
        }
        public void Test_Decrypt_WithInvalidAdditionalData_Fails()
        {
            using (var key = XChaChaKey.Generate())
            {
                var aeadCipher     = new XChaChaAeadCipher();
                var nonce          = XChaChaNonce.Generate().ToArray();
                var additionalData = Encoding.UTF8.GetBytes(DateTime.Now.ToString());

                const int messageLength = 1024 * 1024;
                var       message       = RandomBytesGenerator.NextBytes(messageLength);
                var       ciphertext    = aeadCipher.Encrypt(message, key, new XChaChaNonce(nonce), additionalData);

                var    invalidAdditionalData = Encoding.UTF8.GetBytes("banana");
                Action action    = () => { aeadCipher.Decrypt(ciphertext, key, new XChaChaNonce(nonce), invalidAdditionalData); };
                var    exception = Assert.Throws <CryptographicException>(action);

                Assert.Equal("decryption failed", exception.Message);
            }
        }
        public void Test_EncryptWithReturn_MaxMessageLengthExceeded_ThrowsException()
        {
            unsafe
            {
                using (var key = XChaChaKey.Generate())
                {
                    var    aeadCipher = new XChaChaAeadCipher();
                    Action action     = () =>
                    {
                        var message        = new ReadOnlySpan <byte>(IntPtr.Zero.ToPointer(), int.MaxValue);
                        var additionalData = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
                        var nonce          = XChaChaNonce.Generate();
                        aeadCipher.Encrypt(message, key, nonce, additionalData);
                    };

                    var exception = Assert.Throws <ArgumentException>(action);
                    Assert.Equal("message is too long", exception.Message);
                }
            }
        }
        public void Test_Decrypt_Fails_ThrowsCryptographicException(Type cipherType)
        {
            using (var key = XChaChaKey.Generate())
            {
                var cipher = (XChaChaSecretKeyCipher)Activator.CreateInstance(cipherType);
                var nonce  = XChaChaNonce.Generate();

                const int messageLength = 1024 * 1024;
                var       message       = RandomBytesGenerator.NextBytes(messageLength);
                var       ciphertext    = new byte[cipher.GetCipherTextLength(message.Length)];

                cipher.Encrypt(message, ciphertext, key, nonce);

                Action action = () =>
                {
                    var wrongNonce = XChaChaNonce.Generate();
                    cipher.Decrypt(ciphertext, ciphertext, key, wrongNonce);
                };
                var exception = Assert.Throws <CryptographicException>(action);
                Assert.Equal("decryption failed", exception.Message);
            }
        }