Class EncryptionModule
- Namespace
- Nucs.JsonSettings.Modulation
- Assembly
- Nucs.JsonSettings.dll
Encrypts the serialized configuration with a symmetric algorithm from the .NET base class
library (System.Security.Cryptography). The algorithm is selected with
Algorithm; the default, AesCbc, is
byte-for-byte compatible with every file this library has ever written.
public class EncryptionModule : Module, IDisposable
- Inheritance
-
EncryptionModule
- Implements
- Derived
- Inherited Members
Remarks
The secret can be supplied three ways:
- a text password (string/SecureString), stored as a SecureString and stretched into a key with PBKDF2;
- a binary password (byte[], via EncryptionModule(byte[])), stretched with the same PBKDF2 construction - a distinct credential from the text password with the same bytes;
- a raw key (byte[], via FromRawKey(byte[])), used verbatim with no derivation - its length must match the chosen algorithm.
Exactly one source is active per module. There is no algorithm or key-size marker in the file, so decrypting requires configuring the same Algorithm, KeySize and secret the file was written with - exactly as the password has always had to match.
Constructors
EncryptionModule(byte[])
A binary password. Run through the same PBKDF2 derivation as a text password - stretched and salted - but a DIFFERENT credential from the text password whose UTF-8 bytes happen to match.
public EncryptionModule(byte[] password)
Parameters
passwordbyte[]
EncryptionModule(Func<byte[]>)
A binary password resolved on demand. See EncryptionModule(byte[]).
public EncryptionModule(Func<byte[]> passwordFetcher)
Parameters
EncryptionModule(Func<byte[]>, bool)
protected EncryptionModule(Func<byte[]> fetcher, bool rawKey)
Parameters
EncryptionModule(Func<SecureString>)
public EncryptionModule(Func<SecureString> passwordFetcher)
Parameters
passwordFetcherFunc<SecureString>
EncryptionModule(Func<string>)
public EncryptionModule(Func<string> passwordFetcher)
Parameters
EncryptionModule(SecureString)
public EncryptionModule(SecureString password)
Parameters
passwordSecureString
EncryptionModule(string)
public EncryptionModule(string password)
Parameters
passwordstring
Fields
EmptyString
public static readonly SecureString EmptyString
Field Value
Properties
Algorithm
The symmetric algorithm used, by default AesCbc - the historical, on-disk-compatible format.
public virtual EncryptionAlgorithm Algorithm { get; set; }
Property Value
KeySize
The AES key size for the AES-based algorithms, by default Aes256. Ignored by ChaCha20Poly1305, which is always 256-bit.
public virtual KeySize KeySize { get; set; }
Property Value
Password
The password passed during constructor stored as a SecureString in memory.
public virtual SecureString Password { get; set; }
Property Value
Methods
AfterDecryptInternal(JsonSettings, ref byte[])
Wrong-password heuristic for the unauthenticated AesCbc
path. Runs once the whole decrypt chain has completed, so data is the
final plaintext this library is about to read as JSON.
protected virtual void AfterDecryptInternal(JsonSettings sender, ref byte[] data)
Parameters
senderJsonSettingsdatabyte[]
Remarks
WHY ONLY FOR AES-CBC. Every other algorithm authenticates during DecryptInternal(JsonSettings, ref byte[]) (a GCM/CCM/ChaCha20 tag, or the HMAC of AES-CBC-HMAC), so a wrong key or a tampered file already failed there with a real integrity error and never reaches here. AES-CBC alone cannot tell a wrong key from a right one: CBC decryption with the wrong key yields random bytes, and PKCS7 padding validation accepts those by chance roughly once in 256 attempts. When it does, decryption "succeeds", the garbage reaches the JSON parser, and the user is told the file is corrupt rather than that the password was wrong. Checking the final plaintext is valid UTF-8 is a diagnostic for that case.
WHY AFTER THE CHAIN AND NOT INSIDE DecryptInternal(JsonSettings, ref byte[]). The test rests on "what this library encrypts is always UTF-8 JSON", which holds only for the FINAL plaintext. A caller may attach another module inside the encryption layer - a compressor, an encoder - and then this module's own output is that module's still-encoded input, with no reason to be UTF-8. Deferring to AfterDecrypt inspects the plaintext no matter how many layers produced it.
This is a diagnostic improvement for AES-CBC, not an integrity guarantee - the authenticated algorithms are what provide that.
Attach(JsonSettings)
public override void Attach(JsonSettings socket)
Parameters
socketJsonSettings
CloneFetcher(byte[])
protected static Func<byte[]> CloneFetcher(byte[] material)
Parameters
materialbyte[]
Returns
Deattach(JsonSettings)
public override void Deattach(JsonSettings socket)
Parameters
socketJsonSettings
DecryptInternal(JsonSettings, ref byte[])
protected virtual void DecryptInternal(JsonSettings sender, ref byte[] data)
Parameters
senderJsonSettingsdatabyte[]
EncryptInternal(JsonSettings, ref byte[])
protected virtual void EncryptInternal(JsonSettings sender, ref byte[] data)
Parameters
senderJsonSettingsdatabyte[]
FromRawKey(byte[])
Builds a module that uses key verbatim as the key, with no key
derivation. For the default AES algorithms the key must be 16, 24 or 32 bytes; other
algorithms validate their own length when the module runs.
public static EncryptionModule FromRawKey(byte[] key)
Parameters
keybyte[]
Returns
FromRawKey(Func<byte[]>)
Builds a module that uses the key returned by keyFetcher verbatim,
with no key derivation. The key length must match the chosen algorithm each time it is
resolved.
public static EncryptionModule FromRawKey(Func<byte[]> keyFetcher)