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