forked from tuneinsight/lattigo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ciphertext.go
299 lines (237 loc) · 8.17 KB
/
ciphertext.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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
package rlwe
import (
"fmt"
"github.com/tuneinsight/lattigo/v4/ring"
"github.com/tuneinsight/lattigo/v4/utils"
)
// Ciphertext is a generic type for RLWE ciphertexts.
type Ciphertext struct {
MetaData
Value []*ring.Poly
}
// NewCiphertext returns a new Ciphertext with zero values and an associated
// MetaData set to the Parameters default value.
func NewCiphertext(params Parameters, degree, level int) (ct *Ciphertext) {
ct = new(Ciphertext)
ct.Value = make([]*ring.Poly, degree+1)
for i := 0; i < degree+1; i++ {
ct.Value[i] = ring.NewPoly(params.N(), level)
}
ct.MetaData = MetaData{Scale: params.defaultScale, IsNTT: params.defaultNTTFlag}
return
}
// NewCiphertextAtLevelFromPoly constructs a new Ciphertext at a specific level
// where the message is set to the passed poly. No checks are performed on poly and
// the returned Ciphertext will share its backing array of coefficients.
// Returned Ciphertext's MetaData is empty.
func NewCiphertextAtLevelFromPoly(level int, poly [2]*ring.Poly) (ct *Ciphertext) {
v0, v1 := new(ring.Poly), new(ring.Poly)
v0.Coeffs, v1.Coeffs = poly[0].Coeffs[:level+1], poly[1].Coeffs[:level+1]
v0.Buff, v1.Buff = poly[0].Buff[:poly[0].N()*(level+1)], poly[1].Buff[:poly[1].N()*(level+1)]
return &Ciphertext{Value: []*ring.Poly{v0, v1}}
}
// NewCiphertextRandom generates a new uniformly distributed Ciphertext of degree, level.
func NewCiphertextRandom(prng utils.PRNG, params Parameters, degree, level int) (ciphertext *Ciphertext) {
ciphertext = NewCiphertext(params, degree, level)
PopulateElementRandom(prng, params, ciphertext)
return
}
// Degree returns the degree of the target Ciphertext.
func (ct *Ciphertext) Degree() int {
return len(ct.Value) - 1
}
// Level returns the level of the target Ciphertext.
func (ct *Ciphertext) Level() int {
return len(ct.Value[0].Coeffs) - 1
}
// GetScale gets the scale of the target ciphertext.
func (ct *Ciphertext) GetScale() Scale {
return ct.Scale
}
// SetScale sets the scale of the target ciphertext.
func (ct *Ciphertext) SetScale(scale Scale) {
ct.Scale = scale
}
// Resize resizes the degree of the target element.
// Sets the NTT flag of the added poly equal to the NTT flag
// to the poly at degree zero.
func (ct *Ciphertext) Resize(degree, level int) {
if ct.Level() != level {
for i := range ct.Value {
ct.Value[i].Resize(level)
}
}
if ct.Degree() > degree {
ct.Value = ct.Value[:degree+1]
} else if ct.Degree() < degree {
for ct.Degree() < degree {
ct.Value = append(ct.Value, []*ring.Poly{ring.NewPoly(ct.Value[0].N(), level)}...)
}
}
}
// SwitchCiphertextRingDegreeNTT changes the ring degree of ctIn to the one of ctOut.
// Maps Y^{N/n} -> X^{N} or X^{N} -> Y^{N/n}.
// If the ring degree of ctOut is larger than the one of ctIn, then the ringQ of ctIn
// must be provided (otherwise, a nil pointer).
// The ctIn must be in the NTT domain and ctOut will be in the NTT domain.
func SwitchCiphertextRingDegreeNTT(ctIn *Ciphertext, ringQSmallDim, ringQLargeDim *ring.Ring, ctOut *Ciphertext) {
NIn, NOut := len(ctIn.Value[0].Coeffs[0]), len(ctOut.Value[0].Coeffs[0])
if NIn > NOut {
gap := NIn / NOut
buff := make([]uint64, NIn)
for i := range ctOut.Value {
for j := range ctOut.Value[i].Coeffs {
tmpIn, tmpOut := ctIn.Value[i].Coeffs[j], ctOut.Value[i].Coeffs[j]
ringQLargeDim.InvNTTSingle(j, tmpIn, buff)
for w0, w1 := 0, 0; w0 < NOut; w0, w1 = w0+1, w1+gap {
tmpOut[w0] = buff[w1]
}
ringQSmallDim.NTTSingle(j, tmpOut, tmpOut)
}
}
} else {
for i := range ctOut.Value {
ring.MapSmallDimensionToLargerDimensionNTT(ctIn.Value[i], ctOut.Value[i])
}
}
ctOut.MetaData = ctIn.MetaData
}
// SwitchCiphertextRingDegree changes the ring degree of ctIn to the one of ctOut.
// Maps Y^{N/n} -> X^{N} or X^{N} -> Y^{N/n}.
// If the ring degree of ctOut is larger than the one of ctIn, then the ringQ of ctIn
// must be provided (otherwise, a nil pointer).
func SwitchCiphertextRingDegree(ctIn *Ciphertext, ctOut *Ciphertext) {
NIn, NOut := len(ctIn.Value[0].Coeffs[0]), len(ctOut.Value[0].Coeffs[0])
gapIn, gapOut := NOut/NIn, 1
if NIn > NOut {
gapIn, gapOut = 1, NIn/NOut
}
for i := range ctOut.Value {
for j := range ctOut.Value[i].Coeffs {
tmp0, tmp1 := ctOut.Value[i].Coeffs[j], ctIn.Value[i].Coeffs[j]
for w0, w1 := 0, 0; w0 < NOut; w0, w1 = w0+gapIn, w1+gapOut {
tmp0[w0] = tmp1[w1]
}
}
}
ctOut.MetaData = ctIn.MetaData
}
// CopyNew creates a new element as a copy of the target element.
func (ct *Ciphertext) CopyNew() *Ciphertext {
ctxCopy := new(Ciphertext)
ctxCopy.Value = make([]*ring.Poly, ct.Degree()+1)
for i := range ct.Value {
ctxCopy.Value[i] = ct.Value[i].CopyNew()
}
ctxCopy.MetaData = ct.MetaData
return ctxCopy
}
// Copy copies the input element and its parameters on the target element.
func (ct *Ciphertext) Copy(ctxCopy *Ciphertext) {
if ct != ctxCopy {
for i := range ctxCopy.Value {
ct.Value[i].Copy(ctxCopy.Value[i])
}
ct.MetaData = ctxCopy.MetaData
}
}
// El returns a pointer to this Element
func (ct *Ciphertext) El() *Ciphertext {
return ct
}
// GetSmallestLargest returns the provided element that has the smallest degree as a first
// returned value and the largest degree as second return value. If the degree match, the
// order is the same as for the input.
func GetSmallestLargest(el0, el1 *Ciphertext) (smallest, largest *Ciphertext, sameDegree bool) {
switch {
case el0.Degree() > el1.Degree():
return el1, el0, false
case el0.Degree() < el1.Degree():
return el0, el1, false
}
return el0, el1, true
}
// PopulateElementRandom creates a new rlwe.Element with random coefficients.
func PopulateElementRandom(prng utils.PRNG, params Parameters, ct *Ciphertext) {
sampler := ring.NewUniformSampler(prng, params.RingQ())
for i := range ct.Value {
sampler.Read(ct.Value[i])
}
}
// MarshalBinarySize returns the length in bytes of the target Ciphertext.
func (ct *Ciphertext) MarshalBinarySize() (dataLen int) {
// 1 byte : Degree
dataLen++
for _, ct := range ct.Value {
dataLen += ct.MarshalBinarySize64()
}
dataLen += ct.MetaData.MarshalBinarySize()
return dataLen
}
// MarshalBinary encodes a Ciphertext on a byte slice. The total size
// in bytes is 4 + 8* N * numberModuliQ * (degree + 1).
func (ct *Ciphertext) MarshalBinary() (data []byte, err error) {
data = make([]byte, ct.MarshalBinarySize())
_, err = ct.Encode64(data)
return
}
// Encode64 encodes the target Ciphertext on a byte array, using 8 bytes per coefficient.
// It returns the number of written bytes, and the corresponding error, if it occurred.
func (ct *Ciphertext) Encode64(data []byte) (ptr int, err error) {
if len(data) < ct.MarshalBinarySize() {
return 0, fmt.Errorf("Encode64: len(data) is too small")
}
if ptr, err = ct.MetaData.Encode64(data); err != nil {
return
}
data[ptr] = uint8(ct.Degree() + 1)
ptr++
var inc int
for _, pol := range ct.Value {
if inc, err = pol.Encode64(data[ptr:]); err != nil {
return
}
ptr += inc
}
return
}
// UnmarshalBinary decodes a previously marshaled Ciphertext on the target Ciphertext.
func (ct *Ciphertext) UnmarshalBinary(data []byte) (err error) {
if _, err = ct.Decode64(data); err != nil {
return
}
if ct.MarshalBinarySize() != len(data) {
return fmt.Errorf("remaining unparsed data")
}
return nil
}
// Decode64 decodes a slice of bytes in the target Ciphertext and returns the number of bytes decoded.
// The method will first try to write on the buffer. If this step fails, either because the buffer isn't
// allocated or because it has the wrong size, the method will allocate the correct buffer.
// Assumes that each coefficient is encoded on 8 bytes.
func (ct *Ciphertext) Decode64(data []byte) (ptr int, err error) {
if ptr, err = ct.MetaData.Decode64(data); err != nil {
return
}
if degree := int(data[ptr]); ct.Value == nil {
ct.Value = make([]*ring.Poly, degree)
} else {
if len(ct.Value) > degree {
ct.Value = ct.Value[:degree]
} else {
ct.Value = append(ct.Value, make([]*ring.Poly, degree-len(ct.Value))...)
}
}
ptr++
var inc int
for i := range ct.Value {
if ct.Value[i] == nil {
ct.Value[i] = new(ring.Poly)
}
if inc, err = ct.Value[i].Decode64(data[ptr:]); err != nil {
return
}
ptr += inc
}
return
}