github.com/onflow/flow-go/crypto@v0.24.8/bls_multisig.go (about) 1 //go:build relic 2 // +build relic 3 4 package crypto 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/onflow/flow-go/crypto/hash" 11 ) 12 13 // BLS multi-signature using BLS12-381 curve 14 // ([zcash]https://github.com/zkcrypto/pairing/blob/master/src/bls12_381/README.md#bls12-381) 15 // Pairing, ellipic curve and modular arithmetic is using Relic library. 16 // This implementation does not include any security against side-channel attacks. 17 18 // existing features: 19 // - the same BLS set-up in bls.go 20 // - Use the proof of possession scheme (PoP) to prevent against rogue public-key attack. 21 // - Non-interactive aggregation of private keys, public keys and signatures. 22 // - Non-interactive subtraction of multiple public keys from an (aggregated) public key. 23 // - Multi-signature verification of an aggregated signature of a single message 24 // under multiple public keys. 25 // - Multi-signature verification of an aggregated signature of multiple messages under 26 // multiple public keys. 27 // - batch verification of multiple signatures of a single message under multiple 28 // public keys: use a binary tree of aggregations to find the invalid signatures. 29 30 // #cgo CFLAGS: -g -Wall -std=c99 31 // #cgo LDFLAGS: -L${SRCDIR}/relic/build/lib -l relic_s 32 // #include "bls_include.h" 33 import "C" 34 35 // the PoP hasher, used to generate and verify PoPs 36 // The key is based on blsPOPCipherSuite which guarantees 37 // that hash_to_field of PoP is orthogonal to all hash_to_field functions 38 // used for signatures. 39 var popKMAC = internalExpandMsgXOFKMAC128(blsPOPCipherSuite) 40 41 // BLSGeneratePOP returns a proof of possession (PoP) for the receiver private key. 42 // 43 // The KMAC hasher used in the function is guaranteed to be orthogonal to all hashers used 44 // for signatures or SPoCK proofs on this package. This means a specific domain tag is used 45 // to generate PoP and is not used by any other application. 46 // 47 // The function returns: 48 // - (nil, notBLSKeyError) if the input key is not of type BLS BLS12-381 49 // - (pop, nil) otherwise 50 func BLSGeneratePOP(sk PrivateKey) (Signature, error) { 51 _, ok := sk.(*prKeyBLSBLS12381) 52 if !ok { 53 return nil, notBLSKeyError 54 } 55 // sign the public key 56 return sk.Sign(sk.PublicKey().Encode(), popKMAC) 57 } 58 59 // BLSVerifyPOP verifies a proof of possession (PoP) for the receiver public key. 60 // 61 // The function internally uses the same KMAC hasher used to generate the PoP. 62 // The hasher is guaranteed to be orthogonal to any hasher used to generate signature 63 // or SPoCK proofs on this package. 64 // Note that verifying a PoP against an idenity public key fails. 65 // 66 // The function returns: 67 // - (false, notBLSKeyError) if the input key is not of type BLS BLS12-381 68 // - (validity, nil) otherwise 69 func BLSVerifyPOP(pk PublicKey, s Signature) (bool, error) { 70 _, ok := pk.(*pubKeyBLSBLS12381) 71 if !ok { 72 return false, notBLSKeyError 73 } 74 // verify the signature against the public key 75 return pk.Verify(s, pk.Encode(), popKMAC) 76 } 77 78 // AggregateBLSSignatures aggregates multiple BLS signatures into one. 79 // 80 // Signatures could be generated from the same or distinct messages, they 81 // could also be the aggregation of other signatures. 82 // The order of the signatures in the slice does not matter since the aggregation 83 // is commutative. The slice should not be empty. 84 // No G1 membership check is performed on the input signatures. 85 // 86 // The function returns: 87 // - (nil, blsAggregateEmptyListError) if no signatures are provided (input slice is empty) 88 // - (nil, invalidSignatureError) if a deserialization of at least one signature fails (input is an invalid serialization of a 89 // compressed E1 element following [zcash] 90 // https://www.ietf.org/archive/id/draft-irtf-cfrg-pairing-friendly-curves-08.html#name-zcash-serialization-format-). 91 // G1 membership is not checked. 92 // - (nil, error) if an unexpected error occurs 93 // - (aggregated_signature, nil) otherwise 94 func AggregateBLSSignatures(sigs []Signature) (Signature, error) { 95 // set BLS context 96 blsInstance.reInit() 97 98 // check for empty list 99 if len(sigs) == 0 { 100 return nil, blsAggregateEmptyListError 101 } 102 103 // flatten the shares (required by the C layer) 104 flatSigs := make([]byte, 0, signatureLengthBLSBLS12381*len(sigs)) 105 for i, sig := range sigs { 106 if len(sig) != signatureLengthBLSBLS12381 { 107 return nil, fmt.Errorf("signature at index %d has an invalid length: %w", i, invalidSignatureError) 108 } 109 flatSigs = append(flatSigs, sig...) 110 } 111 aggregatedSig := make([]byte, signatureLengthBLSBLS12381) 112 113 // add the points in the C layer 114 result := C.ep_sum_vector_byte( 115 (*C.uchar)(&aggregatedSig[0]), 116 (*C.uchar)(&flatSigs[0]), 117 (C.int)(len(sigs))) 118 119 switch result { 120 case valid: 121 return aggregatedSig, nil 122 case invalid: 123 return nil, invalidSignatureError 124 default: 125 return nil, fmt.Errorf("aggregating signatures failed") 126 } 127 } 128 129 // AggregateBLSPrivateKeys aggregates multiple BLS private keys into one. 130 // 131 // The order of the keys in the slice does not matter since the aggregation 132 // is commutative. The slice should not be empty. 133 // No check is performed on the input private keys. 134 // Input or output private keys could be equal to the identity element (zero). Note that any 135 // signature generated by the identity key is invalid (to avoid equivocation issues). 136 // 137 // The function returns: 138 // - (nil, notBLSKeyError) if at least one key is not of type BLS BLS12-381 139 // - (nil, blsAggregateEmptyListError) if no keys are provided (input slice is empty) 140 // - (aggregated_key, nil) otherwise 141 func AggregateBLSPrivateKeys(keys []PrivateKey) (PrivateKey, error) { 142 // set BLS context 143 blsInstance.reInit() 144 145 // check for empty list 146 if len(keys) == 0 { 147 return nil, blsAggregateEmptyListError 148 } 149 150 scalars := make([]scalar, 0, len(keys)) 151 for i, sk := range keys { 152 skBls, ok := sk.(*prKeyBLSBLS12381) 153 if !ok { 154 return nil, fmt.Errorf("key at index %d is invalid: %w", i, notBLSKeyError) 155 } 156 scalars = append(scalars, skBls.scalar) 157 } 158 159 var sum scalar 160 C.bn_new_wrapper((*C.bn_st)(&sum)) 161 C.bn_sum_vector((*C.bn_st)(&sum), (*C.bn_st)(&scalars[0]), 162 (C.int)(len(scalars))) 163 return newPrKeyBLSBLS12381(&sum), nil 164 } 165 166 // AggregateBLSPublicKeys aggregate multiple BLS public keys into one. 167 // 168 // The order of the keys in the slice does not matter since the aggregation 169 // is commutative. The slice should not be empty. 170 // No check is performed on the input public keys. The input keys are guaranteed by 171 // the package constructors to be on the G2 subgroup. 172 // Input or output keys can be equal to the identity key. Note that any 173 // signature verified against the identity key is invalid (to avoid equivocation issues). 174 // 175 // The function returns: 176 // - (nil, notBLSKeyError) if at least one key is not of type BLS BLS12-381 177 // - (nil, blsAggregateEmptyListError) no keys are provided (input slice is empty) 178 // - (aggregated_key, nil) otherwise 179 func AggregateBLSPublicKeys(keys []PublicKey) (PublicKey, error) { 180 // set BLS context 181 blsInstance.reInit() 182 183 // check for empty list 184 if len(keys) == 0 { 185 return nil, blsAggregateEmptyListError 186 } 187 188 points := make([]pointG2, 0, len(keys)) 189 for i, pk := range keys { 190 pkBLS, ok := pk.(*pubKeyBLSBLS12381) 191 if !ok { 192 return nil, fmt.Errorf("key at index %d is invalid: %w", i, notBLSKeyError) 193 } 194 points = append(points, pkBLS.point) 195 } 196 197 var sum pointG2 198 C.ep2_sum_vector((*C.ep2_st)(&sum), (*C.ep2_st)(&points[0]), 199 (C.int)(len(points))) 200 201 sumKey := newPubKeyBLSBLS12381(&sum) 202 return sumKey, nil 203 } 204 205 // IdentityBLSPublicKey returns an identity public key which corresponds to the point 206 // at infinity in G2 (identity element of G2). 207 func IdentityBLSPublicKey() PublicKey { 208 // set BLS context 209 blsInstance.reInit() 210 211 identity := *newPubKeyBLSBLS12381(nil) 212 // set the point to infinity 213 C.ep2_set_infty((*C.ep2_st)(&identity.point)) 214 identity.isIdentity = true 215 return &identity 216 } 217 218 // RemoveBLSPublicKeys removes multiple BLS public keys from a given (aggregated) public key. 219 // 220 // The common use case assumes the aggregated public key was initially formed using 221 // the keys to be removed (directly or using other aggregated forms). However the function 222 // can still be called in different use cases. 223 // The order of the keys to be removed in the slice does not matter since the removal 224 // is commutative. The slice of keys to be removed can be empty. 225 // No check is performed on the input public keys. The input keys are guaranteed by the 226 // package constructors to be on the G2 subgroup. 227 // Input or output keys can be equal to the identity key. 228 // 229 // The function returns: 230 // - (nil, notBLSKeyError) if at least one input key is not of type BLS BLS12-381 231 // - (remaining_key, nil) otherwise 232 func RemoveBLSPublicKeys(aggKey PublicKey, keysToRemove []PublicKey) (PublicKey, error) { 233 // set BLS context 234 blsInstance.reInit() 235 236 aggPKBLS, ok := aggKey.(*pubKeyBLSBLS12381) 237 if !ok { 238 return nil, notBLSKeyError 239 } 240 241 pointsToSubtract := make([]pointG2, 0, len(keysToRemove)) 242 for i, pk := range keysToRemove { 243 pkBLS, ok := pk.(*pubKeyBLSBLS12381) 244 if !ok { 245 return nil, fmt.Errorf("key at index %d is invalid: %w", i, notBLSKeyError) 246 } 247 pointsToSubtract = append(pointsToSubtract, pkBLS.point) 248 } 249 250 // check for empty list to avoid a cgo edge case 251 if len(keysToRemove) == 0 { 252 return aggKey, nil 253 } 254 255 var resultPoint pointG2 256 C.ep2_subtract_vector((*C.ep2_st)(&resultPoint), (*C.ep2_st)(&aggPKBLS.point), 257 (*C.ep2_st)(&pointsToSubtract[0]), (C.int)(len(pointsToSubtract))) 258 259 resultKey := newPubKeyBLSBLS12381(&resultPoint) 260 return resultKey, nil 261 } 262 263 // VerifyBLSSignatureOneMessage is a multi-signature verification that verifies a 264 // BLS signature of a single message against multiple BLS public keys. 265 // 266 // The input signature could be generated by aggregating multiple signatures of the 267 // message under multiple private keys. The public keys corresponding to the signing 268 // private keys are passed as input to this function. 269 // The caller must make sure the input public keys's proofs of possession have been 270 // verified prior to calling this function (or each input key is sum of public keys of 271 // which proofs of possession have been verified). 272 // 273 // The input hasher is the same hasher used to generate all initial signatures. 274 // The order of the public keys in the slice does not matter. 275 // Membership check is performed on the input signature but is not performed on the input 276 // public keys (membership is guaranteed by using the package functions). 277 // If the input public keys add up to the identity public key, the signature is invalid 278 // to avoid signature equivocation issues. 279 // 280 // This is a special case function of VerifyBLSSignatureManyMessages, using a single 281 // message and hasher. 282 // 283 // The function returns: 284 // - (false, nilHasherError) if hasher is nil 285 // - (false, invalidHasherSizeError) if hasher's output size is not 128 bytes 286 // - (false, notBLSKeyError) if at least one key is not of type pubKeyBLSBLS12381 287 // - (nil, blsAggregateEmptyListError) if input key slice is empty 288 // - (false, error) if an unexpected error occurs 289 // - (validity, nil) otherwise 290 func VerifyBLSSignatureOneMessage( 291 pks []PublicKey, s Signature, message []byte, kmac hash.Hasher, 292 ) (bool, error) { 293 // public key list must be non empty, this is checked internally by AggregateBLSPublicKeys 294 aggPk, err := AggregateBLSPublicKeys(pks) 295 if err != nil { 296 return false, fmt.Errorf("verify signature one message failed: %w", err) 297 } 298 return aggPk.Verify(s, message, kmac) 299 } 300 301 // VerifyBLSSignatureManyMessages is a multi-signature verification that verifies a 302 // BLS signature under multiple messages and public keys. 303 // 304 // The input signature could be generated by aggregating multiple signatures of distinct 305 // messages under distinct private keys. The verification is performed against the message 306 // at index (i) and the public key at the same index (i) of the input messages and public keys. 307 // The hasher at index (i) is used to hash the message at index (i). 308 // 309 // Since the package only supports the Proof of Possession scheme, the function does not enforce 310 // input messages to be distinct. Thereore, the caller must make sure the input public keys's proofs 311 // of possession have been verified prior to calling this function (or each input key is sum of public 312 // keys of which proofs of possession have been verified). 313 // 314 // The verification is optimized to compute one pairing per distinct message, or one pairing 315 // per distinct key, whatever way offers less pairings calls. If all messages are the same, the 316 // function has the same behavior as VerifyBLSSignatureOneMessage. If there is one input message and 317 // input public key, the function has the same behavior as pk.Verify. 318 // Membership check is performed on the input signature. 319 // In order to avoid equivocation issues, any identity public key results in the overall 320 // signature being invalid. 321 // 322 // The function returns: 323 // - (false, nilHasherError) if a hasher is nil 324 // - (false, invalidHasherSizeError) if a hasher's output size is not 128 bytes 325 // - (false, notBLSKeyError) if at least one key is not a BLS BLS12-381 key 326 // - (false, invalidInputsError) if size of keys is not matching the size of messages and hashers 327 // - (false, blsAggregateEmptyListError) if input key slice `pks` is empty 328 // - (false, error) if an unexpected error occurs 329 // - (validity, nil) otherwise 330 func VerifyBLSSignatureManyMessages( 331 pks []PublicKey, s Signature, messages [][]byte, kmac []hash.Hasher, 332 ) (bool, error) { 333 // set BLS context 334 blsInstance.reInit() 335 336 // check signature length 337 if len(s) != signatureLengthBLSBLS12381 { 338 return false, nil 339 } 340 // check the list lengths 341 if len(pks) == 0 { 342 return false, fmt.Errorf("invalid list of public keys: %w", blsAggregateEmptyListError) 343 } 344 if len(pks) != len(messages) || len(kmac) != len(messages) { 345 return false, invalidInputsErrorf( 346 "input lists must be equal, messages are %d, keys are %d, hashers are %d", 347 len(messages), 348 len(pks), 349 len(kmac)) 350 } 351 352 // compute the hashes 353 hashes := make([][]byte, 0, len(messages)) 354 for i, k := range kmac { 355 if err := checkBLSHasher(k); err != nil { 356 return false, fmt.Errorf("hasher at index %d is invalid: %w ", i, err) 357 } 358 hashes = append(hashes, k.ComputeHash(messages[i])) 359 } 360 361 // two maps to count the type (keys or messages) with the least distinct elements. 362 // mapPerHash maps hashes to keys while mapPerPk maps keys to hashes. 363 // The comparison of the maps length minimizes the number of pairings to 364 // compute by aggregating either public keys or the message hashes in 365 // the verification equation. 366 mapPerHash := make(map[string][]pointG2) 367 mapPerPk := make(map[pointG2][][]byte) 368 // Note: mapPerPk is using a cgo structure as map keys which may lead to 2 equal public keys 369 // being considered distinct. This does not make the verification equation wrong but leads to 370 // computing extra pairings. This case is considered unlikely to happen since a caller is likely 371 // to use the same struct for a same public key. 372 // One way to fix this is to use the public key encoding as the map keys and store the "pointG2" 373 // structure with the map value, which adds more complexity and processing time. 374 375 // fill the 2 maps 376 for i, pk := range pks { 377 pkBLS, ok := pk.(*pubKeyBLSBLS12381) 378 if !ok { 379 return false, fmt.Errorf( 380 "public key at index %d is invalid: %w", 381 i, notBLSKeyError) 382 } 383 // check identity check 384 if pkBLS.isIdentity { 385 return false, nil 386 } 387 388 mapPerHash[string(hashes[i])] = append(mapPerHash[string(hashes[i])], pkBLS.point) 389 mapPerPk[pkBLS.point] = append(mapPerPk[pkBLS.point], hashes[i]) 390 } 391 392 var verif (C.int) 393 //compare the 2 maps for the shortest length 394 if len(mapPerHash) < len(mapPerPk) { 395 // aggregate keys per distinct hashes 396 // using the linearity of the pairing on the G2 variables. 397 flatDistinctHashes := make([]byte, 0) 398 lenHashes := make([]uint32, 0) 399 pkPerHash := make([]uint32, 0, len(mapPerHash)) 400 allPks := make([]pointG2, 0) 401 for hash, pksVal := range mapPerHash { 402 flatDistinctHashes = append(flatDistinctHashes, []byte(hash)...) 403 lenHashes = append(lenHashes, uint32(len([]byte(hash)))) 404 pkPerHash = append(pkPerHash, uint32(len(pksVal))) 405 allPks = append(allPks, pksVal...) 406 } 407 verif = C.bls_verifyPerDistinctMessage( 408 (*C.uchar)(&s[0]), 409 (C.int)(len(mapPerHash)), 410 (*C.uchar)(&flatDistinctHashes[0]), 411 (*C.uint32_t)(&lenHashes[0]), 412 (*C.uint32_t)(&pkPerHash[0]), 413 (*C.ep2_st)(&allPks[0]), 414 ) 415 416 } else { 417 // aggregate hashes per distinct key 418 // using the linearity of the pairing on the G1 variables. 419 distinctPks := make([]pointG2, 0, len(mapPerPk)) 420 hashPerPk := make([]uint32, 0, len(mapPerPk)) 421 flatHashes := make([]byte, 0) 422 lenHashes := make([]uint32, 0) 423 for pk, hashesVal := range mapPerPk { 424 distinctPks = append(distinctPks, pk) 425 hashPerPk = append(hashPerPk, uint32(len(hashesVal))) 426 for _, h := range hashesVal { 427 flatHashes = append(flatHashes, h...) 428 lenHashes = append(lenHashes, uint32(len(h))) 429 } 430 } 431 432 verif = C.bls_verifyPerDistinctKey( 433 (*C.uchar)(&s[0]), 434 (C.int)(len(mapPerPk)), 435 (*C.ep2_st)(&distinctPks[0]), 436 (*C.uint32_t)(&hashPerPk[0]), 437 (*C.uchar)(&flatHashes[0]), 438 (*C.uint32_t)(&lenHashes[0])) 439 } 440 441 switch verif { 442 case invalid: 443 return false, nil 444 case valid: 445 return true, nil 446 default: 447 return false, fmt.Errorf("signature verification failed") 448 } 449 } 450 451 // BatchVerifyBLSSignaturesOneMessage is a batch verification of multiple 452 // BLS signatures of a single message against multiple BLS public keys that 453 // is faster than verifying the signatures one by one. 454 // 455 // Each signature at index (i) of the input signature slice is verified against 456 // the public key of the same index (i) in the input key slice. 457 // The input hasher is the same used to generate all signatures. 458 // The returned boolean slice is a slice so that the value at index (i) is true 459 // if signature (i) verifies against public key (i), and false otherwise. 460 // 461 // The caller must make sure the input public keys's proofs of possession have been 462 // verified prior to calling this function (or each input key is sum of public 463 // keys of which proofs of possession have been verified). 464 // 465 // Membership checks are performed on the input signatures but are not performed 466 // on the input public keys (which are guaranteed by the package to be on the correct 467 // G2 subgroup). 468 // In order to avoid equivocation issues, any identity public key results in the corresponding 469 // signature being invalid. 470 // 471 // The function returns: 472 // - ([]false, nilHasherError) if a hasher is nil 473 // - ([]false, invalidHasherSizeError) if a hasher's output size is not 128 bytes 474 // - ([]false, notBLSKeyError) if at least one key is not of type BLS BLS12-381 475 // - ([]false, invalidInputsError) if size of keys is not matching the size of signatures 476 // - ([]false, blsAggregateEmptyListError) if input key slice is empty 477 // - ([]false, error) if an unexpected error occurs 478 // - ([]validity, nil) otherwise 479 func BatchVerifyBLSSignaturesOneMessage( 480 pks []PublicKey, sigs []Signature, message []byte, kmac hash.Hasher, 481 ) ([]bool, error) { 482 // set BLS context 483 blsInstance.reInit() 484 485 // empty list check 486 if len(pks) == 0 { 487 return []bool{}, fmt.Errorf("invalid list of public keys: %w", blsAggregateEmptyListError) 488 } 489 490 if len(pks) != len(sigs) { 491 return []bool{}, invalidInputsErrorf( 492 "keys length %d and signatures length %d are mismatching", 493 len(pks), 494 len(sigs)) 495 } 496 497 verifBool := make([]bool, len(sigs)) 498 if err := checkBLSHasher(kmac); err != nil { 499 return verifBool, err 500 } 501 502 // an invalid signature with an incorrect header but correct length 503 invalidSig := make([]byte, signatureLengthBLSBLS12381) 504 invalidSig[0] = invalidBLSSignatureHeader // incorrect header 505 506 // flatten the shares (required by the C layer) 507 flatSigs := make([]byte, 0, signatureLengthBLSBLS12381*len(sigs)) 508 pkPoints := make([]pointG2, 0, len(pks)) 509 510 for i, pk := range pks { 511 pkBLS, ok := pk.(*pubKeyBLSBLS12381) 512 if !ok { 513 return verifBool, fmt.Errorf("key at index %d is invalid: %w", i, notBLSKeyError) 514 } 515 pkPoints = append(pkPoints, pkBLS.point) 516 517 if len(sigs[i]) != signatureLengthBLSBLS12381 || pkBLS.isIdentity { 518 // force the signature to be invalid by replacing it with an invalid array 519 // that fails the deserialization in C.ep_read_bin_compact 520 flatSigs = append(flatSigs, invalidSig...) 521 } else { 522 flatSigs = append(flatSigs, sigs[i]...) 523 } 524 } 525 526 // hash the input to 128 bytes 527 h := kmac.ComputeHash(message) 528 verifInt := make([]byte, len(verifBool)) 529 530 C.bls_batchVerify( 531 (C.int)(len(verifInt)), 532 (*C.uchar)(&verifInt[0]), 533 (*C.ep2_st)(&pkPoints[0]), 534 (*C.uchar)(&flatSigs[0]), 535 (*C.uchar)(&h[0]), 536 (C.int)(len(h)), 537 ) 538 539 for i, v := range verifInt { 540 if (C.int)(v) != valid && (C.int)(v) != invalid { 541 return verifBool, fmt.Errorf("batch verification failed") 542 } 543 verifBool[i] = ((C.int)(v) == valid) 544 } 545 546 return verifBool, nil 547 } 548 549 // blsAggregateEmptyListError is returned when a list of BLS objects (e.g. signatures or keys) 550 // is empty or nil and thereby represents an invalid input. 551 var blsAggregateEmptyListError = errors.New("list cannot be empty") 552 553 // IsBLSAggregateEmptyListError checks if err is an `blsAggregateEmptyListError`. 554 // blsAggregateEmptyListError is returned when a BLS aggregation function is called with 555 // an empty list which is not allowed in some aggregation cases to avoid signature equivocation 556 // issues. 557 func IsBLSAggregateEmptyListError(err error) bool { 558 return errors.Is(err, blsAggregateEmptyListError) 559 } 560 561 // notBLSKeyError is returned when a private or public key 562 // used is not a BLS on BLS12 381 key. 563 var notBLSKeyError = errors.New("input key has to be a BLS on BLS12-381 key") 564 565 // IsNotBLSKeyError checks if err is an `notBLSKeyError`. 566 // notBLSKeyError is returned when a private or public key 567 // used is not a BLS on BLS12 381 key. 568 func IsNotBLSKeyError(err error) bool { 569 return errors.Is(err, notBLSKeyError) 570 } 571 572 // invalidSignatureError is returned when a signature input does not serialize to a 573 // valid element on E1 of the BLS12-381 curve (but without checking the element is on subgroup G1). 574 var invalidSignatureError = errors.New("input signature does not deserialize to an E1 element") 575 576 // IsInvalidSignatureError checks if err is an `invalidSignatureError` 577 // invalidSignatureError is returned when a signature input does not serialize to a 578 // valid element on E1 of the BLS12-381 curve (but without checking the element is on subgroup G1). 579 func IsInvalidSignatureError(err error) bool { 580 return errors.Is(err, invalidSignatureError) 581 }