Я пытаюсь закодировать функцию шифрования паролей на С++ (Примечание: только в образовательных целях. На самом деле я не буду хранить с ней свои пароли.), но я не уверен, как создать случайную 32-байтовую соль, используя предопределенный набор символов . Как бы я это сделал?
#include <random>
#include <iostream>
using namespace std;
void genSalt() {
const char charset[] = {
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"!£$%^&*():@~#'?/><.,|`¬¦"
};
}
Код должен генерировать случайную 32-байтовую строку на основе определенного набора символов 'charset'. Я не уверен, как этого добиться.
Если вы можете использовать C++11
, вы можете использовать random_shuffle
для перетасовки массива элементов, а затем всегда возвращать первые 32
элементы.
Что-то вроде следующего должно работать для вашего случая:
void genSalt() {
static char charset[] = {
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!£$%^&*():@~#'?/><.,|`¬¦" };
random_shuffle(begin(charset), end(charset));
for(int i = 0 ; i < 32; i++)
cout<<charset[i];
cout<<endl;
}
Обратите внимание, что этот подход не возвращает солевые последовательности с повторением. Это как-то хорошо для образовательных целей, поскольку это работает и его легко реализовать.
Другой подход заключается в случайном выборе элементов из последовательности один за другим, как показано ниже:
void genSalt() {
static const char charset[] = {
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!£$%^&*():@~#'?/><.,|`¬¦" };
for (int i = 0; i < 32; ++i) {
cout<<charset[rand() % (sizeof(charset) - 1)];
}
}
@WhozCraig Хороший вопрос. Но общая идея остается в силе. Спасибо
Первое, на что вы должны обратить внимание, это хранение некоторых из этих символов в исходном файле. В зависимости от кодировки, £
, ¬
или ¦
потенциально могут состоять из более чем одного байта, что может испортить ваш результат; вы должны хранить их как жестко закодированные байты, если хотите, чтобы все шло гладко независимо от кодировки:
const char charset[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"!\xA3$%^&*():@~#'?/><.,|`\xAC\xA6";
// \xA3 = single-byte extended encoding for £
// \xAC = ¬
// \xA6 = ¦
(И под гладкостью я подразумеваю использование этих символов в вашей соли, несмотря ни на что, это плохая идея, поскольку они могут конфликтовать с другими кодировками, в которых используется соль.)
Что касается генерации соли пароля, я не криптолог, но мне кажется немного сомнительным использовать что-либо, кроме криптографически безопасного генератора псевдослучайных чисел. Если это просто упражнение на C++, встроенные генераторы случайных чисел будут в порядке. На самом деле я никогда не использовал функции PRNG С++ 11, так что это тоже полезное упражнение для меня.
Вы начинаете с создания random_device
, а затем движка рандомизатора:
#include <random>
std::random_device my_random_device;
std::default_random_engine my_random_engine(my_random_device());
Существуют различные механизмы, которые вы можете выбрать для точной настройки ваших случайных чисел, и по умолчанию используется выбор, определяемый реализацией (я предполагаю, что с этим вы также можете легко подключить криптографически безопасный генератор).
random_device
обрабатывает заполнение автоматически, тогда как, если вы используете старую функцию rand
в стиле C, вам нужно вызвать srand
с начальным значением (например, системным временем), чтобы инициализировать его перед созданием чего-либо.
Чтобы выбрать персонажей из вашего источника соли, вы выбираете метод распространения, о котором вы можете получить более подробную информацию здесь: https://en.cppreference.com/w/cpp/numeric/random
В этом случае вам нужно равномерное распределение по набору солевых символов:
std::uniform_int_distribution<int> random_number(0, sizeof(charset) - 1);
Который вы можете вызвать как random_number(my_random_engine)
, чтобы получить число между 0 и индексом последнего символа (не забудьте минус 1, чтобы пропустить нулевой терминатор).
И тогда с этим легко сэмплировать символы и строить строку:
std::string salt;
salt.reserve( 32 );
for( int i = 0; i < 32; i++ ) {
salt.push_back(charset[random_number(my_random_engine)]);
}
std::cout << "salt result: " << salt << std::endl;
Рабочий пример: https://wandbox.org/permlink/mGd8pYP9Y3injuuG
Еще одна вещь, которую я хотел бы упомянуть, это распространенная ошибка использования %
против случайного числа. Например, рассмотрим этот тестовый пример с использованием старой функции rand()
в стиле C:
int main() {
// Seed randomizer
srand( time(0) );
// Print a random number between 0 and 1999
int number = rand() % 2000;
std::cout << number;
}
Обычно людям все равно, потому что это «достаточно случайно», но вы не получите равномерного распределения по модулю (%
). rand()
генерирует число между 0
и RAND_MAX
, и вместо этого вы должны масштабировать возвращаемый диапазон, чтобы он соответствовал желаемому диапазону.
// Sample random number between 0 and 1999. (add 1 to rand max to make 2000 not slightly possible)
int number = rand() * 2000 / (RAND_MAX+1)
Не забудьте также выбрать подходящую функцию PRNG, соответствующую вашим потребностям, особенно при поиске достаточно сложных коэффициентов. Если вы ищете 1 из 1 миллиона, вы можете никогда не найти результат, если функция PRNG не равномерно покрывает желаемый диапазон.
Примечание.
std::random_shuffle
устарело в С++ 14, удалено из С++ 17. Используйтеstd::shuffle
(доступно начиная с С++ 11) и прилично подготовленный prng.