Детерминированная генерация криптографически безопасных ключей и IVEC

Фон

Я разрабатываю систему, которая позволяет разрабатывать схемы динамической аутентификации для пользователя статического веб-контента. Мотивация состоит в том, чтобы предварительно сгенерировать большие объемы сложного для генерации, но конфиденциального веб-контента, а затем обслуживать его статически с аутентификацией на основе файлов cookie (встраивая обратимо зашифрованную информацию), принудительно применяемой веб-сервером. Использование примитива шифрования в режиме AEAD.

Проблема

Мне нужно сгенерировать IVEC и ключи, действительные в течение определенного периода времени, скажем, одну неделю (текущая действительная пара). и что прошлые IVEC/ключи также действительны, скажем, 2 недели (исторически действительные), и любые данные, зашифрованные с исторически действительными секретами, будут просто повторно зашифрованы с помощью текущего действительного IVEC/KEY.

Что мне нужно, так это детерминированный CSPRNG, который задает случайное число и фразу-пароль и который может создавать в индексированном виде 64-битные или 128-битные блоки чисел. Если я использую «недели с 1 января 1970 года» в качестве одного из элементов индекса моей гипотетической CSPRNG, я смогу построить систему, которая автоматически меняет ключи с течением времени.

Подход, который я рассматриваю

Сейчас я не вижу такой функциональности в cryptopp, или я достаточно хорошо знаю терминологию, а так как cryptopp является самой продвинутой из существующих библиотек шифрования, я не уверен, что найду другую. Итак, если я не могу найти реализацию, я должен свернуть свою собственную. Поможет ли генерация статической строковой структуры из конкатенированных данных, а затем ее хеширование (показано ниже)?

RIPEMD160(RandomPreGeneratedFixedNonce:PassPhrase:UInt64SinceEpoch:128BitBlockIndexNumber);

Примечание. Номера блоков будут назначены и иметь обычную структуру, поэтому, например, для 128-битного дайджеста первые 64 бита блока 0 будут для ivec, а весь элемент 1 — для 128-битного ключа.

Является ли это разумным подходом (--.т.е. криптографически безопасным)?

-- изменить: опубликовать принять комментарий --

После некоторого размышления я решил объединить то, что я первоначально считал парольной фразой и одноразовым номером/солью, в 16-байтовый (криптографически стойкий) ключ и использовать методы, описанные в PKCS #5, для получения нескольких ключей на основе времени. Нет необходимости в соли, так как парольные фразы не используются.


person Hassan Syed    schedule 29.11.2010    source источник
comment
пожалуйста, пометьте C++, если применимо   -  person Jason S    schedule 30.11.2010


Ответы (2)


Интересный вопрос.

Во-первых, ваши начальные векторы не обязательно должны быть криптографически стойкими случайными величинами, но они должны быть уникальными для каждого сообщения. IV на самом деле является своего рода солт-значением, которое гарантирует, что похожие сообщения, зашифрованные с использованием одного и того же ключа, не будут выглядеть одинаково после того, как они зашифрованы. Вы можете использовать любой быстрый генератор псевдослучайных чисел, чтобы сгенерировать IV, а затем отправить его (желательно в зашифрованном виде) вместе с зашифрованными данными.

Ключи, конечно, должны быть настолько прочными, насколько вы сможете их изготовить.

Ваше предложение хешировать текстовую строку, содержащую одноразовый номер, фразу-пароль и данные о достоверности, кажется мне очень разумным — оно в целом соответствует тому, что делается другими системами, использующими фразу-пароль для генерации ключа. Вы должны хешировать больше много раз, а не только один раз, чтобы сделать генерацию ключа вычислительно затратной (что будет большей проблемой для любого, кто пытается взломать ключ, чем для вас).

Вы также можете ознакомиться со схемой генерации ключей, изложенной в PKCS#5 (например, по адресу http://www.faqs.org/rfcs/rfc2898.html), которая реализована в cryptopp как PasswordBasedKeyDerivationFunction. Этот механизм уже широко используется и считается достаточно безопасным (обратите внимание, что PKCS#5 рекомендует хэшировать данные парольной фразы не менее 1000 раз). Вы можете просто добавить период действия и данные индекса к фразе-паролю и использовать функцию PasswordBasedKeyDerivationFunction в ее нынешнем виде.

Вы не говорите, какой алгоритм шифрования вы предлагаете использовать для шифрования данных, но я бы посоветовал вам выбрать что-то широко используемое и заведомо безопасное... и, в частности, я бы посоветовал вам использовать AES. Я бы также предложил использовать одну из функций дайджеста SHA (возможно, в качестве входных данных для функции PasswordBasedKeyDerivationFunction). SHA-2 актуален, но SHA-1 достаточно для целей генерации ключей.

Вы также не говорите, какую длину ключа вы хотите сгенерировать, но вы должны знать, что количество энтропии в ваших ключах зависит от длины используемой вами парольной фразы, и если парольная фраза не очень< /em> long, который будет намного меньше идеальной длины ключа.

Самым слабым звеном в этой схеме является сама фраза-пароль, и это всегда будет ограничивать уровень безопасности, которого вы можете достичь. Пока вы солите свои данные (как вы это делаете) и делаете генерацию ключей дорогостоящей для замедления атак грубой силы, все должно быть в порядке.

person dajames    schedule 01.12.2010
comment
Очень красиво написан ответ. Проблема с отправкой IV вместе с сообщением заключается в том, что это делает сообщение больше (в данном случае файл cookie) / Однако я не хочу проектировать систему, в которой IV остается статическим навсегда. Я разработал аналогичную систему раньше (исследовательский прототип), и я убедился, что сгенерированный файл cookie начинается с отметки времени, в которой он был сгенерирован, это гарантировало, что каждое сообщение было уникальным, а также эти файлы cookie имели 2 линейно увеличивающихся идентификатора GUID, где по крайней мере один из них был уникальный для каждого файла cookie. Измененный GUID появился после метки времени. Однако система имела жестко запрограммированный IV. - person Hassan Syed; 01.12.2010
comment
Прототип системы, над которой я сейчас работаю, в настоящее время сначала выполняет проверку подлинности (hmac-ripemd160), затем добавляет дайджест к сообщению, а затем запускает blowfish поверх дайджеста: сообщения. Я не знал о режиме AEAD, когда проектировал свою систему. Я должен провести больше исследований в этом районе. Я надеюсь, что один из режимов AEAD использует дайджест по дизайну. - person Hassan Syed; 01.12.2010
comment
RFC Что вы думаете о жестко закодированном IV (или изменяющемся еженедельно) при условии, что сообщения начинаются с данных, которые часто меняются? - person Hassan Syed; 01.12.2010
comment
FWIW, я видел сообщения об атаках по сторонним каналам на AES. Вероятно, это нормально, если у вас есть контроль над сервером, но если вы позволите запускать ненадежные собственные процессы, я бы подумал об использовании чего-то другого. - person David Thornley; 02.12.2010
comment
Я просмотрел первые несколько страниц стандарта pcks5 (версия 2.1) в соответствии с вашей рекомендацией. очень приятно видеть, что мой ход мыслей на самом деле является стандартизированной передовой практикой. Я надеюсь, что производные классы в cryptopp поддерживают подход, основанный на псевдослучайных функциях, изложенный в стандарте. - person Hassan Syed; 02.12.2010
comment
Цель IV состоит в том, чтобы гарантировать, что каждое сообщение генерирует уникальный зашифрованный текст, даже если один и тот же ключ используется для нескольких сообщений. Причина этого заключается в том, чтобы избежать выдачи какой-либо информации об открытом тексте посредством тривиальной проверки зашифрованного текста (хотя это также усложняет различные атаки с известным открытым текстом / известным зашифрованным текстом). Если вы уверены, что сообщения с открытым текстом достаточно различаются, чтобы эти проблемы не вызывали беспокойства, вы можете обойтись без IV или с использованием фиксированного известного значения. IV, равный 0, удивительно распространены в реальных системах. - person dajames; 02.12.2010
comment
dajames, Hassan Syed: жестко закодированные IV полезны тогда и только тогда, когда вы меняете ключ для каждого сообщения. Пожалуйста, даже не думайте о повторном использовании пары ключей IV только потому, что сообщения достаточно разные. Вот простое и наглядное объяснение: cryptosmith.com/archives/70 Некоторые режимы шифрования не уязвимы. к этой атаке, но лучше перестраховаться. - person blaze; 08.12.2010
comment
@blaze: я думаю, вы преувеличиваете. Это хороший пример, но иллюстрация применима только к одноразовому блокноту, а ее вывод относится только к плохо разработанному шифру, такому как WEP. То, как IV влияет на работу шифра, зависит от режима цепочки, но в например режиме CBC все, что IV делает, эффективно добавляет известный случайный блок шифра к началу сообщения, чтобы выдержать это каждое сообщение отлично от других. - person dajames; 08.12.2010
comment
@dajames: одноразовый блокнот - это ключевой поток, используемый только один раз, обычно с данными. Каждый хороший или плохой потоковый шифр или блочный шифр в режиме OFB (также AES :) по существу генерирует такой ключевой поток и, следовательно, должен использоваться только один раз с каждой комбинацией ключ-IV. Да, в CBC такой проблемы нет, но стоит ли полагаться на комментарий НИКОГДА НЕ МЕНЯЙТЕ ЦЕПОЧНЫЙ РЕЖИМ ЗДЕСЬ в своем коде? Лучше иметь явный счетчик сообщений как часть IV или что-то в этом роде. - person blaze; 08.12.2010

Что мне нужно, так это детерминированный CSPRNG, который задает случайное число и фразу-пароль и который может создавать в индексированном виде 64-битные или 128-битные блоки чисел. Если я использую «недели с 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.

Также см. тему Воспроизводимость для класса RandomNumberGenerator на вики Crypto++.

person jww    schedule 16.02.2015