Roydl.Crypto provides a simple, generic way to hash virtually any .NET type. Generic extensions are available for almost every type, and a growing set of algorithms is offered — some of which are performance-optimized and likely more powerful than any other pure C# library of their kind.
- Prerequisites
- Install
- Checksum Algorithms
- Checksum Performance
- Usage
- Encryption Algorithms
- Would you like to help?
- .NET 10 LTS or higher
- Supported platforms: Windows, Linux, macOS
- Hardware acceleration (optional): SSE4.2, SSSE3, AVX2, or AVX-512 capable CPU
$ dotnet add package Roydl.Crypto
| Name | Bit Width | Algorithm | Type | Hardware Support |
|---|---|---|---|---|
| Adler-32 | 32-bit | Standard | Cyclic | AVX-512 AVX2 SSSE3 |
| CRC | from 8-bit to 82-bit |
88 presets available + customizable | Cyclic | iSCSI @ SSE4.2 CPU iSCSI+PKZip @ ARM |
| MD5 | 128-bit | Built-in + HMAC keyed-hash support | Cryptographic | See ¹ |
| SHA1 | 160-bit | Built-in + HMAC keyed-hash support | Cryptographic | See ¹ |
| SHA2 | 256-bit 384-bit 512-bit |
Built-in + HMAC keyed-hash support | Cryptographic | See ¹ |
| SHA3 | 256-bit 384-bit 512-bit |
Built-in + HMAC keyed-hash support | Cryptographic | See ¹ |
¹ Hardware acceleration for cryptographic algorithms is handled transparently by the .NET runtime and depends on the underlying platform (e.g. Intel SHA Extensions for SHA1 and SHA2-256 on supported CPUs).
Especially for Adler and CRC, the performance in software mode should be much better than with any other pure C# library, but similar to libraries that work with C/C++ imports. However, I couldn't find any other library with hardware support, not even with imports.
| Algorithm | Library | Mode | Speed |
|---|---|---|---|
| Adler-32 | This | Software | 3028.4 MiB/s |
| Adler-32 | This | Hardware (SSSE3) | 36449.9 MiB/s |
| Adler-32 | This | Hardware (AVX2) | 73427.1 MiB/s |
| CRC-32 | Crc32.NET | Software | 2334.3 MiB/s |
| CRC-32 | This | Software | 3098.6 MiB/s |
| CRC-32 | This | Hardware | 12910.5 MiB/s |
| SHA2-256 | Built-in | Software | 2282.7 MiB/s |
| SHA3-256 | Built-in | Software | 670.5 MiB/s |
Benchmark methodology
| Component | Details |
|---|---|
| CPU | AMD Ryzen 5 7600 (6C/12T, 5.1 GHz boost) |
| RAM | 32 GB DDR5 |
| OS | Manjaro Linux (Kernel 6.19.2-1) |
| Runtime | .NET 10 |
| Build | Release (dotnet run -c Release) |
In the test case, a 64 KiB packet with random bytes is generated, which is sent over and over again within 9 seconds by the function that computes the hash. During this process, it is determined several times how much data could be hashed within 1 second. 9 seconds proved to be the optimal duration — increasing this time does not provide more accurate results. Running multiple repetitions yields more reliable results: all individual values are recorded, outliers identified via min/max comparison, and the final result taken as the average of 20 runs. You can find the test case here.
The GetChecksum extension method retrieves a string representation of the computed hash.
The value can be almost anything: bool, sbyte, byte, short, ushort, char, int, uint, long, ulong, Half, float, double, decimal, Enum, IntPtr, UIntPtr, Vector{T}, Vector2, Vector3, Vector4, Matrix3x2, Matrix4x4, Plane, Quaternion, Complex, BigInteger, DateTime, DateTimeOffset, TimeSpan, Guid, Rune, Stream, StreamReader, FileInfo, any IEnumerable{T} byte sequence (e.g. Array), any IEnumerable{T} char sequence (e.g. string), and many more.
Not every type is a meaningful candidate for hashing, but all are supported for completeness.
string hash = value.GetChecksum(ChecksumAlgo.Sha1);
Console.WriteLine(hash);
// Output:
// 12a5ba5baa1664f73e6279f23354bd90c8981a81A string containing a file path has an additional method:
string hash = value.GetFileChecksum(); // SHA-256 is used when `ChecksumAlgo` is undefinedThe GetCipher extension method retrieves an unsigned 64-bit integer representation of the computed hash. It follows the same rules outlined above. This can be useful with cyclic computed hashes.
ulong hash = value.GetCipher(ChecksumAlgo.Crc64);Note that HMAC keyed-hashing is only supported for cryptographic algorithms via instances by setting a secret key.
Sha3 instance = Sha3.Create(new byte[128] { /* some bytes */ });The ComputeHash methods use the secret key until DestroySecretKey is called.
instance.ComputeHash(value);An instance provides a computed hash in several variants:
ReadOnlySpan<byte> rawHash = instance.RawHash;
BigInteger cipher = instance.CipherHash; // The integral type depends on the bit length, e.g. CRC-32 is `UInt32`
string lowercase = instance.Hash;
string uppercase = instance.ToString(true);Casting is also supported to get a hash:
byte[] copyOfRawHash = (byte[])instance;
ulong cipher = (ulong)instance; // Numeric conversions are unchecked conversions of the `instance.CipherHash` field
string lowercase = (string)instance;Instances also provide equality operators for quick comparison:
bool equ = (instance1 == instance2);
bool neq = (instance1 != instance2);If you need a different CRC algorithm, you can easily create your own variation.
This is an example for CRC-32/POSIX, but it should support many others from 8-bit to almost infinite bits.
const int width = 32;
const uint check = 0x765e7680u;
const uint poly = 0x04c11db7u;
const uint init = 0x00000000u;
const bool refIn = false;
const bool refOut = false;
const uint xorOut = 0xffffffffu;
const uint mask = 0xffffffffu;
const bool skipValidation = false;Sets a new CrcConfig with the constants from above. The data are automatically validated with the given check.
var cfg = new CrcConfig32(width, check, poly, init, refIn, refOut, xorOut, mask, skipValidation);Compute the hash directly via the configuration structure:
cfg.ComputeHash(stream, out uint cipher);Or load it into the CRC class which has more features, and compute the hash code from there.
The value can be of type Stream, byte[], string, FileInfo, or a string containing a file path.
var crc = new Crc<uint>(config);
crc.ComputeHash(value);As mentioned earlier, instances offer computed hashes in several variants:
ReadOnlyMemory<byte> rawHash = crc.RawHash;
uint cipher = crc.CipherHash;
string lowercase = crc.Hash;Check out the CRC configuration manager to see more examples.
| Name | Algorithm |
|---|---|
| Rijndael (AES) | 128-bit block size; optional: 128, 192, or 256-bit key size, configurable cipher and padding modes |
Note: Rijndael with a 128-bit block size is equivalent to AES. For modern applications, prefer
SymmetricKeySize.Large(256-bit) and an authenticated cipher mode such as GCM where possible.
byte[] password = new byte[] { /* some bytes */ };
byte[] salt = new byte[] { /* some bytes */ };
using var aes = new Rijndael(password, salt, 1000, SymmetricKeySize.Large);
aes.Encrypt(streamToEncrypt, encryptedStream);
aes.Decrypt(streamToDecrypt, decryptedStream);- Star this Project ⭐ and show me that this project interests you 🤗
- Open an Issue ☕ to give me your feedback and tell me your ideas and wishes for the future 😎
- Open a Ticket 📫 if you don't have a GitHub account, you can contact me directly on my website 😉
- Donate by PayPal 💸 to buy me some cakes 🍰