哈希(hash),是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
取模是最简单的哈希方法之一,例如:设定当前模数为 5,哈希函数为 f,那么 f(7)=2,f(
13) = 3 。
当两个不同的输入,经过哈希函数的运算,得到了相同的值,我们称这种现象为哈希冲突,例如:f(6)=f(11)=1。
Bob 作为学校网站的开发者,他被要求在不明文传输密码的前提下,实现用户登录,其中密码为26个小写英文字母。经过查阅资料,他找到了如下的算法:
const long long p1 = 998244353, p2 = 1000000007;
void next_secret(long long &secret, long long index_number) {
secret = (secret ^ index_number) * index_number % p1 * p2 % p1;
}
long long string_hash(long long secret, string str) {
long long hash_value = 0;
for (int i = 0; i < str.length(); i++){
hash_value = (hash_value + secret * (str[i] - 'a' + 1)) % p1;
next_secret(secret, i + 1);
}
return hash_value;
}
这个算法的做法为,首先把小写英文字母映射成1~26的数字,使用密钥与转换后数字相乘,加到最终的哈希之中,每对被加密的字符串操作一位,都使用当前的编号,将密文按照指定算法进行更新,其中哈希模数为固定的p1。
这个算法的用法为,每次向后台发送登录请求时,随机获取一个密钥,使用密钥将用户输入的密码进行哈希,得到一个哈希值,传输时,只发送密钥和哈希值,让后台根据密钥使用相同的算法,根据数据库中保存的明文密码计算得到哈希值,若两哈希值相同,则判定为密码正确。
由于 Bob 的疏忽,在每次发送登录请求时,他并没有随机获取一个密钥,而是使用了一个常数作为密钥,这样就会大大降低这套系统的安全性。
现在,输入密钥和一个字符串,请你找到另一个与输入字符串不同的字符串(长度可与原串不同),使得他们的哈希值相等。