|
| 1 | +.. SPDX-License-Identifier: GPL-2.0 |
| 2 | +.. include:: ../disclaimer-zh_CN.rst |
| 3 | +:Original: Documentation/security/siphash.rst |
| 4 | + |
| 5 | +:翻译: |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +===================================== |
| 10 | +SipHash - 一种短输入伪随机函数(PRF) |
| 11 | +===================================== |
| 12 | + |
| 13 | +:作者: Jason A.Donenfeld < [email protected]> |
| 14 | + |
| 15 | +SipHash是一种加密安全的伪随机函数,即一种用于生成伪随机密钥的哈 |
| 16 | +希函数,因为其在处理短输入时表现出色,因此得名。其由密码学家 |
| 17 | +Daniel J. Bernstein和Jean-Philippe Aumasson设计。目的主要是替 |
| 18 | +代其他哈希函数,例如:jhash,md5_transform,sha1_transform等。 |
| 19 | + |
| 20 | +SipHash采用一个完全由随机数生成的密钥,以及一个输入缓冲区或者 |
| 21 | +多个输入整数,它输出一个与随机数难以区分的整数,你可以将它作 |
| 22 | +为安全序列、安全cookies的一部分,或者对其进行掩码处理,以便在 |
| 23 | +哈希表中使用。 |
| 24 | + |
| 25 | +生成密钥 |
| 26 | +======== |
| 27 | + |
| 28 | +密钥应来源于加密安全的随机数生成,要么使用get random bytes |
| 29 | +要么使用get random once:: |
| 30 | + |
| 31 | + siphash_key_t key; |
| 32 | + get_random_bytes(&key, sizeof(key)); |
| 33 | + |
| 34 | +如果你的密钥来源不是这两个,那么你的做法是错的。 |
| 35 | + |
| 36 | +使用函数 |
| 37 | +======== |
| 38 | + |
| 39 | +这个函数有两个变种,一种是接受整数列表,另一种是接受缓冲区:: |
| 40 | + |
| 41 | + u64 siphash(const void *data, size_t len, const siphash_key_t *key); |
| 42 | + |
| 43 | +和:: |
| 44 | + |
| 45 | + u64 siphash_1u64(u64, const siphash_key_t *key); |
| 46 | + u64 siphash_2u64(u64, u64, const siphash_key_t *key); |
| 47 | + u64 siphash_3u64(u64, u64, u64, const siphash_key_t *key); |
| 48 | + u64 siphash_4u64(u64, u64, u64, u64, const siphash_key_t *key); |
| 49 | + u64 siphash_1u32(u32, const siphash_key_t *key); |
| 50 | + u64 siphash_2u32(u32, u32, const siphash_key_t *key); |
| 51 | + u64 siphash_3u32(u32, u32, u32, const siphash_key_t *key); |
| 52 | + u64 siphash_4u32(u32, u32, u32, u32, const siphash_key_t *key); |
| 53 | + |
| 54 | +如果向一个通用的hsiphash函数传递一个恒定长度的常量,他将 |
| 55 | +在编译的时候将常量折叠,并自动选择一个优化后的函数。 |
| 56 | + |
| 57 | +哈希表键函数的用法:: |
| 58 | + |
| 59 | + struct some_hashtable { |
| 60 | + DECLARE_HASHTABLE(hashtable, 8); |
| 61 | + siphash_key_t key; |
| 62 | + }; |
| 63 | + |
| 64 | + void init_hashtable(struct some_hashtable *table) |
| 65 | + { |
| 66 | + get_random_bytes(&table->key, sizeof(table->key)); |
| 67 | + } |
| 68 | + |
| 69 | + static inline hlist_head *some_hashtable_bucket(struct some_hashtable *table, struct interesting_input *input) |
| 70 | + { |
| 71 | + return &table->hashtable[siphash(input, sizeof(*input), &table->key) & (HASH_SIZE(table->hashtable) - 1)]; |
| 72 | + } |
| 73 | + |
| 74 | +然后,你可以像往常一样对返回的哈希存储桶进行迭代。 |
| 75 | + |
| 76 | +安全性 |
| 77 | +====== |
| 78 | + |
| 79 | +SipHash有着非常高的安全性,因为其有128位的密钥。只要密钥是保密的, |
| 80 | +即使攻击者看到多个输出,也无法猜测出函数的正确输出,因为2^128次 |
| 81 | +方个输出是非常庞大的。 |
| 82 | + |
| 83 | +Linux实现了SipHash的“2-4”变体 |
| 84 | + |
| 85 | +Struct-passing陷阱 |
| 86 | +================== |
| 87 | + |
| 88 | +通常情况下,XuY函数的输出长度不够大,因此你可能需要传递一个预填充 |
| 89 | +的结构体给SipHash,在这样做时,务必确保结构体没有填充空隙,最简单 |
| 90 | +的方法就是将结构体的成员按照大小降序的方式排序,并且使用offsetofend() |
| 91 | +函数代替sizeof()来获取结构体大小,出于性能的考虑,如果可以的话,最 |
| 92 | +好将结构体按右边界对齐,示例如下:: |
| 93 | + |
| 94 | + const struct { |
| 95 | + struct in6_addr saddr; |
| 96 | + u32 counter; |
| 97 | + u16 dport; |
| 98 | + } __aligned(SIPHASH_ALIGNMENT) combined = { |
| 99 | + .saddr = *(struct in6_addr *)saddr, |
| 100 | + .counter = counter, |
| 101 | + .dport = dport |
| 102 | + }; |
| 103 | + u64 h = siphash(&combined, offsetofend(typeof(combined), dport), &secret); |
| 104 | + |
| 105 | +资源 |
| 106 | +==== |
| 107 | + |
| 108 | +如果你有兴趣了解更多信息,请阅读SipHash论文: |
| 109 | +https://131002.net/siphash/siphash.pdf |
| 110 | + |
| 111 | +------------------------------------------------------------------------------- |
| 112 | + |
| 113 | +=========================================== |
| 114 | +HalfSipHash 是 SipHash 的一个较不安全的变种 |
| 115 | +=========================================== |
| 116 | + |
| 117 | +:作者: Jason A.Donenfeld < [email protected]> |
| 118 | + |
| 119 | +如果你认为SipHash的速度不够快,无法满足你的需求,那么你可以 |
| 120 | +使用HalfSipHash,这是一种令人担忧但是有用的选择。HalfSipHash |
| 121 | +将SipHash的轮数从“2-4”降低到“1-3”,更令人担心的是,它使用一 |
| 122 | +个容易被穷举攻击的64位密钥(输出为32位),而不是SipHash的128位 |
| 123 | +密钥,不过,这对于要求高性能“jhash”用户来说这是比较好的选择。 |
| 124 | + |
| 125 | +HalfSipHash是通过 "hsiphash" 系列函数提供的。 |
| 126 | + |
| 127 | +.. warning:: |
| 128 | + 绝对不要在作为哈希表键函数之外使用hsiphash函数,只有在你 |
| 129 | + 能完全能确定输出永远不会从内核传输出去的情况下才能使用, |
| 130 | + 作为缓解哈希表泛洪拒绝服务攻击的一种手段,它仅在某些情况 |
| 131 | + 下比jhash好用。 |
| 132 | + |
| 133 | +在64位的内核中,hsiphash函数实际上实现的是SipHash-1-3,这是一 |
| 134 | +种减少轮数的SipHash变形,而不是HalfSipHash-1-3。这是因为在64位 |
| 135 | +代码中SipHash-1-3的性能与HalfSipHash-1-3相当,甚至可能更快,请 |
| 136 | +注意,这并不意味这在64位的内核中,hsihpash函数与siphash函数相 |
| 137 | +同,也不意味着他们是安全的;hsihash函数仍然使用一种不太安全的 |
| 138 | +减少轮数的算法,并将输出截断为32位。 |
| 139 | + |
| 140 | +生成哈希密钥 |
| 141 | +============ |
| 142 | + |
| 143 | +密钥应始终来源于加密安全的随机数生成,要么使用get random bytes |
| 144 | +要么使用get random once:: |
| 145 | + |
| 146 | + hsiphash_key_t key; |
| 147 | + get_random_bytes(&key, sizeof(key)); |
| 148 | + |
| 149 | +如果你的钥匙来源不是这两个,那么你的做法是错的。 |
| 150 | + |
| 151 | +使用哈希函数 |
| 152 | +============ |
| 153 | + |
| 154 | +这个函数有两种变体,一个是接受整数列表,另一种是接受缓冲区:: |
| 155 | + |
| 156 | + u32 hsiphash(const void *data, size_t len, const hsiphash_key_t *key); |
| 157 | + |
| 158 | +和:: |
| 159 | + |
| 160 | + u32 hsiphash_1u32(u32, const hsiphash_key_t *key); |
| 161 | + u32 hsiphash_2u32(u32, u32, const hsiphash_key_t *key); |
| 162 | + u32 hsiphash_3u32(u32, u32, u32, const hsiphash_key_t *key); |
| 163 | + u32 hsiphash_4u32(u32, u32, u32, u32, const hsiphash_key_t *key); |
| 164 | + |
| 165 | +如果向一个通用的hsiphash函数传递一个恒定长度的常量,他将在编译 |
| 166 | +的时候将常量折叠,并自动选择一个优化后的函数。 |
| 167 | + |
| 168 | +哈希表键函数的用法 |
| 169 | +================== |
| 170 | + |
| 171 | +:: |
| 172 | + |
| 173 | + struct some_hashtable { |
| 174 | + DECLARE_HASHTABLE(hashtable, 8); |
| 175 | + hsiphash_key_t key; |
| 176 | + }; |
| 177 | + |
| 178 | + void init_hashtable(struct some_hashtable *table) |
| 179 | + { |
| 180 | + get_random_bytes(&table->key, sizeof(table->key)); |
| 181 | + } |
| 182 | + |
| 183 | + static inline hlist_head *some_hashtable_bucket(struct some_hashtable *table, struct interesting_input *input) |
| 184 | + { |
| 185 | + return &table->hashtable[hsiphash(input, sizeof(*input), &table->key) & (HASH_SIZE(table->hashtable) - 1)]; |
| 186 | + } |
| 187 | + |
| 188 | +然后,你可以像往常一样对返回的哈希存储桶进行迭代。 |
| 189 | + |
| 190 | +性能 |
| 191 | +==== |
| 192 | + |
| 193 | +hsiphash()大约比jhash()慢三倍,这是因为有许多替换,不过这些都不是问题, |
| 194 | +因为哈希表查找不是瓶颈。而且,这些牺牲是为了hsiphash()的安全性和DoS抗 |
| 195 | +性,这是值得的。 |
0 commit comments