1

I am trying to write an AES key to a file then read it later. I'm using the Crypto++ library, and the AES key is initialized as follows. Below, byte is a typedef of unsigned char.

byte key[CryptoPP::AES::MAX_KEYLENGTH]

The key is 32 bytes long. I try to write it into a file with this:

FILE* file = fopen("C:\\key", "wb");
fwrite(key, 1, sizeof(key), file);
fclose(file);

And recover it using:

FILE* read_file = fopen("C:\\key", "rb");
fseek(read_file, 0, SEEK_END);
long int size = ftell(read_file);
fclose(read_file);

read_file = fopen("C:\\key", "rb");
unsigned char * in = (unsigned char *)malloc(size);
byte readed_key = fread(in, sizeof(unsigned char), size, read_file);
fclose(read_file);
if (key == readed_key)
{
    cout << "this is the right key !";
}
free(in);

However, I get an error message:

incompatible operand types : byte* and byte.

I don't understand why, since readed_key and key are initialized with byte and not byte*.


I looked at AES on the Crypto++ wiki, and the key is generated as follow. I figured out that I was just creating the key (not generating it):

SecByteBlock key(0x00, AES::MAX_KEYLENGTH);
rnd.GenerateBlock( key, key.size() );

With that I can't use

std::vector<byte> key(32);
rnd.GenerateBlock(key, key.size());

Because rnd.Generateblock can't convert std::vector< byte > into byte*

This is driving me crazy....

jww
  • 83,594
  • 69
  • 338
  • 732
EinderJam
  • 347
  • 2
  • 19
  • You should probably avoid `FILE*`; and stick to C++ and use an `ifstream`. Or, use a Crypto++ [`FileSource`](https://www.cryptopp.com/wiki/FileSource). Also see [std::byte](https://www.cryptopp.com/wiki/Std::byte) on the Crypto++ wiki and [Issue 442, Test C++17 byte change with dry runs from various projects](https://github.com/weidai11/cryptopp/issues/442) in the issue tracker. `byte` is getting ready to change to `CryptoPP::byte`. – jww Jul 19 '17 at 14:15
  • Possible duplicate of [How to read a binary file into a vector of unsigned chars](https://stackoverflow.com/q/15138353), [Loading a file into a vector](https://stackoverflow.com/q/7241871), [Efficient way of reading a file into an std::vector?](https://stackoverflow.com/q/4761529/608639), [Reading and writing a std::vector into a file correctly with iterators](https://stackoverflow.com/q/36506297), [Reading and writing a std::vector into a file correctly](https://stackoverflow.com/q/12372531), [How to read a file into vector in C++?](https://stackoverflow.com/q/15138785), etc. – jww Jul 20 '17 at 12:50

2 Answers2

3

How to read and write an AES key to and from a file?

I'm going to avoid your code since its mostly C code. Andrew pointed out some of the problems with it, so there's no sense in rehashing it. Instead I'll show you the Crypto++ and C++ way of doing things. I'll also discuss SecByteBlock a little.


Here's the Crypto++ way to read the data into a byte array using sources and sinks. You can read more about them at Pipelines in the Crypto++ wiki.

byte key[CryptoPP::AES::MAX_KEYLENGTH];
FileSource fs("C:\\key.bin", true, new ArraySink(key, sizeof(key)));

Here's the Crypto++ way to write data to a file using sources and sinks.

byte key[CryptoPP::AES::MAX_KEYLENGTH];
ArraySource as(key, sizeof(key), true, new FileSink("C:\\key.bin"));

Here's the C++ way to read the data into a byte array using streams. Its taken from Reading and writing binary file

byte key[CryptoPP::AES::MAX_KEYLENGTH];
std::ifstream fs("C:\\key.bin", std::ios::binary);
fs.read(key, sizeof(key));

Here's the C++ way to write data to a file using streams.

byte key[CryptoPP::AES::MAX_KEYLENGTH];
std::ofstream fs("C:\\key.bin", std::ios::binary);
fs.write(key, sizeof(key));

std::vector<byte> key(32);
rnd.GenerateBlock(key, key.size());

Because rnd.Generateblock can't convert std::vector< byte > into byte*. This is driving me crazy....

Here, you need a non-const pointer to the first element. Take the address of the first element in your vector. The same would apply to a std::string.

std::vector<byte> key(32);
rnd.GenerateBlock(&key[0], key.size());

Since the key is sensitive, you should use a SecByteBlock. It zeroizes the key from memory once you are done using it.

Generally speaking, if the information is sensitive, then you want to use a SecBlock<T>. In the case of SecByteBlock, T is a byte and there's a typedef for SecByteBlock. But you can make a SecBlock<T> out of anything.

Here's the Crypto++ way to read the data into a SecByteBlock using sources and sinks.

SecByteBlock key(AES::MAX_KEYLENGTH);
FileSource fs("C:\\key.bin", true, new ArraySink(key.begin(), key.size()));

SecByteBlock can initialize elements to a known value upon construction. You are using the feature below. All elements are initialized to 0x00.

SecByteBlock key(0x00, AES::MAX_KEYLENGTH);
rnd.GenerateBlock(key, key.size());

Since you are overwriting the elements immediately with random data, you should forgo initialization. Just ask for an uninitialized block of memory:

SecByteBlock key(AES::MAX_KEYLENGTH);
rnd.GenerateBlock(key, key.size());
jww
  • 83,594
  • 69
  • 338
  • 732
2

key is an array of byte, i.e. its type is byte[32] (assuming that CryptoPP::AES::MAX_KEYLENGTH is an integer constant equal to 32). byte[N] decays to byte* in certain contexts, including during comparisons using == such as in your if (key == readed_key) line.

readed_key is defined to be byte, which is not the same.

Also, you're using fread (3) incorrectly; the return value is the number of items read, not the data that was read. The data you read is stored in the in array. You need to compare each element of key against each element of in, to check that the keys are the same.

Edit: Thanks to @vasek for pointing out that the comparison should be written

if (memcmp(key, in, size) == 0) { /* keys are equal */ }

Since you're using C++ (The Crypto++ library is a C++ library), you could use higher-level abstractions to avoid manual memory management with malloc/free and make the code easier to work with:

live example:

#include <cstdint>
#include <fstream>
#include <iostream>
#include <vector>

using byte = char;

int main() {
    std::vector<byte> key(32);
    // Code would go here to generate the `key`
    // ...

    {
        std::ofstream out("C:\\key", std::ios::out | std::ios::binary);
        out.write(key.data(), key.size());
    } // file is closed automatically here!

    std::vector<byte> read_key(32);
    {
        std::ifstream in("C:\\key", std::ios::in | std::ios::binary);
        in.read(read_key.data(), read_key.size());
    }

    if (key == read_key) {
        std::cout << "Keys are equal!\n";
    }

    return 0;    
}

Here, the std::vector<byte> handles the memory allocation (you just tell it the size in the constructor; use .data() to get a pointer to the contents; comparison of the full key comes for free with std::vector's operator==), and the C++ iostreams read/write is a little easier to follow compared to fread/fwrite. You can still do the seek to determine the key size; I'll leave that as an exercise to the reader.

Andrew
  • 4,822
  • 1
  • 19
  • 38
  • 1
    Also worth noting comparation should be done using `if (memcmp(key, in, size) == 0) { /* keys are equal */ }` – vasek Jul 19 '17 at 09:47
  • I understand.So is there a way (with fread or not) to return the data in the file and store it in a byte array ? – EinderJam Jul 19 '17 at 09:51
  • 1
    `fread(in, sizeof(unsigned char), size, read_file)` is correct, but key is not in `fread`'s return value, but in `in` array. – vasek Jul 19 '17 at 09:52
  • ok."in" is an unsigned char*,so i don't want to store the readed key in it.Could i put a byte variable instead of "in" in the fread ? – EinderJam Jul 19 '17 at 10:07
  • 1
    Yes, you can. Give it a try. – Andrew Jul 19 '17 at 10:09
  • Maybe worth mentioning... `if (memcmp(key, in, size) == 0)` is not constant time. If OP needs a constant time comparison to avoid leaking information, then he/she should use [`VerifyBufsEqual`](https://www.cryptopp.com/wiki/VerifyBufsEqual). But I'm guessing he needs something different to avoid the oracle... He/Shee should be using an [Authenticated Encryption](https://www.cryptopp.com/wiki/Authenticated_Encryption) mode of operation; or maybe, a Key Confirmation operation. – jww Jul 20 '17 at 12:45
  • @jww yes. I'd go so far as to say, unless this is a personal project to learn how AES works, the OP shouldn't be implementing crypto at all; it's very hard to do right, even with a library like Crypto++, because the code that uses it must also be aware of how to avoid leaking or weaknesses. – Andrew Jul 20 '17 at 14:46