My secure container

    When developing a project in C ++, it became necessary to create a secure container. Rather, cross-platform classes at the source code level that support secure storage of information from a few bytes to gigabytes, which makes it necessary to support streaming encryption / decryption.
    Having no alternative to hire a professional cryptographer at this stage, he set about creating 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 go this way from scratch, as saving their time and effort.

    During the study and analysis of the information, OpenSSL were selected (judging by the excellent speed compared to other libraries, it did not experience difficulties when incorporating into the project, cross-platform), as well as the AES encryption algorithm (Rijndael) with a key of 128, 192, 256 to choose, as A recognized (relevant committee you yourself know) modern encryption standard.

    The very first question is a cryptographically strong generator of (pseudo) random numbers and its installation (seeding). Only it is suitable for generating, for example, a cryptographic key. The generator algorithm, quite logically, is taken from OpenSSL, but with seeding the issue is more complicated. POSIX systems that have / dev / * random relieve me of the responsibility for 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 the messages in the window, since this is an unnecessary problem for the user. I settled on a copy of the screen, plus my five cents. They consisted in the fact that I use the generation by Windows of a unique GUID,
    By the way, in a real test on Windows XP SP2 x64, the OpenSSL generator stated that it was already sufficiently sealed by default. Why - I do not know, but as a potential alternative, a solution has been proposed (and activated on systems where additional seating is required).

    Move on. Assuming that the key itself is stored separately, thus, the container should contain all the necessary information for decryption. In 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).

    Regarding the storage of the initial vector, RFC 3394 exists here .however, upon careful study, it turned out that it uses a constant value of the initial initialization vector. At the same time, the book "An introduction to cryptography" clearly states that the initial vector should in no case be constant ( here ). Therefore, I abandoned this algorithm in favor of creating an initialization vector with a cryptographically strong random number generator, which is certainly worse than initialization with an additional random seed, but much better than a constant vector.

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

    Let's move on to the title. To make the length of the header a multiple of 16 bytes (the size of the AES block), we recall the length of the vector is 16 bytes, then it is better to fit all the additional data into the other 16 bytes. 8 bytes took the size (yeah, big data). I gave the remaining 8 bytes to a magic number, with some probability guaranteeing that the key was selected correctly. And here I also tried to get away from the pre-prepared magic number (M). My idea is this: 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 is, and I change the second with the condition that
    A1 + A2 '= M

    A2 '= A2 + D

    D = M - (A1 + A2)

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

    Aligning the data size by 16 bytes I do the most trivially, by appending the required number of random bytes to the end, which I also create a cryptographically strong OpenSSL generator.

    Thus, I get rid of constant numbers in the source data and (as shown above) a constant initialization vector.
    Further, a technical matter: the 48-byte header I encode AES in block mode (ecb), then the data blocks are encrypted in streaming mode (cbc).


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

    Also popular now: