Here we describe thoroughly the encryption scheme applied in Naamari. You can analyze it, remove the weaknesses, make improvings, — and fabricate your own «Naamari» thing from scratch. Or you can exploit the cryptographic holes to read original content that naive users tried to conceal with this nest of backdoors and beetles. Perhaps you'll be kind enough (and not restricted by your employers) to report us of the flaws detected.
What follows is an extract from Naamari manual (the «Scheme» and «Nuances» items). Maybe you download the release and read the entire manual instead.
Unless you verify/write the source code yourself, there is no guarantee that it does exactly what is stated.
These specifications conform to Naamari v1.3.2.
Naamari's context consists of:
• HALLWAY — «sluice», the folder on the local drive where the files and folders are taken from for encryption, and where they are put after decryption. Here the files have original content.
• NOMINIS — the folder on the local drive, it keeps the structure of the folders and files placed in the storage, along with their names. Each file, or file-nomen, stores the 32-byte sequence (salt), with that, owning the key, the «distorted» name of the corresponding file in storage is calculated.
• HARBOUR — the folder on the local drive, the local storage of encrypted files. The encrypted files may be 30–50 bytes longer than source ones due to the padding and the integrity check hash. The descriptors-«twins» with the dot at the start of a name contain the information about the file and the actions that the transport module has to perform on it. The descriptor format is given below.
• KEYS — five 32-byte main keys. At the start the user specifies the 160-byte file on the local drive with five 32-byte protokeys. They are translated into the main ones after the passphrase has been typed. The KEYS are rather salts, actually.
The protokeys, the passphrase, and NOMINIS is a «secret» part. No one besides the user should have any kind of access to them.
Among Naamari operations, there are two «essentially cryptographic», different by a direction. When the user sends the object from the sluice to the storage, it (along with its child nodes, if it's a folder) is added to the tree-like structure of the folders and files in NOMINIS: there appear the folders and files with the same names, but the files are files-nomens, they contain only the salt. From the salt, the name of the file (hexadecimal notation of a certain hash) is determined, where the encrypted content is written (the encryption is based on another salt), and the descriptor is placed into the file with the same name, except for the additional dot at the start; these two files are in HARBOUR.
When the user retrieves the object from the current path in NOMINIS, the corresponding tree of the folders and files is constructed in the sluice, and the files are filled with their content, taken and decrypted from the local storage, which is HARBOUR (if the local version is present); the determination of the name in a storage from the salt in the file-nomen happens in the same way.
How does it run?
Here we've applied AES-256 and SHA-256. The latter's with a modification described in [1, 5.4.2] to counteract the length extension and the partial-message collision problems: for message m, we calculate
h(m) = SHAd-256(m) = SHA256 (SHA256 (0512 || m))
where 0512 — 512 zero bits/64 zero bytes (block length of compression function), to which we append the m itself. Hereinafter, the «hash» term means exactly the h(m).
The encryption of 16-byte blocks is done with a «basic» AES mode.
The implementation of AES-256 in C we've taken as the basis belongs to Ilya O. Levin and Hal Finney (literatecode.com/aes256), the implementation of SHA-256 in C belongs to Brad Conte (github.com/B-Con/crypto-algorithms).
We assume the little-endian byte order: in memory, low bytes are placed at low addresses.
Negative 8-byte values are represented in complementary code: for k from 1 to 263, the value (–k) is stored in memory as (264–k).
The protokeys are sequential 32-byte blocks in the given 160-byte file. Let's designate them PKEY-R, PKEY-C, PKEY-S, PKEY-H, and PKEY-B respectively.
To get the KEYS from the protokeys, the pass-hash hash of the passphrase is calculated, the passphrase being a sequence of ASCII-codes of the symbols typed, including the LF at the end of the line. The resultant KEYS are the hashes of the sequences, consisting of the corresponding protokey and pass-hash: KEY-i = h(PKEY-i || pass-hash), where i runs through R, C, S, H, and B.
The rand-omen salt in the file-nomen from NOMINIS for the new file is calculated as the hash of the sequence, consisting of (SCO) KEY-R, the relative path of the file in NOMINIS, the 8-byte current time in milliseconds since the beginning of Unix epoch, the per-session 8-byte counter of operations (incremented-on-access), and the 8-byte amount of available physical memory.
The name of the new file in the storage is a 64-symbol hexadecimal notation of the 32-byte hash of the SCO KEY-C and rand-omen.
The descriptor of the new file in the storage has the same name with a dot at the start. The format of 100-byte descriptor structure is as follows:
When a new file from the sluice comes to the storage (or the new version of an existing file), the new salt is made for that version. It is a hash of SCO KEY-S, rand-omen salt from the file-nomen, the current time since the beginning of Unix epoch, the per-session counter of operations (incremented-on-access), and the amount of available physical memory.
To encrypt the file/its new version, after getting the new salt, at first its content is aligned up to the 16-byte block boundary with a non-zero number of identical bytes, the value of each byte is their number (1 to 16). The data-hash check hash is calculated for the SCO KEY-H and aligned data.
The 16-byte blocks of the aligned data are enumerated from 1 to n. The block of a number k is AES-encrypted with a block-key, which is a hash of SCO KEY-B, salt and 8-byte value of k.
The 32-byte data-hash is divided into two 16-byte blocks: the first one is encrypted with a block-key with the same salt and k = –(n+1), and the second — with k = –(n+2). The derived 32-byte block is appended to n blocks of encrypted aligned data.
The decryption of the file to retrieve it into the sluice, in case the salt is present, occurs «in the reverse order», in 2 passes (by default). The first one is a check one, nothing is written to the file-receiver. If certain conditions are violated, the file is recognized as corrupted and the decryption is halted.
The file length should be not less than 16+32=48 and a multiple of 16. If it isn't, the file is corrupted.
The encrypted data is divided into n'+2 16-byte blocks, enumerated from 1. First n' blocks are AES-decrypted with block-keys being the hash of SCO KEY-B, salt and 8-byte block number k. The blocks (n'+1) and (n'+2) are AES-decrypted with such block-keys for k = –(n'+1) and k = –(n'+2) respectively. The blocks are processed one by one, without occupying the redundant memory.
We verify the identity of two hashes: the control one — data-hash' of SCO KEY-H and the first n' blocks, and the appended one — ap-hash' (blocks n'+1, n'+2). If the hashes are not equal, the file is corrupted.
The last byte of the n'-th block, last-byte', is one of the padding bytes, it should be equal to their number, hence its value must be from 1 to 16. If it isn't, the file is corrupted.
last-byte' last bytes of the n'-th block should be identical and equal to last-byte'. If they aren't, the file is corrupted.
If the hashes are equal, the second pass occurs, similar to the first, except for the n' decrypted blocks (with padding bytes in the n'-th one being discarded) are written into the file-receiver.
In so doing, the (premeditatedly) corrupted encrypted version doesn't overwrite the one existing in the sluice, and it doesn't occupy the additional space on the drive. However, the decryption is performed twice, hence this operation is two times slower than the previous one.
The user can alter the default behaviour, — then there is a single pass, the decrypted data is written immediately, and if the hashes are not equal, or one of the conditions described is violated, the file-receiver will be deleted.
In a descriptor, the time of the last modification and the size of file version (local or remote) are packed into 16-byte block. It is encrypted and decrypted with a block-key, which is the hash of SCO KEY-B, the corresponding salt and 8-byte zero.
The code for almost all these transformations you'll find in Naamari/cryptrav/translator.cpp.
B i b l i o g r a p h y
1. Neils Ferguson, Bruce Schneier, Tadayoshi Kohno. Cryptography Engineering: Design Principles and Practical Applications. — Wiley Publishing, Inc. — 2010.
2. Jonathan Katz, Yehuda Lindell. Introduction to Modern Cryptography, 2nd edition. — CRC Press. — 2015.
3. Bruce Schneier. Applied Cryptography, 2nd edition, Protocols, Algorithms, and Source Code in C. — John Wiley & Sons, Inc. — 1996.
4. Alfred J. Menezes, Paul C. van Oorschot, Scott A. Vanstone. Handbook of Applied Cryptography. — CRC Press. — 2001. — Available from cacr.math.uwaterloo.ca/hac/
5. Ross J. Anderson. Security Engineering: A Guide to Building Dependable Distributed Systems. — John Wiley & Sons, Inc. — 2008.
Copyright © 2014–2019 Sunkware
This site gathers statistics with StatCounter