github.com/onflow/flow-go/crypto@v0.24.8/bls_thresholdsign.go (about) 1 //go:build relic 2 // +build relic 3 4 package crypto 5 6 // #cgo CFLAGS: -g -Wall -std=c99 7 // #include "bls_thresholdsign_include.h" 8 import "C" 9 10 import ( 11 "fmt" 12 "sync" 13 14 "github.com/onflow/flow-go/crypto/hash" 15 ) 16 17 // BLS-based threshold signature on BLS 12-381 curve 18 // The BLS settings are the same as in the signature 19 // scheme defined in the package. 20 21 // A threshold signature scheme allows any subset of (t+1) 22 // valid signature shares to reconstruct the threshold signature. 23 // Up to (t) shares do not reveal any information about the threshold 24 // signature. 25 // Although the API allows using arbitrary values of (t), 26 // the threshold signature scheme is secure in the presence of up to (t) 27 // malicious participants when (t < n/2). 28 // In order to optimize equally for unforgeability and robustness, 29 // the input threshold value (t) should be set to t = floor((n-1)/2). 30 31 // The package offers two api for BLS threshold signature: 32 // - stateful api where a structure holds all information 33 // of the threshold signature protocols and is recommended 34 // to be used for safety and to reduce protocol inconsistencies. 35 // - stateless api with signature reconstruction. Verifying and storing 36 // the signature shares has to be managed outside of the library. 37 38 // blsThresholdSignatureParticipant implements ThresholdSignatureParticipant 39 // based on the BLS signature scheme 40 type blsThresholdSignatureParticipant struct { 41 // embed the follower 42 *blsThresholdSignatureInspector 43 // the index of the current participant 44 myIndex int 45 // the current participant private key (a threshold KG output) 46 myPrivateKey PrivateKey 47 } 48 49 // blsThresholdSignatureInspector implements ThresholdSignatureInspector 50 // based on the BLS signature scheme 51 type blsThresholdSignatureInspector struct { 52 // size of the group 53 size int 54 // the threshold t of the scheme where (t+1) shares are 55 // required to reconstruct a signature 56 threshold int 57 // the group public key (a threshold KG output) 58 groupPublicKey PublicKey 59 // the group public key shares (a threshold KG output) 60 publicKeyShares []PublicKey 61 // the hasher to be used for all signatures 62 hasher hash.Hasher 63 // the message to be signed. Signature shares and the threshold signature 64 // are verified against this message 65 message []byte 66 // the valid signature shares received from other participants 67 shares map[index]Signature 68 // the threshold signature. It is equal to nil if less than (t+1) shares are 69 // received 70 thresholdSignature Signature 71 // lock for atomic operations 72 lock sync.RWMutex 73 } 74 75 // NewBLSThresholdSignatureParticipant creates a new instance of Threshold signature Participant using BLS. 76 // A participant is able to participate in a threshold signing protocol as well as following the 77 // protocol. 78 // 79 // A new instance is needed for each set of public keys and message. 80 // If the key set or message change, a new structure needs to be instantiated. 81 // Participants are defined by their public key share, and are indexed from 0 to n-1. The current 82 // participant is indexed by `myIndex` and holds the input private key 83 // where n is the length of the public key shares slice. 84 // 85 // The function returns 86 // - (nil, invalidInputsError) if: 87 // - n is not in [`ThresholdSignMinSize`, `ThresholdSignMaxSize`] 88 // - threshold value is not in interval [1, n-1] 89 // - input private key and public key at my index do not match 90 // - (nil, notBLSKeyError) if the private or at least one public key is not of type BLS BLS12-381. 91 // - (pointer, nil) otherwise 92 func NewBLSThresholdSignatureParticipant( 93 groupPublicKey PublicKey, 94 sharePublicKeys []PublicKey, 95 threshold int, 96 myIndex int, 97 myPrivateKey PrivateKey, 98 message []byte, 99 dsTag string, 100 ) (*blsThresholdSignatureParticipant, error) { 101 102 size := len(sharePublicKeys) 103 if myIndex >= size || myIndex < 0 { 104 return nil, invalidInputsErrorf( 105 "the current index must be between 0 and %d, got %d", 106 size-1, myIndex) 107 } 108 109 // check private key is BLS key 110 if _, ok := myPrivateKey.(*prKeyBLSBLS12381); !ok { 111 return nil, fmt.Errorf("private key of participant %d is not valid: %w", myIndex, notBLSKeyError) 112 } 113 114 // create the follower 115 follower, err := NewBLSThresholdSignatureInspector(groupPublicKey, sharePublicKeys, threshold, message, dsTag) 116 if err != nil { 117 return nil, fmt.Errorf("create a threshold signature follower failed: %w", err) 118 } 119 120 // check the private key, index and corresponding public key are consistent 121 currentPublicKey := sharePublicKeys[myIndex] 122 if !myPrivateKey.PublicKey().Equals(currentPublicKey) { 123 return nil, invalidInputsErrorf("private key is not matching public key at index %d", myIndex) 124 } 125 126 return &blsThresholdSignatureParticipant{ 127 blsThresholdSignatureInspector: follower, 128 myIndex: myIndex, // current participant index 129 myPrivateKey: myPrivateKey, // myPrivateKey is the current participant's own private key share 130 }, nil 131 } 132 133 // NewBLSThresholdSignatureInspector creates a new instance of Threshold signature follower using BLS. 134 // It only allows following the threshold signing protocol . 135 // 136 // A new instance is needed for each set of public keys and message. 137 // If the key set or message change, a new structure needs to be instantiated. 138 // Participants are defined by their public key share, and are indexed from 0 to n-1 139 // where n is the length of the public key shares slice. 140 // 141 // The function returns 142 // - (nil, invalidInputsError) if: 143 // - n is not in [`ThresholdSignMinSize`, `ThresholdSignMaxSize`] 144 // - threshold value is not in interval [1, n-1] 145 // - (nil, notBLSKeyError) at least one public key is not of type pubKeyBLSBLS12381 146 // - (pointer, nil) otherwise 147 func NewBLSThresholdSignatureInspector( 148 groupPublicKey PublicKey, 149 sharePublicKeys []PublicKey, 150 threshold int, 151 message []byte, 152 dsTag string, 153 ) (*blsThresholdSignatureInspector, error) { 154 155 size := len(sharePublicKeys) 156 if size < ThresholdSignMinSize || size > ThresholdSignMaxSize { 157 return nil, invalidInputsErrorf( 158 "size should be between %d and %d, got %d", 159 ThresholdSignMinSize, ThresholdSignMaxSize, size) 160 } 161 if threshold >= size || threshold < MinimumThreshold { 162 return nil, invalidInputsErrorf( 163 "the threshold must be between %d and %d, got %d", 164 MinimumThreshold, size-1, threshold) 165 } 166 167 // check keys are BLS keys 168 for i, pk := range sharePublicKeys { 169 if _, ok := pk.(*pubKeyBLSBLS12381); !ok { 170 return nil, fmt.Errorf("key at index %d is invalid: %w", i, notBLSKeyError) 171 } 172 } 173 if _, ok := groupPublicKey.(*pubKeyBLSBLS12381); !ok { 174 return nil, fmt.Errorf("group key is invalid: %w", notBLSKeyError) 175 } 176 177 return &blsThresholdSignatureInspector{ 178 size: size, 179 threshold: threshold, 180 message: message, 181 hasher: NewExpandMsgXOFKMAC128(dsTag), 182 shares: make(map[index]Signature), 183 thresholdSignature: nil, 184 groupPublicKey: groupPublicKey, // groupPublicKey is the group public key corresponding to the group secret key 185 publicKeyShares: sharePublicKeys, // sharePublicKeys are the public key shares corresponding to the private key shares 186 }, nil 187 } 188 189 // SignShare generates a signature share using the current private key share. 190 // 191 // The function does not add the share to the internal pool of shares and do 192 // not update the internal state. 193 // This function is thread safe and non-blocking 194 // 195 // The function returns 196 // - (nil, error) if an unexpected error occurs 197 // - (signature, nil) otherwise 198 func (s *blsThresholdSignatureParticipant) SignShare() (Signature, error) { 199 share, err := s.myPrivateKey.Sign(s.message, s.hasher) 200 if err != nil { 201 return nil, fmt.Errorf("share signing failed: %w", err) 202 } 203 return share, nil 204 } 205 206 // validIndex returns invalidInputsError error if given index is valid and nil otherwise. 207 // This function is thread safe. 208 func (s *blsThresholdSignatureInspector) validIndex(orig int) error { 209 if orig >= s.size || orig < 0 { 210 return invalidInputsErrorf( 211 "origin input is invalid, should be positive less than %d, got %d", 212 s.size, orig) 213 } 214 return nil 215 } 216 217 // VerifyShare verifies the input signature against the stored message and stored 218 // key at the input index. 219 // 220 // This function does not update the internal state and is thread-safe. 221 // Returns: 222 // - (true, nil) if the signature is valid 223 // - (false, nil) if `orig` is valid but the signature share does not verify against 224 // the public key share and message. 225 // - (false, invalidInputsError) if `orig` is an invalid index value 226 // - (false, error) for all other unexpected errors 227 func (s *blsThresholdSignatureInspector) VerifyShare(orig int, share Signature) (bool, error) { 228 // validate index 229 if err := s.validIndex(orig); err != nil { 230 return false, err 231 } 232 return s.publicKeyShares[orig].Verify(share, s.message, s.hasher) 233 } 234 235 // VerifyThresholdSignature verifies the input signature against the stored 236 // message and stored group public key. 237 // 238 // This function does not update the internal state and is thread-safe. 239 // Returns: 240 // - (true, nil) if the signature is valid 241 // - (false, nil) if signature is invalid 242 // - (false, error) for all other unexpected errors 243 func (s *blsThresholdSignatureInspector) VerifyThresholdSignature(thresholdSignature Signature) (bool, error) { 244 return s.groupPublicKey.Verify(thresholdSignature, s.message, s.hasher) 245 } 246 247 // EnoughShares indicates whether enough shares have been accumulated in order to reconstruct 248 // a group signature. 249 // 250 // This function is thread safe. 251 // Returns: 252 // - true if and only if at least (threshold+1) shares were added 253 func (s *blsThresholdSignatureInspector) EnoughShares() bool { 254 s.lock.RLock() 255 defer s.lock.RUnlock() 256 257 return s.enoughShares() 258 } 259 260 // non thread safe version of EnoughShares 261 func (s *blsThresholdSignatureInspector) enoughShares() bool { 262 // len(s.signers) is always <= s.threshold + 1 263 return len(s.shares) == (s.threshold + 1) 264 } 265 266 // HasShare checks whether the internal map contains the share of the given index. 267 // This function is thread safe and locks the internal state. 268 // The function returns: 269 // - (false, invalidInputsError) if the index is invalid 270 // - (false, nil) if index is valid and share is not in the map 271 // - (true, nil) if index is valid and share is in the map 272 func (s *blsThresholdSignatureInspector) HasShare(orig int) (bool, error) { 273 // validate index 274 if err := s.validIndex(orig); err != nil { 275 return false, err 276 } 277 278 s.lock.RLock() 279 defer s.lock.RUnlock() 280 281 return s.hasShare(index(orig)), nil 282 } 283 284 // non thread safe version of HasShare, and assumes input is valid 285 func (s *blsThresholdSignatureInspector) hasShare(orig index) bool { 286 _, ok := s.shares[orig] 287 return ok 288 } 289 290 // TrustedAdd adds a signature share to the internal pool of shares 291 // without verifying the signature against the message and the participant's 292 // public key. This function is thread safe and locks the internal state. 293 // 294 // The share is only added if the signer index is valid and has not been 295 // added yet. Moreover, the share is added only if not enough shares were collected. 296 // The function returns: 297 // - (true, nil) if enough signature shares were already collected and no error occurred 298 // - (false, nil) if not enough shares were collected and no error occurred 299 // - (false, invalidInputsError) if index is invalid 300 // - (false, duplicatedSignerError) if a signature for the index was previously added 301 func (s *blsThresholdSignatureInspector) TrustedAdd(orig int, share Signature) (bool, error) { 302 // validate index 303 if err := s.validIndex(orig); err != nil { 304 return false, err 305 } 306 307 s.lock.Lock() 308 defer s.lock.Unlock() 309 310 if s.hasShare(index(orig)) { 311 return false, duplicatedSignerErrorf("share for %d was already added", orig) 312 } 313 314 if s.enoughShares() { 315 return true, nil 316 } 317 s.shares[index(orig)] = share 318 return s.enoughShares(), nil 319 } 320 321 // VerifyAndAdd verifies a signature share (same as `VerifyShare`), 322 // and may or may not add the share to the local pool of shares. 323 // This function is thread safe and locks the internal state. 324 // 325 // The share is only added if the signature is valid, the signer index is valid and has not been 326 // added yet. Moreover, the share is added only if not enough shares were collected. 327 // Boolean returns: 328 // - First boolean output is true if the share is valid and no error is returned, and false otherwise. 329 // - Second boolean output is true if enough shares were collected and no error is returned, and false otherwise. 330 // 331 // Error returns: 332 // - invalidInputsError if input index is invalid. A signature that doesn't verify against the signer's 333 // public key is not considered an invalid input. 334 // - duplicatedSignerError if signer was already added. 335 // - other errors if an unexpected exception occurred. 336 func (s *blsThresholdSignatureInspector) VerifyAndAdd(orig int, share Signature) (bool, bool, error) { 337 // validate index 338 if err := s.validIndex(orig); err != nil { 339 return false, false, err 340 } 341 342 s.lock.Lock() 343 defer s.lock.Unlock() 344 345 // check share is new 346 if s.hasShare(index(orig)) { 347 return false, false, duplicatedSignerErrorf("share for %d was already added", orig) 348 } 349 350 // verify the share 351 verif, err := s.publicKeyShares[index(orig)].Verify(share, s.message, s.hasher) 352 if err != nil { 353 return false, false, fmt.Errorf("verification of share failed: %w", err) 354 } 355 356 enough := s.enoughShares() 357 if verif && !enough { 358 s.shares[index(orig)] = share 359 } 360 return verif, s.enoughShares(), nil 361 } 362 363 // ThresholdSignature returns the threshold signature if the threshold was reached. 364 // The threshold signature is reconstructed only once is cached for subsequent calls. 365 // 366 // The function is thread-safe. 367 // Returns: 368 // - (signature, nil) if no error occurred 369 // - (nil, notEnoughSharesError) if not enough shares were collected 370 // - (nil, invalidSignatureError) if at least one collected share does not serialize to a valid BLS signature. 371 // - (nil, invalidInputsError) if the constructed signature failed to verify against the group public key and stored 372 // message. This post-verification is required for safety, as `TrustedAdd` allows adding invalid signatures. 373 // - (nil, error) for any other unexpected error. 374 func (s *blsThresholdSignatureInspector) ThresholdSignature() (Signature, error) { 375 s.lock.Lock() 376 defer s.lock.Unlock() 377 378 // check cached thresholdSignature 379 if s.thresholdSignature != nil { 380 return s.thresholdSignature, nil 381 } 382 383 // reconstruct the threshold signature 384 thresholdSignature, err := s.reconstructThresholdSignature() 385 if err != nil { 386 return nil, err 387 } 388 s.thresholdSignature = thresholdSignature 389 return thresholdSignature, nil 390 } 391 392 // reconstructThresholdSignature reconstructs the threshold signature from at least (t+1) shares. 393 // Returns: 394 // - (signature, nil) if no error occurred 395 // - (nil, notEnoughSharesError) if not enough shares were collected 396 // - (nil, invalidSignatureError) if at least one collected share does not serialize to a valid BLS signature. 397 // - (nil, invalidInputsError) if the constructed signature failed to verify against the group public key and stored message. 398 // - (nil, error) for any other unexpected error. 399 func (s *blsThresholdSignatureInspector) reconstructThresholdSignature() (Signature, error) { 400 401 if !s.enoughShares() { 402 return nil, notEnoughSharesErrorf("number of signature shares %d is not enough, %d are required", 403 len(s.shares), s.threshold+1) 404 } 405 thresholdSignature := make([]byte, signatureLengthBLSBLS12381) 406 407 // prepare the C layer inputs 408 shares := make([]byte, 0, len(s.shares)*signatureLengthBLSBLS12381) 409 signers := make([]index, 0, len(s.shares)) 410 for index, share := range s.shares { 411 shares = append(shares, share...) 412 signers = append(signers, index) 413 } 414 415 // set BLS settings 416 blsInstance.reInit() 417 418 // Lagrange Interpolate at point 0 419 result := C.G1_lagrangeInterpolateAtZero( 420 (*C.uchar)(&thresholdSignature[0]), 421 (*C.uchar)(&shares[0]), 422 (*C.uint8_t)(&signers[0]), (C.int)(s.threshold+1)) 423 424 if result != valid { 425 return nil, invalidSignatureError 426 } 427 428 // Verify the computed signature 429 verif, err := s.VerifyThresholdSignature(thresholdSignature) 430 if err != nil { 431 return nil, fmt.Errorf("internal error while verifying the threshold signature: %w", err) 432 } 433 if !verif { 434 return nil, invalidInputsErrorf( 435 "constructed threshold signature does not verify against the group public key, check shares and public key") 436 } 437 438 return thresholdSignature, nil 439 } 440 441 // BLSReconstructThresholdSignature is a stateless BLS api that takes a list of 442 // BLS signatures and their signers' indices and returns the threshold signature. 443 // 444 // size is the number of participants, it must be in the range [ThresholdSignMinSize..ThresholdSignMaxSize]. 445 // threshold is the threshold value, it must be in the range [MinimumThreshold..size-1]. 446 // The function does not check the validity of the shares, and does not check 447 // the validity of the resulting signature. 448 // BLSReconstructThresholdSignature returns: 449 // - (nil, error) if the inputs are not in the correct range, if the threshold is not reached 450 // - (nil, duplicatedSignerError) if input signers are not distinct. 451 // - (nil, invalidSignatureError) if at least one of the first (threshold+1) signatures. 452 // does not serialize to a valid E1 point. 453 // - (threshold_sig, nil) otherwise. 454 // 455 // If the number of shares reaches the required threshold, only the first threshold+1 shares 456 // are considered to reconstruct the signature. 457 func BLSReconstructThresholdSignature(size int, threshold int, 458 shares []Signature, signers []int) (Signature, error) { 459 // set BLS settings 460 blsInstance.reInit() 461 462 if size < ThresholdSignMinSize || size > ThresholdSignMaxSize { 463 return nil, invalidInputsErrorf( 464 "size should be between %d and %d", 465 ThresholdSignMinSize, 466 ThresholdSignMaxSize) 467 } 468 if threshold >= size || threshold < MinimumThreshold { 469 return nil, invalidInputsErrorf( 470 "the threshold must be between %d and %d, got %d", 471 MinimumThreshold, size-1, 472 threshold) 473 } 474 475 if len(shares) != len(signers) { 476 return nil, invalidInputsErrorf( 477 "the number of signature shares is not matching the number of signers") 478 } 479 480 if len(shares) < threshold+1 { 481 return nil, invalidInputsErrorf( 482 "the number of signatures does not reach the threshold") 483 } 484 485 // map to check signers are distinct 486 m := make(map[index]bool) 487 488 // flatten the shares (required by the C layer) 489 flatShares := make([]byte, 0, signatureLengthBLSBLS12381*(threshold+1)) 490 indexSigners := make([]index, 0, threshold+1) 491 for i, share := range shares { 492 flatShares = append(flatShares, share...) 493 // check the index is valid 494 if signers[i] >= size || signers[i] < 0 { 495 return nil, invalidInputsErrorf( 496 "signer index #%d is invalid", i) 497 } 498 // check the index is new 499 if _, isSeen := m[index(signers[i])]; isSeen { 500 return nil, duplicatedSignerErrorf( 501 "%d is a duplicate signer", index(signers[i])) 502 } 503 m[index(signers[i])] = true 504 indexSigners = append(indexSigners, index(signers[i])) 505 } 506 507 thresholdSignature := make([]byte, signatureLengthBLSBLS12381) 508 // Lagrange Interpolate at point 0 509 if C.G1_lagrangeInterpolateAtZero( 510 (*C.uchar)(&thresholdSignature[0]), 511 (*C.uchar)(&flatShares[0]), 512 (*C.uint8_t)(&indexSigners[0]), (C.int)(threshold+1), 513 ) != valid { 514 return nil, invalidSignatureError 515 } 516 return thresholdSignature, nil 517 } 518 519 // EnoughShares is a stateless function that takes the value of the threshold 520 // and a shares number and returns true if the shares number is enough 521 // to reconstruct a threshold signature. 522 // 523 // The function returns: 524 // - (false, invalidInputsErrorf) if input threshold is less than 1 525 // - (false, nil) if threshold is valid but shares are not enough. 526 // - (true, nil) if the threshold is valid but shares are enough. 527 func EnoughShares(threshold int, sharesNumber int) (bool, error) { 528 if threshold < MinimumThreshold { 529 return false, invalidInputsErrorf( 530 "the threshold can't be smaller than %d, got %d", 531 MinimumThreshold, threshold) 532 } 533 return sharesNumber > threshold, nil 534 } 535 536 // BLSThresholdKeyGen is a key generation for a BLS-based 537 // threshold signature scheme with a trusted dealer. 538 // 539 // The function returns : 540 // - (nil, nil, nil, invalidInputsErrorf) if: 541 // - n is not in [`ThresholdSignMinSize`, `ThresholdSignMaxSize`] 542 // - threshold value is not in interval [1, n-1] 543 // - (groupPrivKey, []pubKeyShares, groupPubKey, nil) otherwise 544 func BLSThresholdKeyGen(size int, threshold int, seed []byte) ([]PrivateKey, 545 []PublicKey, PublicKey, error) { 546 if size < ThresholdSignMinSize || size > ThresholdSignMaxSize { 547 return nil, nil, nil, invalidInputsErrorf( 548 "size should be between %d and %d, got %d", 549 ThresholdSignMinSize, 550 ThresholdSignMaxSize, 551 size) 552 } 553 if threshold >= size || threshold < MinimumThreshold { 554 return nil, nil, nil, invalidInputsErrorf( 555 "the threshold must be between %d and %d, got %d", 556 MinimumThreshold, 557 size-1, 558 threshold) 559 } 560 561 // set BLS settings 562 blsInstance.reInit() 563 564 // the scalars x and G2 points y 565 x := make([]scalar, size) 566 y := make([]pointG2, size) 567 var X0 pointG2 568 569 // seed relic 570 if err := seedRelic(seed); err != nil { 571 return nil, nil, nil, fmt.Errorf("seeding relic failed: %w", err) 572 } 573 // Generate a polynomial P in Zr[X] of degree t 574 a := make([]scalar, threshold+1) 575 randZrStar(&a[0]) // non-identity key 576 if threshold > 0 { 577 for i := 1; i < threshold; i++ { 578 randZr(&a[i]) 579 } 580 randZrStar(&a[threshold]) // enforce the polynomial degree 581 } 582 // compute the shares 583 for i := index(1); int(i) <= size; i++ { 584 C.Zr_polynomialImage( 585 (*C.bn_st)(&x[i-1]), 586 (*C.ep2_st)(&y[i-1]), 587 (*C.bn_st)(&a[0]), (C.int)(len(a)), 588 (C.uint8_t)(i), 589 ) 590 } 591 // group public key 592 generatorScalarMultG2(&X0, &a[0]) 593 // export the keys 594 skShares := make([]PrivateKey, size) 595 pkShares := make([]PublicKey, size) 596 var pkGroup PublicKey 597 for i := 0; i < size; i++ { 598 skShares[i] = newPrKeyBLSBLS12381(&x[i]) 599 pkShares[i] = newPubKeyBLSBLS12381(&y[i]) 600 } 601 pkGroup = newPubKeyBLSBLS12381(&X0) 602 603 // public key shares and group public key 604 // are sampled uniformly at random. The probability of 605 // generating an identity key is therefore negligible. 606 return skShares, pkShares, pkGroup, nil 607 }