"Буквенно-цифровой" хэш-A-Z, 0-9


Я ищу функцию, которая будет генерировать "буквенно-цифровой хэш". Учитывая исходную строку, он создает определенную результирующую строку, которая может содержать любую букву a-z или цифру 0-9 и не может быть реверсивно спроектирована для получения источника. Это будет использоваться для генерации паролей для системы, основанной на секретных данных, поэтому строки между 8 и 12 символами являются идеальными, и безопасный хэш также будет идеальным.

Я думаю, что могу использовать обычный побитовый хэш, XOR-сложить его до 64 бит (если я использую, например, SHA256), а затем взять результат 5 бит за раз (производя число 0-31) и искать код символа для использования из индексированной упорядоченной коллекции. Есть 26 букв и 10 цифр, что означает, что я должен буду оставить некоторые из них (вероятно, удаляя символы, которые могут быть ошибочно приняты за другие, если они написаны от руки). 64 бита, по 5 бит за раз, дадут 12-символьную строку с оставшимися 4 битами.

Однако меня беспокоят две вещи: во-первых, введение предвзятости путем принятия число битов не-степени-2; и во-вторых, что делать с оставшимися битами. Использую ли я их как есть, зная, что будет только 16 возможностей, оставляю ли я их (и теряю данные, возможно, вводя смещение), или я включаю еще один бит, чтобы сделать 13-символьную строку (и откуда должен прийти последний бит)?

EDIT: Вот мой текущий удар по нему; он берет перечисляемый байт (как и массив байт, созданный большинством алгоритмов хэширования) и возвращает строку:

    /// <summary>
    /// Converts an IEnumerable of bytes to a string representation which can have any lowercase letter a-z except for l, o, q and z, and any digit 0-9.
    /// Uses 5 bits of the byte array at a time to generate numbers from 0 to 31, which are then translated to letters or numbers.
    /// </summary>
    /// <param name="toConvert">the byte array to convert.</param>
    /// <returns>A string containing the alphanumeric case-insensitive representation of the bytes in the array.</returns>
    public static string ToInsensitiveAlphaNumericString(this IEnumerable<byte> toConvert)
    {
        var chars = new[]
                        {
                            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'p', 'r', 's', 't',
                            'u', 'v', 'w', 'x', 'y', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
                        };

        var enumerator = toConvert.GetEnumerator();
        enumerator.MoveNext();

        int buffer = enumerator.Current;
        short bufferLength = 8;
        const int valueLength = 5;

        var builder = new StringBuilder();

        while (true)
        {
            var value = buffer >> (bufferLength - valueLength);

            builder.Append(chars[value]);

            buffer = buffer - (value << (bufferLength - valueLength));
            bufferLength -= valueLength;

            if(bufferLength < valueLength )
            {
                if (enumerator.MoveNext())
                {
                    buffer = (buffer << 8) + enumerator.Current;
                    bufferLength += 8;
                }
                else
                {
                    //here's the main question; to include, or not to include?
                    if (bufferLength > 0)
                        builder.Append(chars[buffer]);
                    break;
                }
            }
        }

        return builder.ToString();
    }
2 11

2 ответа:

Как насчет генерации SHA256, а затем Base36 кодирования результата? Никаких остатков, никаких предубеждений...

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

Если вы просто используете эти биты, как они есть (так что один символ имеет только 16 возможностей), у вас все еще есть полные 64 бита энтропии. Если вы довольны 64 битами энтропии (что, похоже, так и есть), нет причин возражать, что один символ имеет ограниченный диапазон.

Если у вас есть какая-то причина (эстетика?) чтобы все символы имели полный диапазон, тогда вы можете отбросить эти 4 бита, но вы будете снижать себя до 60 бит энтропии. Если бы вы могли ... был доволен 8-символьными паролями, тогда кажется, что 60 бит - это тоже много.

Так что какой бы из них не был проще, он должен работать нормально.