Crypto ++ AES в GCM производит аналогичные шифротексты - PullRequest
0 голосов
/ 14 ноября 2018

Я пытаюсь реализовать чистое, правильное и безопасное шифрование AES от начала до конца. Для моих целей я просто хочу сгенерировать зашифрованный текст выходных данных некоторых тестовых программ, чтобы имитировать, какой тип зашифрованного текста будет написан, скажем, сервер облачного хранения. Прочитав об этом некоторые, я пришел к выводу, что для моих целей аутентифицированное шифрование, создаваемое AES в режиме GCM, представляет собой шифротексты, которые я видел в использовании на реальных серверах.

Начиная с примера кода на странице Crypto ++ Wiki, я придумал следующий фрагмент, чтобы прочитать некоторые строки из файла и вывести зашифрованный текст. Проблема в том, что шифротексты, которые я получаю, очень похожи, верхняя половина идентична во многих выходных данных. Я подозреваю, что это связано с тем, как я использую / инициализирую AutoSeededRandomPool, но я не знаю достаточно об этом, чтобы понять, как это исправить. Похоже, что основной код выполняет вызовы ОС (Ubuntu 16.04) для генерации энтропии, что заставляет меня поверить, что, возможно, между вызовами недостаточно времени для изменения этого значения.

Заранее благодарим за любую помощь, которую вы можете оказать.

#include "osrng.h"
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include "cryptlib.h"
#include "hex.h"
#include "filters.h"
#include "aes.h"
#include "gcm.h"
#include "secblock.h"

using namespace std;
using CryptoPP::AutoSeededRandomPool;
using CryptoPP::AutoSeededX917RNG;
using CryptoPP::HexEncoder;
using CryptoPP::HexDecoder;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::AuthenticatedEncryptionFilter;
using CryptoPP::AuthenticatedDecryptionFilter;
using CryptoPP::AES;
using CryptoPP::GCM;
using CryptoPP::SecByteBlock;

// Single randomly seeded RNG
AutoSeededRandomPool rnd;

// Generate a string buffer for the input/output data
string plainText, cipherText, encoded;

// Generate a random key
SecByteBlock key(AES::DEFAULT_KEYLENGTH);
rnd.GenerateBlock(key, key.size());

// Generate an initial value vector (public but unique per msg)
SecByteBlock iv(AES::BLOCKSIZE);
rnd.GenerateBlock(iv, iv.size());

for (size_t i = 0; i < numLines; ++i)
{
    getline(inputFileStream, plainText);

    // Do encryption
    GCM<AES>::Encryption e;
    e.SetKeyWithIV(key, key.size(), iv, iv.size());
    StringSource
    (
        plainText,
        true,
        new AuthenticatedEncryptionFilter
        (
            e,
            new StringSink(cipherText)
        )
    );

    encoded.clear();
    StringSource
    (
        cipherText,
        true,
        new HexEncoder
        (
            new StringSink(encoded)
        )
    );
    cout << "Cipher Text: " << encoded << endl;
}

Вот пример выходных шифротекстов:

Текст шифра:

9DE3A67D5FF42A15834460CD4489B20A352ECEB5F801F7349F3A989DAE8C02675CB48ADDD00604139353F2DEC6335DF8156DA66ACEF953F2E573BB3D88E7AF7D59EE311DC8056CDB0B90B30A232DD7ECB219FB2F9F2D9898B98A1B6749FC8B88D00B5E08DC4EF9C3A52521298D6FBFD75A9A71E8A253D8B9F06D17B07442DA543B8E1CCCEC1E7D7084A1A24DAA71CB688AC2ECD840731F5D57AA7BC61DE5837411596561C36659D95451A003A0E27697528B9BB6B67763F6

Текст шифра:

9DE3A67D5FF42A15834460CD4489B20A352ECEB5F801F7349F3A989DAE8C02675CB48ADDD00604139353F2DEC6335DF8156DA66ACEF953F2E573BB3D88E7AF7D59EE311DC8056CDB0B90B30A232DD7ECB219FB2F9F2D9898B98A1B6749FC8B88D00B5E08DC4EF9C3A52521298D6FBFD75A9A71E8A253D8B9F06D17B07442DA543B8E1CCCEC1E7D7084A1A24DAA71CB688AC2ECD840731F5D57AA7BC61DE5837411596561C36659D95451A003A0E27697528B9BB6B67763F6D8B9F0691B616C2E996B5E473860EE348C09A1F0FC

Перемещая IV поколение за пределы цикла, происходит аналогичное поведение:

Текст шифра:

25C638C32E67A7A2C65BF37ABA7C30C19C2714D95FBB68E6E57560CCE2C20F266E6C30768108CF7E01C195991B61AF7FE4F4FA691AFEAAFCFB74292EBE30B9236147722F5785D8F21070D3ACF9E476E39235B4C362D14BF7B2FC2A5BFC0297FCBCBEC37795626029CC30B404A6DB67EA652F5A7FA294D039C4A09BC611F74D8C9FFBD26F49C54470E2C41463440AF050D7FF160CD923FFD0CA6FAF1DB66947C5896B1A39A8E9B694A025E2F521229BDC15C48C5F3AD27A87

Текст шифра:

25C638C32E67A7A2C65BF37ABA7C30C19C2714D95FBB68E6E57560CCE2C20F266E6C30768108CF7E01C195991B61AF7FE4F4FA691AFEAAFCFB74292EBE30B9236147722F5785D8F21070D3ACF9E476E39235B4C362D14BF7B2FC2A5BFC0297FCBCBEC37795626029CC30B404A6DB67EA652F5A7FA294D039C4A09BC611F74D8C9FFBD26F49C54470E2C41463440AF050D7FF160CD923FFD0CA6FAF1DB66947C5896B1A39A8E9B694A025E2F521229BDC15C48C5F3AD27A87FA3272CFE669E235CE452FCEEA59CC8CA34554FF25

Приложение к ответу ниже:

В коде, который я пренебрег, нужно очищать строку "cipherText" на каждой итерации цикла. По-видимому, операции StringSink просто добавляли новые зашифрованные тексты в конец cipherText, поэтому они были идентичны. Спасибо за помощь!

1 Ответ

0 голосов
/ 14 ноября 2018

Вы генерируете ключ и IV вне цикла for. Это означает, что вы будете использовать ту же комбинацию ключ / IV для строк, которые вы шифруете. GCM - это в основном CTR-шифрование с тегом аутентификации. Это означает, что похожие биты в открытом тексте приведут к одинаковому значению. GCM безопасен только до тех пор, пока IV никогда не повторяется .

Поместите поколение IV в петлю, и вы должны быть в порядке. Обратите внимание, что для GCM 12 байтов IV более эффективны и, возможно, более безопасны, чем 16 байтов IV. Случайные IV часто ставятся перед зашифрованным текстом (путем записи их в StringSink вручную).

В исходном коде вы допустили две ошибки. Вы распечатали ключ вместо IV, что заставило вас поверить, что ваш RNG был неправ. Кроме того, вы записали в строковую переменную ciphertext без предварительной очистки. Это заставило StringSink добавить следующий зашифрованный текст (и тег аутентификации) к строке ciphertext. Таким образом, вы получили как первый, так и второй зашифрованный текст, когда распечатали его.

...