Use custom encryption
SolFS includes built-in support for powerful and secure symmetric encryption using 256-bit AES algorithm. Encryption can be applied to individual file (each file having it's own encryption key), to the whole storage (encryption is applied to pages of the storage, rather than to file pages) or both.
However, there are certain scenarious such as PKI-based encryption or Digital Rights Management (discussed in the knowledgebase articles), which require more sophisticated security techniques. In this case you can use custom encryption.
Custom encryption is specified by setting encryption mode for the file or storage to ecCustom. In this mode SolFS will fire various OnData* and OnHash* events (see the details below) which you must handle. The events are used to encrypt or decrypt the data, to calculate hash and to validate the existing hash (i.e. check if the data corresponds to the stored hash).
In APIs that have events (VCL, .NET, C++ class) the event handlers are set by assigning them to events, exposed by SolFSStorage class. Alternatively you can pass the event handlers as parameters to CreateOpenCB() constructor.
You can use any encryption algorithm that you need to encrypt the data. There exists one limitation though - encryption or decryption operation may adjust the size of the data block only within 32-byte boundary. This is fine for symmetric encryption algorithms, which perform only transformation of bytes (in ECB mode they may expand the block size to 16- or 32-byte boundary). However if you want to keep any additional information about the encrypted data, you would need to store this information somewhere else (for example, in file tags or alternate streams for files or in RootData for the storage itself).
Encryption and decryption events have Key parameter. This parameter is used to pass the password, set for the storage or the individual file, to the event handler. You can set the file password to be the actual key used for encryption (and you will receive this password via Key parameter in your encryption/decryption event handlers) or you can pass some key identifier there.
Passing the key identifier instead of the encryption key is used in the following cases:
- when the encryption key itself is not extractable (for example, when it's stored on cryptographic hardware device);
- when you operate with keys which are not text strings (for example, X.509 certificates or OpenPGP keys;
- to strengthen security of the keys, which are embedded into the application code. In this case you can obfuscate the application code which retrieves and uses the key and you don't need to pass the key to the library and back.
Due to certain design specifics, encryption and decryption events don't include the name of the file being encrypted/decrypted. If you need the file names, you can take the following approach: pass the file name (or the hash of the file name or file ID) in the Key parameter together with the encryption key or key identifier.
Remember that encryption handler (especially in case of whole-storage encryption) is invoked really frequently so key lookup using the key identifier must be as fast as possible.
Hashes are used to determine correctness of the provided encryption key. If you use custom encryption, you must provide event handlers for hash calculation and validation. However you are not mandated to perform actual hashing operations. If you want to further strengthen security or you have other means for controlling correctness of the key, you can return some constant value as a result of hash calculation, and return True as a result of hash validation.
Here's the list of events that your application needs to handle:
- OnDataEncrypt. You must encrypt the data block.
- OnDataDecrypt. You must decrypt the data block.
- OnHashCalculate. You must calculate hash of the data block.
- OnHashValidate. You must check validity of the hash by calculating the hash of the given data block and comparing the calculated hash with the given hash.