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

     1  package keyutils
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  	"math"
     7  	"testing"
     8  
     9  	"github.com/btcsuite/btcd/btcec/v2"
    10  	lcrypto "github.com/libp2p/go-libp2p/core/crypto"
    11  	"github.com/libp2p/go-libp2p/core/peer"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  	"github.com/stretchr/testify/suite"
    15  
    16  	fcrypto "github.com/onflow/flow-go/crypto"
    17  )
    18  
    19  // KeyTranslatorTestSuite tests key conversion from Flow keys to LibP2P keys
    20  type KeyTranslatorTestSuite struct {
    21  	suite.Suite
    22  }
    23  
    24  // TestKeyTranslatorTestSuite runs all the test methods in this test suite
    25  func TestKeyTranslatorTestSuite(t *testing.T) {
    26  	suite.Run(t, new(KeyTranslatorTestSuite))
    27  }
    28  
    29  // TestPrivateKeyConversion tests that Private keys are successfully converted from Flow to LibP2P representation
    30  func (k *KeyTranslatorTestSuite) TestPrivateKeyConversion() {
    31  
    32  	// test all the ECDSA curves that are supported by the translator for private key conversion
    33  	sa := []fcrypto.SigningAlgorithm{fcrypto.ECDSAP256, fcrypto.ECDSASecp256k1}
    34  	prKeyLen := []int{fcrypto.PrKeyLenECDSAP256, fcrypto.PrKeyLenECDSASecp256k1}
    35  	// offset of the key raw bytes in the libp2p encoding
    36  	offset := []int{7, 0}
    37  
    38  	loops := 50
    39  	for j, s := range sa {
    40  		for i := 0; i < loops; i++ {
    41  			// generate seed
    42  			seed := k.createSeed()
    43  			// generate a Flow private key
    44  			fpk, err := fcrypto.GeneratePrivateKey(s, seed)
    45  			require.NoError(k.T(), err)
    46  
    47  			// convert it to a LibP2P private key
    48  			lpk, err := LibP2PPrivKeyFromFlow(fpk)
    49  			require.NoError(k.T(), err)
    50  
    51  			// get the raw bytes of both the keys
    52  			fbytes := fpk.Encode()
    53  			lbytes, err := lpk.Raw()
    54  			lbytes = lbytes[offset[j] : offset[j]+prKeyLen[j]]
    55  			require.NoError(k.T(), err)
    56  
    57  			// compare the raw bytes
    58  			require.Equal(k.T(), fbytes, lbytes)
    59  		}
    60  	}
    61  }
    62  
    63  // RawUncompressed returns the bytes of the key in an uncompressed format (like Flow library)
    64  // This function is added to the test since Raw function from libp2p only returns the compressed format
    65  func rawUncompressed(key lcrypto.PubKey) ([]byte, error) {
    66  	k, ok := key.(*lcrypto.Secp256k1PublicKey)
    67  	if !ok {
    68  		return nil, fmt.Errorf("libp2p public key must be of type Secp256k1PublicKey")
    69  	}
    70  	return (*btcec.PublicKey)(k).SerializeUncompressed()[1:], nil
    71  }
    72  
    73  // TestPublicKeyConversion tests that Public keys are successfully converted from Flow to LibP2P representation
    74  func (k *KeyTranslatorTestSuite) TestPublicKeyConversion() {
    75  
    76  	// test the algorithms that are supported by the translator for public key conversion (currently only ECDSA 256)
    77  	// ECDSASecp256k1 doesn't work and throws a 'invalid pub key length 64' error
    78  	sa := []fcrypto.SigningAlgorithm{fcrypto.ECDSAP256, fcrypto.ECDSASecp256k1}
    79  	// the offset of the public key bytes in the x509 encoding of an ECDSA P-256 public key
    80  	x509offset := 27
    81  
    82  	loops := 50
    83  	for _, s := range sa {
    84  		for i := 0; i < loops; i++ {
    85  			// generate seed
    86  			seed := k.createSeed()
    87  			fpk, err := fcrypto.GeneratePrivateKey(s, seed)
    88  			require.NoError(k.T(), err)
    89  
    90  			// get the Flow public key
    91  			fpublic := fpk.PublicKey()
    92  
    93  			// convert the Flow public key to a Libp2p public key
    94  			lpublic, err := LibP2PPublicKeyFromFlow(fpublic)
    95  			require.NoError(k.T(), err)
    96  
    97  			// compare raw bytes of the public keys
    98  			fbytes := fpublic.Encode()
    99  			var lbytes []byte
   100  			if s == fcrypto.ECDSAP256 {
   101  				lbytes, err = lpublic.Raw()
   102  				lbytes = lbytes[x509offset:]
   103  			} else if s == fcrypto.ECDSASecp256k1 {
   104  				lbytes, err = rawUncompressed(lpublic)
   105  			}
   106  			require.NoError(k.T(), err)
   107  
   108  			require.Equal(k.T(), fbytes, lbytes)
   109  		}
   110  	}
   111  }
   112  
   113  func (k *KeyTranslatorTestSuite) TestPublicKeyRoundTrip() {
   114  	sa := []fcrypto.SigningAlgorithm{fcrypto.ECDSAP256, fcrypto.ECDSASecp256k1}
   115  	loops := 50
   116  	for _, s := range sa {
   117  		for i := 0; i < loops; i++ {
   118  
   119  			// generate seed
   120  			seed := k.createSeed()
   121  			fpk, err := fcrypto.GeneratePrivateKey(s, seed)
   122  			require.NoError(k.T(), err)
   123  
   124  			// get the Flow public key
   125  			fpublic := fpk.PublicKey()
   126  
   127  			// convert the Flow public key to a Libp2p public key
   128  			lpublic, err := LibP2PPublicKeyFromFlow(fpublic)
   129  			require.NoError(k.T(), err)
   130  
   131  			fpublic2, err := FlowPublicKeyFromLibP2P(lpublic)
   132  			require.NoError(k.T(), err)
   133  			require.Equal(k.T(), fpublic, fpublic2)
   134  
   135  		}
   136  	}
   137  
   138  }
   139  
   140  // TestLibP2PIDGenerationIsConsistent tests that a LibP2P peer ID generated using Flow ECDSA key is deterministic
   141  func (k *KeyTranslatorTestSuite) TestPeerIDGenerationIsConsistent() {
   142  	// generate a seed which will be used for both - Flow keys and Libp2p keys
   143  	seed := k.createSeed()
   144  
   145  	// generate a Flow private key
   146  	fpk, err := fcrypto.GeneratePrivateKey(fcrypto.ECDSAP256, seed)
   147  	require.NoError(k.T(), err)
   148  
   149  	// get the Flow public key
   150  	fpublic := fpk.PublicKey()
   151  
   152  	// convert it to the Libp2p Public key
   153  	lconverted, err := LibP2PPublicKeyFromFlow(fpublic)
   154  	require.NoError(k.T(), err)
   155  
   156  	// check that the LibP2P Id generation is deterministic
   157  	var prev peer.ID
   158  	for i := 0; i < 100; i++ {
   159  
   160  		// generate a Libp2p Peer ID from the converted public key
   161  		fpeerID, err := peer.IDFromPublicKey(lconverted)
   162  		require.NoError(k.T(), err)
   163  
   164  		if i > 0 {
   165  			err = prev.Validate()
   166  			require.NoError(k.T(), err)
   167  			assert.Equal(k.T(), prev, fpeerID, "peer ID generation is not deterministic")
   168  		}
   169  		prev = fpeerID
   170  	}
   171  }
   172  
   173  func (k *KeyTranslatorTestSuite) createSeed() []byte {
   174  	seedLen := int(math.Max(fcrypto.KeyGenSeedMinLenECDSAP256, fcrypto.KeyGenSeedMinLenECDSASecp256k1))
   175  	seed := make([]byte, seedLen)
   176  	n, err := rand.Read(seed)
   177  	require.NoError(k.T(), err)
   178  	require.Equal(k.T(), n, seedLen)
   179  	return seed
   180  }