github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/crypto/crypto.go (about)

     1  package crypto
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  
     7  	"github.com/onflow/cadence/runtime"
     8  	"github.com/onflow/crypto"
     9  	"github.com/onflow/crypto/hash"
    10  
    11  	"github.com/onflow/flow-go/fvm/errors"
    12  	"github.com/onflow/flow-go/model/flow"
    13  	msig "github.com/onflow/flow-go/module/signature"
    14  )
    15  
    16  func HashWithTag(hashAlgo hash.HashingAlgorithm, tag string, data []byte) ([]byte, error) {
    17  	var hasher hash.Hasher
    18  
    19  	switch hashAlgo {
    20  	case hash.SHA2_256, hash.SHA3_256, hash.SHA2_384, hash.SHA3_384, hash.Keccak_256:
    21  		var err error
    22  		if hasher, err = NewPrefixedHashing(hashAlgo, tag); err != nil {
    23  			return nil, errors.NewValueErrorf(err.Error(), "verification failed")
    24  		}
    25  	case hash.KMAC128:
    26  		hasher = msig.NewBLSHasher(tag)
    27  	default:
    28  		err := errors.NewValueErrorf(fmt.Sprint(hashAlgo), "hashing algorithm type not found")
    29  		return nil, fmt.Errorf("hashing failed: %w", err)
    30  	}
    31  
    32  	return hasher.ComputeHash(data), nil
    33  }
    34  
    35  // RuntimeToCryptoSigningAlgorithm converts a runtime signature algorithm to a crypto signature algorithm.
    36  func RuntimeToCryptoSigningAlgorithm(s runtime.SignatureAlgorithm) crypto.SigningAlgorithm {
    37  	switch s {
    38  	case runtime.SignatureAlgorithmECDSA_P256:
    39  		return crypto.ECDSAP256
    40  	case runtime.SignatureAlgorithmECDSA_secp256k1:
    41  		return crypto.ECDSASecp256k1
    42  	case runtime.SignatureAlgorithmBLS_BLS12_381:
    43  		return crypto.BLSBLS12381
    44  	default:
    45  		return crypto.UnknownSigningAlgorithm
    46  	}
    47  }
    48  
    49  // CryptoToRuntimeSigningAlgorithm converts a crypto signature algorithm to a runtime signature algorithm.
    50  func CryptoToRuntimeSigningAlgorithm(s crypto.SigningAlgorithm) runtime.SignatureAlgorithm {
    51  	switch s {
    52  	case crypto.ECDSAP256:
    53  		return runtime.SignatureAlgorithmECDSA_P256
    54  	case crypto.ECDSASecp256k1:
    55  		return runtime.SignatureAlgorithmECDSA_secp256k1
    56  	case crypto.BLSBLS12381:
    57  		return runtime.SignatureAlgorithmBLS_BLS12_381
    58  	default:
    59  		return runtime.SignatureAlgorithmUnknown
    60  	}
    61  }
    62  
    63  // RuntimeToCryptoHashingAlgorithm converts a runtime hash algorithm to a crypto hashing algorithm.
    64  func RuntimeToCryptoHashingAlgorithm(s runtime.HashAlgorithm) hash.HashingAlgorithm {
    65  	switch s {
    66  	case runtime.HashAlgorithmSHA2_256:
    67  		return hash.SHA2_256
    68  	case runtime.HashAlgorithmSHA3_256:
    69  		return hash.SHA3_256
    70  	case runtime.HashAlgorithmSHA2_384:
    71  		return hash.SHA2_384
    72  	case runtime.HashAlgorithmSHA3_384:
    73  		return hash.SHA3_384
    74  	case runtime.HashAlgorithmKMAC128_BLS_BLS12_381:
    75  		return hash.KMAC128
    76  	case runtime.HashAlgorithmKECCAK_256:
    77  		return hash.Keccak_256
    78  	default:
    79  		return hash.UnknownHashingAlgorithm
    80  	}
    81  }
    82  
    83  // CryptoToRuntimeHashingAlgorithm converts a crypto hashing algorithm to a runtime hash algorithm.
    84  func CryptoToRuntimeHashingAlgorithm(h hash.HashingAlgorithm) runtime.HashAlgorithm {
    85  	switch h {
    86  	case hash.SHA2_256:
    87  		return runtime.HashAlgorithmSHA2_256
    88  	case hash.SHA3_256:
    89  		return runtime.HashAlgorithmSHA3_256
    90  	case hash.SHA2_384:
    91  		return runtime.HashAlgorithmSHA2_384
    92  	case hash.SHA3_384:
    93  		return runtime.HashAlgorithmSHA3_384
    94  	case hash.KMAC128:
    95  		return runtime.HashAlgorithmKMAC128_BLS_BLS12_381
    96  	case hash.Keccak_256:
    97  		return runtime.HashAlgorithmKECCAK_256
    98  	default:
    99  		return runtime.HashAlgorithmUnknown
   100  	}
   101  }
   102  
   103  // ValidatePublicKey returns :
   104  // - nil if key is valid and no exception occurred.
   105  // - crypto.invalidInputsError if key is invalid and no exception occurred.
   106  // - panics if an exception occurred.
   107  func ValidatePublicKey(signAlgo runtime.SignatureAlgorithm, pk []byte) error {
   108  	sigAlgo := RuntimeToCryptoSigningAlgorithm(signAlgo)
   109  
   110  	_, err := crypto.DecodePublicKey(sigAlgo, pk)
   111  
   112  	if err != nil {
   113  		if crypto.IsInvalidInputsError(err) {
   114  			return err
   115  		}
   116  		panic(fmt.Errorf("validate public key failed with unexpected error %w", err))
   117  	}
   118  	return nil
   119  }
   120  
   121  // VerifySignatureFromRuntime performs signature verification using raw values provided
   122  // by the Cadence runtime.
   123  //
   124  // The signature/hash function combinations accepted are:
   125  //   - ECDSA (on both curves P-256 and secp256k1) with any of SHA2-256/SHA3-256/Keccak256.
   126  //   - BLS (on BLS12-381 curve) with the specific KMAC128 for BLS.
   127  //
   128  // The tag is applied to the message depending on the hash function used.
   129  //
   130  // The function errors:
   131  //   - NewValueErrorf for any user error
   132  //   - panic for any other unexpected error
   133  func VerifySignatureFromRuntime(
   134  	signature []byte,
   135  	tag string,
   136  	message []byte,
   137  	rawPublicKey []byte,
   138  	signatureAlgorithm runtime.SignatureAlgorithm,
   139  	hashAlgorithm runtime.HashAlgorithm,
   140  ) (bool, error) {
   141  
   142  	sigAlgo := RuntimeToCryptoSigningAlgorithm(signatureAlgorithm)
   143  	if sigAlgo == crypto.UnknownSigningAlgorithm {
   144  		return false, errors.NewValueErrorf(fmt.Sprintf("%d", signatureAlgorithm), "signature algorithm type not found")
   145  	}
   146  
   147  	hashAlgo := RuntimeToCryptoHashingAlgorithm(hashAlgorithm)
   148  	if hashAlgo == hash.UnknownHashingAlgorithm {
   149  		return false, errors.NewValueErrorf(fmt.Sprintf("%d", hashAlgorithm), "hashing algorithm type not found")
   150  	}
   151  
   152  	// check ECDSA compatibilites
   153  	if sigAlgo == crypto.ECDSAP256 || sigAlgo == crypto.ECDSASecp256k1 {
   154  		// hashing compatibility
   155  		if hashAlgo != hash.SHA2_256 && hashAlgo != hash.SHA3_256 && hashAlgo != hash.Keccak_256 {
   156  			return false, errors.NewValueErrorf(sigAlgo.String(), "cannot use hashing algorithm type %s with signature signature algorithm type %s",
   157  				hashAlgo, sigAlgo)
   158  		}
   159  		// tag constraints are checked when initializing a prefix-hasher
   160  
   161  		// check BLS compatibilites
   162  	} else if sigAlgo == crypto.BLSBLS12381 && hashAlgo != hash.KMAC128 {
   163  		// hashing compatibility
   164  		return false, errors.NewValueErrorf(sigAlgo.String(), "cannot use hashing algorithm type %s with signature signature algorithm type %s",
   165  			hashAlgo, sigAlgo)
   166  		// there are no tag constraints
   167  	}
   168  
   169  	// decode the public key
   170  	publicKey, err := crypto.DecodePublicKey(sigAlgo, rawPublicKey)
   171  	if err != nil {
   172  		return false, errors.NewValueErrorf(hex.EncodeToString(rawPublicKey), "cannot decode public key: %w", err)
   173  	}
   174  
   175  	// create a hasher
   176  	var hasher hash.Hasher
   177  	switch hashAlgo {
   178  	case hash.SHA2_256, hash.SHA3_256, hash.Keccak_256:
   179  		var err error
   180  		if hasher, err = NewPrefixedHashing(hashAlgo, tag); err != nil {
   181  			return false, errors.NewValueErrorf(err.Error(), "runtime verification failed")
   182  		}
   183  	case hash.KMAC128:
   184  		hasher = msig.NewBLSHasher(tag)
   185  	default:
   186  		return false, errors.NewValueErrorf(fmt.Sprint(hashAlgo), "hashing algorithm type not found")
   187  	}
   188  
   189  	valid, err := publicKey.Verify(signature, message, hasher)
   190  	if err != nil {
   191  		// All inputs are guaranteed to be valid at this stage.
   192  		// The check for crypto.InvalidInputs is only a sanity check
   193  		if crypto.IsInvalidInputsError(err) {
   194  			return false, err
   195  		}
   196  		panic(fmt.Errorf("verify runtime signature failed with unexpected error %w", err))
   197  	}
   198  
   199  	return valid, nil
   200  }
   201  
   202  // VerifySignatureFromRuntime performs signature verification using raw values provided
   203  // by the Cadence runtime.
   204  //
   205  // The signature/hash function combinations accepted are:
   206  //   - ECDSA (on both curves P-256 and secp256k1) with any of SHA2-256/SHA3-256.
   207  //
   208  // The tag is applied to the message as a constant length prefix.
   209  //
   210  // The function errors:
   211  //   - NewValueErrorf for any user error
   212  //   - panic for any other unexpected error
   213  func VerifySignatureFromTransaction(
   214  	signature []byte,
   215  	message []byte,
   216  	pk crypto.PublicKey,
   217  	hashAlgo hash.HashingAlgorithm,
   218  ) (bool, error) {
   219  
   220  	// check ECDSA compatibilites
   221  	if pk.Algorithm() != crypto.ECDSAP256 && pk.Algorithm() != crypto.ECDSASecp256k1 {
   222  		// TODO: check if we should panic
   223  		// This case only happens in production if there is a bug
   224  		return false, errors.NewUnknownFailure(fmt.Errorf(
   225  			pk.Algorithm().String(), "is not supported in transactions"))
   226  	}
   227  	// hashing compatibility
   228  	if hashAlgo != hash.SHA2_256 && hashAlgo != hash.SHA3_256 {
   229  		// TODO: check if we should panic
   230  		// This case only happens in production if there is a bug
   231  		return false, errors.NewUnknownFailure(fmt.Errorf(
   232  			hashAlgo.String(), "is not supported in transactions"))
   233  	}
   234  
   235  	hasher, err := NewPrefixedHashing(hashAlgo, flow.TransactionTagString)
   236  	if err != nil {
   237  		return false, errors.NewValueErrorf(err.Error(), "transaction verification failed")
   238  	}
   239  
   240  	valid, err := pk.Verify(signature, message, hasher)
   241  	if err != nil {
   242  		// All inputs are guaranteed to be valid at this stage.
   243  		// The check for crypto.InvalidInputs is only a sanity check
   244  		if crypto.IsInvalidInputsError(err) {
   245  			return false, err
   246  		}
   247  		// unexpected error in normal operations
   248  		panic(fmt.Errorf("verify transaction signature failed with unexpected error %w", err))
   249  	}
   250  
   251  	return valid, nil
   252  }
   253  
   254  // VerifyPOP verifies a proof of possession (PoP) for the receiver public key; currently only works for BLS
   255  func VerifyPOP(pk *runtime.PublicKey, s crypto.Signature) (bool, error) {
   256  
   257  	key, err := crypto.DecodePublicKey(crypto.BLSBLS12381, pk.PublicKey)
   258  	if err != nil {
   259  		// at this stage, the runtime public key is valid and there are no possible user value errors
   260  		panic(fmt.Errorf("verify PoP failed: runtime BLS public key should be valid %x", pk.PublicKey))
   261  	}
   262  
   263  	valid, err := crypto.BLSVerifyPOP(key, s)
   264  	if err != nil {
   265  		// no user errors possible at this stage
   266  		panic(fmt.Errorf("verify PoP failed with unexpected error %w", err))
   267  	}
   268  	return valid, nil
   269  }
   270  
   271  // AggregateSignatures aggregate multiple signatures into one; currently only works for BLS
   272  func AggregateSignatures(sigs [][]byte) (crypto.Signature, error) {
   273  	s := make([]crypto.Signature, 0, len(sigs))
   274  	for _, sig := range sigs {
   275  		s = append(s, sig)
   276  	}
   277  
   278  	aggregatedSignature, err := crypto.AggregateBLSSignatures(s)
   279  	if err != nil {
   280  		// check for a user error
   281  		if crypto.IsBLSAggregateEmptyListError(err) || crypto.IsInvalidSignatureError(err) {
   282  			return nil, err
   283  		}
   284  		panic(fmt.Errorf("aggregate BLS signatures failed with unexpected error %w", err))
   285  	}
   286  	return aggregatedSignature, nil
   287  }
   288  
   289  // AggregatePublicKeys aggregate multiple public keys into one; currently only works for BLS
   290  func AggregatePublicKeys(keys []*runtime.PublicKey) (*runtime.PublicKey, error) {
   291  	pks := make([]crypto.PublicKey, 0, len(keys))
   292  	for _, key := range keys {
   293  		// TODO: avoid validating the public keys again since Cadence makes sure runtime keys have been validated.
   294  		// This requires exporting an unsafe function in the crypto package.
   295  		pk, err := crypto.DecodePublicKey(crypto.BLSBLS12381, key.PublicKey)
   296  		if err != nil {
   297  			// at this stage, the runtime public key is valid and there are no possible user value errors
   298  			panic(fmt.Errorf("aggregate BLS public keys failed: runtime public key should be valid %x", key.PublicKey))
   299  		}
   300  		pks = append(pks, pk)
   301  	}
   302  
   303  	pk, err := crypto.AggregateBLSPublicKeys(pks)
   304  	if err != nil {
   305  		// check for a user error
   306  		if crypto.IsBLSAggregateEmptyListError(err) || crypto.IsNotBLSKeyError(err) {
   307  			return nil, err
   308  		}
   309  		panic(fmt.Errorf("aggregate BLS public keys failed with unexpected error %w", err))
   310  	}
   311  
   312  	return &runtime.PublicKey{
   313  		PublicKey: pk.Encode(),
   314  		SignAlgo:  CryptoToRuntimeSigningAlgorithm(crypto.BLSBLS12381),
   315  	}, nil
   316  }