github.com/onflow/flow-go/crypto@v0.24.8/dkg_test.go (about) 1 //go:build relic 2 // +build relic 3 4 package crypto 5 6 import ( 7 crand "crypto/rand" 8 "fmt" 9 mrand "math/rand" 10 "sync" 11 "testing" 12 "time" 13 14 log "github.com/sirupsen/logrus" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 ) 18 19 var gt *testing.T 20 21 func TestDKG(t *testing.T) { 22 t.Run("FeldmanVSSSimple", testFeldmanVSSSimple) 23 t.Run("FeldmanVSSQual", testFeldmanVSSQual) 24 t.Run("JointFeldman", testJointFeldman) 25 } 26 27 // optimal threshold (t) to allow the largest number of malicious participants (m) 28 // assuming the protocol requires: 29 // 30 // m<=t for unforgeability 31 // n-m>=t+1 for robustness 32 func optimalThreshold(size int) int { 33 return (size - 1) / 2 34 } 35 36 // Testing the happy path of Feldman VSS by simulating a network of n participants 37 func testFeldmanVSSSimple(t *testing.T) { 38 log.SetLevel(log.ErrorLevel) 39 40 n := 4 41 for threshold := MinimumThreshold; threshold < n; threshold++ { 42 t.Run(fmt.Sprintf("FeldmanVSS (n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 43 dkgCommonTest(t, feldmanVSS, n, threshold, happyPath) 44 }) 45 } 46 } 47 48 type testCase int 49 50 const ( 51 happyPath testCase = iota 52 invalidShares 53 invalidVector 54 invalidComplaint 55 invalidComplaintAnswer 56 duplicatedMessages 57 ) 58 59 type behavior int 60 61 const ( 62 honest behavior = iota 63 manyInvalidShares 64 fewInvalidShares 65 invalidVectorBroadcast 66 invalidComplaintBroadcast 67 timeoutedComplaintBroadcast 68 invalidSharesComplainTrigger 69 invalidComplaintAnswerBroadcast 70 duplicatedSendAndBroadcast 71 ) 72 73 // Testing Feldman VSS with the qualification system by simulating a network of n participants 74 func testFeldmanVSSQual(t *testing.T) { 75 log.SetLevel(log.ErrorLevel) 76 77 n := 4 78 // happy path, test multiple values of thresold 79 for threshold := MinimumThreshold; threshold < n; threshold++ { 80 t.Run(fmt.Sprintf("FeldmanVSSQual_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 81 dkgCommonTest(t, feldmanVSSQual, n, threshold, happyPath) 82 }) 83 } 84 85 // unhappy path, with focus on the optimal threshold value 86 n = 5 87 threshold := optimalThreshold(n) 88 // unhappy path, with invalid shares 89 t.Run(fmt.Sprintf("FeldmanVSSQual_InvalidShares_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 90 dkgCommonTest(t, feldmanVSSQual, n, threshold, invalidShares) 91 }) 92 // unhappy path, with invalid vector 93 t.Run(fmt.Sprintf("FeldmanVSSQual_InvalidVector_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 94 dkgCommonTest(t, feldmanVSSQual, n, threshold, invalidVector) 95 }) 96 // unhappy paths with invalid complaints and complaint answers 97 // are only tested within joint feldman. 98 } 99 100 // Testing JointFeldman by simulating a network of n participants 101 func testJointFeldman(t *testing.T) { 102 log.SetLevel(log.ErrorLevel) 103 104 n := 4 105 var threshold int 106 // happy path, test multiple values of thresold 107 for threshold = MinimumThreshold; threshold < n; threshold++ { 108 t.Run(fmt.Sprintf("JointFeldman_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 109 dkgCommonTest(t, jointFeldman, n, threshold, happyPath) 110 }) 111 } 112 113 // unhappy path, with focus on the optimal threshold value 114 n = 5 115 threshold = optimalThreshold(n) 116 // unhappy path, with invalid shares 117 t.Run(fmt.Sprintf("JointFeldman_InvalidShares_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 118 dkgCommonTest(t, jointFeldman, n, threshold, invalidShares) 119 }) 120 // unhappy path, with invalid vector 121 t.Run(fmt.Sprintf("JointFeldman_InvalidVector_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 122 dkgCommonTest(t, jointFeldman, n, threshold, invalidVector) 123 }) 124 // unhappy path, with invalid complaints 125 t.Run(fmt.Sprintf("JointFeldman_InvalidComplaints_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 126 dkgCommonTest(t, jointFeldman, n, threshold, invalidComplaint) 127 }) 128 // unhappy path, with invalid complaint answers 129 t.Run(fmt.Sprintf("JointFeldman_InvalidComplaintAnswers_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 130 dkgCommonTest(t, jointFeldman, n, threshold, invalidComplaintAnswer) 131 }) 132 // unhappy path, with duplicated messages (all types) 133 t.Run(fmt.Sprintf("JointFeldman_DuplicatedMessages_(n,t)=(%d,%d)", n, threshold), func(t *testing.T) { 134 dkgCommonTest(t, jointFeldman, n, threshold, duplicatedMessages) 135 }) 136 } 137 138 // Supported Key Generation protocols 139 const ( 140 feldmanVSS = iota 141 feldmanVSSQual 142 jointFeldman 143 ) 144 145 func newDKG(dkg int, size int, threshold int, myIndex int, 146 processor DKGProcessor, dealerIndex int) (DKGState, error) { 147 switch dkg { 148 case feldmanVSS: 149 return NewFeldmanVSS(size, threshold, myIndex, processor, dealerIndex) 150 case feldmanVSSQual: 151 return NewFeldmanVSSQual(size, threshold, myIndex, processor, dealerIndex) 152 case jointFeldman: 153 return NewJointFeldman(size, threshold, myIndex, processor) 154 default: 155 return nil, fmt.Errorf("non supported protocol") 156 } 157 } 158 159 func dkgCommonTest(t *testing.T, dkg int, n int, threshold int, test testCase) { 160 gt = t 161 log.Info("DKG protocol set up") 162 163 // create the participant channels 164 chans := make([]chan *message, n) 165 lateChansTimeout1 := make([]chan *message, n) 166 lateChansTimeout2 := make([]chan *message, n) 167 for i := 0; i < n; i++ { 168 chans[i] = make(chan *message, 5*n) 169 lateChansTimeout1[i] = make(chan *message, 5*n) 170 lateChansTimeout2[i] = make(chan *message, 5*n) 171 } 172 173 // number of dealers in the protocol 174 var dealers int 175 if dkg == jointFeldman { 176 dealers = n 177 } else { 178 dealers = 1 179 } 180 181 // create n processors for all participants 182 processors := make([]testDKGProcessor, 0, n) 183 for current := 0; current < n; current++ { 184 list := make([]bool, dealers) 185 processors = append(processors, testDKGProcessor{ 186 current: current, 187 chans: chans, 188 lateChansTimeout1: lateChansTimeout1, 189 lateChansTimeout2: lateChansTimeout2, 190 protocol: dkgType, 191 malicious: honest, 192 disqualified: list, 193 }) 194 } 195 196 // Update processors depending on the test 197 // 198 // r1 and r2 is the number of malicious participants, each group with a slight diffrent behavior. 199 // - r1 participants of indices 0 to r1-1 behave maliciously and will get disqualified by honest participants. 200 // - r2 participants of indices r1 to r1+r2-1 will behave maliciously at first but will recover and won't be 201 // disqualified by honest participants. The r2 participants may or may not obtain correct protocol results. 202 var r1, r2 int 203 // h is the index of the first honest participant. All participant with indices greater than or equal to h are honest. 204 // Checking the final protocol results is done for honest participants only. 205 // Whether the r2 participants belong to the honest participants or not depend on the malicious behavior (detailed below). 206 var h int 207 208 switch test { 209 case happyPath: 210 // r1 = r2 = 0 211 212 case invalidShares: 213 r1 = mrand.Intn(dealers + 1) // dealers with invalid shares and will get disqualified 214 r2 = mrand.Intn(dealers - r1 + 1) // dealers with invalid shares but will recover 215 h = r1 216 217 var i int 218 for i = 0; i < r1; i++ { 219 processors[i].malicious = manyInvalidShares 220 } 221 for ; i < r1+r2; i++ { 222 processors[i].malicious = fewInvalidShares 223 } 224 t.Logf("%d participants will be disqualified, %d other participants will recover\n", r1, r2) 225 226 case invalidVector: 227 r1 = 1 + mrand.Intn(dealers) // dealers with invalid vector and will get disqualified 228 h = r1 229 230 // in this case r2 = 0 231 for i := 0; i < r1; i++ { 232 processors[i].malicious = invalidVectorBroadcast 233 } 234 t.Logf("%d participants will be disqualified\n", r1) 235 236 case invalidComplaint: 237 r1 = 1 + mrand.Intn(dealers-1) // participants with invalid complaints and will get disqualified. 238 // r1>= 1 to have at least one malicious dealer, and r1<leadrers-1 to leave space for the trigger dealer below. 239 r2 = mrand.Intn(dealers - r1) // participants with timeouted complaints: they are considered qualified by honest participants 240 // but their results are invalid 241 h = r1 + r2 // r2 shouldn't be verified for protocol correctness 242 243 for i := 0; i < r1; i++ { 244 processors[i].malicious = invalidComplaintBroadcast 245 } 246 for i := r1; i < r1+r2; i++ { 247 processors[i].malicious = timeoutedComplaintBroadcast 248 } 249 // The participant (r1+r2) will send wrong shares and cause the 0..r1+r2-1 dealers to send complaints. 250 // This participant doesn't risk getting disqualified as the complaints against them 251 // are invalid and won't count. The participant doesn't even answer the complaint. 252 processors[r1+r2].malicious = invalidSharesComplainTrigger 253 t.Logf("%d participants will be disqualified, %d other participants won't be disqualified.\n", r1, r2) 254 255 case invalidComplaintAnswer: 256 r1 = 1 + mrand.Intn(dealers-1) // participants with invalid complaint answers and will get disqualified. 257 // r1>= 1 to have at least one malicious dealer, and r1<leadrers-1 to leave space for the complaint sender. 258 h = r1 259 // the 0..r1-1 dealers will send invalid shares to n-1 to trigger complaints. 260 for i := 0; i < r1; i++ { 261 processors[i].malicious = invalidComplaintAnswerBroadcast 262 } 263 t.Logf("%d participants will be disqualified\n", r1) 264 case duplicatedMessages: 265 // r1 = r2 = 0 266 // participant 0 will send duplicated shares, verif vector and complaint to all participants 267 processors[0].malicious = duplicatedSendAndBroadcast 268 // participant 1 is a complaint trigger, it sents a wrong share to 0 to trigger a complaint. 269 // it also sends duplicated complaint answers. 270 processors[1].malicious = invalidSharesComplainTrigger 271 272 default: 273 panic("test case not supported") 274 } 275 276 // number of participants to test 277 lead := 0 278 var sync sync.WaitGroup 279 280 // create DKG in all participants 281 for current := 0; current < n; current++ { 282 var err error 283 processors[current].dkg, err = newDKG(dkg, n, threshold, 284 current, &processors[current], lead) 285 require.NoError(t, err) 286 } 287 288 phase := 0 289 if dkg == feldmanVSS { // jump to the last phase since there is only one phase for feldmanVSS 290 phase = 2 291 } 292 293 // start DKG in all participants 294 // start listening on the channels 295 seed := make([]byte, SeedMinLenDKG) 296 sync.Add(n) 297 298 log.Info("DKG protocol starts") 299 300 for current := 0; current < n; current++ { 301 processors[current].startSync.Add(1) 302 go dkgRunChan(&processors[current], &sync, t, phase) 303 } 304 305 for current := 0; current < n; current++ { 306 // start dkg in parallel 307 // ( one common PRG is used internally for all instances which causes a race 308 // in generating randoms and leads to non-deterministic keys. If deterministic keys 309 // are required, switch to sequential calls to dkg.Start() ) 310 go func(current int) { 311 _, err := crand.Read(seed) 312 require.NoError(t, err) 313 err = processors[current].dkg.Start(seed) 314 require.Nil(t, err) 315 processors[current].startSync.Done() // avoids reading messages when a dkg instance hasn't started yet 316 }(current) 317 } 318 phase++ 319 320 // sync the two timeouts and start the next phase 321 for ; phase <= 2; phase++ { 322 sync.Wait() 323 // post processing required for timeout edge case tests 324 go timeoutPostProcess(processors, t, phase) 325 sync.Add(n) 326 for current := 0; current < n; current++ { 327 go dkgRunChan(&processors[current], &sync, t, phase) 328 } 329 } 330 331 // synchronize the main thread to end all DKGs 332 sync.Wait() 333 334 // assertions and results: 335 336 // check the disqualified list for all non-disqualified participants 337 expected := make([]bool, dealers) 338 for i := 0; i < r1; i++ { 339 expected[i] = true 340 } 341 342 for i := h; i < n; i++ { 343 t.Logf("participant %d is not disqualified, its disqualified list is:\n", i) 344 t.Log(processors[i].disqualified) 345 assert.Equal(t, expected, processors[i].disqualified) 346 } 347 // check if DKG is successful 348 if (dkg == jointFeldman && (r1 > threshold || (n-r1) <= threshold)) || 349 (dkg == feldmanVSSQual && r1 == 1) { // case of a single dealer 350 t.Logf("dkg failed, there are %d disqualified participants\n", r1) 351 // DKG failed, check for final errors 352 for i := r1; i < n; i++ { 353 err := processors[i].finalError 354 assert.Error(t, err) 355 assert.True(t, IsDKGFailureError(err)) 356 } 357 } else { 358 t.Logf("dkg succeeded, there are %d disqualified participants\n", r1) 359 // DKG has succeeded, check for final errors 360 for i := h; i < n; i++ { 361 assert.NoError(t, processors[i].finalError) 362 } 363 // DKG has succeeded, check the final keys 364 for i := h; i < n; i++ { 365 assert.True(t, processors[h].pk.Equals(processors[i].pk), 366 "2 group public keys are mismatching") 367 } 368 } 369 370 } 371 372 // time after which a silent channel causes switching to the next dkg phase 373 const phaseSwitchTimeout = 200 * time.Millisecond 374 375 // This is a testing function 376 // It simulates processing incoming messages by a participant 377 // it assumes proc.dkg is already running 378 func dkgRunChan(proc *testDKGProcessor, 379 sync *sync.WaitGroup, t *testing.T, phase int) { 380 for { 381 select { 382 // if a message is received, handle it 383 case newMsg := <-proc.chans[proc.current]: 384 proc.startSync.Wait() // avoids reading a message when the receiving dkg instance 385 // hasn't started yet. 386 if newMsg.channel == private { 387 err := proc.dkg.HandlePrivateMsg(newMsg.orig, newMsg.data) 388 require.Nil(t, err) 389 } else { 390 err := proc.dkg.HandleBroadcastMsg(newMsg.orig, newMsg.data) 391 require.Nil(t, err) 392 } 393 // if no message is received by the channel, call the DKG timeout 394 case <-time.After(phaseSwitchTimeout): 395 proc.startSync.Wait() // avoids racing when starting isn't over yet 396 switch phase { 397 case 0: 398 log.Infof("%d shares phase ended\n", proc.current) 399 err := proc.dkg.NextTimeout() 400 require.Nil(t, err) 401 case 1: 402 log.Infof("%d complaints phase ended \n", proc.current) 403 err := proc.dkg.NextTimeout() 404 require.Nil(t, err) 405 case 2: 406 log.Infof("%d dkg ended \n", proc.current) 407 _, pk, _, err := proc.dkg.End() 408 proc.finalError = err 409 proc.pk = pk 410 } 411 sync.Done() 412 return 413 } 414 } 415 } 416 417 // post processing required for some edge case tests 418 func timeoutPostProcess(processors []testDKGProcessor, t *testing.T, phase int) { 419 switch phase { 420 case 1: 421 for i := 0; i < len(processors); i++ { 422 go func(i int) { 423 for len(processors[0].lateChansTimeout1[i]) != 0 { 424 // to test timeouted messages, late messages are copied to the main channels 425 msg := <-processors[0].lateChansTimeout1[i] 426 processors[0].chans[i] <- msg 427 } 428 }(i) 429 } 430 case 2: 431 for i := 0; i < len(processors); i++ { 432 go func(i int) { 433 for len(processors[0].lateChansTimeout2[i]) != 0 { 434 // to test timeouted messages, late messages are copied to the main channels 435 msg := <-processors[0].lateChansTimeout2[i] 436 processors[0].chans[i] <- msg 437 } 438 }(i) 439 } 440 } 441 } 442 443 // implements DKGProcessor interface 444 type testDKGProcessor struct { 445 // instnce of DKG 446 dkg DKGState 447 // index of the current participant in the protocol 448 current int 449 // group public key, output of DKG 450 pk PublicKey 451 // final disqualified list 452 disqualified []bool 453 // final output error of the DKG 454 finalError error 455 // type of malicious behavior 456 malicious behavior 457 // start DKG syncer 458 startSync sync.WaitGroup 459 460 // main message channels 461 chans []chan *message 462 // extra channels for late messges with regards to the first timeout, and second timeout 463 lateChansTimeout1 []chan *message 464 lateChansTimeout2 []chan *message 465 // type of the protocol 466 protocol int 467 468 // only used when testing the threshold signature stateful api 469 ts *blsThresholdSignatureParticipant 470 keys *statelessKeys 471 } 472 473 const ( 474 dkgType int = iota 475 tsType 476 ) 477 478 const ( 479 broadcast int = iota 480 private 481 ) 482 483 type message struct { 484 orig int 485 protocol int 486 channel int 487 data []byte 488 } 489 490 func (proc *testDKGProcessor) Disqualify(participant int, logInfo string) { 491 gt.Logf("%d disqualifies %d, %s\n", proc.current, participant, logInfo) 492 proc.disqualified[participant] = true 493 } 494 495 func (proc *testDKGProcessor) FlagMisbehavior(participant int, logInfo string) { 496 gt.Logf("%d flags a misbehavior from %d: %s", proc.current, participant, logInfo) 497 } 498 499 // This is a testing function 500 // it simulates sending a message from one participant to another 501 func (proc *testDKGProcessor) PrivateSend(dest int, data []byte) { 502 go func() { 503 log.Infof("%d sending to %d", proc.current, dest) 504 if proc.malicious == fewInvalidShares || proc.malicious == manyInvalidShares || 505 proc.malicious == invalidSharesComplainTrigger || proc.malicious == invalidComplaintAnswerBroadcast || 506 proc.malicious == duplicatedSendAndBroadcast { 507 proc.invalidShareSend(dest, data) 508 return 509 } 510 proc.honestSend(dest, data) 511 }() 512 } 513 514 // This is a testing function 515 // it simulates sending a honest message from one participant to another 516 func (proc *testDKGProcessor) honestSend(dest int, data []byte) { 517 gt.Logf("%d honestly sending to %d:\n%x\n", proc.current, dest, data) 518 newMsg := &message{proc.current, proc.protocol, private, data} 519 proc.chans[dest] <- newMsg 520 } 521 522 // This is a testing function 523 // it simulates sending a malicious message from one participant to another 524 // This function simulates the behavior of a malicious participant. 525 func (proc *testDKGProcessor) invalidShareSend(dest int, data []byte) { 526 527 // check the behavior 528 var recipients int // number of recipients to send invalid shares to 529 switch proc.malicious { 530 case manyInvalidShares: 531 recipients = proc.dkg.Threshold() + 1 // t < recipients <= n 532 case fewInvalidShares: 533 recipients = proc.dkg.Threshold() // 0 <= recipients <= t 534 case invalidSharesComplainTrigger: 535 recipients = proc.current // equal to r1+r2, which causes all r1+r2 to complain 536 case invalidComplaintAnswerBroadcast: 537 recipients = 0 // treat this case separately as the complaint trigger is the participant n-1 538 case duplicatedSendAndBroadcast: 539 proc.honestSend(dest, data) 540 proc.honestSend(dest, data) 541 return 542 default: 543 panic("invalid share send not supported") 544 } 545 546 // copy of data 547 newData := make([]byte, len(data)) 548 copy(newData, data) 549 550 newMsg := &message{proc.current, proc.protocol, private, newData} 551 originalMsg := &message{proc.current, proc.protocol, private, data} 552 553 // check destination 554 if (dest < recipients) || (proc.current < recipients && dest < recipients+1) || 555 (proc.malicious == invalidComplaintAnswerBroadcast && dest == proc.dkg.Size()-1) { 556 // choose a random reason for an invalid share 557 coin := mrand.Intn(7) 558 gt.Logf("%d maliciously sending to %d, coin is %d\n", proc.current, dest, coin) 559 switch coin { 560 case 0: 561 // value doesn't match the verification vector 562 newMsg.data[8]++ 563 proc.chans[dest] <- newMsg 564 case 1: 565 // empty message 566 newMsg.data = newMsg.data[:0] 567 proc.chans[dest] <- newMsg 568 case 2: 569 // valid message length but invalid share length 570 newMsg.data = newMsg.data[:1] 571 proc.chans[dest] <- newMsg 572 case 3: 573 // invalid value 574 for i := 0; i < len(newMsg.data); i++ { 575 newMsg.data[i] = 0xFF 576 } 577 proc.chans[dest] <- newMsg 578 case 4: 579 // do not send the share at all 580 return 581 case 5: 582 // wrong header: will cause a complaint 583 newMsg.data[0] = byte(feldmanVSSVerifVec) 584 proc.chans[dest] <- newMsg 585 case 6: 586 // message will be sent after the shares timeout and will be considered late 587 // by the receiver. All late messages go into a separate channel and will be sent to 588 // the main channel after the shares timeout. 589 proc.lateChansTimeout1[dest] <- newMsg 590 return 591 } 592 593 } else { 594 gt.Logf("turns out to be a honest send\n%x\n", data) 595 } 596 // honest send case: this is the only message sent 597 // malicious send case: this is a second correct send, to test the second message gets ignored 598 // by the receiver (sender has been tagged malicious after the first send) 599 proc.chans[dest] <- originalMsg 600 601 } 602 603 // This is a testing function 604 // it simulates broadcasting a message from one participant to all participants 605 func (proc *testDKGProcessor) Broadcast(data []byte) { 606 go func() { 607 log.Infof("%d Broadcasting:", proc.current) 608 609 if data[0] == byte(feldmanVSSVerifVec) && proc.malicious == invalidVectorBroadcast { 610 proc.invalidVectorBroadcast(data) 611 } else if data[0] == byte(feldmanVSSComplaint) && 612 (proc.malicious == invalidComplaintBroadcast || proc.malicious == timeoutedComplaintBroadcast) { 613 proc.invalidComplaintBroadcast(data) 614 } else if data[0] == byte(feldmanVSSComplaintAnswer) && proc.malicious == invalidComplaintAnswerBroadcast { 615 proc.invalidComplaintAnswerBroadcast(data) 616 } else if proc.malicious == duplicatedSendAndBroadcast || 617 (data[0] == byte(feldmanVSSComplaintAnswer) && proc.malicious == invalidSharesComplainTrigger) { 618 // the complaint trigger also sends duplicated complaint answers 619 proc.honestBroadcast(data) 620 proc.honestBroadcast(data) 621 } else { 622 proc.honestBroadcast(data) 623 } 624 }() 625 } 626 627 func (proc *testDKGProcessor) honestBroadcast(data []byte) { 628 gt.Logf("%d honestly broadcasting:\n%x\n", proc.current, data) 629 newMsg := &message{proc.current, proc.protocol, broadcast, data} 630 for i := 0; i < len(proc.chans); i++ { 631 if i != proc.current { 632 proc.chans[i] <- newMsg 633 } 634 } 635 } 636 637 func (proc *testDKGProcessor) invalidVectorBroadcast(data []byte) { 638 newMsg := &message{proc.current, proc.protocol, broadcast, data} 639 640 // choose a random reason of an invalid vector 641 coin := mrand.Intn(5) 642 gt.Logf("%d malicious vector broadcast, coin is %d\n", proc.current, coin) 643 switch coin { 644 case 0: 645 // invalid point serialization 646 newMsg.data[1] = 0xFF 647 case 1: 648 // invalid length 649 newMsg.data = newMsg.data[:5] 650 case 2: 651 // do not send the vector at all 652 return 653 case 3: 654 // wrong header 655 newMsg.data[0] = byte(feldmanVSSShare) 656 case 4: 657 // send the vector after the first timeout, equivalent to not sending at all 658 // as the vector should be ignored. 659 for i := 0; i < proc.dkg.Size(); i++ { 660 if i != proc.current { 661 proc.lateChansTimeout1[i] <- newMsg 662 } 663 } 664 return 665 } 666 gt.Logf("%x\n", newMsg.data) 667 for i := 0; i < proc.dkg.Size(); i++ { 668 if i != proc.current { 669 proc.chans[i] <- newMsg 670 } 671 } 672 } 673 674 func (proc *testDKGProcessor) invalidComplaintBroadcast(data []byte) { 675 newMsg := &message{proc.current, proc.protocol, broadcast, data} 676 677 if proc.malicious == invalidComplaintBroadcast { 678 679 // choose a random reason for an invalid complaint 680 coin := mrand.Intn(2) 681 gt.Logf("%d malicious complaint broadcast, coin is %d\n", proc.current, coin) 682 switch coin { 683 case 0: 684 // invalid complainee 685 newMsg.data[1] = byte(proc.dkg.Size() + 1) 686 case 1: 687 // invalid length 688 newMsg.data = make([]byte, complaintSize+5) 689 copy(newMsg.data, data) 690 } 691 gt.Logf("%x\n", newMsg.data) 692 for i := 0; i < len(proc.chans); i++ { 693 if i != proc.current { 694 proc.chans[i] <- newMsg 695 } 696 } 697 } else if proc.malicious == timeoutedComplaintBroadcast { 698 gt.Logf("%d timeouted complaint broadcast\n", proc.current) 699 // send the complaint after the second timeout, equivalent to not sending at all 700 // as the complaint should be ignored. 701 for i := 0; i < len(proc.chans); i++ { 702 if i != proc.current { 703 proc.lateChansTimeout2[i] <- newMsg 704 } 705 } 706 return 707 } 708 } 709 710 func (proc *testDKGProcessor) invalidComplaintAnswerBroadcast(data []byte) { 711 newMsg := &message{proc.current, proc.protocol, broadcast, data} 712 713 // choose a random reason for an invalid complaint 714 coin := mrand.Intn(3) 715 gt.Logf("%d malicious complaint answer broadcast, coin is %d\n", proc.current, coin) 716 switch coin { 717 case 0: 718 // invalid complainee 719 newMsg.data[1] = byte(proc.dkg.Size() + 1) 720 case 1: 721 // invalid length 722 newMsg.data = make([]byte, complaintAnswerSize+5) 723 copy(newMsg.data, data) 724 case 2: 725 // no answer at all 726 return 727 } 728 //gt.Logf("%x\n", newMsg.data) 729 for i := 0; i < len(proc.chans); i++ { 730 if i != proc.current { 731 proc.chans[i] <- newMsg 732 } 733 } 734 } 735 736 // implements a dummy DKGProcessor 737 type dummyTestDKGProcessor struct { 738 } 739 740 func (proc dummyTestDKGProcessor) PrivateSend(int, []byte) {} 741 func (proc dummyTestDKGProcessor) Broadcast([]byte) {} 742 func (proc dummyTestDKGProcessor) Disqualify(int, string) {} 743 func (proc dummyTestDKGProcessor) FlagMisbehavior(int, string) {} 744 745 func TestDKGErrorTypes(t *testing.T) { 746 t.Run("dkgFailureError sanity", func(t *testing.T) { 747 failureError := dkgFailureErrorf("some error") 748 invInpError := invalidInputsErrorf("") 749 otherError := fmt.Errorf("some error") 750 assert.True(t, IsDKGFailureError(failureError)) 751 assert.False(t, IsDKGFailureError(otherError)) 752 assert.False(t, IsDKGFailureError(invInpError)) 753 assert.False(t, IsDKGFailureError(nil)) 754 assert.False(t, IsInvalidInputsError(failureError)) 755 }) 756 757 t.Run("dkgInvalidStateTransitionError sanity", func(t *testing.T) { 758 failureError := dkgInvalidStateTransitionErrorf("some error") 759 invInpError := invalidInputsErrorf("") 760 otherError := fmt.Errorf("some error") 761 assert.True(t, IsDKGInvalidStateTransitionError(failureError)) 762 assert.False(t, IsInvalidInputsError(failureError)) 763 assert.False(t, IsDKGInvalidStateTransitionError(invInpError)) 764 assert.False(t, IsDKGInvalidStateTransitionError(otherError)) 765 assert.False(t, IsDKGInvalidStateTransitionError(nil)) 766 }) 767 } 768 769 func TestDKGTransitionErrors(t *testing.T) { 770 n := 5 771 threshold := 3 772 myIndex := 0 773 dealer := 1 774 seed := make([]byte, SeedMinLenDKG) 775 776 t.Run("feldman VSS", func(t *testing.T) { 777 state, err := NewFeldmanVSS(n, threshold, myIndex, dummyTestDKGProcessor{}, dealer) 778 require.NoError(t, err) 779 // calls before start 780 err = state.ForceDisqualify(1) 781 assert.True(t, IsDKGInvalidStateTransitionError(err)) 782 err = state.HandlePrivateMsg(1, []byte{}) 783 assert.True(t, IsDKGInvalidStateTransitionError(err)) 784 err = state.HandleBroadcastMsg(1, []byte{}) 785 assert.True(t, IsDKGInvalidStateTransitionError(err)) 786 _, _, _, err = state.End() 787 assert.True(t, IsDKGInvalidStateTransitionError(err)) 788 }) 789 790 t.Run("Feldman VSS Qualif and joint-Feldman ", func(t *testing.T) { 791 stateFVSSQ, err := NewFeldmanVSSQual(n, threshold, myIndex, dummyTestDKGProcessor{}, dealer) 792 require.NoError(t, err) 793 stateJF, err := NewJointFeldman(n, threshold, myIndex, dummyTestDKGProcessor{}) 794 require.NoError(t, err) 795 796 for _, state := range []DKGState{stateFVSSQ, stateJF} { 797 // calls before start 798 err = state.ForceDisqualify(1) 799 assert.True(t, IsDKGInvalidStateTransitionError(err)) 800 err = state.HandlePrivateMsg(1, []byte{}) 801 assert.True(t, IsDKGInvalidStateTransitionError(err)) 802 err = state.HandleBroadcastMsg(1, []byte{}) 803 assert.True(t, IsDKGInvalidStateTransitionError(err)) 804 _, _, _, err = state.End() 805 assert.True(t, IsDKGInvalidStateTransitionError(err)) 806 err = state.NextTimeout() 807 assert.True(t, IsDKGInvalidStateTransitionError(err)) 808 // after start 809 err = state.Start(seed) 810 require.NoError(t, err) 811 _, _, _, err = state.End() 812 assert.True(t, IsDKGInvalidStateTransitionError(err)) 813 // after first timeout 814 err = state.NextTimeout() 815 require.NoError(t, err) 816 err = state.Start(seed) 817 assert.True(t, IsDKGInvalidStateTransitionError(err)) 818 _, _, _, err = state.End() 819 assert.True(t, IsDKGInvalidStateTransitionError(err)) 820 // after second timeout 821 err = state.NextTimeout() 822 require.NoError(t, err) 823 err = state.Start(seed) 824 assert.True(t, IsDKGInvalidStateTransitionError(err)) 825 err = state.NextTimeout() 826 assert.True(t, IsDKGInvalidStateTransitionError(err)) 827 // after end 828 _, _, _, err = state.End() 829 require.True(t, IsDKGFailureError(err)) 830 err = state.NextTimeout() 831 assert.True(t, IsDKGInvalidStateTransitionError(err)) 832 } 833 }) 834 }