diff --git a/.gitignore b/.gitignore index 7d6bdbf60..37793444d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ project.lock.json # Build outputs build/target/ + +# Benchmark results +BenchmarkDotNet.Artifacts/ diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt b/src/Data/Key.ECDSA.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA.Encrypted.txt rename to src/Data/Key.ECDSA.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA.txt b/src/Data/Key.ECDSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA.txt rename to src/Data/Key.ECDSA.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt b/src/Data/Key.ECDSA384.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA384.Encrypted.txt rename to src/Data/Key.ECDSA384.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt b/src/Data/Key.ECDSA384.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA384.txt rename to src/Data/Key.ECDSA384.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt b/src/Data/Key.ECDSA521.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA521.Encrypted.txt rename to src/Data/Key.ECDSA521.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt b/src/Data/Key.ECDSA521.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.ECDSA521.txt rename to src/Data/Key.ECDSA521.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.Encrypted.txt b/src/Data/Key.OPENSSH.ECDSA.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.Encrypted.txt rename to src/Data/Key.OPENSSH.ECDSA.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.txt b/src/Data/Key.OPENSSH.ECDSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA.txt rename to src/Data/Key.OPENSSH.ECDSA.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.Encrypted.txt b/src/Data/Key.OPENSSH.ECDSA384.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.Encrypted.txt rename to src/Data/Key.OPENSSH.ECDSA384.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.txt b/src/Data/Key.OPENSSH.ECDSA384.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA384.txt rename to src/Data/Key.OPENSSH.ECDSA384.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.Encrypted.txt b/src/Data/Key.OPENSSH.ECDSA521.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.Encrypted.txt rename to src/Data/Key.OPENSSH.ECDSA521.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.txt b/src/Data/Key.OPENSSH.ECDSA521.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ECDSA521.txt rename to src/Data/Key.OPENSSH.ECDSA521.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.Encrypted.txt b/src/Data/Key.OPENSSH.ED25519.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.Encrypted.txt rename to src/Data/Key.OPENSSH.ED25519.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.txt b/src/Data/Key.OPENSSH.ED25519.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.ED25519.txt rename to src/Data/Key.OPENSSH.ED25519.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.Encrypted.txt b/src/Data/Key.OPENSSH.RSA.Encrypted.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.Encrypted.txt rename to src/Data/Key.OPENSSH.RSA.Encrypted.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.txt b/src/Data/Key.OPENSSH.RSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.OPENSSH.RSA.txt rename to src/Data/Key.OPENSSH.RSA.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.128.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Aes.128.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.128.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Aes.128.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.192.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Aes.192.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.192.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Aes.192.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.256.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Aes.256.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Aes.256.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Aes.256.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Des.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Des.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt b/src/Data/Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt rename to src/Data/Key.RSA.Encrypted.Des.Ede3.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt b/src/Data/Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt rename to src/Data/Key.RSA.Encrypted.Des.Ede3.CFB.1234567890.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.RSA.txt b/src/Data/Key.RSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.RSA.txt rename to src/Data/Key.RSA.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt b/src/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt rename to src/Data/Key.SSH2.DSA.Encrypted.Des.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.SSH2.DSA.txt b/src/Data/Key.SSH2.DSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.SSH2.DSA.txt rename to src/Data/Key.SSH2.DSA.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt b/src/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt rename to src/Data/Key.SSH2.RSA.Encrypted.Des.CBC.12345.txt diff --git a/src/Renci.SshNet.Tests/Data/Key.SSH2.RSA.txt b/src/Data/Key.SSH2.RSA.txt similarity index 100% rename from src/Renci.SshNet.Tests/Data/Key.SSH2.RSA.txt rename to src/Data/Key.SSH2.RSA.txt diff --git a/src/Renci.SshNet.Benchmarks/Program.cs b/src/Renci.SshNet.Benchmarks/Program.cs new file mode 100644 index 000000000..3249baa99 --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Program.cs @@ -0,0 +1,27 @@ +using BenchmarkDotNet.Running; + +namespace Renci.SshNet.Benchmarks +{ + class Program + { + static void Main(string[] args) + { + // Usage examples: + // 1. Run all benchmarks: + // dotnet run -c Release -- --filter * + // 2. List all benchmarks: + // dotnet run -c Release -- --list flat + // 3. Run a subset of benchmarks based on a filter (of a benchmark method's fully-qualified name, + // e.g. "Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers.AesCipherBenchmarks.Encrypt_CBC"): + // dotnet run -c Release -- --filter *Ciphers* + // 4. Run benchmarks and include memory usage statistics in the output: + // dotnet run -c Release -- filter *Rsa* --memory + // 3. Print help: + // dotnet run -c Release -- --help + + // See also https://benchmarkdotnet.org/articles/guides/console-args.html + + _ = BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + } + } +} diff --git a/src/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj b/src/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj new file mode 100644 index 000000000..daf3f5095 --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Renci.SshNet.Benchmarks.csproj @@ -0,0 +1,31 @@ + + + + Exe + net7.0 + enable + enable + + $(NoWarn);CS1591 + + + + + + + + + + + + + + + diff --git a/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs b/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs new file mode 100644 index 000000000..ff414cc4a --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/AesCipherBenchmarks.cs @@ -0,0 +1,38 @@ +using BenchmarkDotNet.Attributes; +using Renci.SshNet.Security.Cryptography.Ciphers; +using Renci.SshNet.Security.Cryptography.Ciphers.Modes; + +namespace Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers +{ + [MemoryDiagnoser] + public class AesCipherBenchmarks + { + private readonly byte[] _key; + private readonly byte[] _iv; + private readonly byte[] _data; + + public AesCipherBenchmarks() + { + _key = new byte[32]; + _iv = new byte[16]; + _data = new byte[256]; + + Random random = new(Seed: 12345); + random.NextBytes(_key); + random.NextBytes(_iv); + random.NextBytes(_data); + } + + [Benchmark] + public byte[] Encrypt_CBC() + { + return new AesCipher(_key, new CbcCipherMode(_iv), null).Encrypt(_data); + } + + [Benchmark] + public byte[] Decrypt_CBC() + { + return new AesCipher(_key, new CbcCipherMode(_iv), null).Decrypt(_data); + } + } +} diff --git a/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs b/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs new file mode 100644 index 000000000..f25e6db71 --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Security/Cryptography/Ciphers/RsaCipherBenchmarks.cs @@ -0,0 +1,48 @@ +using BenchmarkDotNet.Attributes; + +using Renci.SshNet.Security; +using Renci.SshNet.Security.Cryptography.Ciphers; + +namespace Renci.SshNet.Benchmarks.Security.Cryptography.Ciphers +{ + [MemoryDiagnoser] + public class RsaCipherBenchmarks + { + private readonly RsaKey _privateKey; + private readonly RsaKey _publicKey; + private readonly byte[] _data; + + public RsaCipherBenchmarks() + { + _data = new byte[128]; + + Random random = new(Seed: 12345); + random.NextBytes(_data); + + using (var s = typeof(RsaCipherBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.RSA.txt")) + { + _privateKey = (RsaKey)((KeyHostAlgorithm) new PrivateKeyFile(s).HostKey).Key; + + // The implementations of RsaCipher.Encrypt/Decrypt differ based on whether the supplied RsaKey has private key information + // or only public. So we extract out the public key information to a separate variable. + _publicKey = new RsaKey() + { + Public = _privateKey.Public + }; + } + } + + [Benchmark] + public byte[] Encrypt() + { + return new RsaCipher(_publicKey).Encrypt(_data); + } + + // RSA Decrypt does not work + // [Benchmark] + // public byte[] Decrypt() + // { + // return new RsaCipher(_privateKey).Decrypt(_data); + // } + } +} diff --git a/src/Renci.SshNet.Benchmarks/Security/Cryptography/ED25519DigitalSignatureBenchmarks.cs b/src/Renci.SshNet.Benchmarks/Security/Cryptography/ED25519DigitalSignatureBenchmarks.cs new file mode 100644 index 000000000..2f07a2d3a --- /dev/null +++ b/src/Renci.SshNet.Benchmarks/Security/Cryptography/ED25519DigitalSignatureBenchmarks.cs @@ -0,0 +1,41 @@ +using BenchmarkDotNet.Attributes; + +using Renci.SshNet.Security; +using Renci.SshNet.Security.Cryptography; + +namespace Renci.SshNet.Benchmarks.Security.Cryptography +{ + [MemoryDiagnoser] + public class ED25519DigitalSignatureBenchmarks + { + private readonly ED25519Key _key; + private readonly byte[] _data; + private readonly byte[] _signature; + + public ED25519DigitalSignatureBenchmarks() + { + _data = new byte[128]; + + Random random = new(Seed: 12345); + random.NextBytes(_data); + + using (var s = typeof(ED25519DigitalSignatureBenchmarks).Assembly.GetManifestResourceStream("Renci.SshNet.Benchmarks.Data.Key.OPENSSH.ED25519.txt")) + { + _key = (ED25519Key) ((KeyHostAlgorithm) new PrivateKeyFile(s).HostKey).Key; + } + _signature = new ED25519DigitalSignature(_key).Sign(_data); + } + + [Benchmark] + public byte[] Sign() + { + return new ED25519DigitalSignature(_key).Sign(_data); + } + + [Benchmark] + public bool Verify() + { + return new ED25519DigitalSignature(_key).Verify(_data, _signature); + } + } +} diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index 94a4e8015..aa9d79a2a 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -14,33 +14,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/src/Renci.SshNet.sln b/src/Renci.SshNet.sln index 34cc0f483..459d129c4 100644 --- a/src/Renci.SshNet.sln +++ b/src/Renci.SshNet.sln @@ -42,6 +42,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D21A4D03-0 ..\test\Directory.Build.props = ..\test\Directory.Build.props EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.Benchmarks", "Renci.SshNet.Benchmarks\Renci.SshNet.Benchmarks.csproj", "{A8C83FF2-B733-4A01-8D4E-D6DA2D420484}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.IntegrationTests", "Renci.SshNet.IntegrationTests\Renci.SshNet.IntegrationTests.csproj", "{EEF98046-729C-419E-932D-4E569073C8CC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.TestTools.OpenSSH", "Renci.SshNet.TestTools.OpenSSH\Renci.SshNet.TestTools.OpenSSH.csproj", "{78239046-2019-494E-B6EC-240AF787E4D0}" @@ -88,6 +90,26 @@ Global {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|Mixed Platforms.Build.0 = Release|Any CPU {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x64.ActiveCfg = Release|Any CPU {C45379B9-17B1-4E89-BC2E-6D41726413E8}.Release|x86.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|ARM.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|ARM.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|x64.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|x64.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|x86.ActiveCfg = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Debug|x86.Build.0 = Debug|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|Any CPU.Build.0 = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|ARM.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|ARM.Build.0 = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|x64.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|x64.Build.0 = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|x86.ActiveCfg = Release|Any CPU + {A8C83FF2-B733-4A01-8D4E-D6DA2D420484}.Release|x86.Build.0 = Release|Any CPU {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|Any CPU.Build.0 = Debug|Any CPU {EEF98046-729C-419E-932D-4E569073C8CC}.Debug|ARM.ActiveCfg = Debug|Any CPU