libcdoc 0.1.8
|
This document provides an overview of how to use the libcdoc library for encryption and decryption workflows.
The libcdoc library supports two container formats:
The libcdoc library is built around three main components that work together to handle encryption, decryption, and key management. It is compatible with Windows, macOS, and Linux for desktop environments, as well as iOS and Android for mobile platforms:
libcdoc::NetworkBackend
. This component ensures that the libcdoc::NetworkBackend
has the necessary information to communicate with external servers.In addition to these backends, the library provides two key classes for working with CDOC containers:
These components interact with each other to enable secure encryption and decryption workflows. The following diagram illustrates their relationships:
Most methods in the library return a libcdoc::result_t
status value, which can indicate the following:
nextFile
methods, this indicates the end of the file list in a multi-file source.Encryption is managed by the libcdoc::CDocWriter
object.
The following diagram illustrates the encryption workflow:
To use encryption, you must create or implement a libcdoc::CryptoBackend
class. The default implementation is sufficient for public key encryption schemes. For symmetric key encryption, you must implement at least one of the following methods:
This method copies either the password (for PBKDF-based keys) or the plain AES key into the dst
vector. While simple, this method may expose the password or key in memory.
This method returns the key material for a symmetric key (either password or plain key) derivation. The default implementation calls getSecret
and performs PBKDF2_SHA256 if the key is password-based.
This method calculates the Key Encryption Key (KEK) pre-master from a symmetric key (either password or key-based). The default implementation calls getKeyMaterial
and performs a local HKDF extract.
If you intend to use a key-server, you must subclass libcdoc::NetworkBackend
and implement the following methods:
Returns the client's TLS certificate for authentication with the key-server.
Returns the list of acceptable peer certificates for the key-server.
Signs the provided digest for TLS authentication.
In addition to implementing libcdoc::NetworkBackend
, you must also instantiate a libcdoc::Configuration
subclass.
The libcdoc::Configuration
class is required to retrieve key-server parameters. You must subclass it and implement the following method:
Returns the configuration value for a given domain/parameter combination. For key-servers:
KEYSERVER_SEND_URL
.The libcdoc::CDocWriter
object is created using one of the following static methods:
dst
, ofs
, path
**: Input stream to read file content.take_ownership
**: Indicates if libcdoc::CDocWriter
takes ownership of src
object.version
**: Specifies the file format (1 for CDOC version 1, 2 for CDOC version 2).crypto
**: A libcdoc::CryptoBackend
instance (required).network
**: A libcdoc::NetworkBackend
instance (optional, for key-server use) or nullptr
.conf
**: A libcdoc::Configuration
instance (optional, for key-server use) or nullptr
.The libcdoc::CDocWriter
does not take ownership of crypto
, network
and conf
objects, so they should be deleted by caller.
Add Recipients
Add one or more recipients using:
Begin Encryption
Start the encryption process:
Write Files
Add files and write their data:
Finish Encryption
Finalize the encryption process:
Decryption is managed by the libcdoc::CDocReader
object.
The following diagram illustrates the decryption workflow:
Create or implement a CryptoBackend class. To decrypt all lock types, at least the following methods have to be implemented:
Derives a shared secret from document's public key and recipient's private key by using ECDH1 algorithm.
Decrypts data using RSA private key.
Also one of the symmetric key methods listed in encryption section for symmetric key support.
It has to be implemented and supplied if server-based capsules are needed.
To decrypt server capsules, configuration has to contain the value of the following entry (domain is key-server id as in encryption):
To determine whether a file or libcdoc::DataSource
is a valid CDOC container, use:
Both methods return the container version (1 or 2) or a negative value if the file/source is invalid.
The libcdoc::CDocReader
has to be created with one of the following static methods:
src
, path
, ifs
**: Input stream to read file content.take_ownership
**: Indicates if libcdoc::CDocReader
takes ownership of src
object.crypto
**: A libcdoc::CryptoBackend
instance (required when case decrypting) or nullptr
.network
**: A libcdoc::NetworkBackend
instance (optional, for key-server use) or nullptr
.conf
**: A libcdoc::Configuration
instance (optional, for key-server use) or nullptr
.The libcdoc::CDocReader
does not take ownership of crypto
, network
and conf
objects, so they should be deleted by caller.
The list of locks in file can be obtained by method:
The order of locks is the same as in CDoc container and the 0-based index is used to refer to the lock in decryption methods.
As a convenience method, a public-key lock can be looked up by a certificate (DER-encoded):
Returns the index of a lock that can be opened by the private key of the certificate, or negative number if not found.
Once the correct lock is chosen, the FMK (File Master Key) of the container has to be obtained:
Depending on the lock type the method calls appropriate methods of CryptoBackend (and NetworkBackend) implementation to obtain and decrypt FMK.
After that the FMK can be used to start the encryption:
Individual files can be read by nextFile method:
The method returns the name and size of the next file in encrypted stream, or END_OF_STREAM if there are no more files. Due to the structure of CDoc container, files have to be processed sequentially - there is no way to rewind the stream. The name returned is the exact filename in encrypted stream. If the application intends to save the file with the same name, it has to verify that the path is safe.
The actual decrypted data can be read with method:
This reads the data from current file.
When all files are read, the finalizer has to be called:
The decrypted data should not be used before successful finalization because it performs the final check of data integrity. If it fails, the data should be assumed incomplete or corrupted.
Below is an example of how to implement decryption using the libcdoc::CDocReader
object and a custom libcdoc::CryptoBackend
subclass:
This example demonstrates how to:
libcdoc::CryptoBackend
to implement the required decryption methods (deriveECDH1
and decryptRSA
).libcdoc::CDocReader
instance and use it to process an encrypted CDOC container.