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