Legacy Kryptel 6 Storage
Kryptel 6 container consists of six parts, some of which may be missing.
[Container Header] Always present [Data Area] Optional, contains data blocks [Agent Data Area] Optional [Directory] Always present [Header Copy] Always present [Trailer] Always present
Container header contains IDs of the components used for encryption, component initialization values, and other critical data required for opening the container.
|2||Header size including header hash|
|2||Version of handler that created the container|
|2||Version of handler required to process the container|
|16||Storage Handler component ID (must be CID_BASIC_STORAGE)|
|16||Agent component ID|
|16||Cipher component ID|
|16||Compressor component ID|
|16||Hash function component ID|
|20||Cipher parameter block|
|4||Compressor parameter block|
|12||Hash function parameter block|
|2||Size of reserved area (must be 0)|
|. . .||Reserved area (not present in Kryptel 6 storage)|
|4||Number of key verification passes|
|HashSize||Key verification block|
|4||Size of agent data field (0 if no agent data)|
|6||Agent data address (points to directory if no agent data)|
|HashSize||Agent data HMAC (0 if no agent data)|
|6||Size of the directory area|
|BlockSize||Directory area init vector (encrypt it to get the actual vector)|
|HashSize||Directory HMAC (computed on unencrypted data)|
|STR||Cipher name string|
|STR||Cipher scheme string|
|STR||Compressor name string|
|STR||Compressor scheme string|
|STR||Hash function name string|
|STR||Hash function scheme string|
|16||MD5 hash of the header|
STR represents a UTF-16 string; first word (2 bytes) contains the string length in characters. The string does not contain trailing zero. Each UNICODE character occupies 2 bytes.
The container tag, the offset to the handler component ID, and the offset to the agent component ID are constants. The container tag goes first; the position of the agent/handler component IDs must remain the same for the given tag in order for the GetContainerHandlers function to work correctly.
Note that the cipher, the compressor, and the hash function cannot be null components.
Data area is a set of objects' data blocks. Each data block is an independent encrypted stream. Data area is never accessed sequentally so gaps are allowed.
Data recovery block
A data stream may be followed by an optional data recovery block. Note that recovery blocks are created but never accessed except during recovery scan.
|4||Recovery block tag|
|2||Size of the recovery block|
|6||Size of the data stream|
|. . .||Initialization vector|
|16||Data stream MD5 hash|
|2||Size of agent-owned recovery data|
|. . .||Agent-owned recovery data|
|. . .||Bytes 0xE8 to align to BlockSize boundary - 4|
|4||CRC-32 of the previous data|
Unlike data streams and the directory area, recovery blocks are encrypted as a simple sequence of blocks in CBC chaining mode, using zero initialization vector.
Agent Data Area
Raw data block defined by the agent. The storage handler just stores the data that the agent provides.
The container directory is an encrypted stream. The directory data is compressed before encryption. It contains exactly one directory entry for the upper-level object (which in turn may contain any number of child objects). In some sense this object may be considered as the container itself.
|2||Object start tag|
|6||Size of object's data block (0 if no data)|
|6||Size of uncompressed data block (present if data size > 0)|
|6||Address of data block (present if data size > 0)|
|2||Size of associated recovery block (present if data size > 0)|
|BlockSize||Initialization vector (present if data size > 0)|
|16||Data block MD5 hash (present if data size > 0)|
|4||Size of object's attribute block (0 if no attributes)|
|. . .||Attribute block|
|. . .||[Optional directory entries for child objects]|
|2||Object end tag|
If the object has a data block, it is an encrypted stream. The stream is always compressed, however the storage client (i.e. an agent) can specify the compression level when calling IEncryptedObject::CreateStream, and that level may be set to CT_NO_COMPRESSION. The Kryptel agents use this method to turn off compression for specific files.
Trailer is used for container verification only. The standard Open operation does not read it.
|2||Header size including the header hash field|
|6||Address of agent data area|
|16||MD5 hash of the header|
|16||MD5 hash of the data area|
|16||MD5 hash of the agent data area|
|16||MD5 hash of the directory area|
|16||MD5 hash of the trailer|
All hashes are computed on the encrypted data so that the user could verify the container integrity without entering the password. If the corresponding area is not present, the hash value is ignored (setting it to 0 is recommended).
Container tag 0x07AA050B Object start tag 0x050B Object end tag 0x07AA Trailer tag 0x0B0507AA Recovery block tag 0xE0EAF0C8
Key Usage Notes
Although cipher and HMAC (HMACs are computed for agent data and directory, and are used for key verification) use the same key, they use it differently.
Cipher uses first no more than KeySize bytes of the key (padded with zeroes if necessary).
HMAC reverts and inverts the significant bytes of the key, pads with zeroes if necessary, and uses the first 64 bytes of the result.
For example, in the default case of 256-bit AES and SHA-512, the cipher will use the first 256 bits of the SHA-512 password hash, and HMAC will use the whole reversed and inverted password hash. In most cases this method ensures that cipher and HMAC use completely different or at least weakly related keys.
In case of user-defined binary key (IDENT_BINARY_KEY), HMAC assumes its size is 512 bits and prepares (reverts and inverts) that portion. It is responsibility of the user to pad the key with zeros up to BINARY_KEY_SIZE. Note that end-user Kryptel does not fully supports user-defined binary keys – Kryptel Browser can open a container, encrypted with a user-defined key, however creating such a container requires programmatic access to the file agent.