靶病变; DR:
使用相同的参数,两个散列函数产生相同的结果。要实现这一点,需要满足的前提条件很少。
我正在构建一个包含Rust和Python部分的系统。我需要一个哈希库,在两端为相同的输入生成相同的值。我认为Python和Rust也使用SipHash 1 - 3,所以我尝试使用它。
巨蟒:
>>> import ctypes
>>> ctypes.c_size_t(hash(b'abcd')).value
14608482441665817778
>>> getsizeof(ctypes.c_size_t(hash(b'abcd')).value)
36
>>> type(b'abcd')
<class 'bytes'>
rust eclipse :
use hashers::{builtin::DefaultHasher};
use std::hash::{Hash, Hasher};
pub fn hash_str(s: &str) -> u64 {
let mut hasher = DefaultHasher::new();
s.hash(&mut hasher);
hasher.finish()
}
pub fn hash_bytes(b: &[u8]) -> u64 {
let mut hasher = DefaultHasher::new();
b.hash(&mut hasher);
hasher.finish()
}
fn test_hash_str() {
let s1: &str = "abcd";
let h1: u64 = hash_str(s1);
assert_eq!(h1, 13543138095457285553);
}
#[test]
fn test_hash_bytes() {
let b1: &[u8] = "abcd".as_bytes();
let h1: u64 = hash_bytes(b1);
assert_eq!(h1, 18334232741324577590);
}
不幸的是,我不能在两端产生相同的值。有没有办法得到相同的值?
更新:
在签入Python实现之后,有一个细节是我最初忽略的,Python在每次运行时都使用一种随机盐,这意味着我从Python函数中得到的结果不可能与Rust版本相同。
可以使用PYTHONHASHSEED = 0 python禁用此功能...
然而,这仍然不能使Python产生与Rust版本相同的值,我在两端都尝试了定制的SipHash实现,结果是一致的:
两者都使用siphasher::sip::sipHasher13;和DefaultHasher产生相同的输出。String的结果与& str相同,但与. as_bytes()版本不同。
#[test]
fn test_hash_string() {
let s1: String = "abcd".to_string();
let h1: u64 = hash_string(s1);
assert_eq!(h1, 13543138095457285553);
}
#[test]
fn test_hash_str() {
let s1: &str = "abcd";
let h1: u64 = hash_str(s1);
assert_eq!(h1, 13543138095457285553);
}
#[test]
fn test_hash_bytes() {
let b1: &[u8] = "abcd".as_bytes();
let h1: u64 = hash_bytes(b1);
assert_eq!(h1, 18334232741324577590);
}
在Python端禁用随机化后:
sh = SipHash(c=1, d=3)
h = sh.auth(0, "abcd")
assert h == 16416137402921954953
1条答案
按热度按时间xdyibdwo1#
没有指定内部算法,因此不应在发布时依赖它及其散列。
.hash()
类型功能,它也不适用于外部哈希;它会对数据进行一些未指定的内部二进制化。使用hasher的.write
功能直接向其提供二进制数据。也就是说,解决方案是使用特定的散列库来实现兼容性,而不是使用内部散列库。
在Rust中,如果您需要siphash-1 - 3,这可能是
siphasher
。不过,我不确定Python,因为我有一段时间没有使用它了。下面是Rust的示例代码:
请注意,虽然我真的不推荐它,但Rust的内部hasher也是如此:
x一个一个一个一个x一个一个二个x
背景
那么,为什么
s.hash()
和s.as_bytes().hash()
的行为会很奇怪呢?让我们编写一个简单的调试散列器:
一个三个三个一个
现在我们有了答案:
s
似乎附加了0xff
。这也可以在其源代码中看到:s.as_bytes()
似乎在前面附加了奇怪的字节,在它的源代码中可以看到这是字符串的长度: