github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/crypto.go (about) 1 package native 2 3 import ( 4 "crypto/elliptic" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "math/big" 9 10 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" 11 "github.com/decred/dcrd/dcrec/secp256k1/v4" 12 "github.com/nspcc-dev/neo-go/pkg/config" 13 "github.com/nspcc-dev/neo-go/pkg/core/dao" 14 "github.com/nspcc-dev/neo-go/pkg/core/interop" 15 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 16 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 17 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 18 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 19 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 20 "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" 21 "github.com/nspcc-dev/neo-go/pkg/util" 22 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 23 "github.com/twmb/murmur3" 24 "golang.org/x/crypto/sha3" 25 ) 26 27 // Crypto represents CryptoLib contract. 28 type Crypto struct { 29 interop.ContractMD 30 } 31 32 // HashFunc is a delegate representing a hasher function with 256 bytes output length. 33 type HashFunc func([]byte) util.Uint256 34 35 // NamedCurveHash identifies a pair of named elliptic curve and hash function. 36 type NamedCurveHash byte 37 38 // Various pairs of named elliptic curves and hash functions. 39 const ( 40 Secp256k1Sha256 NamedCurveHash = 22 41 Secp256r1Sha256 NamedCurveHash = 23 42 Secp256k1Keccak256 NamedCurveHash = 122 43 Secp256r1Keccak256 NamedCurveHash = 123 44 ) 45 46 const cryptoContractID = -3 47 48 func newCrypto() *Crypto { 49 c := &Crypto{ContractMD: *interop.NewContractMD(nativenames.CryptoLib, cryptoContractID)} 50 defer c.BuildHFSpecificMD(c.ActiveIn()) 51 52 desc := newDescriptor("sha256", smartcontract.ByteArrayType, 53 manifest.NewParameter("data", smartcontract.ByteArrayType)) 54 md := newMethodAndPrice(c.sha256, 1<<15, callflag.NoneFlag) 55 c.AddMethod(md, desc) 56 57 desc = newDescriptor("ripemd160", smartcontract.ByteArrayType, 58 manifest.NewParameter("data", smartcontract.ByteArrayType)) 59 md = newMethodAndPrice(c.ripemd160, 1<<15, callflag.NoneFlag) 60 c.AddMethod(md, desc) 61 62 desc = newDescriptor("murmur32", smartcontract.ByteArrayType, 63 manifest.NewParameter("data", smartcontract.ByteArrayType), 64 manifest.NewParameter("seed", smartcontract.IntegerType)) 65 md = newMethodAndPrice(c.murmur32, 1<<13, callflag.NoneFlag) 66 c.AddMethod(md, desc) 67 68 desc = newDescriptor("verifyWithECDsa", smartcontract.BoolType, 69 manifest.NewParameter("message", smartcontract.ByteArrayType), 70 manifest.NewParameter("pubkey", smartcontract.ByteArrayType), 71 manifest.NewParameter("signature", smartcontract.ByteArrayType), 72 manifest.NewParameter("curveHash", smartcontract.IntegerType)) 73 md = newMethodAndPrice(c.verifyWithECDsa, 1<<15, callflag.NoneFlag) 74 c.AddMethod(md, desc) 75 76 desc = newDescriptor("bls12381Serialize", smartcontract.ByteArrayType, 77 manifest.NewParameter("g", smartcontract.InteropInterfaceType)) 78 md = newMethodAndPrice(c.bls12381Serialize, 1<<19, callflag.NoneFlag) 79 c.AddMethod(md, desc) 80 81 desc = newDescriptor("bls12381Deserialize", smartcontract.InteropInterfaceType, 82 manifest.NewParameter("data", smartcontract.ByteArrayType)) 83 md = newMethodAndPrice(c.bls12381Deserialize, 1<<19, callflag.NoneFlag) 84 c.AddMethod(md, desc) 85 86 desc = newDescriptor("bls12381Equal", smartcontract.BoolType, 87 manifest.NewParameter("x", smartcontract.InteropInterfaceType), 88 manifest.NewParameter("y", smartcontract.InteropInterfaceType)) 89 md = newMethodAndPrice(c.bls12381Equal, 1<<5, callflag.NoneFlag) 90 c.AddMethod(md, desc) 91 92 desc = newDescriptor("bls12381Add", smartcontract.InteropInterfaceType, 93 manifest.NewParameter("x", smartcontract.InteropInterfaceType), 94 manifest.NewParameter("y", smartcontract.InteropInterfaceType)) 95 md = newMethodAndPrice(c.bls12381Add, 1<<19, callflag.NoneFlag) 96 c.AddMethod(md, desc) 97 98 desc = newDescriptor("bls12381Mul", smartcontract.InteropInterfaceType, 99 manifest.NewParameter("x", smartcontract.InteropInterfaceType), 100 manifest.NewParameter("mul", smartcontract.ByteArrayType), 101 manifest.NewParameter("neg", smartcontract.BoolType)) 102 md = newMethodAndPrice(c.bls12381Mul, 1<<21, callflag.NoneFlag) 103 c.AddMethod(md, desc) 104 105 desc = newDescriptor("bls12381Pairing", smartcontract.InteropInterfaceType, 106 manifest.NewParameter("g1", smartcontract.InteropInterfaceType), 107 manifest.NewParameter("g2", smartcontract.InteropInterfaceType)) 108 md = newMethodAndPrice(c.bls12381Pairing, 1<<23, callflag.NoneFlag) 109 c.AddMethod(md, desc) 110 111 desc = newDescriptor("keccak256", smartcontract.ByteArrayType, 112 manifest.NewParameter("data", smartcontract.ByteArrayType)) 113 md = newMethodAndPrice(c.keccak256, 1<<15, callflag.NoneFlag, config.HFCockatrice) 114 c.AddMethod(md, desc) 115 return c 116 } 117 118 func (c *Crypto) sha256(_ *interop.Context, args []stackitem.Item) stackitem.Item { 119 bs, err := args[0].TryBytes() 120 if err != nil { 121 panic(err) 122 } 123 return stackitem.NewByteArray(hash.Sha256(bs).BytesBE()) 124 } 125 126 func (c *Crypto) ripemd160(_ *interop.Context, args []stackitem.Item) stackitem.Item { 127 bs, err := args[0].TryBytes() 128 if err != nil { 129 panic(err) 130 } 131 return stackitem.NewByteArray(hash.RipeMD160(bs).BytesBE()) 132 } 133 134 func (c *Crypto) murmur32(_ *interop.Context, args []stackitem.Item) stackitem.Item { 135 bs, err := args[0].TryBytes() 136 if err != nil { 137 panic(err) 138 } 139 seed := toUint32(args[1]) 140 h := murmur3.SeedSum32(seed, bs) 141 result := make([]byte, 4) 142 binary.LittleEndian.PutUint32(result, h) 143 return stackitem.NewByteArray(result) 144 } 145 146 func (c *Crypto) verifyWithECDsa(_ *interop.Context, args []stackitem.Item) stackitem.Item { 147 msg, err := args[0].TryBytes() 148 if err != nil { 149 panic(fmt.Errorf("invalid message stackitem: %w", err)) 150 } 151 pubkey, err := args[1].TryBytes() 152 if err != nil { 153 panic(fmt.Errorf("invalid pubkey stackitem: %w", err)) 154 } 155 signature, err := args[2].TryBytes() 156 if err != nil { 157 panic(fmt.Errorf("invalid signature stackitem: %w", err)) 158 } 159 curve, hasher, err := curveHasherFromStackitem(args[3]) 160 if err != nil { 161 panic(fmt.Errorf("invalid curveHash stackitem: %w", err)) 162 } 163 hashToCheck := hasher(msg) 164 pkey, err := keys.NewPublicKeyFromBytes(pubkey, curve) 165 if err != nil { 166 panic(fmt.Errorf("failed to decode pubkey: %w", err)) 167 } 168 res := pkey.Verify(signature, hashToCheck.BytesBE()) 169 return stackitem.NewBool(res) 170 } 171 172 func curveHasherFromStackitem(si stackitem.Item) (elliptic.Curve, HashFunc, error) { 173 curve, err := si.TryInteger() 174 if err != nil { 175 return nil, nil, err 176 } 177 if !curve.IsInt64() { 178 return nil, nil, errors.New("not an int64") 179 } 180 c := curve.Int64() 181 switch c { 182 case int64(Secp256k1Sha256): 183 return secp256k1.S256(), hash.Sha256, nil 184 case int64(Secp256r1Sha256): 185 return elliptic.P256(), hash.Sha256, nil 186 case int64(Secp256k1Keccak256): 187 return secp256k1.S256(), Keccak256, nil 188 case int64(Secp256r1Keccak256): 189 return elliptic.P256(), Keccak256, nil 190 default: 191 return nil, nil, errors.New("unsupported curve/hash type") 192 } 193 } 194 195 func (c *Crypto) bls12381Serialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { 196 val, ok := args[0].(*stackitem.Interop).Value().(blsPoint) 197 if !ok { 198 panic(errors.New("not a bls12381 point")) 199 } 200 return stackitem.NewByteArray(val.Bytes()) 201 } 202 203 func (c *Crypto) bls12381Deserialize(_ *interop.Context, args []stackitem.Item) stackitem.Item { 204 buf, err := args[0].TryBytes() 205 if err != nil { 206 panic(fmt.Errorf("invalid serialized bls12381 point: %w", err)) 207 } 208 p := new(blsPoint) 209 err = p.FromBytes(buf) 210 if err != nil { 211 panic(err) 212 } 213 return stackitem.NewInterop(*p) 214 } 215 216 func (c *Crypto) bls12381Equal(_ *interop.Context, args []stackitem.Item) stackitem.Item { 217 a, okA := args[0].(*stackitem.Interop).Value().(blsPoint) 218 b, okB := args[1].(*stackitem.Interop).Value().(blsPoint) 219 if !(okA && okB) { 220 panic("some of the arguments are not a bls12381 point") 221 } 222 res, err := a.EqualsCheckType(b) 223 if err != nil { 224 panic(err) 225 } 226 return stackitem.NewBool(res) 227 } 228 229 func (c *Crypto) bls12381Add(_ *interop.Context, args []stackitem.Item) stackitem.Item { 230 a, okA := args[0].(*stackitem.Interop).Value().(blsPoint) 231 b, okB := args[1].(*stackitem.Interop).Value().(blsPoint) 232 if !(okA && okB) { 233 panic("some of the arguments are not a bls12381 point") 234 } 235 236 p, err := blsPointAdd(a, b) 237 if err != nil { 238 panic(err) 239 } 240 return stackitem.NewInterop(p) 241 } 242 243 func scalarFromBytes(bytes []byte, neg bool) (*fr.Element, error) { 244 alpha := new(fr.Element) 245 if len(bytes) != fr.Bytes { 246 return nil, fmt.Errorf("invalid multiplier: 32-bytes scalar is expected, got %d", len(bytes)) 247 } 248 // The input bytes are in the LE form, so we can't use fr.Element.SetBytesCanonical as far 249 // as it accepts BE. Confirmed by https://github.com/neo-project/neo/issues/2647#issuecomment-1129849870 250 // and by https://github.com/nspcc-dev/neo-go/pull/3043#issuecomment-1733424840. 251 v, err := fr.LittleEndian.Element((*[fr.Bytes]byte)(bytes)) 252 if err != nil { 253 return nil, fmt.Errorf("invalid multiplier: failed to decode scalar: %w", err) 254 } 255 *alpha = v 256 if neg { 257 alpha.Neg(alpha) 258 } 259 return alpha, nil 260 } 261 262 func (c *Crypto) bls12381Mul(_ *interop.Context, args []stackitem.Item) stackitem.Item { 263 a, okA := args[0].(*stackitem.Interop).Value().(blsPoint) 264 if !okA { 265 panic("multiplier is not a bls12381 point") 266 } 267 mulBytes, err := args[1].TryBytes() 268 if err != nil { 269 panic(fmt.Errorf("invalid multiplier: %w", err)) 270 } 271 neg, err := args[2].TryBool() 272 if err != nil { 273 panic(fmt.Errorf("invalid negative argument: %w", err)) 274 } 275 alpha, err := scalarFromBytes(mulBytes, neg) 276 if err != nil { 277 panic(err) 278 } 279 alphaBi := new(big.Int) 280 alpha.BigInt(alphaBi) 281 282 p, err := blsPointMul(a, alphaBi) 283 if err != nil { 284 panic(err) 285 } 286 return stackitem.NewInterop(p) 287 } 288 289 func (c *Crypto) bls12381Pairing(_ *interop.Context, args []stackitem.Item) stackitem.Item { 290 a, okA := args[0].(*stackitem.Interop).Value().(blsPoint) 291 b, okB := args[1].(*stackitem.Interop).Value().(blsPoint) 292 if !(okA && okB) { 293 panic("some of the arguments are not a bls12381 point") 294 } 295 296 p, err := blsPointPairing(a, b) 297 if err != nil { 298 panic(err) 299 } 300 return stackitem.NewInterop(p) 301 } 302 303 func (c *Crypto) keccak256(_ *interop.Context, args []stackitem.Item) stackitem.Item { 304 bs, err := args[0].TryBytes() 305 if err != nil { 306 panic(err) 307 } 308 return stackitem.NewByteArray(Keccak256(bs).BytesBE()) 309 } 310 311 // Metadata implements the Contract interface. 312 func (c *Crypto) Metadata() *interop.ContractMD { 313 return &c.ContractMD 314 } 315 316 // Initialize implements the Contract interface. 317 func (c *Crypto) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { 318 return nil 319 } 320 321 // InitializeCache implements the Contract interface. 322 func (c *Crypto) InitializeCache(blockHeight uint32, d *dao.Simple) error { 323 return nil 324 } 325 326 // OnPersist implements the Contract interface. 327 func (c *Crypto) OnPersist(ic *interop.Context) error { 328 return nil 329 } 330 331 // PostPersist implements the Contract interface. 332 func (c *Crypto) PostPersist(ic *interop.Context) error { 333 return nil 334 } 335 336 // ActiveIn implements the Contract interface. 337 func (c *Crypto) ActiveIn() *config.Hardfork { 338 return nil 339 } 340 341 // Keccak256 hashes the incoming byte slice using the 342 // keccak256 algorithm. 343 func Keccak256(data []byte) util.Uint256 { 344 var hash util.Uint256 345 hasher := sha3.NewLegacyKeccak256() 346 _, _ = hasher.Write(data) 347 348 hasher.Sum(hash[:0]) 349 return hash 350 }