github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/keyutils/keyTranslator_test.go (about)

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