diff --git a/doc/tests/router_info.mk b/doc/tests/router_info.mk index 75165de..b4b1687 100644 --- a/doc/tests/router_info.mk +++ b/doc/tests/router_info.mk @@ -1,34 +1,52 @@ -test-router-info-all: test-router-info-published test-router-info-addresses test-router-info-identity test-router-info-misc test-router-info-create test-router-info-10k +test-router-info-all: test-router-info-creation test-router-info-published-date test-router-info-identity test-router-info-addresses test-router-info-serialization test-router-info-signature test-router-info-capabilities test-router-info-version test-router-info-good-version test-router-info-uncongested test-router-info-reachable test-router-info-10k -test-router-info-published: - $(GO) test -v ./lib/common/router_info -run TestPublishedReturnsCorrectDate - $(GO) test -v ./lib/common/router_info -run TestPublishedReturnsCorrectErrorWithPartialDate - $(GO) test -v ./lib/common/router_info -run TestPublishedReturnsCorrectErrorWithInvalidData +test-router-info-creation: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoCreation + $(GO) test -v ./lib/common/router_info -run TestCreateRouterInfo -test-router-info-addresses: - $(GO) test -v ./lib/common/router_info -run TestRouterAddressCountReturnsCorrectCount - $(GO) test -v ./lib/common/router_info -run TestRouterAddressCountReturnsCorrectErrorWithInvalidData - $(GO) test -v ./lib/common/router_info -run TestRouterAddressesReturnsAddresses - $(GO) test -v ./lib/common/router_info -run TestRouterAddressesReturnsAddressesWithMultiple +test-router-info-published-date: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoPublishedDate test-router-info-identity: - $(GO) test -v ./lib/common/router_info -run TestRouterIdentityIsCorrect + $(GO) test -v ./lib/common/router_info -run TestRouterInfoRouterIdentity -test-router-info-misc: - $(GO) test -v ./lib/common/router_info -run TestPeerSizeIsZero - $(GO) test -v ./lib/common/router_info -run TestOptionsAreCorrect - $(GO) test -v ./lib/common/router_info -run TestSignatureIsCorrectSize +test-router-info-addresses: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoAddresses -test-router-info-create: - $(GO) test -v ./lib/common/router_info -run TestCreateRouterInfo +test-router-info-serialization: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoSerialization + +test-router-info-signature: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoSignature + +test-router-info-capabilities: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoCapabilities + +test-router-info-version: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoVersion + +test-router-info-good-version: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoGoodVersion + +test-router-info-uncongested: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoUnCongested + +test-router-info-reachable: + $(GO) test -v ./lib/common/router_info -run TestRouterInfoReachable test-router-info-10k: $(GO) test -v ./lib/common/router_info -run Test10K .PHONY: test-router-info-all \ - test-router-info-published \ - test-router-info-addresses \ + test-router-info-creation \ + test-router-info-published-date \ test-router-info-identity \ - test-router-info-misc \ - test-router-info-create \ + test-router-info-addresses \ + test-router-info-serialization \ + test-router-info-signature \ + test-router-info-capabilities \ + test-router-info-version \ + test-router-info-good-version \ + test-router-info-uncongested \ + test-router-info-reachable \ test-router-info-10k \ No newline at end of file diff --git a/lib/common/router_info/router_info_test.go b/lib/common/router_info/router_info_test.go index 11fb4c4..6819cd6 100644 --- a/lib/common/router_info/router_info_test.go +++ b/lib/common/router_info/router_info_test.go @@ -2,208 +2,232 @@ package router_info import ( "bytes" + "crypto/rand" "fmt" - "testing" - - common "github.com/go-i2p/go-i2p/lib/common/data" - "github.com/go-i2p/go-i2p/lib/common/router_address" + "github.com/go-i2p/go-i2p/lib/common/certificate" + "github.com/go-i2p/go-i2p/lib/common/data" "github.com/go-i2p/go-i2p/lib/common/router_identity" + "github.com/go-i2p/go-i2p/lib/common/signature" + "github.com/go-i2p/go-i2p/lib/crypto" "github.com/stretchr/testify/assert" + "golang.org/x/crypto/openpgp/elgamal" + "testing" + "time" + + "github.com/go-i2p/go-i2p/lib/common/router_address" ) -func buildRouterIdentity() router_identity.RouterIdentity { - router_ident_data := make([]byte, 128+256) - router_ident_data = append(router_ident_data, []byte{0x05, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}...) - return_data, _, _ := router_identity.ReadRouterIdentity(router_ident_data) - return return_data -} - -func buildDate() []byte { - date_data := []byte{0x00, 0x00, 0x00, 0x00, 0x05, 0x26, 0x5c, 0x00} - return date_data -} - -func buildMapping() *common.Mapping { - mapping, _ := common.GoMapToMapping(map[string]string{"host": "127.0.0.1", "port": "4567"}) - return mapping -} - -func buildRouterAddress(transport string) router_address.RouterAddress { - router_address_bytes := []byte{0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - str, _ := common.ToI2PString(transport) - router_address_bytes = append(router_address_bytes, []byte(str)...) - router_address_bytes = append(router_address_bytes, buildMapping().Data()...) - return_data, _, _ := router_address.ReadRouterAddress(router_address_bytes) - return return_data -} - -func buildFullRouterInfo(rid ...[]byte) (RouterInfo, error) { - var ri RouterInfo - var err error - if rid == nil || len(rid) == 0 { - router_info_data := make([]byte, 0) - router_info_data = append(router_info_data, buildRouterIdentity().KeysAndCert.Bytes()...) - router_info_data = append(router_info_data, buildDate()...) - router_info_data = append(router_info_data, 0x01) - router_info_data = append(router_info_data, buildRouterAddress("foo").Bytes()...) - router_info_data = append(router_info_data, 0x00) - router_info_data = append(router_info_data, buildMapping().Data()...) - router_info_data = append(router_info_data, make([]byte, 40)...) - ri, _, err = ReadRouterInfo(router_info_data) - } else { - ri, _, err = ReadRouterInfo(rid[0]) +func generateTestRouterInfo(t *testing.T, publishedTime time.Time) (*RouterInfo, error) { + // Step 1: Generate Signing Key Pair (Ed25519) + var ed25519PrivKey crypto.Ed25519PrivateKey + _, err := (&ed25519PrivKey).Generate() + if err != nil { + return nil, fmt.Errorf("failed to generate Ed25519 private key: %v", err) } - return ri, err -} -func TestPublishedReturnsCorrectDate(t *testing.T) { - assert := assert.New(t) - - router_info, _ := buildFullRouterInfo() - date := router_info.Published() - assert.Equal(int64(86400), date.Time().Unix(), "RouterInfo.Published() did not return correct date") -} - -func TestPublishedReturnsCorrectErrorWithPartialDate(t *testing.T) { - assert := assert.New(t) - - router_info, err := buildFullRouterInfo() - assert.Nil(err) - bytes, err := router_info.Bytes() - router_info, err = buildFullRouterInfo(bytes[:387+4]) - //_ := router_info.Published() - if assert.NotNil(err) { - assert.Equal("error parsing date: not enough data", err.Error()) + ed25519PubKeyRaw, err := ed25519PrivKey.Public() + if err != nil { + return nil, fmt.Errorf("failed to derive Ed25519 public key: %v", err) } -} -func TestPublishedReturnsCorrectErrorWithInvalidData(t *testing.T) { - assert := assert.New(t) - - router_info, err := buildFullRouterInfo() - if assert.Nil(err) { - bytes, err := router_info.Bytes() - router_info, err = buildFullRouterInfo(bytes[:56]) - if assert.NotNil(err) { - assert.Equal("error parsing KeysAndCert: data is smaller than minimum valid size", err.Error()) - } - } else { - assert.Fail("error building router info") + ed25519PubKey, ok := ed25519PubKeyRaw.(crypto.SigningPublicKey) + if !ok { + return nil, fmt.Errorf("failed to assert Ed25519 public key to SigningPublicKey") } -} -func TestRouterAddressCountReturnsCorrectCount(t *testing.T) { - assert := assert.New(t) - - router_info, _ := buildFullRouterInfo() - count := router_info.RouterAddressCount() - assert.Equal(1, count, "RouterInfo.RouterAddressCount() did not return correct count") -} - -func TestRouterAddressCountReturnsCorrectErrorWithInvalidData(t *testing.T) { - assert := assert.New(t) - - router_info, err := buildFullRouterInfo() - if assert.Nil(err) { - bytes, err := router_info.Bytes() - router_info, err = buildFullRouterInfo(bytes[:387+8]) - - count := router_info.RouterAddressCount() - if assert.NotNil(err) { - assert.Equal("error parsing router addresses: not enough data", err.Error()) - } - assert.Equal(0, count) + // Step 2: Generate Encryption Key Pair (ElGamal) + var elgamalPrivKey elgamal.PrivateKey + err = crypto.ElgamalGenerate(&elgamalPrivKey, rand.Reader) + if err != nil { + return nil, fmt.Errorf("failed to generate ElGamal private key: %v", err) } -} -func TestRouterAddressesReturnsAddresses(t *testing.T) { - assert := assert.New(t) - - router_info, err := buildFullRouterInfo() - router_addresses := router_info.RouterAddresses() - assert.Nil(err) - if assert.Equal(1, len(router_addresses)) { - assert.Equal( - 0, - bytes.Compare( - []byte(buildRouterAddress("foo").Bytes()), - []byte(router_addresses[0].Bytes()), - ), - ) + // Convert ElGamal Private Key to crypto.ElgPrivateKey + var elgPrivKey crypto.ElgPrivateKey + xBytes := elgamalPrivKey.X.Bytes() + if len(xBytes) > 256 { + return nil, fmt.Errorf("ElGamal private key X too large") } -} + copy(elgPrivKey[256-len(xBytes):], xBytes) -func TestRouterAddressesReturnsAddressesWithMultiple(t *testing.T) { - assert := assert.New(t) - - router_info_data := make([]byte, 0) - router_info_data = append(router_info_data, buildRouterIdentity().KeysAndCert.Bytes()...) - router_info_data = append(router_info_data, buildDate()...) - router_info_data = append(router_info_data, 0x03) - router_info_data = append(router_info_data, buildRouterAddress("foo0").Bytes()...) - router_info_data = append(router_info_data, buildRouterAddress("foo1").Bytes()...) - router_info_data = append(router_info_data, buildRouterAddress("foo2").Bytes()...) - router_info_data = append(router_info_data, 0x00) - router_info_data = append(router_info_data, buildMapping().Data()...) - router_info_data = append(router_info_data, make([]byte, 40)...) - router_info, _, _ := ReadRouterInfo(router_info_data) - - count := router_info.RouterAddressCount() - if assert.Equal(3, count) { - router_addresses := router_info.RouterAddresses() - for i := 0; i < 3; i++ { - assert.Equal( - 0, - bytes.Compare( - []byte(buildRouterAddress(fmt.Sprintf("foo%d", i)).Bytes()), - []byte(router_addresses[i].Bytes()), - ), - ) - } + // Convert ElGamal Public Key to crypto.ElgPublicKey + var elgPubKey crypto.ElgPublicKey + yBytes := elgamalPrivKey.PublicKey.Y.Bytes() + if len(yBytes) > 256 { + return nil, fmt.Errorf("ElGamal public key Y too large") } + copy(elgPubKey[256-len(yBytes):], yBytes) + + // Ensure ElGamal Public Key implements crypto.PublicKey interface + var _ crypto.PublicKey = elgPubKey + + // Step 3: Create KeyCertificate specifying key types + var payload bytes.Buffer + + signingPublicKeyType, err := data.NewIntegerFromInt(7, 2) + if err != nil { + return nil, fmt.Errorf("failed to create signing public key type integer: %v", err) + } + + cryptoPublicKeyType, err := data.NewIntegerFromInt(0, 2) + if err != nil { + return nil, fmt.Errorf("failed to create crypto public key type integer: %v", err) + } + + // Write the bytes of the Integer instances to the payload + payload.Write(*signingPublicKeyType) + payload.Write(*cryptoPublicKeyType) + + // Create KeyCertificate + cert, err := certificate.NewCertificateWithType(certificate.CERT_KEY, payload.Bytes()) + if err != nil { + return nil, fmt.Errorf("failed to create new certificate: %v", err) + } + + // Step 4: Create RouterIdentity + routerIdentity, err := router_identity.NewRouterIdentity(elgPubKey, ed25519PubKey, *cert, nil) + if err != nil { + return nil, fmt.Errorf("failed to create RouterIdentity: %v", err) + } + + // Step 5: Create RouterAddress + options := map[string]string{} + routerAddress, err := router_address.NewRouterAddress(3, <-time.After(1*time.Second), "NTCP2", options) + if err != nil { + return nil, fmt.Errorf("failed to create RouterAddress: %v", err) + } + routerAddresses := []*router_address.RouterAddress{routerAddress} + + // Step 6: Create RouterInfo + routerInfo, err := NewRouterInfo(routerIdentity, publishedTime, routerAddresses, nil, &ed25519PrivKey, signature.SIGNATURE_TYPE_EDDSA_SHA512_ED25519) + if err != nil { + return nil, fmt.Errorf("failed to create RouterInfo: %v", err) + } + + return routerInfo, nil } -func TestPeerSizeIsZero(t *testing.T) { +// TestRouterInfoCreation verifies that a RouterInfo object can be created without errors. +func TestRouterInfoCreation(t *testing.T) { assert := assert.New(t) - router_info, _ := buildFullRouterInfo() - size := router_info.PeerSize() - assert.Equal(0, size, "RouterInfo.PeerSize() did not return 0") + // Use helper function to generate a RouterInfo + publishedTime := time.Now() + routerInfo, err := generateTestRouterInfo(t, publishedTime) + + assert.Nil(err, "RouterInfo creation should not return an error") + assert.NotNil(routerInfo, "RouterInfo should not be nil") } -func TestOptionsAreCorrect(t *testing.T) { +// TestRouterInfoPublishedDate verifies that the published date is correctly set and retrieved. +func TestRouterInfoPublishedDate(t *testing.T) { assert := assert.New(t) - router_info, _ := buildFullRouterInfo() - options := router_info.Options() - assert.Equal( - 0, - bytes.Compare( - []byte(buildMapping().Data()), - []byte(options.Data()), - ), - ) + publishedTime := time.Unix(86400, 0) // 1 day since epoch + routerInfo, err := generateTestRouterInfo(t, publishedTime) + + assert.Nil(err, "RouterInfo creation should not return an error") + assert.Equal(publishedTime.Unix(), routerInfo.Published().Time().Unix(), "Published date should match the input date") } -func TestSignatureIsCorrectSize(t *testing.T) { +// TestRouterInfoRouterIdentity verifies that the RouterIdentity is correctly set. +func TestRouterInfoRouterIdentity(t *testing.T) { assert := assert.New(t) - router_info, _ := buildFullRouterInfo() - signature := router_info.Signature() - assert.Equal(40, len(signature)) + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + routerIdentity := routerInfo.RouterIdentity() + assert.NotNil(routerIdentity, "RouterIdentity should not be nil") } -func TestRouterIdentityIsCorrect(t *testing.T) { +// TestRouterInfoAddresses verifies that the RouterAddresses are correctly set and retrieved. +func TestRouterInfoAddresses(t *testing.T) { assert := assert.New(t) - router_info, _ := buildFullRouterInfo() - router_identity := router_info.RouterIdentity() - // assert.Nil(err) - assert.Equal( - 0, - bytes.Compare( - []byte(buildRouterIdentity().KeysAndCert.Bytes()), - []byte(router_identity.KeysAndCert.Bytes()), - ), - ) + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + addresses := routerInfo.RouterAddresses() + assert.NotNil(addresses, "RouterAddresses should not be nil") + assert.Greater(len(addresses), 0, "RouterAddresses should have at least one address") +} + +// TestRouterInfoSerialization verifies that the RouterInfo can be serialized to bytes without error. +func TestRouterInfoSerialization(t *testing.T) { + assert := assert.New(t) + + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + bytes, err := routerInfo.Bytes() + assert.Nil(err, "Serialization should not return an error") + assert.NotNil(bytes, "Serialized bytes should not be nil") + assert.Greater(len(bytes), 0, "Serialized bytes should have a length greater than zero") +} + +// TestRouterInfoSignature verifies that the signature is correctly set in the RouterInfo. +func TestRouterInfoSignature(t *testing.T) { + assert := assert.New(t) + + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + signature := routerInfo.Signature() + assert.NotNil(signature, "Signature should not be nil") +} + +// TestRouterInfoCapabilities verifies the RouterCapabilities method functionality. +func TestRouterInfoCapabilities(t *testing.T) { + assert := assert.New(t) + + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + capabilities := routerInfo.RouterCapabilities() + assert.NotEmpty(capabilities, "RouterCapabilities should not be empty") +} + +// TestRouterInfoVersion verifies the RouterVersion method functionality. +func TestRouterInfoVersion(t *testing.T) { + assert := assert.New(t) + + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + version := routerInfo.RouterVersion() + assert.NotEmpty(version, "RouterVersion should not be empty") +} + +// TestRouterInfoGoodVersion verifies the GoodVersion method functionality. +func TestRouterInfoGoodVersion(t *testing.T) { + assert := assert.New(t) + + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + isGoodVersion := routerInfo.GoodVersion() + assert.IsType(true, isGoodVersion, "GoodVersion should return a boolean") +} + +// TestRouterInfoUnCongested verifies the UnCongested method functionality. +func TestRouterInfoUnCongested(t *testing.T) { + assert := assert.New(t) + + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + isUncongested := routerInfo.UnCongested() + assert.IsType(true, isUncongested, "UnCongested should return a boolean") +} + +// TestRouterInfoReachable verifies the Reachable method functionality. +func TestRouterInfoReachable(t *testing.T) { + assert := assert.New(t) + + routerInfo, err := generateTestRouterInfo(t, time.Now()) + assert.Nil(err, "RouterInfo creation should not return an error") + + isReachable := routerInfo.Reachable() + assert.IsType(true, isReachable, "Reachable should return a boolean") }