AES解密大于可用RAM

时间:2017-11-08 11:54:02

标签: c++ encryption aes mbed

到目前为止,我曾经使用AES解密文件(位于USB记忆棒上),如下所示:

FILE * fp = fopen(filePath, "r");
vector<char> encryptedChars;

if (fp == NULL) {
    //Could not open file
    continue;
}

while(true) {
    int nextEncryptedChar = fgetc(fp);

    if (nextEncryptedChar == EOF) {
        break;
    }

    encryptedChars.push_back(nextEncryptedChar);
 }

 fclose(fp);

 char encryptedFileArray[encryptedChars.size()];
 int encryptedByteCount = encryptedChars.size();

 for (int x = 0; x < aantalChars; x++) {
     encryptedFileArray[x] = encryptedChars[x];
 }

 encryptedChars.clear();

 AES aes;

 //Decrypt the message in-place
 aes.setup(key, AES::KEY_128, AES::MODE_CBC, iv);
 aes.decrypt(encryptedFileArray, sizeof(encryptedFileArray));
 aes.clear();

这适用于小文件。此时,我正在从USB记忆棒打开一个文件并将所有字符存储到一个矢量中并将矢量复制到一个数组中。我知道&amp; encryptedChars [0]也可以用作数组指针并节省一些内存。

现在我要解密一个256Kb的文件(而不是1Kb)。将数据复制到源阵列至少需要256Kb的RAM。但是我只有100Kb可供使用,因此无法创建包含加密数据的源数组。

所以我尝试使用Fope * fopen给我作为FILE指针,并在同一个USB记忆棒上创建一个新文件作为目标指针。我希望解密轮将使用USB记忆棒的内存而不是堆上的可用内存。

FILE * fp = fopen(encryptedFilePath, "r");
FILE * fpDecrypt = fopen(decryptedFilePath, "w+");

if (fp == NULL || fpDecrypt == NULL) {
     //Could not open file!?
     return;
}

AES aes;

//Decrypt the message in-place
aes.setup(key, AES::KEY_128, AES::MODE_CBC, iv);
aes.decrypt((const char*)fp, fpDecrypt, firmwareSize); 
aes.clear();

不幸的是,系统锁定(不明白为什么)。

有没有人知道我是否可以将FILE *传递给一个函数,该函数需要将const char *作为源并将void *作为目标?

我使用以下库:https://os.mbed.com/users/neilt6/code/AES/docs/tip/AES_8h_source.html

谢谢!

2 个答案:

答案 0 :(得分:3)

许多加密库提供&#34;增量&#34;允许逐段解密/解密数据流的API,而无需将流加载到内存中。不幸的是,似乎the library you're using没有(或者,至少没有明确记录它)。

但是,如果您知道how CBC mode encryption works,则可以自行推广。基本上,您需要做的就是获取前一个密文块的最后一个AES块(即最后16个字节),并在解密(或加密)下一个块时将其用作IV,如下所示:

char buffer[1024];  // this needs to be a multiple of 16 bytes!
char ivTemp[16];
while(true) {
    int bytesRead = fread(buffer, 1, sizeof(buffer), inputFile);

    // save last 16 bytes of ciphertext as IV for next block
    if (bytesRead == sizeof(buffer)) memcpy(ivTemp, buffer + bytesRead - 16, 16);

    // decrypt the message in-place
    AES aes;
    aes.setup(key, AES::KEY_128, AES::MODE_CBC, iv);
    aes.decrypt(buffer, bytesRead);
    aes.clear();

    // write out decrypted data (todo: check for write errors!)
    fwrite(buffer, 1, bytesRead, outputFile);

    // use the saved last 16 bytes of ciphertext as IV for next block
    if (bytesRead == sizeof(buffer)) memcpy(iv, ivTemp, 16);

    if (bytesRead < sizeof(buffer)) break;  // end of file (or read error)
}

请注意,此代码将覆盖iv数组。那应该没问题,因为你不应该再使用相同的IV两次。 (事实上​​,在CBC模式下,加密器应随机选择IV,使用加密安全的RNG,并与消息一起发送。通常的做法是简单地将IV添加到消息文件中。)< / p>

此外,上面的代码效率低于它需要的,因为它调用aes.setup(),因此重新运行每个块的整个AES密钥扩展。遗憾的是,我无法找到任何记录的方式来告诉您的加密库在不重新运行设置的情况下更改IV。

但是,在下面的评论中查看由implementation of your library链接的Sister Fister,看起来它已经用最后一个密文块替换了它的内部副本。因此,看起来你真正需要做的就是为每个块调用aes.decrypt()而不用之间的设置调用,如下所示:

char buffer[1024];  // this needs to be a multiple of 16 bytes!

AES aes;
aes.setup(key, AES::KEY_128, AES::MODE_CBC, iv);

while(true) {
    int bytesRead = fread(buffer, 1, sizeof(buffer), inputFile);

    // decrypt the chunk of data in-place (continuing from previous chunk)
    aes.decrypt(buffer, bytesRead);

    // write out decrypted data (todo: check for write errors!)
    fwrite(buffer, 1, bytesRead, outputFile);

    if (bytesRead < sizeof(buffer)) break;  // end of file (or read error)
}
aes.clear();

请注意,此代码依赖于加密库的一项功能, not 似乎已明确记录,即多次调用aes.decrypt()将导致解密链接正确。 (对于CBC模式来说,这实际上是一件非常合理的事情,但是如果没有阅读代码或找到明确的文档说明,你就永远无法确定。)你应该确保有一个全面的测试套件,以及在升级库时重新运行测试。

另请注意,我还没有对这些示例中的任何一个进行测试,因此显然可能存在错误或拼写错误。此外,您的加密库的文档有点稀疏,因此它可能无法正常工作,就像我假设它一样。在使用之前,请先根据此代码测试任何内容!

答案 1 :(得分:0)

一般来说,如果某些东西不适合记忆,你可以诉诸:

  1. 随机访问文件。使用fseek查找位置并读取或写入您需要的内容。内存要求最低。
  2. 批量处理适合内存的处理。内存要求可调,但算法必须适合此。
  3. 系统虚拟内存,允许您保留系统可以解决的大块,您有可用磁盘空间和系统设置。这通常是透明的,具体取决于您的系统。
  4. 其他分页内存机制。
  5. 由于AES加密是以128位为单位进行的,并且您的内存不足,因此您应该对文件使用随机访问。