-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Rationale
There is a general need for a number of ciphers for encryption. Todays mix of interfaces and classes has become a little disjointed. Also there is no support for AEAD style ciphers as they need the ability to provide extra authentication information. The current designs are also prone to allocation and these are hard to avoid due to the returning arrays.
Proposed API
A general purpose abstract base class that will be implemented by concrete classes. This will allow for expansion and also by having a class rather than static methods we have the ability to make extension methods as well as hold state between calls. The API should allow for recycling of the class to allow for lower allocations (not needing a new instance each time, and to catch say unmanaged keys). Due to the often unmanaged nature of resources that are tracked the class should implement IDisposable
public abstract class Cipher : IDisposable
{
public virtual int TagSize { get; }
public virtual int IVSize { get; }
public virtual int BlockSize { get; }
public virtual bool SupportsAssociatedData { get; }
public abstract void Init(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv);
public abstract void Init(ReadOnlySpan<byte> iv);
public abstract int Update(ReadOnlySpan<byte> input, Span<byte> output);
public abstract int Finish(ReadOnlySpan<byte> input, Span<byte> output);
public abstract void AddAssociatedData(ReadOnlySpan<byte> associatedData);
public abstract int GetTag(Span<byte> span);
public abstract void SetTag(ReadOnlySpan<byte> tagSpan);
}Example Usage
(the input/output source is a mythical span based stream like IO source)
using (var cipher = new AesGcmCipher(bitsize: 256))
{
cipher.Init(myKey, nonce);
while (!inputSource.EOF)
{
var inputSpan = inputSource.ReadSpan(cipher.BlockSize);
cipher.Update(inputSpan);
outputSource.Write(inputSpan);
}
cipher.AddAssociatedData(extraInformation);
cipher.Finish(finalBlockData);
cipher.GetTag(tagData);
}API Behaviour
- If get tag is called before finish a [exception type?] should be thrown and the internal state should be set to invalid
- If the tag is invalid on finish for decrypt it should be an exception thrown
- Once finished is called, a call to anything other than one of the Init methods will throw
- Once Init is called, a second call without "finishing" will throw
- If the type expects an key supplied (a straight "new'd" up instance) if the Initial "Init" call only has an IV it will throw
- If the type was generated say from a store based key and you attempt to change the key via Init and not just the IV it will throw
- If get tag is not called before dispose or Init should an exception be thrown? To stop the user not collecting the tag by accident?
Reference dotnet/corefx#7023
Updates
- Changed nonce to IV.
- Added behaviour section
- Removed the single input/output span cases from finish and update, they can just be extension methods
- Changed a number of spans to readonlyspan as suggested by @bartonjs
- Removed Reset, Init with IV should be used instead