Это даже не хеш, потому что в charset
нет 82 символов.Это больше похоже на синтаксический анализ строки как числа base-82, где вы можете использовать только первые 16 символов.Это было бы полностью обратимо, если бы оно не использовало числа с плавающей точкой, которые неточны для целых чисел такого большого размера.В случае, если вы не знаете, почему, упрощенная версия заключается в том, что операция внутри цикла:
g * 82 + d
дает различный результат для каждого возможного значения g и d, если d меньше 82потому что между g * 82 и (g + 1) * 82 достаточно места для 82 различных d s (от 0 до 81).Каждый другой результат обратим обратно к g и d путем деления на 82;все значение g, а остаток d.Когда каждая операция внутри цикла обратима, вы можете полностью изменить ее.
Таким образом, как если бы вы могли вручную преобразовать число в десятичное число с помощью цикла, который разделяет одну цифру за раз, вы можете преобразовать эту неточную величинуномер в базе 82:
const getDigits = (value, base) => {
const result = [];
while (value) {
result.push(value % base);
value /= base;
}
return result.reverse();
};
const getLetter = index =>
String.fromCharCode(97 + index);
const getPreimage = value =>
getDigits(value, 82n)
.map(Number)
.map(getLetter)
.join('');
console.log(getPreimage(29662550362n));
console.log(getPreimage(16530092119764772n));
Результаты начинаются с «i», поскольку g
начинается с 8 вместо 0. Второе число также достаточно велико, чтобы не быть уникальным (в отличие от agile
'хэш', который может быть представлен точно числом JavaScript), но если вы просто пытались найти любой прообраз, этого достаточно.
function hash(str) {
let g = 8;
let charset = "abcdefghijklmnop";
for(let i = 0; i < str.length; i++) {
g = (g * 82 + charset.indexOf(str[i]));
}
return g;
}
for (const s of ['hijackec', 'hijacked', 'hijackee', 'hijackef', 'hijackeg']) {
console.log(s, hash(s) === 16530092119764772);
}