本文介绍了一种实用的安全聚合算法(Secure Aggregation Protocol),主要参考了Google的论文Practical Secure Aggregation for Privacy-Preserving Machine Learning 。
本文首发于我的博客,
原文地址请点击这里!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
谷歌的Bonawitz等人,在2017年的CCS中提出了一种安全聚合加密方案(SMPC),服务器只能看到聚合完成之后的梯度,不能知道每个用户的私有的真实梯度值。这种方法适用于大规模终端(例如手机)通过一个服务器,共同计算各自输入之和的情形,但是前提是不泄露某个特定的终端的输入,无论是对服务器还是对其他的终端。
在介绍该算法之前,首先介绍几个该算法中需要用的几个算法。
-
秘密共享(Secret Sharing)
Shamir算法是秘密共享协议的一种实现,本文使t用的就是这种算法,其基本思想是分发者通过秘密多项式,将秘密s分解为n个秘密分发个持有者,其中任意不少于t个秘密均能恢复密文,而任意少于t个秘密均无法得到密文的任何信息。
本文使用 { ( u , s u ) } u ∈ U ← SS.share ( s , t , U ) , V ⊆ U , ∣ V ∣ ≥ t \left\{\left(u, s_{u}\right)\right\}_{u \in \mathcal{U}} \leftarrow \text { SS.share }(s, t, \mathcal{U}), \mathcal{V} \subseteq \mathcal{U},|\mathcal{V}| \geq t {(u,su)}u∈U← SS.share (s,t,U),V⊆U,∣V∣≥t,来表示将秘密s拆分成n份,传入集合 U ( 大 小 为 n , 表 示 n 个 秘 密 分 发 持 有 者 ) , s , t \mathcal{U}(大小为n,表示n个秘密分发持有者),s,t U(大小为n,表示n个秘密分发持有者),s,t,针对每一个集合中的每一个u输出它自己那一条拆分后的密文。
使用 SS.recon ( { ( u , s u ) } u ∈ V , t ) = s \text { SS.recon }\left(\left\{\left(u, s_{u}\right)\right\}_{u \in \mathcal{V}}, t\right)=s SS.recon ({(u,su)}u∈V,t)=s来表示恢复密文。
-
密钥协商(Key Agreement)
DH密钥交换是一个常用的密钥交换协议,DH密钥交换的目的,是让想要通信的Alice、Bob双方,他们之间能够拥有一个私密的密钥,这个密钥只有A和B两个人知道。DH密钥交换包含如下步骤:
-
首先,Alice和Bob商量好DH的参数,一个大数素数 q \mathcal{q} q,和 z p \mathbb{z}_p zp上的一个生成元 g g g(1< g g g< q \mathcal{q} q,一种比较特殊的素数)。这一步我们用公式表示为, K A . p a r a m ( k ) → ( G ′ , g , q , H ) , H 为 哈 希 函 数 KA.param(k) \rightarrow \left(\mathbb{G}^{\prime}, g, q, H\right),H为哈希函数 KA.param(k)→(G′,g,q,H),H为哈希函数
-
然后通过公共参数生成各自的公钥和私钥:
K A . gen ( G ′ , g , q , H ) → ( x , g x ) , x 为 私 钥 , g x 为 生 成 的 公 钥 KA. \operatorname{gen}(G',g,q,H) \rightarrow\left(x, g^{x}\right),x为私钥,g^{x}为生成的公钥 KA.gen(G′,g,q,H)→(x,gx),x为私钥,gx为生成的公钥
-
Alice和Bob分别将公钥发送给对方
-
然后双方通过公钥生成一个用于通信的密钥, s u , v = H ( ( g x v ) x u ) s_{u, v}=H\left(\left(g^{x_{v}}\right)^{x_{u}}\right) su,v=H((gxv)xu),用公式表示为, K A . a g r e e ( x u , g x v ) → s u , v KA.agree\left(x_{u}, g^{x_{v}}\right) \rightarrow s_{u, v} KA.agree(xu,gxv)→su,v
-
-
认证加密(Authenticated Encryption)
其实就是使用一个key对一段明文进行加密,同时使用相同的key可以进行解密。用公式表示为:
AE.dec ( c , AE.enc ( c , x ) ) = x , x 表 示 需 加 密 的 明 文 , c 表 示 k e y \operatorname{AE.dec}(c, \operatorname{AE.enc}(c, x))=x,x表示需加密的明文,c表示key AE.dec(c,AE.enc(c,x))=x,x表示需加密的明文,c表示key
-
伪随机数生成器(Pseudorandom Generator)
给定一个随机数种子,生成无关的随机数,使用 P R G ( s e e d ) PRG(seed) PRG(seed)来表示输出的随机数。
-
数字签名(Signature Scheme )
对一段明文进行数字签名,以保证其内容没有遭到篡改。
- 首先输入k,生成一对公钥和私钥, SIG.gen ( k ) → ( d P K , d S K ) \operatorname{SIG.gen}(k) \rightarrow\left(d^{P K}, d^{S K}\right) SIG.gen(k)→(dPK,dSK)
- 然后通过签名算法对一段消息m用私钥进行签名, S I G . s i g n ( d S K , m ) → σ SIG.sign\left(d^{S K}, m\right) \rightarrow \sigma SIG.sign(dSK,m)→σ
- 然后用公钥进行验证, S I G . v e r ( d P K , m , σ ) → { 0 , 1 } SIG.ver \left(d^{P K}, m, \sigma\right) \rightarrow\{0,1\} SIG.ver(dPK,m,σ)→{0,1}
算法具体实现步骤
-
初始阶段
- 首先,每个客户端初始化一个安全参数k用来生成DH的相关参数, p p ← K A ⋅ param ( k ) p p \leftarrow \mathbf{K A} \cdot \operatorname{param}(k) pp←KA⋅param(k)
- 规定秘密分享协议中的数值n和阈值t。
- 规定m为传输数据向量的大小, x u x_u xu为隐私数据向量。
- 每个客户端和服务器有一个经过身份验证的通道。
- 客户端u从可信第三方针获得一个私钥,对所有的其他用户v第三方给予一个公钥, ( d P K , d S K ) \left(d^{P K}, d^{S K}\right) (dPK,dSK)
-
第0轮(通知Keys)
- 对于客户端u:
- 根据根据pp分别生成两对公钥和私钥,分别为, ( c u P K , c u S K ) ← KA.gen ( p p ) \left(c_{u}^{P K}, c_{u}^{S K}\right) \leftarrow \operatorname{KA.gen}(p p) (cuPK,cuSK)←KA.gen(pp)和 ( s u P K , s u S K ) ← K A ⋅ gen ( p p ) \left(s_{u}^{P K}, s_{u}^{S K}\right) \leftarrow \mathbf{K A} \cdot \operatorname{gen}(p p) (suPK,suSK)←KA⋅gen(pp),然后使用数字签名算法对两个公钥进行签名,生成 σ u ← SIG.sign ( d u S K , c u P K ∥ s u P K ) \sigma_{u} \leftarrow \operatorname{SIG.sign}\left(d_{u}^{S K}, c_{u}^{P K} \| s_{u}^{P K}\right) σu←SIG.sign(duSK,cuPK∥suPK)
- 将生成的两个公钥,连同签名结果即, ( c u P K ∥ s u P K ∥ σ u ) \left(c_{u}^{P K}\left\|s_{u}^{P K}\right\| \sigma_{u}\right) (cuPK∥∥suPK∥∥σu),通过经过验证的通道发送给服务器。
- 对于服务器端:
- 检查所有客户端中收集消息数是否小于t,否则中止算法,将收到消息的客户端集合记为 U 1 \mathcal{U}_1 U1
- 向 U 1 \mathcal{U}_1 U1中的所有客户端广播其与其他客户端对应的客户端id,两个公钥,以及数字签名的结果, { ( v , c v P K , s v P K , σ v ) } v ∈ U 1 \left\{\left(v, c_{v}^{P K}, s_{v}^{P K}, \sigma_{v}\right)\right\}_{v \in \mathcal{U}_{1}} {(v,cvPK,svPK,σv)}v∈U1
- 对于客户端u:
-
第1轮(分享Keys)
-
对于客户端u:
-
收到来自服务器的 { ( v , c v P K , s v P K , σ v ) } v ∈ U 1 \left\{\left(v, c_{v}^{P K}, s_{v}^{P K}, \sigma_{v}\right)\right\}_{v \in \mathcal{U}_{1}} {(v,cvPK,svPK,σv)}v∈U1,为了验证 ∣ U 1 ∣ ≥ t \left|\mathcal{U}_{1}\right| \geq t ∣U1∣≥t是否真的成立,防止服务器伪造客户端来套取客户端数据,先对 U 1 \mathcal{U}_1 U1中的所有客户端进行消息验证,即
∀ v ∈ U 1 , \forall v \in \mathcal{U}_{1}, ∀v∈U1, SIG.ver ( d v P K , c v P K ∥ s v P K , σ u ) = 1 \left(d_{v}^{P K}, c_{v}^{P K} \| s_{v}^{P K}, \sigma_{u}\right)=1 (dvPK,cvPK∥svPK,σu)=1
由于公钥,私钥是可信第三方分发的,如果服务器伪造客户端,那么这个公式就不成立,那么客户端立即中止算法。
-
随机抽样一个 b u b_u bu,用于PRG(伪随机数生成器)的生成种子。
-
通过秘密共享算法生成 S u P K S_u^{PK} SuPK和 b u b_u bu的分享内容(shares),
{ ( v , s u , v S K ) } v ∈ U 1 ← \left\{\left(v, s_{u, v}^{S K}\right)\right\}_{v \in \mathcal{U}_{1}} \leftarrow {(v,su,vSK)}v∈U1← SS.share ( s u S K , t , U 1 ) \left(s_{u}^{S K}, t, \mathcal{U}_{1}\right) (suSK,t,U1)和 { ( v , b u , v ) } v ∈ U 1 ← \left\{\left(v, b_{u, v}\right)\right\}_{v \in \mathcal{U}_{1}} \leftarrow {(v,bu,v)}v∈U1← SS.share ( b u , t , U 1 ) \left(b_u, t, \mathcal{U}_{1}\right) (bu,t,U1)
-
对于 U 1 \mathcal{U}_{1} U1中的其他客户端,针对每个客户端使用认证加密技术,使用的密钥是两个客户端经过密钥协商出来的结果,计算一个
e u , v ← e_{u, v} \leftarrow eu,v← AE.enc ( KA.agree ( c u S K , c v P K ) , u ∥ v ∥ s u , v S K ∥ b u , v ) \left(\text { KA.agree }\left(c_{u}^{S K}, c_{v}^{P K}\right), u\|v\| s_{u, v}^{S K} \| b_{u, v}\right) ( KA.agree (cuSK,cvPK),u∥v∥su,vSK∥bu,v),
e u , v e_{u, v} eu,v携带了share的信息,这个东西后面就会被传给相应的其他客户端。
-
将所有 e u , v e_{u, v} eu,v传输给服务器,这个东西每个都暗中包含了u和v地址数据(大概是客户端id一类的东西)
-
存储本轮中生成的所有信息和收到的信息。
-
-
对于服务器端:
- 检查所有客户端中收集消息数是否小于t,否则中止算法,将收到消息的客户端集合记为 U 2 \mathcal{U}_2 U2,注意 U 2 ⊆ U 1 \mathcal{U}_{2} \subseteq \mathcal{U}_{1} U2⊆U1
- 将收集到的所有 e u , v e_{u, v} eu,v,分别广播给相应的客户端。
-
-
第2轮(收集已经加密过的信息)
-
对于客户端u:
-
收集来自服务器端的与其他客户端对应的 e u , v e_{u, v} eu,v,并且得到集合 U 2 \mathcal{U}_2 U2,如果发现大小小于t,则立即中止算法(因为少于t个客户端的信息无法恢复)。
-
对于每个客户端u,计算他与 U 2 \mathcal{U}_2 U2中其他客户端的共享mask(用来掩盖真实的数据),通过密钥协商得到密钥, s u , v ← K A . a g r e e ( s u S K , s v P K ) s_{u, v} \leftarrow KA.agree \left(s_{u}^{S K}, s_{v}^{P K}\right) su,v←KA.agree(suSK,svPK),然后用这个密钥作为种子,送入PRG(伪随机数生成器),生成与隐私数据向量大小相同的m个,作为mask向量。
即 p u , v = Δ u , v ⋅ P R G ( s u , v ) \boldsymbol{p}_{u, v}=\Delta_{u, v} \cdot \mathbf{P} \mathbf{R} \mathbf{G}\left(s_{u, v}\right) pu,v=Δu,v⋅PRG(su,v),当 u > v , u>v, u>v, Δ u , v = 1 \Delta_{u, v}=1 Δu,v=1 当 u < v u<v u<v, Δ u , v = − 1 \Delta_{u, v}=-1 Δu,v=−1 ( 注意 p u , v + p v , u = 0 , ∀ u ≠ v ) \left(\text {注意 } p_{u, v}+p_{v, u}=0, \forall u \neq v\right) (注意 pu,v+pv,u=0,∀u=v),定义 p u , u = 0 \boldsymbol{p}_{u, u} = 0 pu,u=0
-
计算每个客户端u的个人mask向量 p u = P R G ( b u ) \boldsymbol{p}_{u} = PRG(b_u) pu=PRG(bu),然后计算将隐私数据向量与个人mask向量和共享mask向量的加和,用来mask个人隐私数据,
y u ← x u + p u + ∑ v ∈ U 2 p u , v ( m o d R ) \boldsymbol{y}_{u} \leftarrow \boldsymbol{x}_{u}+\boldsymbol{p}_{u}+\sum_{v \in \mathcal{U}_{2}} \boldsymbol{p}_{u, v}(\bmod R) yu←xu+pu+∑v∈U2pu,v(modR)
-
如果上述过程中任意操作,例如密钥协商,PRG操作失败,那么直接中止操作,否则将生成的 y n y_n yn发送给服务器。
-
-
对于服务器端:
- 检查所有客户端中收集 y n y_n yn数量是否小于t,否则中止算法,将收到消息的客户端集合记为 U 3 \mathcal{U}_3 U3,注意 U 3 ⊆ U 2 \mathcal{U}_{3} \subseteq \mathcal{U}_{2} U3⊆U2,将 U 3 \mathcal{U}_3 U3的列表发送给每个用户。
-
-
第3轮(一致性检查,我认为该部分主要是为了防止服务器恶意模拟客户端以套取信息)
-
对于客户端u:
-
收集收到的客户端集合 U 3 \mathcal{U}_3 U3,查看 U 3 \mathcal{U}_3 U3的大小是否大于t,小于t直接中止算法。
-
若大于等于t,那么将 U 3 \mathcal{U}_3 U3使用数字签名进行某种形式的签名, σ u ′ ← SIG.sign ( d u S K , U 3 ) \sigma_{u}^{\prime} \leftarrow \operatorname{SIG.sign}\left(d_{u}^{S K}, \mathcal{U}_{3}\right) σu′←SIG.sign(duSK,U3),得到一个 σ u ′ \sigma_{u}^{\prime} σu′。
这个东西主要是为了防止服务器在上一步恶意欺骗大多数客户端以套取信息,因为如果上一步中服务器故意伪造某一个客户端掉线的假象,那么想要套取信息的那个客户端和其他客户端收到的 U 3 \mathcal{U}_3 U3就会是不一样的,这样签名就会出问题,可以被客户端发现服务器端造假。
-
-
对于服务器端:
- 收集所有 σ u ′ \sigma_{u}^{\prime} σu′,确认收到的消息数是否大于t,否则中止,如果大于t,那么将这部分用户记为, U 4 ⊆ U 3 \mathcal{U}_{4} \subseteq \mathcal{U}_{3} U4⊆U3,对于该集合中的每一个用户,发送对应的签名结果 { v , σ v ′ } v ∈ U 4 \left\{v, \sigma_{v}^{\prime}\right\}_{v \in \mathcal{U}_{4}} {v,σv′}v∈U4
-
-
第4轮(汇聚信息并解密)
-
对于客户端u:
- 收到来自服务器端的签名结果 { v , σ v ′ } v ∈ U 4 \left\{v, \sigma_{v}^{\prime}\right\}_{v \in \mathcal{U}_{4}} {v,σv′}v∈U4,确认 U 4 ⊆ U 3 \mathcal{U}_{4} \subseteq \mathcal{U}_{3} U4⊆U3,且数量大于等于t,然后确认SIG.ver ( d P K , U 3 , σ v ′ ) = 1 \left(d^{P K}, \mathcal{U}_{3}, \sigma_{v}^{\prime}\right)=1 (dPK,U3,σv′)=1 对于所有的 v ∈ U 4 v \in \mathcal{U}_{4} v∈U4,这一步主要是防止服务器端故意欺骗客户端以套取客户端信息。如果以上任何环节出问题,直接中止算法。
- 在 U 2 \mathcal{U}_{2} U2中除了u的其他客户端中,对于每个客户端v,可以使用密钥来解密认证加密过的 e u , v e_{u, v} eu,v, v ′ ∥ u ′ ∥ s v , u S K ∥ b v , u ← AE.dec ( K A . agree ( c u S K , c v P K ) , e v , u ) v^{\prime}\left\|u^{\prime}\right\| s_{v, u}^{S K} \| b_{v, u} \leftarrow \operatorname{AE.dec}\left(\mathrm{KA} . \operatorname{agree}\left(c_{u}^{S K}, c_{v}^{P K}\right), e_{v, u}\right) v′∥u′∥sv,uSK∥bv,u←AE.dec(KA.agree(cuSK,cvPK),ev,u),得到这四个结果,其中前两个首先验证一下是否满足 u = u ′ ∧ v = v ′ u=u^{\prime} \wedge v=v^{\prime} u=u′∧v=v′,如果不满足说明发错了,或者信息丢失了,直接中止算法。
- 将得到的两个share有选择的发送给服务器,对于客户端 v ∈ U 2 \ U 3 v \in \mathcal{U}_{2} \backslash \mathcal{U}_{3} v∈U2\U3,即掉线的客户端,发送 S v , u S K S_{v, u}^{S K} Sv,uSK , 对于 v ∈ U 3 v \in \mathcal{U}_{3} v∈U3,即正常的客户端,发送 b v , u b_{v, u} bv,u
-
对于服务器端:
-
收集从客户端中收集最少t个信息,这群客户端记为 U 5 \mathcal{U}_{5} U5,否则中止算法
-
对于客户端 v ∈ U 2 \ U 3 v \in \mathcal{U}_{2} \backslash \mathcal{U}_{3} v∈U2\U3,即掉线的客户端,使用秘密分享中的恢复算法,恢复出 s u S K s_{u}^{S K} suSK,即, s u S K ← SS.recon ( { s u , v S K } v ∈ U 5 , t ) s_{u}^{S K} \leftarrow \operatorname{SS.recon}\left(\left\{s_{u, v}^{S K}\right\}_{v \in \mathcal{U}_{5}}, t\right) suSK←SS.recon({su,vSK}v∈U5,t),将它送入PRG计算出针对他和其他客户端的所有 p v , u p_{v,u} pv,u
-
同理,对于 v ∈ U 3 v \in \mathcal{U}_{3} v∈U3,即正常的客户端,使用秘密分享中的恢复算法,恢复出 b u b_u bu, b u ← SS.recon ( { b u , v } v ∈ U 5 , t ) b_{u} \leftarrow \operatorname{SS.recon}\left(\left\{b_{u, v}\right\}_{v \in \mathcal{U}_{5}}, t\right) bu←SS.recon({bu,v}v∈U5,t),送入PRG计算出自己的 p u p_u pu。
-
最后计算出聚合后的结果,即 z = ∑ u ∈ U 3 x u z=\sum_{u \in \mathcal{U}_{3}} x_{u} z=∑u∈U3xu,
∑ u ∈ U 3 x u = ∑ u ∈ U 3 y u − ∑ u ∈ U 3 p u + ∑ u ∈ U 3 , v ∈ U 2 \ U 3 p v , u \sum_{u \in \mathcal{U}_{3}} x_{u}=\sum_{u \in \mathcal{U}_{3}} y_{u}-\sum_{u \in \mathcal{U}_{3}} p_{u}+\sum_{u \in \mathcal{U}_{3}, v \in \mathcal{U}_{2} \backslash \mathcal{U}_{3}} p_{v, u} ∑u∈U3xu=∑u∈U3yu−∑u∈U3pu+∑u∈U3,v∈U2\U3pv,u
-
-
几个需要注意的地方
- 该算法原理是针对隐私数据向量 x u x_u xu,对他进行一个mask的操作,mask主要分为两块,一块是 p u p_u pu,另一块是 p u , v p_{u,v} pu,v,这两个mask可以保证隐私数据不被服务器所获取。
- 服务器是否可以通过欺骗其他客户端以套取某一个客户端的信息?答曰,不行,因为有第3轮的认证加密机制,会在第4轮被客户端发现从而发现发送的客户端集合不一致导致算法中止。
- 在恢复阶段,诚实用户v不会把关于某个用户u的信息 b u b_u bu和 s u S K s_u^{SK} suSK的秘密同时说出去,对于在线的用户u,则会说出 b u b_u bu,不在线的用户,会说出 s u S K s_u^{SK} suSK, b u b_u bu和 s u S K s_u^{SK} suSK就是用来生成 p u p_u pu和 p u , v p_{u,v} pu,v的。
- 如果没有 p u p_u pu,那么如果用户不是真的掉线,而是延迟较大,在服务器已经得到所有的 p u , v p_{u,v} pu,v时,他就可以恢复出该用户的数据,有了 p u p_u pu就难以恢复出数据。
参考资料
1.论文Practical Secure Aggregation for Privacy-Preserving Machine Learning
2.https://blog.csdn.net/qq_37734256/article/details/104223580
3.https://www.cnblogs.com/20189223cjt/p/12529827.html
4.课件:http://jakubkonecny.com/files/2018-01_UW_Federated_Learning.pdf
5.ACM CCS视频:https://www.youtube.com/watch?v=OCy9gPjl-XM&t=1153s