forked from tuneinsight/lattigo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sharing.go
259 lines (205 loc) · 8.93 KB
/
sharing.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// Package dckks implements a distributed (or threshold) version of the CKKS scheme that enables secure multiparty computation solutions with secret-shared secret keys.
package dckks
import (
"math/big"
"github.com/jzhchu/lattigo/ckks"
"github.com/jzhchu/lattigo/drlwe"
"github.com/jzhchu/lattigo/ring"
"github.com/jzhchu/lattigo/rlwe"
"github.com/jzhchu/lattigo/utils"
)
// E2SProtocol is the structure storing the parameters and temporary buffers
// required by the encryption-to-shares protocol.
type E2SProtocol struct {
*drlwe.CKSProtocol
params ckks.Parameters
zero *rlwe.SecretKey
maskBigint []*big.Int
buff *ring.Poly
}
// ShallowCopy creates a shallow copy of E2SProtocol in which all the read-only data-structures are
// shared with the receiver and the temporary buffers are reallocated. The receiver and the returned
// E2SProtocol can be used concurrently.
func (e2s *E2SProtocol) ShallowCopy() *E2SProtocol {
maskBigint := make([]*big.Int, len(e2s.maskBigint))
for i := range maskBigint {
maskBigint[i] = new(big.Int)
}
return &E2SProtocol{
CKSProtocol: e2s.CKSProtocol.ShallowCopy(),
params: e2s.params,
zero: e2s.zero,
maskBigint: maskBigint,
buff: e2s.params.RingQ().NewPoly(),
}
}
// NewE2SProtocol creates a new E2SProtocol struct from the passed CKKS parameters.
func NewE2SProtocol(params ckks.Parameters, sigmaSmudging float64) *E2SProtocol {
e2s := new(E2SProtocol)
e2s.CKSProtocol = drlwe.NewCKSProtocol(params.Parameters, sigmaSmudging)
e2s.params = params
e2s.zero = rlwe.NewSecretKey(params.Parameters)
e2s.maskBigint = make([]*big.Int, params.N())
for i := range e2s.maskBigint {
e2s.maskBigint[i] = new(big.Int)
}
e2s.buff = e2s.params.RingQ().NewPoly()
return e2s
}
// AllocateShare allocates a share of the E2S protocol
func (e2s *E2SProtocol) AllocateShare(level int) (share *drlwe.CKSShare) {
return e2s.CKSProtocol.AllocateShare(level)
}
// GenShare generates a party's share in the encryption-to-shares protocol. This share consist in the additive secret-share of the party
// which is written in secretShareOut and in the public masked-decryption share written in publicShareOut.
// This protocol requires additional inputs which are :
// logBound : the bit length of the masks
// logSlots : the bit length of the number of slots
// ct1 : the degree 1 element the ciphertext to share, i.e. ct1 = ckk.Ciphertext.Value[1].
// The method "GetMinimumLevelForBootstrapping" should be used to get the minimum level at which E2S can be called while still ensure 128-bits of security, as well as the
// value for logBound.
func (e2s *E2SProtocol) GenShare(sk *rlwe.SecretKey, logBound uint, logSlots int, ct *rlwe.Ciphertext, secretShareOut *rlwe.AdditiveShareBigint, publicShareOut *drlwe.CKSShare) {
ringQ := e2s.params.RingQ()
ct1 := ct.Value[1]
levelQ := utils.MinInt(ct1.Level(), publicShareOut.Value.Level())
// Get the upperbound on the norm
// Ensures that bound >= 2^{128+logbound}
bound := ring.NewUint(1)
bound.Lsh(bound, uint(logBound))
boundMax := ring.NewUint(ringQ.Modulus[0])
for i := 1; i < levelQ+1; i++ {
boundMax.Mul(boundMax, ring.NewUint(ringQ.Modulus[i]))
}
var sign int
sign = bound.Cmp(boundMax)
if sign == 1 || bound.Cmp(boundMax) == 1 {
panic("cannot GenShare: ciphertext level is not large enough for refresh correctness")
}
boundHalf := new(big.Int).Rsh(bound, 1)
dslots := 1 << logSlots
if ringQ.Type() == ring.Standard {
dslots *= 2
}
// Generate the mask in Z[Y] for Y = X^{N/(2*slots)}
for i := 0; i < dslots; i++ {
e2s.maskBigint[i] = ring.RandInt(bound)
sign = e2s.maskBigint[i].Cmp(boundHalf)
if sign == 1 || sign == 0 {
e2s.maskBigint[i].Sub(e2s.maskBigint[i], bound)
}
secretShareOut.Value[i].Set(e2s.maskBigint[i])
}
// Encrypt the mask
// Generates an encryption of zero and subtracts the mask
e2s.CKSProtocol.GenShare(sk, e2s.zero, ct, publicShareOut)
ringQ.SetCoefficientsBigintLvl(levelQ, secretShareOut.Value[:dslots], e2s.buff)
// Maps Y^{N/n} -> X^{N} in Montgomery and NTT
ckks.NttAndMontgomeryLvl(levelQ, logSlots, ringQ, false, e2s.buff)
// Subtracts the mask to the encryption of zero
ringQ.SubLvl(levelQ, publicShareOut.Value, e2s.buff, publicShareOut.Value)
}
// GetShare is the final step of the encryption-to-share protocol. It performs the masked decryption of the target ciphertext followed by a
// the removal of the caller's secretShare as generated in the GenShare method.
// If the caller is not secret-key-share holder (i.e., didn't generate a decryption share), `secretShare` can be set to nil.
// Therefore, in order to obtain an additive sharing of the message, only one party should call this method, and the other parties should use
// the secretShareOut output of the GenShare method.
func (e2s *E2SProtocol) GetShare(secretShare *rlwe.AdditiveShareBigint, aggregatePublicShare *drlwe.CKSShare, logSlots int, ct *rlwe.Ciphertext, secretShareOut *rlwe.AdditiveShareBigint) {
ringQ := e2s.params.RingQ()
levelQ := utils.MinInt(ct.Level(), aggregatePublicShare.Value.Level())
// Adds the decryption share on the ciphertext and stores the result in a buff
ringQ.AddLvl(levelQ, aggregatePublicShare.Value, ct.Value[0], e2s.buff)
// Switches the LSSS RNS NTT ciphertext outside of the NTT domain
ringQ.InvNTTLvl(levelQ, e2s.buff, e2s.buff)
dslots := 1 << logSlots
if ringQ.Type() == ring.Standard {
dslots *= 2
}
gap := ringQ.N / dslots
// Switches the LSSS RNS ciphertext outside of the RNS domain
ringQ.PolyToBigintCenteredLvl(levelQ, e2s.buff, gap, e2s.maskBigint)
// Subtracts the last mask
if secretShare != nil {
a := secretShareOut.Value
b := e2s.maskBigint
c := secretShare.Value
for i := range secretShareOut.Value[:dslots] {
a[i].Add(c[i], b[i])
}
} else {
a := secretShareOut.Value
b := e2s.maskBigint
for i := range secretShareOut.Value[:dslots] {
a[i].Set(b[i])
}
}
}
// S2EProtocol is the structure storing the parameters and temporary buffers
// required by the shares-to-encryption protocol.
type S2EProtocol struct {
*drlwe.CKSProtocol
params ckks.Parameters
tmp *ring.Poly
ssBigint []*big.Int
zero *rlwe.SecretKey
}
// ShallowCopy creates a shallow copy of S2EProtocol in which all the read-only data-structures are
// shared with the receiver and the temporary buffers are reallocated. The receiver and the returned
// S2EProtocol can be used concurrently.
func (s2e *S2EProtocol) ShallowCopy() *S2EProtocol {
return &S2EProtocol{
CKSProtocol: s2e.CKSProtocol.ShallowCopy(),
params: s2e.params,
tmp: s2e.params.RingQ().NewPoly(),
ssBigint: make([]*big.Int, s2e.params.N()),
zero: s2e.zero,
}
}
// NewS2EProtocol creates a new S2EProtocol struct from the passed CKKS parameters.
func NewS2EProtocol(params ckks.Parameters, sigmaSmudging float64) *S2EProtocol {
s2e := new(S2EProtocol)
s2e.CKSProtocol = drlwe.NewCKSProtocol(params.Parameters, sigmaSmudging)
s2e.params = params
s2e.tmp = s2e.params.RingQ().NewPoly()
s2e.ssBigint = make([]*big.Int, s2e.params.N())
s2e.zero = rlwe.NewSecretKey(params.Parameters)
return s2e
}
// AllocateShare allocates a share of the S2E protocol
func (s2e S2EProtocol) AllocateShare(level int) (share *drlwe.CKSShare) {
return s2e.CKSProtocol.AllocateShare(level)
}
// GenShare generates a party's in the shares-to-encryption protocol given the party's secret-key share `sk`, a common
// polynomial sampled from the CRS `c1` and the party's secret share of the message.
func (s2e *S2EProtocol) GenShare(sk *rlwe.SecretKey, crs drlwe.CKSCRP, logSlots int, secretShare *rlwe.AdditiveShareBigint, c0ShareOut *drlwe.CKSShare) {
ringQ := s2e.params.RingQ()
c1 := ring.Poly(crs)
if c1.Level() != c0ShareOut.Value.Level() {
panic("cannot GenShare: c1 and c0ShareOut level must be equal")
}
// Generates an encryption share
s2e.CKSProtocol.GenShare(s2e.zero, sk, &rlwe.Ciphertext{Value: []*ring.Poly{nil, &c1}, MetaData: rlwe.MetaData{IsNTT: true}}, c0ShareOut)
dslots := 1 << logSlots
if ringQ.Type() == ring.Standard {
dslots *= 2
}
ringQ.SetCoefficientsBigintLvl(c1.Level(), secretShare.Value[:dslots], s2e.tmp)
// Maps Y^{N/n} -> X^{N} in Montgomery and NTT
ckks.NttAndMontgomeryLvl(c1.Level(), logSlots, ringQ, false, s2e.tmp)
ringQ.AddLvl(c1.Level(), c0ShareOut.Value, s2e.tmp, c0ShareOut.Value)
}
// GetEncryption computes the final encryption of the secret-shared message when provided with the aggregation `c0Agg` of the parties'
// share in the protocol and with the common, CRS-sampled polynomial `c1`.
func (s2e *S2EProtocol) GetEncryption(c0Agg *drlwe.CKSShare, crs drlwe.CKSCRP, ctOut *rlwe.Ciphertext) {
if ctOut.Degree() != 1 {
panic("cannot GetEncryption: ctOut must have degree 1.")
}
c1 := ring.Poly(crs)
if c0Agg.Value.Level() != c1.Level() {
panic("cannot GetEncryption: c0Agg level must be equal to c1 level")
}
if ctOut.Level() != c1.Level() {
panic("cannot GetEncryption: ctOut level must be equal to c1 level")
}
ctOut.Value[0].Copy(c0Agg.Value)
ctOut.Value[1].Copy(&c1)
}