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 }