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