github.com/onflow/flow-go/crypto@v0.24.8/dkg_feldmanvss.go (about) 1 //go:build relic 2 // +build relic 3 4 package crypto 5 6 // #cgo CFLAGS: -g -Wall -std=c99 7 // #include "dkg_include.h" 8 import "C" 9 10 import ( 11 "fmt" 12 ) 13 14 // Implements Feldman Verifiable Secret Sharing using 15 // the BLS set up on the BLS12-381 curve. 16 17 // The secret is a BLS private key generated by a single dealer. 18 // (and hence this is a centralized generation). 19 // The generates key shares for a BLS-based 20 // threshold signature scheme and distributes the shares over the (n) 21 // partcipants including itself. The particpants validate their shares 22 // using a public verifiaction vector shared by the . 23 24 // Private keys are scalar in Zr, where r is the group order of G1/G2 25 // Public keys are in G2. 26 27 // feldman VSS protocol, implements DKGState 28 type feldmanVSSstate struct { 29 // common DKG state 30 *dkgCommon 31 // participant index 32 dealerIndex index 33 // Polynomial P = a_0 + a_1*x + .. + a_t*x^t in Zr[X], the vector size is (t+1) 34 // a_0 is the group private key 35 a []scalar 36 // Public vector of the group, the vector size is (t+1) 37 // A_0 is the group public key 38 vA []pointG2 39 vAReceived bool 40 // Private share of the current participant 41 x scalar 42 xReceived bool 43 // Public keys of the group participants, the vector size is (n) 44 y []pointG2 45 // true if the private share is valid 46 validKey bool 47 } 48 49 // NewFeldmanVSS creates a new instance of Feldman VSS protocol. 50 // 51 // An instance is run by a single participant and is usable for only one protocol. 52 // In order to run the protocol again, a new instance needs to be created 53 // 54 // The function returns: 55 // - (nil, InvalidInputsError) if: 56 // - size if not in [DKGMinSize, DKGMaxSize] 57 // - threshold is not in [MinimumThreshold, size-1] 58 // - myIndex is not in [0, size-1] 59 // - dealerIndex is not in [0, size-1] 60 // 61 // - (dkgInstance, nil) otherwise 62 func NewFeldmanVSS(size int, threshold int, myIndex int, 63 processor DKGProcessor, dealerIndex int) (DKGState, error) { 64 65 common, err := newDKGCommon(size, threshold, myIndex, processor, dealerIndex) 66 if err != nil { 67 return nil, err 68 } 69 70 fvss := &feldmanVSSstate{ 71 dkgCommon: common, 72 dealerIndex: index(dealerIndex), 73 } 74 fvss.init() 75 return fvss, nil 76 } 77 78 func (s *feldmanVSSstate) init() { 79 // set the bls context 80 blsInstance.reInit() 81 s.running = false 82 s.y = nil 83 s.xReceived = false 84 s.vAReceived = false 85 C.bn_new_wrapper((*C.bn_st)(&s.x)) 86 } 87 88 // Start triggers the protocol start for the current participant. 89 // If the current participant is the dealer, then the seed is used 90 // to generate the secret polynomial (including the group private key). 91 // If the current participant is not the dealer, the seed is ignored. 92 // 93 // The function returns: 94 // - dkgInvalidStateTransitionError if the DKG instance is already running. 95 // - error if an unexpected exception occurs 96 // - nil otherwise 97 func (s *feldmanVSSstate) Start(seed []byte) error { 98 if s.running { 99 return dkgInvalidStateTransitionErrorf("dkg is already running") 100 } 101 102 s.running = true 103 // Generate shares if necessary 104 if s.dealerIndex == s.myIndex { 105 return s.generateShares(seed) 106 } 107 return nil 108 } 109 110 // End finalizes the protocol in the current node. 111 // It returns the finalized public data and participants private key share. 112 // - the group public key corresponding to the group secret key 113 // - all the public key shares corresponding to the participants private 114 // key shares. 115 // - the finalized private key which is the current participant's own private key share 116 // 117 // The returned erorr is : 118 // - dkgInvalidStateTransitionError if the DKG instance was not running. 119 // - dkgFailureError if the private key and vector are inconsistent. 120 // - dkgFailureError if the public key share or group public key is identity. 121 // - nil otherwise. 122 func (s *feldmanVSSstate) End() (PrivateKey, PublicKey, []PublicKey, error) { 123 if !s.running { 124 return nil, nil, nil, dkgInvalidStateTransitionErrorf("dkg is not running") 125 } 126 s.running = false 127 if !s.validKey { 128 return nil, nil, nil, dkgFailureErrorf("received private key is invalid") 129 } 130 // private key of the current participant 131 x := newPrKeyBLSBLS12381(&s.x) 132 133 // Group public key 134 Y := newPubKeyBLSBLS12381(&s.vA[0]) 135 136 // The participants public keys 137 y := make([]PublicKey, s.size) 138 for i, p := range s.y { 139 y[i] = newPubKeyBLSBLS12381(&p) 140 } 141 142 // check if current public key share or group public key is identity. 143 // In that case all signatures generated by the key are invalid (as stated by the BLS IETF draft) 144 // to avoid equivocation issues. 145 if (&s.x).isZero() { 146 return nil, nil, nil, dkgFailureErrorf("received private key is identity and is therefore invalid") 147 } 148 if Y.isIdentity { 149 return nil, nil, nil, dkgFailureErrorf("group private key is identity and is therefore invalid") 150 } 151 return x, Y, y, nil 152 } 153 154 const ( 155 shareSize = PrKeyLenBLSBLS12381 156 // the actual verifVectorSize depends on the state and is: 157 // PubKeyLenBLSBLS12381*(t+1) 158 verifVectorSize = PubKeyLenBLSBLS12381 159 ) 160 161 // HandleBroadcastMsg processes a new broadcasted message received by the current participant. 162 // `orig` is the message origin index. 163 // 164 // The function returns: 165 // - dkgInvalidStateTransitionError if the instance is not running 166 // - invalidInputsError if `orig` is not valid (in [0, size-1]) 167 // - nil otherwise 168 func (s *feldmanVSSstate) HandleBroadcastMsg(orig int, msg []byte) error { 169 if !s.running { 170 return dkgInvalidStateTransitionErrorf("dkg is not running") 171 } 172 if orig >= s.Size() || orig < 0 { 173 return invalidInputsErrorf( 174 "wrong origin input, should be less than %d, got %d", 175 s.Size(), 176 orig) 177 } 178 179 // In case a message is received by the origin participant, 180 // the message is just ignored 181 if s.myIndex == index(orig) { 182 return nil 183 } 184 185 if len(msg) == 0 { 186 s.processor.Disqualify(orig, "the received broadcast is empty") 187 return nil 188 } 189 190 // msg = |tag| Data | 191 if dkgMsgTag(msg[0]) == feldmanVSSVerifVec { 192 s.receiveVerifVector(index(orig), msg[1:]) 193 } else { 194 s.processor.Disqualify(orig, 195 fmt.Sprintf("the broadcast header is invalid, got %d", 196 dkgMsgTag(msg[0]))) 197 } 198 return nil 199 } 200 201 // HandlePrivateMsg processes a new private message received by the current participant. 202 // `orig` is the message origin index. 203 // 204 // The function returns: 205 // - dkgInvalidStateTransitionError if the instance is not running 206 // - invalidInputsError if `orig` is not valid (in [0, size-1]) 207 // - nil otherwise 208 func (s *feldmanVSSstate) HandlePrivateMsg(orig int, msg []byte) error { 209 if !s.running { 210 return dkgInvalidStateTransitionErrorf("dkg is not running") 211 } 212 213 if orig >= s.Size() || orig < 0 { 214 return invalidInputsErrorf( 215 "wrong origin, should be positive less than %d, got %d", 216 s.Size(), 217 orig) 218 } 219 220 // In case a private message is received by the origin participant, 221 // the message is just ignored 222 if s.myIndex == index(orig) { 223 return nil 224 } 225 226 // forward received message to receiveShare because private messages 227 // can only be private shares 228 // msg = |tag| Data | 229 s.receiveShare(index(orig), msg) 230 231 return nil 232 } 233 234 // ForceDisqualify forces a participant to get disqualified 235 // for a reason outside of the DKG protocol 236 // The caller should make sure all honest participants call this function, 237 // otherwise, the protocol can be broken. 238 // 239 // The function returns: 240 // - dkgInvalidStateTransitionError if the instance is not running 241 // - invalidInputsError if `orig` is not valid (in [0, size-1]) 242 // - nil otherwise 243 func (s *feldmanVSSstate) ForceDisqualify(participant int) error { 244 if !s.running { 245 return dkgInvalidStateTransitionErrorf("dkg is not running") 246 } 247 if participant >= s.Size() || participant < 0 { 248 return invalidInputsErrorf( 249 "wrong origin input, should be less than %d, got %d", 250 s.Size(), 251 participant) 252 } 253 if index(participant) == s.dealerIndex { 254 s.validKey = false 255 } 256 return nil 257 } 258 259 // generateShares is used by the dealer to generate secret polynomial from the input seed 260 // and derive all private shares and public data. 261 func (s *feldmanVSSstate) generateShares(seed []byte) error { 262 err := seedRelic(seed) 263 if err != nil { 264 return fmt.Errorf("generating shares failed: %w", err) 265 } 266 267 // Generate a polyomial P in Zr[X] of degree t 268 s.a = make([]scalar, s.threshold+1) 269 s.vA = make([]pointG2, s.threshold+1) 270 s.y = make([]pointG2, s.size) 271 // non-zero a[0] - group private key is not zero 272 randZrStar(&s.a[0]) 273 generatorScalarMultG2(&s.vA[0], &s.a[0]) 274 if s.threshold > 0 { 275 for i := 1; i < s.threshold; i++ { 276 C.bn_new_wrapper((*C.bn_st)(&s.a[i])) 277 randZr(&s.a[i]) 278 generatorScalarMultG2(&s.vA[i], &s.a[i]) 279 } 280 // non-zero a[t] to enforce the polynomial degree 281 randZrStar(&s.a[s.threshold]) 282 generatorScalarMultG2(&s.vA[s.threshold], &s.a[s.threshold]) 283 } 284 285 // compute the shares 286 for i := index(1); int(i) <= s.size; i++ { 287 // the dealer's own share 288 if i-1 == s.myIndex { 289 xdata := make([]byte, shareSize) 290 zrPolynomialImage(xdata, s.a, i, &s.y[i-1]) 291 C.bn_read_bin((*C.bn_st)(&s.x), 292 (*C.uchar)(&xdata[0]), 293 PrKeyLenBLSBLS12381, 294 ) 295 continue 296 } 297 // the-other-participant shares 298 data := make([]byte, shareSize+1) 299 data[0] = byte(feldmanVSSShare) 300 zrPolynomialImage(data[1:], s.a, i, &s.y[i-1]) 301 s.processor.PrivateSend(int(i-1), data) 302 } 303 // broadcast the vector 304 vectorSize := verifVectorSize * (s.threshold + 1) 305 data := make([]byte, vectorSize+1) 306 data[0] = byte(feldmanVSSVerifVec) 307 writeVerifVector(data[1:], s.vA) 308 s.processor.Broadcast(data) 309 310 s.vAReceived = true 311 s.xReceived = true 312 s.validKey = true 313 return nil 314 } 315 316 // receives a private share from the 317 func (s *feldmanVSSstate) receiveShare(origin index, data []byte) { 318 // only accept private shares from the . 319 if origin != s.dealerIndex { 320 return 321 } 322 323 if s.xReceived { 324 s.processor.FlagMisbehavior(int(origin), "private share was already received") 325 return 326 } 327 328 // at this point, tag the private message as received 329 s.xReceived = true 330 331 // private message general check 332 // msg = |tag| Data | 333 if len(data) == 0 || dkgMsgTag(data[0]) != feldmanVSSShare { 334 s.validKey = false 335 s.processor.FlagMisbehavior(int(origin), 336 fmt.Sprintf("private share should be non-empty and first byte should be %d, received %#x", 337 feldmanVSSShare, data)) 338 return 339 } 340 341 // consider the remaining data from message 342 data = data[1:] 343 344 if (len(data)) != shareSize { 345 s.validKey = false 346 s.processor.FlagMisbehavior(int(origin), 347 fmt.Sprintf("invalid share size, expects %d, got %d", 348 shareSize, len(data))) 349 return 350 } 351 352 // read the participant private share 353 if C.bn_read_Zr_bin((*C.bn_st)(&s.x), 354 (*C.uchar)(&data[0]), 355 PrKeyLenBLSBLS12381, 356 ) != valid { 357 s.validKey = false 358 s.processor.FlagMisbehavior(int(origin), 359 fmt.Sprintf("invalid share value %x", data)) 360 return 361 } 362 363 if s.vAReceived { 364 s.validKey = s.verifyShare() 365 } 366 } 367 368 // receives the public vector from the 369 func (s *feldmanVSSstate) receiveVerifVector(origin index, data []byte) { 370 // only accept the verification vector from the . 371 if origin != s.dealerIndex { 372 return 373 } 374 375 if s.vAReceived { 376 s.processor.FlagMisbehavior(int(origin), 377 "verification vector was already received") 378 return 379 } 380 381 if verifVectorSize*(s.threshold+1) != len(data) { 382 s.vAReceived = true 383 s.validKey = false 384 s.processor.Disqualify(int(origin), 385 fmt.Sprintf("invalid verification vector size, expects %d, got %d", 386 verifVectorSize*(s.threshold+1), len(data))) 387 return 388 } 389 // read the verification vector 390 s.vA = make([]pointG2, s.threshold+1) 391 err := readVerifVector(s.vA, data) 392 if err != nil { 393 s.vAReceived = true 394 s.validKey = false 395 s.processor.Disqualify(int(origin), 396 fmt.Sprintf("reading the verification vector failed: %s", err)) 397 } 398 399 s.y = make([]pointG2, s.size) 400 s.computePublicKeys() 401 402 s.vAReceived = true 403 if s.xReceived { 404 s.validKey = s.verifyShare() 405 } 406 } 407 408 // zrPolynomialImage computes P(x) = a_0 + a_1*x + .. + a_n*x^n (mod r) in Z/Zr 409 // r being the order of G1 410 // P(x) is written in dest, while g2^P(x) is written in y 411 // x being a small integer 412 func zrPolynomialImage(dest []byte, a []scalar, x index, y *pointG2) { 413 C.Zr_polynomialImage_export((*C.uchar)(&dest[0]), 414 (*C.ep2_st)(y), 415 (*C.bn_st)(&a[0]), (C.int)(len(a)), 416 (C.uint8_t)(x), 417 ) 418 } 419 420 // writeVerifVector exports a vector A into an array of bytes 421 // assuming the array length matches the vector length 422 func writeVerifVector(dest []byte, A []pointG2) { 423 C.ep2_vector_write_bin((*C.uchar)(&dest[0]), 424 (*C.ep2_st)(&A[0]), 425 (C.int)(len(A)), 426 ) 427 } 428 429 // readVerifVector imports A vector from an array of bytes, 430 // assuming the slice length matches the vector length 431 func readVerifVector(A []pointG2, src []byte) error { 432 read := C.ep2_vector_read_bin((*C.ep2_st)(&A[0]), 433 (*C.uchar)(&src[0]), 434 (C.int)(len(A))) 435 if read == valid { 436 return nil 437 } 438 // invalid A vector 439 return invalidInputsErrorf("the verifcation vector does not serialize G2 points") 440 } 441 442 func (s *feldmanVSSstate) verifyShare() bool { 443 // check y[current] == x.G2 444 return C.verifyshare((*C.bn_st)(&s.x), 445 (*C.ep2_st)(&s.y[s.myIndex])) == 1 446 } 447 448 // computePublicKeys extracts the participants public keys from the verification vector 449 // y[i] = Q(i+1) for all participants i, with: 450 // 451 // Q(x) = A_0 + A_1*x + ... + A_n*x^n in G2 452 func (s *feldmanVSSstate) computePublicKeys() { 453 C.G2_polynomialImages( 454 (*C.ep2_st)(&s.y[0]), (C.int)(len(s.y)), 455 (*C.ep2_st)(&s.vA[0]), (C.int)(len(s.vA)), 456 ) 457 }