github.com/koko1123/flow-go-1@v0.29.6/network/p2p/keyutils/keyTranslator.go (about)

     1  package keyutils
     2  
     3  import (
     4  	goecdsa "crypto/ecdsa"
     5  	"crypto/elliptic"
     6  	"crypto/x509"
     7  	"fmt"
     8  	"math/big"
     9  
    10  	lcrypto "github.com/libp2p/go-libp2p/core/crypto"
    11  	lcrypto_pb "github.com/libp2p/go-libp2p/core/crypto/pb"
    12  	"github.com/libp2p/go-libp2p/core/peer"
    13  
    14  	"github.com/onflow/flow-go/crypto"
    15  	fcrypto "github.com/onflow/flow-go/crypto"
    16  )
    17  
    18  // This module is meant to help libp2p <-> flow public key conversions
    19  // Flow's Network Public Keys and LibP2P's public keys are a marshalling standard away from each other and inter-convertible.
    20  // Libp2p supports keys as ECDSA public Keys on either the NIST P-256 curve or the "Bitcoin" secp256k1 curve, see https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids
    21  // libp2p represents the P-256 keys as ASN1-DER and the secp256k1 keys as X9.62 encodings in compressed format
    22  //
    23  // While Flow's key types supports both secp256k1 and P-256 keys (under crypto/ecdsa), note that Flow's networking keys are always P-256 keys.
    24  // Flow represents both the P-256 keys and the secp256k1 keys in uncompressed representation, but their byte serializations (Encode) do not include the X9.62 compression bit
    25  // Flow also makes a X9.62 compressed format (with compression bit) accessible (EncodeCompressed)
    26  
    27  // assigns a big.Int input to a Go ecdsa private key
    28  func setPrKey(c elliptic.Curve, k *big.Int) *goecdsa.PrivateKey {
    29  	priv := new(goecdsa.PrivateKey)
    30  	priv.PublicKey.Curve = c
    31  	priv.D = k
    32  	priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
    33  	return priv
    34  }
    35  
    36  // assigns two big.Int inputs to a Go ecdsa public key
    37  func setPubKey(c elliptic.Curve, x *big.Int, y *big.Int) *goecdsa.PublicKey {
    38  	pub := new(goecdsa.PublicKey)
    39  	pub.Curve = c
    40  	pub.X = x
    41  	pub.Y = y
    42  	return pub
    43  }
    44  
    45  // Both Flow and LibP2P define a crypto package with their own abstraction of Keys
    46  // These utility functions convert a Flow crypto key to a LibP2P key (Flow --> LibP2P)
    47  
    48  // PeerIDFromFlowPublicKey converts a Flow public key to a LibP2P peer ID.
    49  func PeerIDFromFlowPublicKey(networkPubKey fcrypto.PublicKey) (pid peer.ID, err error) {
    50  	pk, err := LibP2PPublicKeyFromFlow(networkPubKey)
    51  	if err != nil {
    52  		err = fmt.Errorf("failed to convert Flow key to LibP2P key: %w", err)
    53  		return
    54  	}
    55  
    56  	pid, err = peer.IDFromPublicKey(pk)
    57  	if err != nil {
    58  		err = fmt.Errorf("failed to convert LibP2P key to peer ID: %w", err)
    59  		return
    60  	}
    61  
    62  	return
    63  }
    64  
    65  // LibP2PPrivKeyFromFlow converts a Flow private key to a LibP2P Private key
    66  func LibP2PPrivKeyFromFlow(fpk fcrypto.PrivateKey) (lcrypto.PrivKey, error) {
    67  	// get the signature algorithm
    68  	keyType, err := keyType(fpk.Algorithm())
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	// based on the signature algorithm, get the appropriate libp2p unmarshaller
    74  	um, ok := lcrypto.PrivKeyUnmarshallers[keyType]
    75  	if !ok {
    76  		return nil, lcrypto.ErrBadKeyType
    77  	}
    78  
    79  	// get the raw dump of the flow key
    80  	bytes := fpk.Encode()
    81  
    82  	// in the case of NIST curves, the raw bytes need to be converted to x509 bytes
    83  	// to accommodate libp2p unmarshaller
    84  	if keyType == lcrypto_pb.KeyType_ECDSA {
    85  		var k big.Int
    86  		k.SetBytes(bytes)
    87  		goKey := setPrKey(elliptic.P256(), &k)
    88  		bytes, err = x509.MarshalECPrivateKey(goKey)
    89  		if err != nil {
    90  			return nil, lcrypto.ErrBadKeyType
    91  		}
    92  	}
    93  	return um(bytes)
    94  }
    95  
    96  // LibP2PPublicKeyFromFlow converts a Flow public key to a LibP2P public key
    97  func LibP2PPublicKeyFromFlow(fpk fcrypto.PublicKey) (lcrypto.PubKey, error) {
    98  	keyType, err := keyType(fpk.Algorithm())
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	um, ok := lcrypto.PubKeyUnmarshallers[keyType]
   103  	if !ok {
   104  		return nil, lcrypto.ErrBadKeyType
   105  	}
   106  
   107  	tempBytes := fpk.Encode()
   108  
   109  	// at this point, keytype is either KeyType_ECDSA or KeyType_Secp256k1
   110  	// and can't hold another value
   111  	var bytes []byte
   112  	if keyType == lcrypto_pb.KeyType_ECDSA {
   113  		var x, y big.Int
   114  		x.SetBytes(tempBytes[:len(tempBytes)/2])
   115  		y.SetBytes(tempBytes[len(tempBytes)/2:])
   116  		goKey := setPubKey(elliptic.P256(), &x, &y)
   117  		bytes, err = x509.MarshalPKIXPublicKey(goKey)
   118  		if err != nil {
   119  			return nil, lcrypto.ErrBadKeyType
   120  		}
   121  	} else if keyType == lcrypto_pb.KeyType_Secp256k1 {
   122  		bytes = make([]byte, crypto.PubKeyLenECDSASecp256k1+1) // libp2p requires an extra byte
   123  		bytes[0] = 4                                           // signals uncompressed form as specified in section 4.3.6/7 of ANSI X9.62.
   124  		copy(bytes[1:], tempBytes)
   125  	}
   126  
   127  	return um(bytes)
   128  }
   129  
   130  // This converts some libp2p PubKeys to a flow PublicKey
   131  // - the supported key types are ECDSA P-256 and ECDSA Secp256k1 public keys,
   132  // - libp2p also supports RSA and Ed25519 keys, which Flow doesn't, their conversion will return an error.
   133  func FlowPublicKeyFromLibP2P(lpk lcrypto.PubKey) (fcrypto.PublicKey, error) {
   134  
   135  	switch ktype := lpk.Type(); ktype {
   136  	case lcrypto_pb.KeyType_ECDSA:
   137  		pubB, err := lpk.Raw()
   138  		if err != nil {
   139  			return nil, lcrypto.ErrBadKeyType
   140  		}
   141  		key, err := x509.ParsePKIXPublicKey(pubB)
   142  		if err != nil {
   143  			// impossible to decode from ASN1.DER
   144  			return nil, lcrypto.ErrBadKeyType
   145  		}
   146  		cryptoKey, ok := key.(*goecdsa.PublicKey)
   147  		if !ok {
   148  			// not recognized as crypto.P-256
   149  			return nil, lcrypto.ErrNotECDSAPubKey
   150  		}
   151  		// ferrying through DecodePublicKey to get the curve checks
   152  		pk_uncompressed := elliptic.Marshal(cryptoKey, cryptoKey.X, cryptoKey.Y)
   153  		// the first bit is the compression bit of X9.62
   154  		pubKey, err := crypto.DecodePublicKey(crypto.ECDSAP256, pk_uncompressed[1:])
   155  		if err != nil {
   156  			return nil, lcrypto.ErrNotECDSAPubKey
   157  		}
   158  		return pubKey, nil
   159  	case lcrypto_pb.KeyType_Secp256k1:
   160  		// libp2p uses the compressed representation, flow the uncompressed one
   161  		lpk_secp256k1, ok := lpk.(*lcrypto.Secp256k1PublicKey)
   162  		if !ok {
   163  			return nil, lcrypto.ErrBadKeyType
   164  		}
   165  		secpBytes, err := lpk_secp256k1.Raw()
   166  		if err != nil { // this never errors
   167  			return nil, lcrypto.ErrBadKeyType
   168  		}
   169  		pk, err := crypto.DecodePublicKeyCompressed(crypto.ECDSASecp256k1, secpBytes)
   170  		if err != nil {
   171  			return nil, lcrypto.ErrNotECDSAPubKey
   172  		}
   173  		return pk, nil
   174  	default:
   175  		return nil, lcrypto.ErrBadKeyType
   176  	}
   177  
   178  }
   179  
   180  // keyType translates Flow signing algorithm constants to the corresponding LibP2P constants
   181  func keyType(sa fcrypto.SigningAlgorithm) (lcrypto_pb.KeyType, error) {
   182  	switch sa {
   183  	case fcrypto.ECDSAP256:
   184  		return lcrypto_pb.KeyType_ECDSA, nil
   185  	case fcrypto.ECDSASecp256k1:
   186  		return lcrypto_pb.KeyType_Secp256k1, nil
   187  	default:
   188  		return -1, lcrypto.ErrBadKeyType
   189  	}
   190  }