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  }