-
Notifications
You must be signed in to change notification settings - Fork 4
ChaCha20
Initial .Net ChaCha20 codebase is from the first .Net implementation ChaCha20
by Scott Bennett. I also considered the NuGet package by Kaarlo Räihä This code updated to support streams and partial blocks and 8-byte nonce. Also ChaChaStream
is implemented on top of it to support simple wrapping of a network stream.
using River.ChaCha;
var stream = new ChaCha20Stream(networkStream, "p@$$word");
var data = Encoding.UTF8.GetBytes("Test Data");
stream.Write(data, 0, data.Length);
var c = stream.Read(buf, 0, buf.Length);
ChaCha20Stream
performs a nonce exchange in the beginning both for writer and reader. Algorythm starts from 0 counter and after that both reader and writer performs encryption with the same pre-shared key.
If you want to work manually with the algorythm:
new ChaCha20("p@$$word", 8); // create a cryptor for 8-byte nonce chacha20
new ChaCha20("p@$$word", 12); // create a cryptor for 12-byte nonce chacha20-ietf
you can pass a key instead of password:
byte[] key = GetKey();
new ChaCha20(key, 8);
you can pass a custom nonce (e.g. retrieved from 3rd party):
byte[] key = GetKey();
byte[] nonce = GetNonce();
new ChaCha20(key, nonce);
You can use Key Deviation Function manually. There is an original ShadowSocks function (MD5 based loop).
byte[] key = ChaCha20.Kdf("p@$$word"); // generates 32-byte deterministic key
Important difference of River from CSharp-ChaCha20 is how partial block handled
if you have 80 bytes, both implementations will process it in two blocks (first 64 second 16) but if you call it second time, River keep track of how many bytes is processed in current block. So, next call river start with 16 bytes remained in block #2 while their implementation will continue from block #3 (which makes it unusable for streams).
Simple API:
var encrypted = chaCha20.Encrypt(data);
Full streaming API (like Array.Copy
):
var bytesEncrypted = chaCha20.Encrypt(fromBuf, 0, toBuf, 0, fromBuf.Length);
bytesEncrypted is just for convinience, I might extract interface out of it in future. For ChaCha bytesEncrypted is always equal to length because this is basically just a XOR with a secret block
Algorythm is stateful. It contains a context of current block (including nonce, key, counter). It also includes bytes remained in block. Every call changes bytes remained in block and a counter.