forked from Zondax/multi-party-sig
-
Notifications
You must be signed in to change notification settings - Fork 0
/
multiply.go
241 lines (209 loc) · 7.53 KB
/
multiply.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
package ot
import (
"crypto/rand"
"errors"
"github.com/cronokirby/safenum"
"github.com/sa8/multi-party-sig/internal/params"
"github.com/sa8/multi-party-sig/pkg/hash"
"github.com/sa8/multi-party-sig/pkg/math/curve"
"github.com/sa8/multi-party-sig/pkg/math/sample"
)
// scalarBytes returns the number of bytes needed to store a scalar in this group.
func scalarBytes(group curve.Curve) int {
return (group.ScalarBits() + 7) & ^0b111
}
// encode converts a scalar to a sequence of bits, using a certain number of scalars for noise.
//
// The noise should be public, but the encoding will be unpredictable, but still decodable.
//
// The noise vector should have a length that's a multiple of 8
func encode(beta curve.Scalar, noise []curve.Scalar) ([]byte, error) {
// This follows Algorithm 4 in Doerner's paper:
// https://eprint.iacr.org/2018/499
group := beta.Curve()
gamma := make([]byte, len(noise)/8)
_, _ = rand.Read(gamma)
acc := group.NewScalar().Set(beta)
mulNat := new(safenum.Nat)
mul := group.NewScalar()
for i := 0; i < len(noise); i++ {
mulNat.SetUint64(uint64((gamma[i>>3] >> (i & 0b111)) & 1))
acc.Sub(mul.SetNat(mulNat).Mul(noise[i]))
}
data, err := acc.MarshalBinary()
if err != nil {
return nil, err
}
data = append(data, gamma...)
return data, nil
}
// makeGadget constructs the gadget vector, using the hash for random values.
//
// This vector is composed of the right powers of two to make our multiplication work,
// along with some public random values for noise.
func makeGadget(ctxHash *hash.Hash, group curve.Curve) []curve.Scalar {
scalarEnd := scalarBytes(group)
// We have space for all the bytes of a scalar, and then noise vectors, padded to a multiple of 8
out := make([]curve.Scalar, 8*((group.ScalarBits()+7)/8+(group.ScalarBits()+2*params.StatParam+7)/8))
// Handle powers of 2, in big endian order
acc := group.NewScalar().SetNat(new(safenum.Nat).SetUint64(1))
for i := (scalarEnd >> 3) - 1; i >= 0; i-- {
for j := 0; j < 8; j++ {
out[(i<<3)|j] = group.NewScalar().Set(acc)
acc.Add(acc)
}
}
// Generate random noise
digest := ctxHash.Fork(&hash.BytesWithDomain{TheDomain: "Multiply Gadget Sampling", Bytes: nil}).Digest()
for i := scalarEnd; i < len(out); i++ {
out[i] = sample.Scalar(digest, group)
}
return out
}
// MultiplySender contains the state for the Sender of the multiplication protocol.
type MultiplySender struct {
// After setup
ctxHash *hash.Hash
group curve.Curve
setup *CorreOTSendSetup
doubleAlpha [2]curve.Scalar
gadget []curve.Scalar
sender *AdditiveOTSender
}
// NewMultiplySender initializes the Sender for the multiplication protocol.
//
// The Sender has a scalar alpha, the Receiver beta, and the goal is to create an additive
// sharing of alpha * beta.
//
// This follows Protocol 5 of https://eprint.iacr.org/2018/4990.
func NewMultiplySender(ctxHash *hash.Hash, setup *CorreOTSendSetup, alpha curve.Scalar) *MultiplySender {
group := alpha.Curve()
gadget := makeGadget(ctxHash, group)
var doubleAlpha [2]curve.Scalar
doubleAlpha[0] = alpha
doubleAlpha[1] = sample.Scalar(rand.Reader, group)
return &MultiplySender{
ctxHash: ctxHash,
group: group,
setup: setup,
gadget: gadget,
doubleAlpha: doubleAlpha,
sender: NewAdditiveOTSender(ctxHash, setup, len(gadget), doubleAlpha),
}
}
// MultiplySendRound1Message is the first message from the Sender in the multiplication protocol.
type MultiplySendRound1Message struct {
Msg *AdditiveOTSendRound1Message
RCheck []curve.Scalar
UCheck curve.Scalar
}
// EmptyMultiplySendRound1Message initializes the message with the correct group and size, for unmarshalling.
func (r *MultiplyReceiver) EmptyMultiplySendRound1Message() *MultiplySendRound1Message {
group := r.group
RCheck := make([]curve.Scalar, len(r.gadget))
for i := 0; i < len(RCheck); i++ {
RCheck[i] = group.NewScalar()
}
return &MultiplySendRound1Message{RCheck: RCheck, UCheck: group.NewScalar()}
}
// Round1 runs the Sender's first round in the multiplication protocol.
func (r *MultiplySender) Round1(msg *MultiplyReceiveRound1Message) (*MultiplySendRound1Message, curve.Scalar, error) {
additiveMsg, result, err := r.sender.Round1(msg.Msg)
if err != nil {
return nil, nil, err
}
digest := r.ctxHash.Fork(&hash.BytesWithDomain{TheDomain: "Multiply Chi Sampling", Bytes: nil}).Digest()
chi0 := sample.Scalar(digest, r.group)
chi1 := sample.Scalar(digest, r.group)
mul := r.group.NewScalar()
uCheck := r.group.NewScalar()
uCheck.Add(mul.Set(r.doubleAlpha[0]).Mul(chi0))
uCheck.Add(mul.Set(r.doubleAlpha[1]).Mul(chi1))
rCheck := make([]curve.Scalar, len(result))
for i := 0; i < len(rCheck); i++ {
rCheck[i] = r.group.NewScalar()
rCheck[i].Add(mul.Set(result[i][0]).Mul(chi0))
rCheck[i].Add(mul.Set(result[i][1]).Mul(chi1))
}
share := r.group.NewScalar()
for i := 0; i < len(result); i++ {
share.Add(mul.Set(result[i][0]).Mul(r.gadget[i]))
}
return &MultiplySendRound1Message{
Msg: additiveMsg,
RCheck: rCheck,
UCheck: uCheck,
}, share, nil
}
// MultiplyReceiver contains the state for the Receiver of the multiplication protocol.
type MultiplyReceiver struct {
// After setup
ctxHash *hash.Hash
group curve.Curve
setup *CorreOTReceiveSetup
beta curve.Scalar
gadget []curve.Scalar
choices []byte
receiver *AdditiveOTReceiver
}
// NewMultiplyReceiver initializes the Receiver for the multiplication protocol.
//
// The Sender has a scalar alpha, the Receiver beta, and the goal is to create an additive
// sharing of alpha * beta.
//
// This follows Protocol 5 of https://eprint.iacr.org/2018/4990.
func NewMultiplyReceiver(ctxHash *hash.Hash, setup *CorreOTReceiveSetup, beta curve.Scalar) (*MultiplyReceiver, error) {
group := beta.Curve()
gadget := makeGadget(ctxHash, group)
choices, err := encode(beta, gadget[scalarBytes(group):])
if err != nil {
return nil, err
}
return &MultiplyReceiver{
ctxHash: ctxHash,
group: group,
setup: setup,
beta: beta,
gadget: gadget,
choices: choices,
receiver: NewAdditiveOTReceiver(ctxHash, setup, group, choices),
}, nil
}
// MultiplyReceiveRound1Message is the first message from the Receiver in the multiplication protocol.
type MultiplyReceiveRound1Message struct {
Msg *AdditiveOTReceiveRound1Message
}
// Round1 runs the first round for the Receiver in the multiplication protocol.
func (r *MultiplyReceiver) Round1() *MultiplyReceiveRound1Message {
msg := r.receiver.Round1()
return &MultiplyReceiveRound1Message{msg}
}
// Round2 runs the second round for the Receiver in the multiplication protocol.
func (r *MultiplyReceiver) Round2(msg *MultiplySendRound1Message) (curve.Scalar, error) {
result, err := r.receiver.Round2(msg.Msg)
if err != nil {
return nil, err
}
digest := r.ctxHash.Fork(&hash.BytesWithDomain{TheDomain: "Multiply Chi Sampling", Bytes: nil}).Digest()
chi0 := sample.Scalar(digest, r.group)
chi1 := sample.Scalar(digest, r.group)
mul := r.group.NewScalar()
checkLeft := r.group.NewScalar()
checkRight := r.group.NewScalar()
choiceNat := new(safenum.Nat)
for i := 0; i < len(result); i++ {
checkLeft.Set(result[i][0]).Mul(chi0)
checkLeft.Add(mul.Set(result[i][1]).Mul(chi1))
checkRight.SetNat(choiceNat.SetUint64(uint64((r.choices[i>>3] >> (i & 0b111)) & 1)))
checkRight.Mul(msg.UCheck)
checkRight.Sub(msg.RCheck[i])
if !checkLeft.Equal(checkRight) {
return nil, errors.New("multiply receive round 2: integrity check failed")
}
}
share := r.group.NewScalar()
for i := 0; i < len(result); i++ {
share.Add(mul.Set(result[i][0]).Mul(r.gadget[i]))
}
return share, nil
}