📜 ⬆️ ⬇️

My protected container

When developing a project in C ++, it became necessary to create a secure container. Rather, cross-platform at the source code level classes that support secure storage of information from several bytes to gigabytes, which makes it necessary to support streaming encryption / decryption.
Having no alternative to hire a professional cryptographer at this stage, he started building a bicycle.
This article is written for an open discussion of the proposed solutions by people close to cryptography, as well as for those who will pass this way from scratch, as saving their time and effort.

In the course of studying and analyzing information, OpenSSL was chosen (excellent speed judging by comparison with other libraries, had no difficulties when included in the project, cross-platform), as well as the AES (Rijndael) encryption algorithm with the key 128, 192, 256 to choose from recognized (by the relevant committee you know who) the modern encryption standard.

The very first question is a cryptographically resistant (pseudo) random number generator and its installation (siding). Only it is suitable for generating, for example, a crypto-resistant key. The generator algorithm, quite logical, is taken from OpenSSL, but with the seeding the question is more complicated. POSIX systems that have / dev / * random remove me from responsibility for the random seed. As for Windows, in OpenSSL you can take a set of messages to the window (better) and the contents of the screen (worse). Despite this, I did not really like the solution with messages in the window, since these are unnecessary problems for the user. I stopped at a copy of the screen, plus my five cents. They consisted in the fact that I use Windows to generate a unique GUID, so in the seed I add a crocodile from GetTickCount () XOR before the copy of the screen, XOR System time XOR GUID with small shifts and transformations.
By the way, in a real test on Windows XP SP2 x64, the OpenSSL generator stated that it is already sufficiently cached by default. Why - I do not know, but as a potential alternative, a solution has been proposed (and is activated on systems where additional seeding is required).

Go ahead. Assuming that the key itself is stored separately, thus the container must contain all the necessary information for decryption. In the case of AES in streaming (cbc) mode, it is necessary to store the initial initialization vector (iv0), as well as the exact data length, since the algorithm works with blocks of 16 bytes (128 bits).
')
As for the storage of the initial vector, there is a standard RFC 3394 , however, upon careful study, it turned out that it uses the constant value of the initial initialization vector. At the same time, in the book “An introduction to cryptography” it is clearly written that in no case should the initial vector be constant ( here ). Therefore, I abandoned this algorithm in favor of creating an initialization vector with a cryptographically stable random number generator, which of course is worse than initialization with an additional random sequence, but much better than a constant vector.

The length of the initial vector is 16 bytes, equal to the length of the data block.

Let's go to the title. To make the header length multiple of 16 bytes (AES block size), we recall the length of the vector 16 bytes, then all the additional data is better fit into the other 16 bytes. 8 bytes took up size (aha, big data). The remaining 8 bytes, I gave a magic number, with some probability guaranteeing that the key is chosen correctly. And here I also tried to get away from the pre-prepared magic number (M). My idea is the following: I take a pre-prepared 4-byte constant number. I generate two persistent random 4-byte A1 and A2. In this case, I leave the first number as it is, and change the second with the condition that
A1 + A2 '= M

Then
A2 '= A2 + D

and
D = M - (A1 + A2)


At the same time, to determine the correctness of the password, I decrypt the first 16-byte header block, take the first two dwords, summarize and see what happened. The probability of coincidence of values ​​is small, but finite. In my case, this is quite acceptable (if the key is incorrect, then it is already the problem of the attacker that he received false-decrypted data).

Aligning the size of data by 16-bytes I do the most trivial, by adding to the end of the desired number of random bytes, which also create a cryptographically resistant generator OpenSSL.

Thus, I get rid of the constant numbers in the source data and (as shown above) the constant initialization vector.
Next thing is the technique: the 48-byte header I encode AES in block mode (ecb), then the data blocks are encrypted in stream mode (cbc).

image

PS What could be improved:
+ make more random the initial seed in Windows (and POSIX?)
+ create an initial initialization vector using a sid, not a generator
+ Perhaps it would be better to encrypt the header in the same streaming mode as part of the data.
+ make more reliable key validation
+ ...?

Source: https://habr.com/ru/post/69829/


All Articles