Первое, на что вы должны обратить внимание - это хранение некоторых из этих символов в исходном файле. В зависимости от кодировки, £
, ¬
или ¦
могут потенциально быть больше одного байта, что может испортить ваш результат; Вы должны хранить их как жестко закодированные байты, если хотите, чтобы все шло гладко, независимо от кодировки:
const char charset[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"!\xA3$%^&*():@~#'?/><.,|`\xAC\xA6";
// \xA3 = single-byte extended encoding for £
// \xAC = ¬
// \xA6 = ¦
(И, мягко говоря, я имею в виду, что использование этих символов в вашей соли - плохая идея, поскольку они могут конфликтовать с другими кодировками, в которых используется соль.)
Что касается генерации соли пароля, я не криптолог, но мне кажется немного сомнительным использование чего-либо, кроме криптографически безопасного генератора псевдослучайных чисел. Если это просто упражнение C ++, встроенные генераторы случайных чисел будут в порядке. На самом деле я никогда не использовал функции PRNG в C ++ 11, поэтому это тоже очень полезное упражнение.
Вы начинаете с создания random_device
, а затем движка рандомизатора:
#include <random>
std::random_device my_random_device;
std::default_random_engine my_random_engine(my_random_device());
Существуют различные механизмы, которые вы можете выбрать для точной настройки случайных чисел, и по умолчанию это выбор, определенный реализацией (я предполагаю, что с этим вы также можете легко подключить криптографически безопасный генератор).
random_device
обрабатывает заполнение автоматически, тогда как если вы используете более старую функцию C-style rand
, вам нужно вызвать 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
Еще одна вещь, которую я хотел бы упомянуть, это распространенная ошибка использования %
против случайного числа. Например, рассмотрим этот контрольный пример с использованием более старой функции C-style rand()
:
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 не будет равномерно покрывать желаемый диапазон.