Детерминированно Генерирование криптографически защищенных ключей и IVEC
Фон
Я разрабатываю систему, которая позволяет разрабатывать схемы динамической аутентификации для пользователя статического веб-контента. Мотивация заключается в том, чтобы предварительно генерировать большое количество сложного для генерируемого, но чувствительного веб-контента, а затем статически ставить его на основе аутентификации на основе файлов cookie (встраиваемых в обратимо зашифрованную информацию), что принудительно выполняется веб-сервером. Использование примитива шифрования AEAD.
Проблема
Мне нужно сгенерировать IVEC и ключи, которые действительны в течение продолжительного времени, скажем, одну неделю (действующая в настоящий момент пара). и что предыдущие IVECs/Keys также действительны в течение 2 недель (исторически обоснованные), и любые данные, зашифрованные исторически достоверными секретами, будут просто зашифрованы с действующим в настоящее время IVEC/KEY.
Мне нужна детерминированная CSPRNG, что семена случайного числа и кодовой фразы, которые могут производиться в индексированных моделях 64-битных или 128-битных блоков. Если я использую неделю с тех пор, как "jan 1 1970", как один из элементов индекса моего гипотетического CSPRNG, я должен иметь возможность создавать систему, которая взамен меняет ключи автоматически со временем.
Подход, который я рассматриваю
Теперь я не вижу такой функции в криптоппе, или я знаю теперь терминологию достаточно хорошо, и поскольку криптопп является самой передовой из библиотек шифрования, я не уверен, что найду еще одну. Итак, если я не могу найти реализацию там, я должен опрокинуться. Будет ли генерировать статическую строковую структуру из конкатенированных данных, а затем хешировать ее (показано ниже), сделать трюк?
ripemd160 (RandomPreGeneratedFixedNonce: Passphrase: UInt64SinceEpoch: 128BitBlockIndexNumber);
Примечание. Блочные числа будут назначены и иметь регулярную структуру, поэтому, например, для 128-битного дайджеста, первые 64-бит блока 0 будут для ivec, а весь элемент 1 для 128-битного ключ.
Является ли это разумным подходом (-, то есть криптографически безопасным)?
- изменить: отправить комментарий -
После некоторого размышления я решил объединить то, что я изначально считал ключевой фразой и nonce/соль, в 16-байтовый (криптографически сильный) ключ и использовать методы, изложенные в PKCS # 5, чтобы получить множественные временные ключи. Нет необходимости в соли, поскольку фразы не используются.
Ответы
Ответ 1
Интересный вопрос.
Во-первых, ваши начальные векторы не должны быть криптографически сильными случайными величинами, но должны быть уникальными для каждого сообщения. IV действительно является своего рода солью, которая гарантирует, что аналогичные сообщения, зашифрованные с использованием одного и того же ключа, выглядят не так, как только они зашифрованы. Вы можете использовать любой быстрый псевдослучайный генератор для генерации IV, а затем отправить его (предпочтительно зашифрованный) вместе с зашифрованными данными.
Ключи, конечно, должны быть такими же сильными, как вы можете их практически сделать.
Ваше предложение хэш-текстовой строки, содержащей данные nonce, passphrase и validity, кажется мне очень разумным - это в целом соответствует тому, что делается другой системой, использующей кодовую фразу для генерации ключа. Вы должны делать хэш более много раз - и не только один раз - чтобы генерировать ключевое поколение вычислительно дорого (что будет большой проблемой для тех, кто пытается перетащить ключ, чем он будет для вас).
Вы также можете взглянуть на схему генерации ключей, изложенную в PKCS # 5 (например, в http://www.faqs.org/rfcs/rfc2898.html), которая реализован в криптопp как PasswordBasedKeyDerivationFunction. Этот механизм уже широко используется и, как известно, является достаточно безопасным (обратите внимание, что PKCS № 5 рекомендует хэшировать данные парольной фразы не менее 1000 раз). Вы можете просто добавить свой период действия и индексировать данные к кодовой фразе и использовать функцию PasswordBasedKeyDerivationFunction как таковой.
Вы не говорите, какой алгоритм шифрования вы собираетесь использовать для шифрования данных, но я бы предложил вам выбрать что-то широко используемое и известное как безопасное... и, в частности, я бы предположил, что вы используете AES. Я также предложил бы использовать одну из функций дайджеста SHA (возможно, в качестве входа в PasswordBasedKeyDerivationFunction). SHA-2 является токовым, но SHA-1 является достаточным для целей генерации ключей.
Вы также не говорите, какую длину ключа вы хотите создать, но вы должны знать, что количество энтропии в ваших ключах зависит от длины используемой фразы, и если кодовая фраза очень длинная это будет намного меньше, чем требуется для ключевого слова.
Самым слабым звеном в этой схеме является ключевая фраза и всегда будет ограничивать уровень безопасности, который вы можете достичь. Пока вы соедините свои данные (как вы это делаете) и сделайте ключевое поколение дорогим, чтобы замедлить атаки грубой силы, вы должны быть в порядке.
Ответ 2
Мне нужна детерминированная CSPRNG, что семена случайного числа и кодовой фразы, которые могут производиться в индексированных моделях 64-битных или 128-битных блоков. Если я использую недели с тех пор, как "jan 1 1970" как один из элементов индекса моего гипотетического CSPRNG, я должен иметь возможность создавать систему, которая взамен меняет ключи автоматически с течением времени.
Ну, я думаю, что часть решения - использовать генератор, не основанный на времени. Таким образом, если обе стороны начинаются с одного и того же семени, то они оба производят один и тот же случайный поток. Вы можете сложить свои "недели с первой недели" 1970 года. Кроме того,
Для этого вы должны использовать OFB_mode<T>::Encryption
. Он может использоваться как генератор, поскольку режим OFB использует AdditiveCipherTemplate<T>
, который происходит от RandomNumberGenerator
.
Фактически, Crpyto ++ использует генератор в test.cpp
, чтобы результаты можно было воспроизвести, если что-то не удается. Здесь вы можете использовать OFB_mode<T>::Encryption
. Это также относится к CTR_Mode<T>::Encryption
:
SecByteBlock seed(32 + 16);
OS_GenerateRandomBlock(false, seed, seed.size());
for(unsigned int i = 0; i < 10; i++)
{
OFB_Mode<AES>::Encryption prng;
prng.SetKeyWithIV(seed, 32, seed + 32, 16);
SecByteBlock t(16);
prng.GenerateBlock(t, t.size());
string s;
HexEncoder hex(new StringSink(s));
hex.Put(t, t.size());
hex.MessageEnd();
cout << "Random: " << s << endl;
}
Вызов OS_GenerateRandomBlock
выводит байты из /dev/{u|s}random
, а затем использует это как смоделированное разделяемое семя. Каждый запуск программы будет отличаться. В течение каждого запуска программы он печатает примерно следующее:
$ ./cryptopp-test.exe
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Random: DF3D3F8E8A21C39C0871B375013AA2CD
Там есть другой генератор, который делает то же самое, но не является частью библиотеки Crypto ++. Его называют AES_RNG
и его основаны на AES-256. Его реализация только для заголовков, и вы можете найти ее в вики Crypto ++ под RandomNumberGenerator.
Также см. раздел Reproducibility для класса RandomNumberGenerator
в вики Crypto ++.