github.com/prysmaticlabs/prysm@v1.4.4/shared/bls/blst/signature.go (about) 1 // +build linux,amd64 linux,arm64 darwin,amd64 windows,amd64 2 // +build !blst_disabled 3 4 package blst 5 6 import ( 7 "bytes" 8 "fmt" 9 "sync" 10 11 "github.com/pkg/errors" 12 "github.com/prysmaticlabs/prysm/shared/bls/common" 13 "github.com/prysmaticlabs/prysm/shared/featureconfig" 14 "github.com/prysmaticlabs/prysm/shared/params" 15 "github.com/prysmaticlabs/prysm/shared/rand" 16 blst "github.com/supranational/blst/bindings/go" 17 ) 18 19 var dst = []byte("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_") 20 21 const scalarBytes = 32 22 const randBitsEntropy = 64 23 24 // Signature used in the BLS signature scheme. 25 type Signature struct { 26 s *blstSignature 27 } 28 29 // SignatureFromBytes creates a BLS signature from a LittleEndian byte slice. 30 func SignatureFromBytes(sig []byte) (common.Signature, error) { 31 if featureconfig.Get().SkipBLSVerify { 32 return &Signature{}, nil 33 } 34 if len(sig) != params.BeaconConfig().BLSSignatureLength { 35 return nil, fmt.Errorf("signature must be %d bytes", params.BeaconConfig().BLSSignatureLength) 36 } 37 signature := new(blstSignature).Uncompress(sig) 38 if signature == nil { 39 return nil, errors.New("could not unmarshal bytes into signature") 40 } 41 // Group check signature. Do not check for infinity since an aggregated signature 42 // could be infinite. 43 if !signature.SigValidate(false) { 44 return nil, errors.New("signature not in group") 45 } 46 return &Signature{s: signature}, nil 47 } 48 49 // Verify a bls signature given a public key, a message. 50 // 51 // In IETF draft BLS specification: 52 // Verify(PK, message, signature) -> VALID or INVALID: a verification 53 // algorithm that outputs VALID if signature is a valid signature of 54 // message under public key PK, and INVALID otherwise. 55 // 56 // In the Ethereum proof of stake specification: 57 // def Verify(PK: BLSPubkey, message: Bytes, signature: BLSSignature) -> bool 58 func (s *Signature) Verify(pubKey common.PublicKey, msg []byte) bool { 59 if featureconfig.Get().SkipBLSVerify { 60 return true 61 } 62 // Signature and PKs are assumed to have been validated upon decompression! 63 return s.s.Verify(false, pubKey.(*PublicKey).p, false, msg, dst) 64 } 65 66 // AggregateVerify verifies each public key against its respective message. This is vulnerable to 67 // rogue public-key attack. Each user must provide a proof-of-knowledge of the public key. 68 // 69 // Note: The msgs must be distinct. For maximum performance, this method does not ensure distinct 70 // messages. 71 // 72 // In IETF draft BLS specification: 73 // AggregateVerify((PK_1, message_1), ..., (PK_n, message_n), 74 // signature) -> VALID or INVALID: an aggregate verification 75 // algorithm that outputs VALID if signature is a valid aggregated 76 // signature for a collection of public keys and messages, and 77 // outputs INVALID otherwise. 78 // 79 // In the Ethereum proof of stake specification: 80 // def AggregateVerify(pairs: Sequence[PK: BLSPubkey, message: Bytes], signature: BLSSignature) -> bool 81 // 82 // Deprecated: Use FastAggregateVerify or use this method in spectests only. 83 func (s *Signature) AggregateVerify(pubKeys []common.PublicKey, msgs [][32]byte) bool { 84 if featureconfig.Get().SkipBLSVerify { 85 return true 86 } 87 size := len(pubKeys) 88 if size == 0 { 89 return false 90 } 91 if size != len(msgs) { 92 return false 93 } 94 msgSlices := make([][]byte, len(msgs)) 95 rawKeys := make([]*blstPublicKey, len(msgs)) 96 for i := 0; i < size; i++ { 97 msgSlices[i] = msgs[i][:] 98 rawKeys[i] = pubKeys[i].(*PublicKey).p 99 } 100 // Signature and PKs are assumed to have been validated upon decompression! 101 return s.s.AggregateVerify(false, rawKeys, false, msgSlices, dst) 102 } 103 104 // FastAggregateVerify verifies all the provided public keys with their aggregated signature. 105 // 106 // In IETF draft BLS specification: 107 // FastAggregateVerify(PK_1, ..., PK_n, message, signature) -> VALID 108 // or INVALID: a verification algorithm for the aggregate of multiple 109 // signatures on the same message. This function is faster than 110 // AggregateVerify. 111 // 112 // In the Ethereum proof of stake specification: 113 // def FastAggregateVerify(PKs: Sequence[BLSPubkey], message: Bytes, signature: BLSSignature) -> bool 114 func (s *Signature) FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool { 115 if featureconfig.Get().SkipBLSVerify { 116 return true 117 } 118 if len(pubKeys) == 0 { 119 return false 120 } 121 rawKeys := make([]*blstPublicKey, len(pubKeys)) 122 for i := 0; i < len(pubKeys); i++ { 123 rawKeys[i] = pubKeys[i].(*PublicKey).p 124 } 125 126 return s.s.FastAggregateVerify(true, rawKeys, msg[:], dst) 127 } 128 129 // Eth2FastAggregateVerify implements a wrapper on top of bls's FastAggregateVerify. It accepts G2_POINT_AT_INFINITY signature 130 // when pubkeys empty. 131 // 132 // Spec code: 133 // def eth2_fast_aggregate_verify(pubkeys: Sequence[BLSPubkey], message: Bytes32, signature: BLSSignature) -> bool: 134 // """ 135 // Wrapper to ``bls.FastAggregateVerify`` accepting the ``G2_POINT_AT_INFINITY`` signature when ``pubkeys`` is empty. 136 // """ 137 // if len(pubkeys) == 0 and signature == G2_POINT_AT_INFINITY: 138 // return True 139 // return bls.FastAggregateVerify(pubkeys, message, signature) 140 func (s *Signature) Eth2FastAggregateVerify(pubKeys []common.PublicKey, msg [32]byte) bool { 141 if featureconfig.Get().SkipBLSVerify { 142 return true 143 } 144 g2PointAtInfinity := append([]byte{0xC0}, make([]byte, 95)...) 145 if len(pubKeys) == 0 && bytes.Equal(s.Marshal(), g2PointAtInfinity) { 146 return true 147 } 148 rawKeys := make([]*blstPublicKey, len(pubKeys)) 149 for i := 0; i < len(pubKeys); i++ { 150 rawKeys[i] = pubKeys[i].(*PublicKey).p 151 } 152 153 return s.s.FastAggregateVerify(true, rawKeys, msg[:], dst) 154 } 155 156 // NewAggregateSignature creates a blank aggregate signature. 157 func NewAggregateSignature() common.Signature { 158 sig := blst.HashToG2([]byte{'m', 'o', 'c', 'k'}, dst).ToAffine() 159 return &Signature{s: sig} 160 } 161 162 // AggregateSignatures converts a list of signatures into a single, aggregated sig. 163 func AggregateSignatures(sigs []common.Signature) common.Signature { 164 if len(sigs) == 0 { 165 return nil 166 } 167 if featureconfig.Get().SkipBLSVerify { 168 return sigs[0] 169 } 170 171 rawSigs := make([]*blstSignature, len(sigs)) 172 for i := 0; i < len(sigs); i++ { 173 rawSigs[i] = sigs[i].(*Signature).s 174 } 175 176 // Signature and PKs are assumed to have been validated upon decompression! 177 signature := new(blstAggregateSignature) 178 signature.Aggregate(rawSigs, false) 179 return &Signature{s: signature.ToAffine()} 180 } 181 182 // Aggregate is an alias for AggregateSignatures, defined to conform to BLS specification. 183 // 184 // In IETF draft BLS specification: 185 // Aggregate(signature_1, ..., signature_n) -> signature: an 186 // aggregation algorithm that compresses a collection of signatures 187 // into a single signature. 188 // 189 // In the Ethereum proof of stake specification: 190 // def Aggregate(signatures: Sequence[BLSSignature]) -> BLSSignature 191 // 192 // Deprecated: Use AggregateSignatures. 193 func Aggregate(sigs []common.Signature) common.Signature { 194 return AggregateSignatures(sigs) 195 } 196 197 // VerifyMultipleSignatures verifies a non-singular set of signatures and its respective pubkeys and messages. 198 // This method provides a safe way to verify multiple signatures at once. We pick a number randomly from 1 to max 199 // uint64 and then multiply the signature by it. We continue doing this for all signatures and its respective pubkeys. 200 // S* = S_1 * r_1 + S_2 * r_2 + ... + S_n * r_n 201 // P'_{i,j} = P_{i,j} * r_i 202 // e(S*, G) = \prod_{i=1}^n \prod_{j=1}^{m_i} e(P'_{i,j}, M_{i,j}) 203 // Using this we can verify multiple signatures safely. 204 func VerifyMultipleSignatures(sigs [][]byte, msgs [][32]byte, pubKeys []common.PublicKey) (bool, error) { 205 if featureconfig.Get().SkipBLSVerify { 206 return true, nil 207 } 208 if len(sigs) == 0 || len(pubKeys) == 0 { 209 return false, nil 210 } 211 rawSigs := new(blstSignature).BatchUncompress(sigs) 212 213 length := len(sigs) 214 if length != len(pubKeys) || length != len(msgs) { 215 return false, errors.Errorf("provided signatures, pubkeys and messages have differing lengths. S: %d, P: %d,M %d", 216 length, len(pubKeys), len(msgs)) 217 } 218 mulP1Aff := make([]*blstPublicKey, length) 219 rawMsgs := make([]blst.Message, length) 220 221 for i := 0; i < length; i++ { 222 mulP1Aff[i] = pubKeys[i].(*PublicKey).p 223 rawMsgs[i] = msgs[i][:] 224 } 225 // Secure source of RNG 226 randGen := rand.NewGenerator() 227 randLock := new(sync.Mutex) 228 229 randFunc := func(scalar *blst.Scalar) { 230 var rbytes [scalarBytes]byte 231 randLock.Lock() 232 // Ignore error as the error will always be nil in `read` in math/rand. 233 randGen.Read(rbytes[:]) 234 randLock.Unlock() 235 // Protect against the generator returning 0. Since the scalar value is 236 // derived from a big endian byte slice, we take the last byte. 237 rbytes[len(rbytes)-1] |= 0x01 238 scalar.FromBEndian(rbytes[:]) 239 } 240 dummySig := new(blstSignature) 241 242 // Validate signatures since we uncompress them here. Public keys should already be validated. 243 return dummySig.MultipleAggregateVerify(rawSigs, true, mulP1Aff, false, rawMsgs, dst, randFunc, randBitsEntropy), nil 244 } 245 246 // Marshal a signature into a LittleEndian byte slice. 247 func (s *Signature) Marshal() []byte { 248 if featureconfig.Get().SkipBLSVerify { 249 return make([]byte, params.BeaconConfig().BLSSignatureLength) 250 } 251 252 return s.s.Compress() 253 } 254 255 // Copy returns a full deep copy of a signature. 256 func (s *Signature) Copy() common.Signature { 257 sign := *s.s 258 return &Signature{s: &sign} 259 } 260 261 // VerifyCompressed verifies that the compressed signature and pubkey 262 // are valid from the message provided. 263 func VerifyCompressed(signature, pub, msg []byte) bool { 264 // Validate signature and PKs since we will uncompress them here 265 return new(blstSignature).VerifyCompressed(signature, true, pub, true, msg, dst) 266 }