Как создать псевдослучайную 32-байтовую строку для использования в качестве соли в криптографической хеш-функции? - PullRequest
2 голосов
/ 29 мая 2019

Я пытаюсь закодировать функцию шифрования пароля в C ++ (Примечание: только для образовательных целей. Я не буду хранить свои пароли вместе с ним.), Но я не уверен, как создать случайную 32-байтовую соль, используяпредопределенный набор символов.Как мне это сделать?

#include <random>
#include <iostream>

using namespace std;


void genSalt() {

    const char charset[] = {

        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "!£$%^&*():@~#'?/><.,|`¬¦"

    };

}

Код должен генерировать случайную 32-байтовую строку на основе определенного набора символов 'charset'.Я не уверен, как этого добиться.

Ответы [ 2 ]

2 голосов
/ 29 мая 2019

Первое, на что вы должны обратить внимание - это хранение некоторых из этих символов в исходном файле. В зависимости от кодировки, £, ¬ или ¦ могут потенциально быть больше одного байта, что может испортить ваш результат; Вы должны хранить их как жестко закодированные байты, если хотите, чтобы все шло гладко, независимо от кодировки:

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 не будет равномерно покрывать желаемый диапазон.

0 голосов
/ 29 мая 2019

Если вы можете использовать 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)];
    }
}
...