├── .gitignore ├── mixmessages ├── generateProto.sh ├── permissioningPoll.go ├── permissioningPoll_test.go ├── clientError.go ├── digest.go ├── ndf.go ├── roundError.go ├── sharePiece.go ├── notifications.go ├── utils.go ├── digesttesthelper.go └── roundInfo.go ├── remoteSync ├── client │ └── client.go └── server │ └── endpoint.go ├── node ├── messageInfo.go ├── handler_test.go ├── node_test.go ├── register_test.go ├── register.go └── phase.go ├── Makefile ├── notificationBot ├── broadcast.go ├── notificationBot_test.go ├── poll.go ├── endpoint.go └── registerNotification_test.go ├── network ├── validation.go ├── slotDigest.go ├── dataStructures │ ├── ipOverride.go │ ├── extendedRoundStorage.go │ ├── ndf_test.go │ ├── roundUpdates.go │ ├── roundData.go │ ├── ipOverride_test.go │ ├── round.go │ ├── ndf.go │ ├── group.go │ ├── round_test.go │ ├── roundData_test.go │ └── roundUpdates_test.go ├── historicalRoundData.go ├── securedNdf.go ├── securedNdf_test.go └── slotDigest_test.go ├── client ├── client.go ├── serverStream.go ├── notificationBot.go ├── notificationBot_test.go ├── mockServer_test.go └── notificationsV2_test.go ├── registration ├── registration_test.go └── endpoint.go ├── testkeys ├── cmix.rip.crt ├── gateway.cmix.rip.crt ├── keys.go ├── keypath.go ├── cmix.rip.key └── gateway.cmix.rip.key ├── LICENSE ├── gateway ├── notifications.go ├── getPermissioningAddress_test.go ├── broadcast_test.go ├── getPermissioningAddress.go ├── broadcast.go ├── authorizer.go ├── registration_test.go ├── notifications_test.go ├── gateway_test.go ├── registration.go ├── authorizer_test.go └── proxy_test.go ├── go.mod ├── udb ├── poll.go ├── endpoint.go └── udb_test.go ├── clientregistrar ├── endpoint.go ├── clientregistrar_test.go └── handler.go ├── .gitlab-ci.yml ├── authorizer ├── endpoint.go ├── authorizer_test.go └── handler.go ├── publicAddress ├── override.go ├── ipLookupServices_test.go ├── ipLookupServices.go └── override_test.go └── testutils └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | # Ignore glide files/folders 3 | glide.lock 4 | vendor/ 5 | # Ignore Jetbrains IDE folder 6 | .idea/* 7 | # Ignore local development scripts 8 | localdev_* 9 | # Ignore logs 10 | *.log 11 | .*.swp 12 | .*.swo 13 | -------------------------------------------------------------------------------- /mixmessages/generateProto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #/////////////////////////////////////////////////////////////////////////////// 4 | #/ Copyright © 2020 xx network SEZC // 5 | #/ // 6 | #/ Use of this source code is governed by a license that can be found in the // 7 | #/ LICENSE file // 8 | #/////////////////////////////////////////////////////////////////////////////// 9 | 10 | protoc -I. -I../vendor --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto -------------------------------------------------------------------------------- /remoteSync/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | "gitlab.com/xx_network/comms/connect" 6 | "gitlab.com/xx_network/primitives/id" 7 | ) 8 | 9 | // Comms is an object used for top-level remote sync client calls. 10 | type Comms struct { 11 | *connect.ProtoComms 12 | } 13 | 14 | // NewClientComms returns a Comms object with given attributes. 15 | func NewClientComms(id *id.ID, pubKeyPem, privKeyPem, salt []byte) (*Comms, error) { 16 | pc, err := connect.CreateCommClient(id, pubKeyPem, privKeyPem, salt) 17 | if err != nil { 18 | return nil, errors.Errorf("Unable to create Client comms: %+v", err) 19 | } 20 | return &Comms{pc}, nil 21 | } 22 | -------------------------------------------------------------------------------- /mixmessages/permissioningPoll.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package mixmessages 9 | 10 | import "gitlab.com/elixxir/primitives/current" 11 | 12 | // GetState gets the state of the node 13 | func (m *PermissioningPoll) GetCurrentActivityState() current.Activity { 14 | if m != nil { 15 | return current.Activity(m.Activity) 16 | } 17 | return current.NOT_STARTED 18 | } 19 | -------------------------------------------------------------------------------- /node/messageInfo.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package node 9 | 10 | // MessageInfo contains information about a comm to be passed into every interface callback 11 | // Specifically, contains sender ID and network address, signature and signature validity 12 | type MessageInfo struct { 13 | SenderId string 14 | Address string 15 | Signature []byte 16 | ValidSignature bool 17 | } 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: update master release update_master update_release build clean 2 | 3 | clean: 4 | rm -rf vendor/ 5 | go mod vendor 6 | 7 | update: 8 | -GOFLAGS="" go get -u all 9 | 10 | build: 11 | go build ./... 12 | go mod tidy 13 | 14 | update_release: 15 | GOFLAGS="" go get gitlab.com/xx_network/primitives@release 16 | GOFLAGS="" go get gitlab.com/elixxir/primitives@release 17 | GOFLAGS="" go get gitlab.com/xx_network/crypto@release 18 | GOFLAGS="" go get gitlab.com/elixxir/crypto@release 19 | GOFLAGS="" go get gitlab.com/xx_network/comms@release 20 | 21 | update_master: 22 | GOFLAGS="" go get gitlab.com/xx_network/primitives@master 23 | GOFLAGS="" go get gitlab.com/elixxir/primitives@master 24 | GOFLAGS="" go get gitlab.com/xx_network/crypto@master 25 | GOFLAGS="" go get gitlab.com/elixxir/crypto@master 26 | GOFLAGS="" go get gitlab.com/xx_network/comms@master 27 | 28 | master: update_master clean build 29 | 30 | release: update_release clean build 31 | -------------------------------------------------------------------------------- /mixmessages/permissioningPoll_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package mixmessages 9 | 10 | import "testing" 11 | 12 | func TestPermissioningPoll_GetActivity(t *testing.T) { 13 | expected := uint32(45) 14 | testRoundInfo := &PermissioningPoll{ 15 | Activity: expected, 16 | } 17 | 18 | received := testRoundInfo.GetActivity() 19 | 20 | if received != expected { 21 | t.Errorf("Received does not match expected for getter function! "+ 22 | "Expected: %+v \n\t"+ 23 | "Received: %+v", expected, received) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mixmessages/clientError.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package mixmessages 9 | 10 | import "hash" 11 | 12 | // Digest hashes the contents of the message in a repeatable manner 13 | // using the provided cryptographic hash. It includes the nonce in the hash 14 | func (m *ClientError) Digest(nonce []byte, h hash.Hash) []byte { 15 | h.Reset() 16 | 17 | // Hash the nodeId 18 | h.Write(m.ClientId) 19 | h.Write([]byte(m.Error)) 20 | 21 | // Hash the nonce 22 | h.Write(nonce) 23 | 24 | // Return the hash 25 | return h.Sum(nil) 26 | } 27 | -------------------------------------------------------------------------------- /notificationBot/broadcast.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains notificationBot -> all servers functionality 9 | 10 | package notificationBot 11 | 12 | import ( 13 | pb "gitlab.com/elixxir/comms/mixmessages" 14 | "gitlab.com/xx_network/comms/connect" 15 | ) 16 | 17 | // NotificationBot -> Permissioning 18 | // Fixme: figure out what to do with notification bot and unified polling 19 | func (nb *Comms) RequestNdf(host *connect.Host, message *pb.NDFHash) (*pb.NDF, error) { 20 | 21 | // Call the ProtoComms RequestNdf call 22 | return nb.RequestNdf(host, message) 23 | } 24 | -------------------------------------------------------------------------------- /network/validation.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package network 9 | 10 | import "fmt" 11 | 12 | // Level of validation types for pulling our round structure 13 | // 14 | // Strict: Signatures are checked every time (intended for nodes) 15 | // Lazy: Only check we're involved, only verifies the first retrieval 16 | // None: no signature checks are done 17 | type ValidationType uint8 18 | 19 | const ( 20 | Strict ValidationType = iota 21 | Lazy 22 | None 23 | ) 24 | 25 | // Stringer for ValidationType 26 | func (s ValidationType) String() string { 27 | switch s { 28 | case Strict: 29 | return "Strict" 30 | case Lazy: 31 | return "Lazy" 32 | case None: 33 | return "None" 34 | default: 35 | return fmt.Sprintf("UNKNOWN STATE: %d", s) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Handles the basic top-level Client comms object 9 | 10 | package client 11 | 12 | import ( 13 | "github.com/pkg/errors" 14 | "gitlab.com/xx_network/comms/connect" 15 | "gitlab.com/xx_network/primitives/id" 16 | ) 17 | 18 | // Client object used to implement endpoints and top-level comms functionality 19 | type Comms struct { 20 | *connect.ProtoComms 21 | } 22 | 23 | // Returns a Comms object with given attributes 24 | func NewClientComms(id *id.ID, pubKeyPem, privKeyPem, salt []byte) (*Comms, error) { 25 | pc, err := connect.CreateCommClient(id, pubKeyPem, privKeyPem, salt) 26 | if err != nil { 27 | return nil, errors.Errorf("Unable to create Client comms: %+v", err) 28 | } 29 | return &Comms{pc}, nil 30 | } 31 | -------------------------------------------------------------------------------- /mixmessages/digest.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package mixmessages 9 | 10 | import ( 11 | "github.com/golang/protobuf/proto" 12 | jww "github.com/spf13/jwalterweatherman" 13 | "gitlab.com/elixxir/crypto/hash" 14 | ) 15 | 16 | // Function to digest Identity 17 | func (i *Identity) Digest() []byte { 18 | // return hash(username|dhPubKey|salt)} 19 | // Generate the hash function 20 | h, err := hash.NewCMixHash() 21 | if err != nil { 22 | jww.FATAL.Panicf("Could not get hash: %+v", err) 23 | } 24 | 25 | // Marshal the message to put into the hash 26 | mb, err := proto.Marshal(i) 27 | if err != nil { 28 | jww.FATAL.Panicf("Could not marshal: %+v", err) 29 | } 30 | 31 | // Hash the Identity data to generate the vector 32 | h.Write(mb) 33 | return h.Sum(nil) 34 | } 35 | -------------------------------------------------------------------------------- /registration/registration_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package registration 9 | 10 | import ( 11 | "fmt" 12 | "gitlab.com/xx_network/primitives/id" 13 | "sync" 14 | "testing" 15 | ) 16 | 17 | var serverPortLock sync.Mutex 18 | var serverPort = 5900 19 | 20 | func getNextServerAddress() string { 21 | serverPortLock.Lock() 22 | defer func() { 23 | serverPort++ 24 | serverPortLock.Unlock() 25 | }() 26 | return fmt.Sprintf("localhost:%d", serverPort) 27 | } 28 | 29 | func TestBadCerts(t *testing.T) { 30 | defer func() { 31 | if r := recover(); r == nil { 32 | t.Errorf("The code did not panic") 33 | } 34 | }() 35 | RegAddress := getNextServerAddress() 36 | testId := id.NewIdFromString("test", id.Generic, t) 37 | 38 | _ = StartRegistrationServer(testId, RegAddress, NewImplementation(), []byte("bad cert"), []byte("bad key"), nil) 39 | } 40 | -------------------------------------------------------------------------------- /testkeys/cmix.rip.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNV 3 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx 4 | GzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJp 5 | cDAeFw0xOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVT 6 | MRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNV 7 | BAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIw 8 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDh 9 | Dwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfs 10 | WYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSE 11 | tJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uA 12 | m3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9 13 | bJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEA 14 | AaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEA 15 | neUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIf 16 | U/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2 17 | qvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4 18 | cyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1R 19 | tgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E5 20 | 6m52PyzMNV+2N21IPppKwA== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2024 xx foundation 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation and/or 10 | other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /testkeys/gateway.cmix.rip.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDgTCCAmmgAwIBAgIJAKLdZ8UigIAeMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV 3 | BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx 4 | GzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEaMBgGA1UEAwwRZ2F0ZXdheSou 5 | Y21peC5yaXAwHhcNMTkwMzA1MTgzNTU0WhcNMjkwMzAyMTgzNTU0WjBvMQswCQYD 6 | VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ2xhcmVtb250 7 | MRswGQYDVQQKDBJQcml2YXRlZ3JpdHkgQ29ycC4xGjAYBgNVBAMMEWdhdGV3YXkq 8 | LmNtaXgucmlwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9+AaxwDP 9 | xHbhLmn4HoZu0oUM48Qufc6T5XEZTrpMrqJAouXk+61Jc0EFH96/sbj7VyvnXPRo 10 | gIENbk2Y84BkB9SkRMIXya/gh9dOEDSgnvj/yg24l3bdKFqBMKiFg00PYB30fU+A 11 | be3OI/le0I+v++RwH2AV0BMq+T6PcAGjCC1Q1ZB0wP9/VqNMWq5lbK9wD46IQiSi 12 | +SgIQeE7HoiAZXrGO0Y7l9P3+VRoXjRQbqfn3ETNL9ZvQuarwAYC9Ix5MxUrS5ag 13 | Omfjc8bfkpYDFAXRXmdKNISJmtCebX2kDrpP8Bdasx7Fzsx59cEUHCl2aJOWXc7R 14 | 5m3juOVL1HUxjQIDAQABoyAwHjAcBgNVHREEFTATghFnYXRld2F5Ki5jbWl4LnJp 15 | cDANBgkqhkiG9w0BAQUFAAOCAQEAMu3xoc2LW2UExAAIYYWEETggLNrlGonxteSu 16 | juJjOR+ik5SVLn0lEu22+z+FCA7gSk9FkWu+v9qnfOfm2Am+WKYWv3dJ5RypW/hD 17 | NXkOYxVJNYFxeShnHohNqq4eDKpdqSxEcuErFXJdLbZP1uNs4WIOKnThgzhkpuy7 18 | tZRosvOF1X5uL1frVJzHN5jASEDAa7hJNmQ24kh+ds/Ge39fGD8pK31CWhnIXeDo 19 | vKD7wivi/gSOBtcRWWLvU8SizZkS3hgTw0lSOf5geuzvasCEYlqrKFssj6cTzbCB 20 | xy3ra3WazRTNTW4TmkHlCUC9I3oWTTxw5iQxF/I2kQQnwR7L3w== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /testkeys/keys.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package testkeys 9 | 10 | import _ "embed" 11 | 12 | //go:embed cmix.rip.crt 13 | var nodeCert []byte 14 | 15 | //go:embed cmix.rip.key 16 | var nodeKey []byte 17 | 18 | //go:embed gateway.cmix.rip.crt 19 | var gatewayCert []byte 20 | 21 | //go:embed gateway.cmix.rip.key 22 | var gatewayKey []byte 23 | 24 | // These functions are used to cover TLS connection code in tests. 25 | 26 | // GetNodeCert returns the contents of cmix.rip.crt. 27 | func GetNodeCert() []byte { return nodeCert } 28 | 29 | // GetNodeKey returns the contents of cmix.rip.key. 30 | func GetNodeKey() []byte { return nodeKey } 31 | 32 | // GetGatewayCert returns the contents of gateway.cmix.rip.crt. 33 | func GetGatewayCert() []byte { return gatewayCert } 34 | 35 | // GetGatewayKey returns the contents of gateway.cmix.rip.key. 36 | func GetGatewayKey() []byte { return gatewayKey } 37 | -------------------------------------------------------------------------------- /node/handler_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Dummy implementation (so you can use for tests) 9 | package node 10 | 11 | import ( 12 | jww "github.com/spf13/jwalterweatherman" 13 | pb "gitlab.com/elixxir/comms/mixmessages" 14 | "gitlab.com/xx_network/comms/connect" 15 | "os" 16 | "testing" 17 | ) 18 | 19 | func TestMain(m *testing.M) { 20 | jww.SetStdoutThreshold(jww.LevelTrace) 21 | connect.TestingOnlyDisableTLS = true 22 | os.Exit(m.Run()) 23 | } 24 | 25 | // Blank struct implementing Handler interface for testing purposes (Passing to StartNode) 26 | type TestInterface struct{} 27 | 28 | func (m TestInterface) NewRound(roundID string) {} 29 | 30 | func (m TestInterface) PostPhase(message *pb.Batch) {} 31 | 32 | func (m TestInterface) StreamPostPhase(message pb.Node_StreamPostPhaseServer) error { 33 | return nil 34 | } 35 | 36 | func (m TestInterface) PostPrecompResult(roundID uint64, 37 | slots []*pb.Slot) error { 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /mixmessages/ndf.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains functions to make the ndf message type conform to a generic 9 | // signing interface 10 | 11 | package mixmessages 12 | 13 | import ( 14 | "gitlab.com/xx_network/comms/messages" 15 | "hash" 16 | ) 17 | 18 | // GetSig returns the RSA signature. 19 | // IF none exists, it creates it, adds it to the object, then returns it. 20 | func (m *NDF) GetSig() *messages.RSASignature { 21 | if m.Signature != nil { 22 | return m.Signature 23 | } 24 | 25 | m.Signature = new(messages.RSASignature) 26 | 27 | return m.Signature 28 | } 29 | 30 | // Digest hashes the contents of the message in a repeatable manner 31 | // using the provided cryptographic hash. It includes the nonce in the hash 32 | func (m *NDF) Digest(nonce []byte, h hash.Hash) []byte { 33 | h.Reset() 34 | 35 | // Hash the ndf and the nonce 36 | h.Write(m.Ndf) 37 | h.Write(nonce) 38 | 39 | // Return the hash 40 | return h.Sum(nil) 41 | } 42 | -------------------------------------------------------------------------------- /network/slotDigest.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package network 9 | 10 | import ( 11 | "encoding/binary" 12 | "gitlab.com/elixxir/comms/mixmessages" 13 | ) 14 | 15 | // GenerateSlotDigest serializes the gateway slot message for the 16 | // client to hash 17 | func GenerateSlotDigest(gatewaySlot *mixmessages.GatewaySlot) []byte { 18 | 19 | var gatewaySlotDigest []byte 20 | gatewaySlotDigest = append(gatewaySlotDigest, gatewaySlot.Message.SenderID...) 21 | gatewaySlotDigest = append(gatewaySlotDigest, gatewaySlot.Message.PayloadA...) 22 | gatewaySlotDigest = append(gatewaySlotDigest, gatewaySlot.Message.PayloadB...) 23 | 24 | for _, kmac := range gatewaySlot.Message.KMACs { 25 | gatewaySlotDigest = append(gatewaySlotDigest, kmac...) 26 | } 27 | 28 | roundIdBytes := make([]byte, 8) 29 | binary.BigEndian.PutUint64(roundIdBytes, gatewaySlot.RoundID) 30 | 31 | gatewaySlotDigest = append(gatewaySlotDigest, roundIdBytes...) 32 | 33 | return gatewaySlotDigest 34 | 35 | } 36 | -------------------------------------------------------------------------------- /network/dataStructures/ipOverride.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | import ( 11 | "gitlab.com/xx_network/primitives/id" 12 | "sync" 13 | ) 14 | 15 | // structure which holds a list of IP address to override 16 | type IpOverrideList struct { 17 | ipOverride map[id.ID]string 18 | sync.Mutex 19 | } 20 | 21 | // creates a new list over IP overrides 22 | func NewIpOverrideList() *IpOverrideList { 23 | return &IpOverrideList{ 24 | ipOverride: make(map[id.ID]string), 25 | } 26 | } 27 | 28 | // sets an id to be overridden with a specific IP address 29 | func (iol *IpOverrideList) Override(oid *id.ID, ip string) { 30 | iol.Lock() 31 | iol.ipOverride[*oid] = ip 32 | iol.Unlock() 33 | } 34 | 35 | // checks if an ip should be overwritten. returns the passed IP if it should not 36 | // be overwritten 37 | func (iol *IpOverrideList) CheckOverride(cid *id.ID, ip string) string { 38 | iol.Lock() 39 | defer iol.Unlock() 40 | if oip, exists := iol.ipOverride[*cid]; exists { 41 | return oip 42 | } 43 | return ip 44 | } 45 | -------------------------------------------------------------------------------- /mixmessages/roundError.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains functions to make the RoundError message type conform to a generic 9 | // signing interface 10 | 11 | package mixmessages 12 | 13 | import ( 14 | "gitlab.com/xx_network/comms/messages" 15 | "hash" 16 | ) 17 | 18 | // GetSig returns the RSA signature. 19 | // IF none exists, it creates it, adds it to the object, then returns it. 20 | func (m *RoundError) GetSig() *messages.RSASignature { 21 | if m.Signature != nil { 22 | return m.Signature 23 | } 24 | 25 | m.Signature = new(messages.RSASignature) 26 | 27 | return m.Signature 28 | } 29 | 30 | // Digest hashes the contents of the message in a repeatable manner 31 | // using the provided cryptographic hash. It includes the nonce in the hash 32 | func (m *RoundError) Digest(nonce []byte, h hash.Hash) []byte { 33 | h.Reset() 34 | 35 | // Hash the nodeId 36 | h.Write(m.NodeId) 37 | h.Write([]byte(m.Error)) 38 | 39 | // Serialize and hash the round ID 40 | h.Write(serializeUin64(m.Id)) 41 | 42 | // Hash the nonce 43 | h.Write(nonce) 44 | 45 | // Return the hash 46 | return h.Sum(nil) 47 | } 48 | -------------------------------------------------------------------------------- /testkeys/keypath.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package testkeys 9 | 10 | import ( 11 | jww "github.com/spf13/jwalterweatherman" 12 | "gitlab.com/xx_network/primitives/utils" 13 | "path/filepath" 14 | "runtime" 15 | ) 16 | 17 | func getDirForFile() string { 18 | // Get the filename we're in 19 | _, currentFile, _, _ := runtime.Caller(0) 20 | return filepath.Dir(currentFile) 21 | } 22 | 23 | // These functions are used to cover TLS connection code in tests 24 | func GetNodeCertPath() string { 25 | return filepath.Join(getDirForFile(), "cmix.rip.crt") 26 | } 27 | 28 | func GetNodeKeyPath() string { 29 | return filepath.Join(getDirForFile(), "cmix.rip.key") 30 | } 31 | 32 | func GetGatewayCertPath() string { 33 | return filepath.Join(getDirForFile(), "gateway.cmix.rip.crt") 34 | } 35 | 36 | func GetGatewayKeyPath() string { 37 | return filepath.Join(getDirForFile(), "gateway.cmix.rip.key") 38 | } 39 | 40 | func LoadFromPath(path string) []byte { 41 | data, err := utils.ReadFile(path) 42 | if err != nil { 43 | jww.FATAL.Panicf("***Check your key!***\nFailed to read file at %s: %+v", path, err) 44 | } 45 | return data 46 | } 47 | -------------------------------------------------------------------------------- /mixmessages/sharePiece.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains functions to make the RoundInfo message type conform to a generic 9 | // signing interface 10 | package mixmessages 11 | 12 | import ( 13 | "gitlab.com/xx_network/comms/messages" 14 | "hash" 15 | ) 16 | 17 | // GetSig returns the RSA signature. 18 | // IF none exists, it creates it, adds it to the object, then returns it. 19 | func (m *SharePiece) GetSig() *messages.RSASignature { 20 | if m.Signature != nil { 21 | return m.Signature 22 | } 23 | 24 | m.Signature = new(messages.RSASignature) 25 | 26 | return m.Signature 27 | } 28 | 29 | // Digest hashes the contents of the message in a repeatable manner 30 | // using the provided cryptographic hash. It includes the nonce in the hash 31 | func (m *SharePiece) Digest(nonce []byte, h hash.Hash) []byte { 32 | h.Reset() 33 | 34 | // Hash the signature piece 35 | h.Write(m.Piece) 36 | 37 | // Hash the round ID 38 | h.Write(serializeUin64(m.RoundID)) 39 | 40 | // Hash the participants in the message 41 | for _, participant := range m.Participants { 42 | h.Write(participant) 43 | } 44 | 45 | // Hash the nonce 46 | h.Write(nonce) 47 | 48 | // Return the hash 49 | return h.Sum(nil) 50 | } 51 | -------------------------------------------------------------------------------- /gateway/notifications.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | "github.com/golang/protobuf/ptypes/any" 12 | jww "github.com/spf13/jwalterweatherman" 13 | pb "gitlab.com/elixxir/comms/mixmessages" 14 | "gitlab.com/xx_network/comms/connect" 15 | ) 16 | 17 | // SendNotificationBatch sends the batch of notification data to the 18 | // notification bot. 19 | func (g *Comms) SendNotificationBatch(host *connect.Host, notifBatch *pb.NotificationBatch) error { 20 | 21 | // Create the send function 22 | f := func(conn connect.Connection) (*any.Any, error) { 23 | // Set up the context 24 | ctx, cancel := host.GetMessagingContext() 25 | defer cancel() 26 | 27 | // Pack data into authenticated message 28 | authMsg, err := g.PackAuthenticatedMessage(notifBatch, host, false) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | // Send the message 34 | _, err = pb.NewNotificationBotClient(conn.GetGrpcConn()). 35 | ReceiveNotificationBatch(ctx, authMsg) 36 | return nil, err 37 | } 38 | 39 | // Execute the Send function 40 | jww.TRACE.Printf("Sending notification data batch to notification bot: %s", notifBatch) 41 | _, err := g.Send(host, f) 42 | 43 | return err 44 | } 45 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module gitlab.com/elixxir/comms 2 | 3 | go 1.21 4 | 5 | require ( 6 | git.xx.network/elixxir/grpc-web-go-client v0.0.0-20230214175953-5b5a8c33d28a 7 | github.com/elliotchance/orderedmap v1.5.1 8 | github.com/golang/protobuf v1.5.2 9 | github.com/pkg/errors v0.9.1 10 | github.com/spf13/jwalterweatherman v1.1.0 11 | gitlab.com/elixxir/crypto v0.0.10 12 | gitlab.com/elixxir/primitives v0.0.4 13 | gitlab.com/xx_network/comms v0.0.6 14 | gitlab.com/xx_network/crypto v0.0.6 15 | gitlab.com/xx_network/primitives v0.0.5 16 | gitlab.com/xx_network/ring v0.0.3 17 | golang.org/x/crypto v0.16.0 18 | golang.org/x/net v0.10.0 19 | google.golang.org/grpc v1.49.0 20 | google.golang.org/protobuf v1.28.1 21 | ) 22 | 23 | require ( 24 | github.com/cenkalti/backoff/v4 v4.1.3 // indirect 25 | github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect 26 | github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect 27 | github.com/gorilla/websocket v1.5.0 // indirect 28 | github.com/improbable-eng/grpc-web v0.15.0 // indirect 29 | github.com/klauspost/compress v1.11.7 // indirect 30 | github.com/klauspost/cpuid/v2 v2.0.12 // indirect 31 | github.com/mitchellh/go-homedir v1.1.0 // indirect 32 | github.com/rs/cors v1.8.2 // indirect 33 | github.com/soheilhy/cmux v0.1.5 // indirect 34 | github.com/zeebo/blake3 v0.2.3 // indirect 35 | go.uber.org/atomic v1.10.0 // indirect 36 | golang.org/x/sys v0.15.0 // indirect 37 | golang.org/x/text v0.14.0 // indirect 38 | google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect 39 | nhooyr.io/websocket v1.8.7 // indirect 40 | src.agwa.name/tlshacks v0.0.0-20220518131152-d2c6f4e2b780 // indirect 41 | ) 42 | -------------------------------------------------------------------------------- /notificationBot/notificationBot_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package notificationBot 9 | 10 | import ( 11 | "fmt" 12 | jww "github.com/spf13/jwalterweatherman" 13 | "gitlab.com/xx_network/comms/connect" 14 | "gitlab.com/xx_network/primitives/id" 15 | "os" 16 | "sync" 17 | "testing" 18 | ) 19 | 20 | func TestMain(m *testing.M) { 21 | jww.SetStdoutThreshold(jww.LevelTrace) 22 | connect.TestingOnlyDisableTLS = true 23 | os.Exit(m.Run()) 24 | } 25 | 26 | var botPortLock sync.Mutex 27 | var botPort = 1500 28 | 29 | // Helper function to prevent port collisions 30 | func getNextAddress() string { 31 | botPortLock.Lock() 32 | defer func() { 33 | botPort++ 34 | botPortLock.Unlock() 35 | }() 36 | return fmt.Sprintf("0.0.0.0:%d", botPort) 37 | } 38 | 39 | // Error path: Start bot with bad certs 40 | func TestBadCerts(t *testing.T) { 41 | testID := id.NewIdFromString("test", id.Generic, t) 42 | 43 | defer func() { 44 | if r := recover(); r == nil { 45 | t.Errorf("The code did not panic") 46 | } 47 | }() 48 | Address := getNextAddress() 49 | 50 | // This should panic and cause the defer func above to run 51 | _ = StartNotificationBot(testID, Address, NewImplementation(), 52 | []byte("bad cert"), []byte("bad key")) 53 | } 54 | -------------------------------------------------------------------------------- /network/historicalRoundData.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package network 9 | 10 | import ( 11 | "errors" 12 | pb "gitlab.com/elixxir/comms/mixmessages" 13 | "gitlab.com/xx_network/primitives/id" 14 | ) 15 | 16 | // Calls the underlying interface's function to get a specific round from history 17 | func (i *Instance) GetHistoricalRound(id id.Round) (*pb.RoundInfo, error) { 18 | if i.ers != nil { 19 | return i.ers.Retrieve(id) 20 | } 21 | return nil, errors.New("no ExternalRoundStorage object was defined on instance creation") 22 | } 23 | 24 | // Calls the underlying interface's function to get specific rounds from history 25 | func (i *Instance) GetHistoricalRounds(rounds []id.Round) ([]*pb.RoundInfo, error) { 26 | if i.ers != nil { 27 | return i.ers.RetrieveMany(rounds) 28 | } 29 | return nil, errors.New("no ExternalRoundStorage object was defined on instance creation") 30 | } 31 | 32 | // Calls the underlying interface's function to get a range of rounds from history 33 | func (i *Instance) GetHistoricalRoundRange(first, last id.Round) ([]*pb.RoundInfo, error) { 34 | if i.ers != nil { 35 | return i.ers.RetrieveRange(first, last) 36 | } 37 | return nil, errors.New("no ExternalRoundStorage object was defined on instance creation") 38 | } 39 | -------------------------------------------------------------------------------- /udb/poll.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains send functions used for polling 9 | 10 | package udb 11 | 12 | import ( 13 | "github.com/golang/protobuf/ptypes" 14 | "github.com/golang/protobuf/ptypes/any" 15 | "github.com/pkg/errors" 16 | jww "github.com/spf13/jwalterweatherman" 17 | pb "gitlab.com/elixxir/comms/mixmessages" 18 | "gitlab.com/xx_network/comms/connect" 19 | ) 20 | 21 | // RequestNdf is used by User Discovery to Request a NDF from permissioning 22 | func (u *Comms) RequestNdf(host *connect.Host) (*pb.NDF, error) { 23 | 24 | // Create the Send Function 25 | f := func(conn connect.Connection) (*any.Any, error) { 26 | // Set up the context 27 | ctx, cancel := host.GetMessagingContext() 28 | defer cancel() 29 | 30 | // Send the message 31 | resultMsg, err := pb.NewRegistrationClient(conn.GetGrpcConn()). 32 | PollNdf(ctx, &pb.NDFHash{Hash: make([]byte, 0)}) 33 | if err != nil { 34 | return nil, errors.New(err.Error()) 35 | } 36 | return ptypes.MarshalAny(resultMsg) 37 | } 38 | 39 | // Execute the Send function 40 | jww.TRACE.Printf("Sending Request Ndf message...") 41 | resultMsg, err := u.Send(host, f) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | result := &pb.NDF{} 47 | return result, ptypes.UnmarshalAny(resultMsg, result) 48 | } 49 | -------------------------------------------------------------------------------- /testkeys/cmix.rip.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA8/RbJWR9kD8IR3YOAqly52fSgOEPCyObHwsENaxSBDPIusWJ 3 | WKB9SZR5+SdQfd1MmOaIm0aouxrIKdZ2pYsc8li8V+xZhbQgYX3dQb/g3lQBY7ii 4 | QLcJiApKLh6Sl/DbOBmrSCZvNOddwcX2B+ZDuMlQpIS0k9I3Ner49l4lML3MkBvg 5 | uPxDMfXysMzwNovbLWezRjYe7R50EWf86pq8Ekpv+4CbcASOp46ELsRBSNVqZaVM 6 | E+3jT4H/poUlfkFibYkWRGwYEjIFUvjoy8LTYyDNNP1skgl/CqYFpOu7B6Y2DMa7 7 | smTTeGqpLVNQS9Ijr/HxtWhtreitwqR/0ewOMQIDAQABAoIBAQDxobnZ2qQoCNbZ 8 | eUw9RLtEC2jMMJ8m6FiQMeg0hX8jHGuY22nD+ArAo6kAqPkoAdcJp2XtbtpXoRpb 9 | nkochCLixBOhfr/ZF+Xuyq0pn7VKYaiSrmE/ekydi5uX/L40cuOfuIUXzMHfg78w 10 | 3DRp9KBlWjlfCvaVZ+U5qYh49h0eHSF0Le9Q6+gAZ/FCGfLYHI+hpmMK+8QlXvD4 11 | XVnjTEH8dUGUarVGxIw6p0ZsF7T6kYgPHG5e2zc6roIqfOWBG4MBkkYUdPECqY1O 12 | sHbZl5TVUK8GdYhX+U3dnCmC4L96n4djVEGQx68fQB6rJ2WE2VPlpDpj+M4wenPX 13 | MpB02ftBAoGBAP08KOnGrNz40nZ6yfVSRBfkyJHsXAV5XTHtw2u7YSPSXN9tq+7Z 14 | AIVRaO9km2SxWfNDdkEge0xmQjKe/1CkjiwqBBjsDI6P1yYQIReoXndSAZ4JmS8P 15 | 6IzdDpv4vDjmC55Y+c5+uFQn8+1zdeYHwQYie+5LxsAKQxo1wbaNaN6JAoGBAPae 16 | QWhZbiEVkftznrPpfAW4Fl/ZlCAIonvn6uYXM/1TMDGqPIOZyfYB6BIFjkdT9aHP 17 | ZhZtFgWNnAyta37EM+FGnDtBTmFJ3tl4gqZWLIK6T2csinrDsdv/s7VpduB0yAE0 18 | sfWuRZoBfEpUof37TS//YR6Ibm/G0IS8LnrSMIhpAoGAYKluFI45vb9c1szX+kSE 19 | qXoy9UB7f7trz3sqdRz5X2sU+FQspOdAQ6NnormMd0sbQrglk4aKigcejaQTYPzv 20 | J/yBw+GWiXRuc6EEgLtME8/Bvkl7p3MzGVHoGbFAZ5eoJ7Fe6WuFgNofSiwgfMXI 21 | 8EaJd9SE8Rj5tC+A2eXwecECgYAxXv05Jq4lcWwIKt1apyNtAa15AtXkk9XzeDpO 22 | VdbSoBTF3I7Aycjktvz+np4dKXHDMwH8+1mtQuw6nX0no5+/OaONOUW3tFIotzdw 23 | lU/T2/iJbyFJ8mNo54fSiYqC5N4lX6dAx+KnMiTvvIGxlt2c/kMzGZ0CQ4r7B7FG 24 | ZU3SAQKBgQCxE34846J4kH6jRsboyZVkdDdzXQ+NeICJXcaHM2okjnT50IG6Qpwd 25 | 0yPXN6xvYW5L+FVb80NfD1y8LkmBerNEMpcwwDL1ZhgiKWQmITESphnYpm3GV9pe 26 | 1vIMaHV6GeX+q/RcLu2kU4hJbH6HDRJxtdkmw/gdSo9vphDgB6qALw== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /testkeys/gateway.cmix.rip.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA9+AaxwDPxHbhLmn4HoZu0oUM48Qufc6T5XEZTrpMrqJAouXk 3 | +61Jc0EFH96/sbj7VyvnXPRogIENbk2Y84BkB9SkRMIXya/gh9dOEDSgnvj/yg24 4 | l3bdKFqBMKiFg00PYB30fU+Abe3OI/le0I+v++RwH2AV0BMq+T6PcAGjCC1Q1ZB0 5 | wP9/VqNMWq5lbK9wD46IQiSi+SgIQeE7HoiAZXrGO0Y7l9P3+VRoXjRQbqfn3ETN 6 | L9ZvQuarwAYC9Ix5MxUrS5agOmfjc8bfkpYDFAXRXmdKNISJmtCebX2kDrpP8Bda 7 | sx7Fzsx59cEUHCl2aJOWXc7R5m3juOVL1HUxjQIDAQABAoIBAQDlZjXh7lJaVFra 8 | BaBnP6rYkeH3+Nu9+qzNLvpo7emBxQ9ksrZUZW8QhyAi+pcV90hEyJD6agc8xjKT 9 | 3qSq1iu9Q4IzUB2LvoVhslTAkrQD7BVt/XzUUGEks33UdmH5vht4rMBUKHuHQQoC 10 | KQsioZgQ2r0ZjGcjhlnDCa7dzeX/7X4ZyCUXwf523UOZl2/7feT/ZDv1rfHz1xeI 11 | j0UGw+RGQpxnQQeWppAZXkuLsyFVHtvMkeu4LRd+lGklAFOdIeLGG1snrUDasAXW 12 | qjP6l/fTUasoRGvKI61mohvaz/8aPr/0RiHnj0A7ujZFuYLNnJUEHE9lkSoM7OxJ 13 | BZJshm5RAoGBAPypdl+yxKldm+OqyRNprC4PNwYIv7ddt3EbXfRRu3OVYxRnHdWy 14 | Vn6BddICMJF0xbLa5fnnjs+QY9VWjLyTB+yKsMV3/bT284S8Q2myRcGam/c33NM4 15 | mgknndqj+DsqlxfsfUMr7EoDbPcnVO4yME+5ybaW+UjVf6j4/V7mNud7AoGBAPsm 16 | dBMQU8NQLmHAv9DtZPEkaoFJnGZsyYABSlb3LIdTPvh6IWnMPZxQwgRg+9/DIW5G 17 | jO6f0NXCOtChyjXc7XqcE0kYba3KwLsBh4BS1R3JQ8UjwcYfJ0zUCtv6GEBySUxL 18 | f6kWg4pramAT7neh4nLw9xKSARQsxns352eLY3iXAoGBAPk4Hq8eoqV81DupRlWD 19 | snZsQ5YMIy83na5PlEleJUxJWkXRwLf9BRwbjp/r0QRo7mkRSi9KnK9YeWEB0ihf 20 | zz0fQGZHiVVrEaEoIkarterWGmmsDIG8HxRQo3/6pOuXgxnyB0yukpAe+OiBP2EE 21 | 2GqLk9Aq8xFIpneBwkpNQnLpAoGACPqT9caEqBvqvJXuhaKnFDPwFtUx4TuMbKYf 22 | KULG9cOVIb8ECMtDfJ4qgYuSFardPBsLmNBf3/SEs8bE+u3+E38W9WsJKMhx4X2p 23 | gX30no7+fELN/65Hcmi2Rq0HRu6PDko4syvZ4g37DJpJ0T8ZYHCa9LABl94N7oPO 24 | IxaJpNsCgYEApPX7Z30N7JsjFKnAVwpHOJbz+eXd6ROwff+uYmbXP3OqZmCqmfg6 25 | YnyyVxElGYeeXV4A8ymbR3tyT7tHephZYeAEDqWY/Hqe7NEbc2CYIq9aJRZjxPSz 26 | 4XXizjIXWRFiYBbdQGA+oCs+e7k+uQFzIcAnnPKSkrH4tCuMqEAlR64= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /gateway/getPermissioningAddress_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | "gitlab.com/elixxir/comms/node" 12 | "gitlab.com/xx_network/comms/connect" 13 | "gitlab.com/xx_network/comms/gossip" 14 | "gitlab.com/xx_network/primitives/id" 15 | "testing" 16 | ) 17 | 18 | // Happy path. 19 | func TestComms_SendGetPermissioningAddress(t *testing.T) { 20 | /* jww.SetLogThreshold(jww.LevelTrace) 21 | jww.SetStdoutThreshold(jww.LevelTrace)*/ 22 | GatewayAddress := getNextGatewayAddress() 23 | ServerAddress := getNextServerAddress() 24 | testID := id.NewIdFromString("test", id.Generic, t) 25 | gateway := StartGateway(testID, GatewayAddress, NewImplementation(), nil, 26 | nil, gossip.DefaultManagerFlags()) 27 | server := node.StartNode(testID, ServerAddress, 0, node.NewImplementation(), 28 | nil, nil) 29 | defer gateway.Shutdown() 30 | defer server.Shutdown() 31 | manager := connect.NewManagerTesting(t) 32 | 33 | params := connect.GetDefaultHostParams() 34 | params.AuthEnabled = false 35 | host, err := manager.AddHost(testID, ServerAddress, nil, params) 36 | if err != nil { 37 | t.Errorf("Unable to call NewHost: %+v", err) 38 | } 39 | 40 | _, err = gateway.SendGetPermissioningAddress(host) 41 | if err != nil { 42 | t.Errorf("SendGetPermissioningAddress returned an error: %+v", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /gateway/broadcast_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | "gitlab.com/elixxir/comms/node" 12 | "gitlab.com/xx_network/comms/connect" 13 | "gitlab.com/xx_network/comms/gossip" 14 | "gitlab.com/xx_network/primitives/id" 15 | "testing" 16 | ) 17 | 18 | // Smoke Test GetBufferInfo 19 | func TestGetRoundBufferInfo(t *testing.T) { 20 | GatewayAddress := getNextGatewayAddress() 21 | ServerAddress := getNextServerAddress() 22 | testID := id.NewIdFromString("test", id.Gateway, t) 23 | nodeID := id.NewIdFromString("test", id.Node, t) 24 | gateway := StartGateway(testID, GatewayAddress, NewImplementation(), nil, 25 | nil, gossip.DefaultManagerFlags()) 26 | server := node.StartNode(nodeID, ServerAddress, 0, node.NewImplementation(), 27 | nil, nil) 28 | defer gateway.Shutdown() 29 | defer server.Shutdown() 30 | manager := connect.NewManagerTesting(t) 31 | 32 | params := connect.GetDefaultHostParams() 33 | params.AuthEnabled = false 34 | host, err := manager.AddHost(testID, ServerAddress, nil, params) 35 | if err != nil { 36 | t.Errorf("Unable to call NewHost: %+v", err) 37 | } 38 | 39 | bufSize, err := gateway.GetRoundBufferInfo(host) 40 | if err != nil { 41 | t.Errorf("GetRoundBufferInfo: Error received: %s", err) 42 | } 43 | if bufSize.RoundBufferSize != 0 { 44 | t.Errorf("GetRoundBufferInfo: Unexpected buffer size.") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /notificationBot/poll.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains notificationBot -> all servers functionality 9 | 10 | package notificationBot 11 | 12 | import ( 13 | "github.com/golang/protobuf/ptypes" 14 | "github.com/golang/protobuf/ptypes/any" 15 | "github.com/pkg/errors" 16 | jww "github.com/spf13/jwalterweatherman" 17 | pb "gitlab.com/elixxir/comms/mixmessages" 18 | "gitlab.com/xx_network/comms/connect" 19 | ) 20 | 21 | // PollNdf gets the NDF from the permissioning server 22 | func (nb *Comms) PollNdf(host *connect.Host, ndfHash []byte) (*pb.NDF, error) { 23 | // Create the Send Function 24 | f := func(conn connect.Connection) (*any.Any, error) { 25 | // Set up the context 26 | ctx, cancel := host.GetMessagingContext() 27 | defer cancel() 28 | 29 | // We use an empty NDF Hash to request an NDF 30 | ndfRequest := &pb.NDFHash{Hash: ndfHash} 31 | 32 | // Send the message 33 | resultMsg, err := pb.NewRegistrationClient(conn.GetGrpcConn()). 34 | PollNdf(ctx, ndfRequest) 35 | if err != nil { 36 | return nil, errors.New(err.Error()) 37 | } 38 | return ptypes.MarshalAny(resultMsg) 39 | } 40 | 41 | // Execute the Send function 42 | jww.TRACE.Printf("Sending Request Ndf message...") 43 | resultMsg, err := nb.Send(host, f) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | result := &pb.NDF{} 49 | err = ptypes.UnmarshalAny(resultMsg, result) 50 | return result, err 51 | } 52 | -------------------------------------------------------------------------------- /gateway/getPermissioningAddress.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | "github.com/golang/protobuf/ptypes" 12 | "github.com/golang/protobuf/ptypes/any" 13 | "github.com/pkg/errors" 14 | jww "github.com/spf13/jwalterweatherman" 15 | pb "gitlab.com/elixxir/comms/mixmessages" 16 | "gitlab.com/xx_network/comms/connect" 17 | "gitlab.com/xx_network/comms/messages" 18 | ) 19 | 20 | // SendGetPermissioningAddress ping server to return the address of 21 | // permissioning. 22 | func (g *Comms) SendGetPermissioningAddress(host *connect.Host) (string, error) { 23 | 24 | // Create the Send Function 25 | f := func(conn connect.Connection) (*any.Any, error) { 26 | // Set up the context 27 | ctx, cancel := host.GetMessagingContext() 28 | defer cancel() 29 | 30 | // Send the message 31 | resultMsg, err := pb.NewNodeClient(conn.GetGrpcConn()). 32 | GetPermissioningAddress(ctx, &messages.Ping{}) 33 | if err != nil { 34 | return nil, errors.New(err.Error()) 35 | } 36 | return ptypes.MarshalAny(resultMsg) 37 | } 38 | 39 | // Execute the Send function 40 | jww.TRACE.Printf("Sending get permissioning address ping.") 41 | resultMsg, err := g.Send(host, f) 42 | if err != nil { 43 | return "", err 44 | } 45 | 46 | // Marshall the result 47 | result := &pb.StrAddress{} 48 | err = ptypes.UnmarshalAny(resultMsg, result) 49 | if err != nil { 50 | return "", err 51 | } 52 | return result.Address, nil 53 | } 54 | -------------------------------------------------------------------------------- /network/dataStructures/extendedRoundStorage.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | import ( 11 | pb "gitlab.com/elixxir/comms/mixmessages" 12 | "gitlab.com/xx_network/primitives/id" 13 | ) 14 | 15 | // The ExtendedRoundStorage (ERS) interface allows storing rounds inside of an external database for clients to pull 16 | // from, because the ring buffer only contains a limited number of them while clients might need to go further back 17 | // into history. 18 | type ExternalRoundStorage interface { 19 | // Store: stores the round info inside the underlying storage medium, which generally is a database. Store will 20 | // add the round info to the database if it doesn't exist and will only overwrite the data if it does exist in the 21 | // event that the update ID of the passed in data is greater than the update ID of the existing round info. 22 | Store(*pb.RoundInfo) error 23 | // Retrieve will return the round info for the given round ID and will return nil but not an error if it does not 24 | // exist. 25 | Retrieve(id id.Round) (*pb.RoundInfo, error) 26 | // RetrieveMany will return all rounds passed in in the ID list, if the round doesn't its reciprocal entry in the 27 | // returned slice will be blank. 28 | RetrieveMany(rounds []id.Round) ([]*pb.RoundInfo, error) 29 | // RetrieveRange will return all rounds in the range, if the round doesn't exist the reciprocal entry in the 30 | // returned slice will be blank. 31 | RetrieveRange(first, last id.Round) ([]*pb.RoundInfo, error) 32 | } 33 | -------------------------------------------------------------------------------- /remoteSync/server/endpoint.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2022 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains remote sync gRPC endpoints 9 | 10 | package server 11 | 12 | import ( 13 | pb "gitlab.com/elixxir/comms/mixmessages" 14 | "gitlab.com/xx_network/comms/messages" 15 | "golang.org/x/net/context" 16 | ) 17 | 18 | // Login to the server, receiving a token 19 | func (rc *Comms) Login(ctx context.Context, message *pb.RsAuthenticationRequest) (*pb.RsAuthenticationResponse, error) { 20 | return rc.handler.Login(message) 21 | } 22 | 23 | // Read data from the server 24 | func (rc *Comms) Read(ctx context.Context, message *pb.RsReadRequest) (*pb.RsReadResponse, error) { 25 | return rc.handler.Read(message) 26 | } 27 | 28 | // Write data to the server 29 | func (rc *Comms) Write(ctx context.Context, message *pb.RsWriteRequest) (*messages.Ack, error) { 30 | return rc.handler.Write(message) 31 | } 32 | 33 | // GetLastModified returns the last time a resource was modified 34 | func (rc *Comms) GetLastModified(ctx context.Context, message *pb.RsReadRequest) (*pb.RsTimestampResponse, error) { 35 | return rc.handler.GetLastModified(message) 36 | } 37 | 38 | // GetLastWrite returns the last time this remote sync server was modified 39 | func (rc *Comms) GetLastWrite(ctx context.Context, message *pb.RsLastWriteRequest) (*pb.RsTimestampResponse, error) { 40 | return rc.handler.GetLastWrite(message) 41 | } 42 | 43 | // ReadDir reads a directory from the server 44 | func (rc *Comms) ReadDir(ctx context.Context, message *pb.RsReadRequest) (*pb.RsReadDirResponse, error) { 45 | return rc.handler.ReadDir(message) 46 | } 47 | -------------------------------------------------------------------------------- /mixmessages/notifications.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package mixmessages 9 | 10 | import ( 11 | "bytes" 12 | "encoding/base64" 13 | "encoding/csv" 14 | "github.com/pkg/errors" 15 | jww "github.com/spf13/jwalterweatherman" 16 | "strings" 17 | ) 18 | 19 | func MakeNotificationsCSV(l []*NotificationData) string { 20 | output := make([][]string, len(l)) 21 | for i, n := range l { 22 | output[i] = []string{base64.StdEncoding.EncodeToString(n.MessageHash), 23 | base64.StdEncoding.EncodeToString(n.IdentityFP)} 24 | } 25 | 26 | buf := &bytes.Buffer{} 27 | w := csv.NewWriter(buf) 28 | if err := w.WriteAll(output); err != nil { 29 | jww.FATAL.Printf("Failed to make notificationsCSV: %+v", err) 30 | } 31 | return string(buf.Bytes()) 32 | } 33 | 34 | func DecodeNotificationsCSV(data string) ([]*NotificationData, error) { 35 | r := csv.NewReader(strings.NewReader(data)) 36 | read, err := r.ReadAll() 37 | if err != nil { 38 | return nil, errors.WithMessage(err, "Failed to decode notifications CSV") 39 | } 40 | 41 | l := make([]*NotificationData, len(read)) 42 | for i, touple := range read { 43 | messageHash, err := base64.StdEncoding.DecodeString(touple[0]) 44 | if err != nil { 45 | return nil, errors.WithMessage(err, "Failed decode an element") 46 | } 47 | identityFP, err := base64.StdEncoding.DecodeString(touple[1]) 48 | if err != nil { 49 | return nil, errors.WithMessage(err, "Failed decode an element") 50 | } 51 | l[i] = &NotificationData{ 52 | EphemeralID: 0, 53 | IdentityFP: identityFP, 54 | MessageHash: messageHash, 55 | } 56 | } 57 | return l, nil 58 | } 59 | -------------------------------------------------------------------------------- /clientregistrar/endpoint.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains registration server gRPC endpoints 9 | 10 | package clientregistrar 11 | 12 | import ( 13 | "github.com/pkg/errors" 14 | jww "github.com/spf13/jwalterweatherman" 15 | pb "gitlab.com/elixxir/comms/mixmessages" 16 | "gitlab.com/xx_network/comms/messages" 17 | "golang.org/x/net/context" 18 | ) 19 | 20 | // Handles validation of reverse-authentication tokens 21 | func (r *Comms) AuthenticateToken(ctx context.Context, 22 | msg *messages.AuthenticatedMessage) (*messages.Ack, error) { 23 | err := r.ValidateToken(msg) 24 | if err != nil { 25 | jww.ERROR.Printf("Unable to authenticate token: %+v", err) 26 | } 27 | return &messages.Ack{}, err 28 | } 29 | 30 | // Handles reception of reverse-authentication token requests 31 | func (r *Comms) RequestToken(context.Context, *messages.Ping) (*messages.AssignToken, error) { 32 | token, err := r.GenerateToken() 33 | return &messages.AssignToken{ 34 | Token: token, 35 | }, err 36 | } 37 | 38 | // RegisterUser event handler which registers a user with the platform 39 | func (r *Comms) RegisterUser(ctx context.Context, msg *pb.ClientRegistration) ( 40 | *pb.SignedClientRegistrationConfirmations, error) { 41 | // Obtain the signed key by passing to registration server 42 | confirmationMessage, err := r.handler.RegisterUser(msg) 43 | // Obtain the error message, if any 44 | errMsg := "" 45 | if err != nil { 46 | errMsg = err.Error() 47 | err = errors.New(err.Error()) 48 | } 49 | confirmationMessage.Error = errMsg 50 | // Return the confirmation message 51 | return confirmationMessage, err 52 | } 53 | -------------------------------------------------------------------------------- /network/securedNdf.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Wrapper for the ndf object 9 | 10 | package network 11 | 12 | import ( 13 | "github.com/pkg/errors" 14 | pb "gitlab.com/elixxir/comms/mixmessages" 15 | ds "gitlab.com/elixxir/comms/network/dataStructures" 16 | "gitlab.com/xx_network/comms/signature" 17 | "gitlab.com/xx_network/crypto/signature/rsa" 18 | "gitlab.com/xx_network/primitives/ndf" 19 | ) 20 | 21 | // wraps the ndf data structure, expoting all the functionality expect the 22 | // ability to change the ndf 23 | type SecuredNdf struct { 24 | f *ds.Ndf 25 | } 26 | 27 | // Initialize a securedNdf from a primitives NetworkDefinition object 28 | func NewSecuredNdf(definition *ndf.NetworkDefinition) (*SecuredNdf, error) { 29 | ndf, err := ds.NewNdf(definition) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return &SecuredNdf{ 34 | ndf, 35 | }, nil 36 | } 37 | 38 | // unexported NDF update code 39 | func (sndf *SecuredNdf) update(m *pb.NDF, key *rsa.PublicKey) error { 40 | err := signature.VerifyRsa(m, key) 41 | if err != nil { 42 | return errors.WithMessage(err, "Could not validate NDF") 43 | } 44 | 45 | return sndf.f.Update(m) 46 | } 47 | 48 | // Get the primitives object for an ndf 49 | func (sndf *SecuredNdf) Get() *ndf.NetworkDefinition { 50 | return sndf.f.Get() 51 | } 52 | 53 | // get the hash of the ndf 54 | func (sndf *SecuredNdf) GetHash() []byte { 55 | return sndf.f.GetHash() 56 | } 57 | 58 | // get the protobuf message NDF 59 | func (sndf *SecuredNdf) GetPb() *pb.NDF { 60 | return sndf.f.GetPb() 61 | } 62 | 63 | // Compare a hash to the stored 64 | func (sndf *SecuredNdf) CompareHash(h []byte) bool { 65 | return sndf.f.CompareHash(h) 66 | } 67 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | before_script: 2 | - go version || echo "Go executable not found." 3 | - echo $CI_BUILD_REF 4 | - echo $CI_PROJECT_DIR 5 | - echo $PWD 6 | 7 | - eval $(ssh-agent -s) 8 | - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null 9 | - mkdir -p ~/.ssh 10 | - chmod 700 ~/.ssh 11 | - ssh-keyscan -t rsa $GITLAB_SERVER > ~/.ssh/known_hosts 12 | - git config --global url."git@$GITLAB_SERVER:".insteadOf "https://gitlab.com/" 13 | - git config --global url."git@$GITLAB_SERVER:".insteadOf "https://git.xx.network/" --add 14 | - export PATH=$HOME/go/bin:$PATH 15 | 16 | stages: 17 | - build 18 | - trigger_integration 19 | 20 | build: 21 | stage: build 22 | image: $DOCKER_IMAGE 23 | script: 24 | - git clean -ffdx 25 | - go mod vendor -v 26 | - go build ./... 27 | - go mod tidy 28 | - mkdir -p testdata 29 | # Test coverage 30 | - go-acc --covermode atomic --output testdata/coverage.out ./... -- -v 31 | - grep -v mixmessages\.pb\.go testdata/coverage.out | grep -v mixmessages_grpc\.pb\.go | grep -v endpoint\.go | grep -v gateway\.go | grep -v -e testkeys | grep -v -e testutils | grep -v node\.go > testdata/coverage-real.out 32 | - go tool cover -func=testdata/coverage-real.out 33 | - go tool cover -html=testdata/coverage-real.out -o testdata/coverage.html 34 | 35 | # Test Coverage Check 36 | - go tool cover -func=testdata/coverage-real.out | grep "total:" | awk '{print $3}' | sed 's/\%//g' > testdata/coverage-percentage.txt 37 | - export CODE_CHECK=$(echo "$(cat testdata/coverage-percentage.txt) >= $MIN_CODE_COVERAGE" | bc -l) 38 | - (if [ "$CODE_CHECK" == "1" ]; then echo "Minimum coverage of $MIN_CODE_COVERAGE succeeded"; else echo "Minimum coverage of $MIN_CODE_COVERAGE failed"; exit 1; fi); 39 | - mkdir -p release 40 | - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-w -s' ./... 41 | - cd release 42 | artifacts: 43 | paths: 44 | - vendor/ 45 | - testdata/ 46 | - release/ 47 | 48 | trigger-integration: 49 | stage: trigger_integration 50 | trigger: 51 | project: elixxir/integration 52 | branch: $CI_COMMIT_REF_NAME 53 | only: 54 | - master 55 | -------------------------------------------------------------------------------- /client/serverStream.go: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file // 6 | /////////////////////////////////////////////////////////////////////////////// 7 | 8 | package client 9 | 10 | import ( 11 | "context" 12 | "git.xx.network/elixxir/grpc-web-go-client/grpcweb" 13 | pb "gitlab.com/elixxir/comms/mixmessages" 14 | "google.golang.org/grpc/metadata" 15 | "io" 16 | "strings" 17 | ) 18 | 19 | var _ pb.Gateway_PollClient = (*serverStream)(nil) 20 | 21 | // serverStream wraps a grpcweb.ServerStream to make it adhere to the 22 | // pb.Gateway_PollClient interface. 23 | type serverStream struct { 24 | ctx context.Context 25 | grpcweb.ServerStream 26 | } 27 | 28 | func newServerStream( 29 | ctx context.Context, ws grpcweb.ServerStream) pb.Gateway_PollClient { 30 | return &serverStream{ctx, ws} 31 | } 32 | 33 | func (s *serverStream) Recv() (*pb.StreamChunk, error) { 34 | m := new(pb.StreamChunk) 35 | if err := s.ServerStream.Receive(s.ctx, m); err != nil { 36 | return nil, err 37 | } 38 | return m, nil 39 | } 40 | 41 | func (s *serverStream) Header() (metadata.MD, error) { 42 | return s.ServerStream.Header() 43 | } 44 | 45 | func (s *serverStream) Trailer() metadata.MD { 46 | return s.ServerStream.Trailer() 47 | } 48 | 49 | func (s *serverStream) CloseSend() error { 50 | return nil 51 | } 52 | 53 | func (s *serverStream) Context() context.Context { 54 | return s.ctx 55 | } 56 | 57 | func (s *serverStream) SendMsg(m interface{}) error { 58 | return s.ServerStream.Send(s.ctx, m) 59 | } 60 | 61 | func (s *serverStream) RecvMsg(m interface{}) error { 62 | err := s.ServerStream.Receive(s.ctx, m) 63 | 64 | // If the response has been closed, return EOF 65 | const responseClosedErr = "http: read on closed response body" 66 | if err != nil && strings.Contains(err.Error(), responseClosedErr) { 67 | return io.EOF 68 | } 69 | 70 | return err 71 | } 72 | -------------------------------------------------------------------------------- /gateway/broadcast.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains logic for miscellaneous send functions 9 | 10 | package gateway 11 | 12 | import ( 13 | "github.com/golang/protobuf/ptypes" 14 | "github.com/golang/protobuf/ptypes/any" 15 | "github.com/pkg/errors" 16 | jww "github.com/spf13/jwalterweatherman" 17 | pb "gitlab.com/elixxir/comms/mixmessages" 18 | "gitlab.com/xx_network/comms/connect" 19 | "gitlab.com/xx_network/comms/messages" 20 | ) 21 | 22 | // GetRoundBufferInfo Asks the server for round buffer info, specifically how 23 | // many rounds have gone through precomputation. 24 | // Note that this function should block if the buffer size is 0 25 | // This allows the caller to continuously poll without spinning too much. 26 | func (g *Comms) GetRoundBufferInfo(host *connect.Host) (*pb.RoundBufferInfo, error) { 27 | 28 | // Create the Send Function 29 | f := func(conn connect.Connection) (*any.Any, error) { 30 | // Set up the context 31 | ctx, cancel := host.GetMessagingContext() 32 | defer cancel() 33 | // Pack message into an authenticated message 34 | authMsg, err := g.PackAuthenticatedMessage(&messages.Ping{}, host, false) 35 | if err != nil { 36 | return nil, errors.New(err.Error()) 37 | } 38 | // Send the message 39 | resultMsg, err := pb.NewNodeClient(conn.GetGrpcConn()). 40 | GetRoundBufferInfo(ctx, authMsg) 41 | if err != nil { 42 | return nil, errors.New(err.Error()) 43 | } 44 | return ptypes.MarshalAny(resultMsg) 45 | } 46 | 47 | // Execute the Send function 48 | jww.TRACE.Printf("Sending Get Round Buffer info message...") 49 | resultMsg, err := g.Send(host, f) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | // Marshall the result 55 | result := &pb.RoundBufferInfo{} 56 | return result, ptypes.UnmarshalAny(resultMsg, result) 57 | } 58 | -------------------------------------------------------------------------------- /network/dataStructures/ndf_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | import ( 11 | "gitlab.com/elixxir/comms/mixmessages" 12 | "gitlab.com/elixxir/comms/testutils" 13 | "testing" 14 | ) 15 | 16 | func setup() *Ndf { 17 | msg := &mixmessages.NDF{ 18 | Ndf: testutils.ExampleNDF, 19 | } 20 | ndf := &Ndf{} 21 | 22 | _ = ndf.Update(msg) 23 | return ndf 24 | } 25 | 26 | func TestNdf_Get(t *testing.T) { 27 | ndf := setup() 28 | 29 | if ndf.Get() == nil { 30 | t.Error("Should have returned ndf.f") 31 | } 32 | } 33 | 34 | func TestNdf_Update(t *testing.T) { 35 | msg := &mixmessages.NDF{ 36 | Ndf: testutils.ExampleNDF, 37 | } 38 | badMsg := &mixmessages.NDF{ 39 | Ndf: []byte("lasagna"), 40 | } 41 | ndf := Ndf{} 42 | 43 | err := ndf.Update(badMsg) 44 | if err == nil { 45 | t.Error("Should have returned error when unable to decode ndf") 46 | } 47 | 48 | err = ndf.Update(msg) 49 | if err != nil { 50 | t.Errorf("Failed to update ndf: %+v", err) 51 | } 52 | 53 | if ndf.f == nil || ndf.hash == nil || ndf.pb == nil { 54 | t.Error("Failed to properly set contents of ndf object") 55 | } 56 | } 57 | 58 | func TestNdf_GetHash(t *testing.T) { 59 | ndf := setup() 60 | 61 | if ndf.GetHash() == nil { 62 | t.Error("Hash not properly returned") 63 | } 64 | } 65 | 66 | func TestNdf_GetPb(t *testing.T) { 67 | ndf := setup() 68 | 69 | if ndf.GetPb() == nil { 70 | t.Error("Pb not properly set") 71 | } 72 | } 73 | 74 | func TestNdf_CompareHash(t *testing.T) { 75 | ndf := &Ndf{} 76 | 77 | ndf = setup() 78 | b := ndf.CompareHash(ndf.hash) 79 | if !b { 80 | t.Error("Should return true when hashes are the same") 81 | } 82 | 83 | b = ndf.CompareHash([]byte("test")) 84 | if b { 85 | t.Error("Should return false when hashes are different") 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /authorizer/endpoint.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains authorizer server gRPC endpoints 9 | 10 | package authorizer 11 | 12 | import ( 13 | "context" 14 | 15 | jww "github.com/spf13/jwalterweatherman" 16 | pb "gitlab.com/elixxir/comms/mixmessages" 17 | "gitlab.com/xx_network/comms/connect" 18 | "gitlab.com/xx_network/comms/messages" 19 | ) 20 | 21 | // Handles validation of reverse-authentication tokens 22 | func (r *Comms) AuthenticateToken(ctx context.Context, 23 | msg *messages.AuthenticatedMessage) (*messages.Ack, error) { 24 | err := r.ValidateToken(msg) 25 | if err != nil { 26 | jww.ERROR.Printf("Unable to authenticate token: %+v", err) 27 | } 28 | return &messages.Ack{}, err 29 | } 30 | 31 | // Handles reception of reverse-authentication token requests 32 | func (r *Comms) RequestToken(context.Context, *messages.Ping) (*messages.AssignToken, error) { 33 | token, err := r.GenerateToken() 34 | return &messages.AssignToken{ 35 | Token: token, 36 | }, err 37 | } 38 | 39 | // Authorizes a node to talk to permissioning 40 | func (r *Comms) Authorize(ctx context.Context, auth *pb.AuthorizerAuth) (ack *messages.Ack, err error) { 41 | address, _, err := connect.GetAddressFromContext(ctx) 42 | if err != nil { 43 | return &messages.Ack{Error: err.Error()}, err 44 | } 45 | returned_err := r.handler.Authorize(auth, address) 46 | errString := "" 47 | if err != nil { 48 | errString = err.Error() 49 | } 50 | return &messages.Ack{Error: errString}, returned_err 51 | } 52 | 53 | // Request a signed certificate for HTTPS 54 | func (r *Comms) RequestCert(ctx context.Context, msg *pb.AuthorizerCertRequest) (*messages.Ack, error) { 55 | return r.handler.RequestCert(msg) 56 | } 57 | 58 | // Request ACME key for HTTPS 59 | func (r *Comms) RequestEABCredentials(ctx context.Context, msg *pb.EABCredentialRequest) (*pb.EABCredentialResponse, error) { 60 | return r.handler.RequestEABCredentials(msg) 61 | } 62 | -------------------------------------------------------------------------------- /network/dataStructures/roundUpdates.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Stores a list of all updates in order of update id 9 | 10 | package dataStructures 11 | 12 | import ( 13 | "github.com/pkg/errors" 14 | pb "gitlab.com/elixxir/comms/mixmessages" 15 | "gitlab.com/xx_network/ring" 16 | ) 17 | 18 | const RoundUpdatesBufLen = 1500 19 | 20 | // Standard ring buffer, but objects come with numbering 21 | type Updates struct { 22 | updates *ring.Buff 23 | } 24 | 25 | // Create a new Updates object 26 | func NewUpdates() *Updates { 27 | // we want each updateId stored in this structure 28 | return &Updates{ 29 | updates: ring.NewBuff(RoundUpdatesBufLen), 30 | } 31 | } 32 | 33 | // Add a round to the ring buffer 34 | func (u *Updates) AddRound(rnd *Round) error { 35 | return u.updates.UpsertById(int(rnd.info.UpdateID), rnd) 36 | } 37 | 38 | // Get a given update ID from the ring buffer 39 | func (u *Updates) GetUpdate(id int) (*pb.RoundInfo, error) { 40 | 41 | val, err := u.updates.GetById(id) 42 | if err != nil { 43 | return nil, errors.Wrapf(err, "Failed to get round by update "+ 44 | "ID with id %d", id) 45 | } 46 | 47 | if val == nil { 48 | return nil, errors.Errorf("Failed to get round by update ID "+ 49 | "with id %d, got nil round", id) 50 | } 51 | 52 | return val.(*Round).Get(), nil 53 | } 54 | 55 | // gets all updates after a given ID 56 | func (u *Updates) GetUpdates(id int) []*pb.RoundInfo { 57 | interfaceList, err := u.updates.GetNewerById(id) 58 | 59 | if err != nil { 60 | return make([]*pb.RoundInfo, 0) 61 | } 62 | 63 | infoList := make([]*pb.RoundInfo, len(interfaceList)) 64 | 65 | addCount := 0 66 | for _, face := range interfaceList { 67 | if face != nil { 68 | rnd := face.(*Round) 69 | // Retrieve and validate the round info object 70 | infoList[addCount] = rnd.Get() 71 | addCount++ 72 | } 73 | 74 | } 75 | 76 | return infoList[:addCount] 77 | } 78 | 79 | // Get the id of the newest update in the buffer 80 | func (u *Updates) GetLastUpdateID() int { 81 | return u.updates.GetNewestId() 82 | } 83 | -------------------------------------------------------------------------------- /node/node_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package node 9 | 10 | import ( 11 | "fmt" 12 | "gitlab.com/xx_network/primitives/id" 13 | "sync" 14 | "testing" 15 | ) 16 | 17 | var serverPortLock sync.Mutex 18 | var serverPort = 15000 // Changed from 5000 to avoid conflicts with macOS Control Center (Airplay on port 5000) 19 | 20 | func getNextServerAddress() string { 21 | serverPortLock.Lock() 22 | defer func() { 23 | serverPort++ 24 | serverPortLock.Unlock() 25 | }() 26 | return fmt.Sprintf("0.0.0.0:%d", serverPort) 27 | } 28 | 29 | // Tests whether the server can be connected to and run an RPC with TLS enabled 30 | //todo: fix and re enable 31 | /*func TestTLS(t *testing.T) { 32 | serverAddress := getNextServerAddress() 33 | 34 | keyPath := testkeys.GetNodeKeyPath() 35 | keyData := testkeys.LoadFromPath(keyPath) 36 | certPath := testkeys.GetNodeCertPath() 37 | certData := testkeys.LoadFromPath(certPath) 38 | testNodeID := id.NewIdFromString("test", id.Node, t) 39 | 40 | server := StartNode(testNodeID, serverAddress, 0, NewImplementation(), 41 | certData, keyData) 42 | serverAddress2 := getNextServerAddress() 43 | server2 := StartNode(testNodeID, serverAddress2, 0, NewImplementation(), 44 | certData, keyData) 45 | defer server.Shutdown() 46 | defer server2.Shutdown() 47 | manager := connect.NewManagerTesting(t) 48 | 49 | params := connect.GetDefaultHostParams() 50 | params.AuthEnabled = false 51 | host, err := manager.AddHost(testNodeID, serverAddress, certData, params) 52 | if err != nil { 53 | t.Errorf("Unable to call NewHost: %+v", err) 54 | } 55 | 56 | _, err = server2.SendAskOnline(host) 57 | if err != nil { 58 | t.Error(err) 59 | } 60 | }*/ 61 | 62 | func TestBadCerts(t *testing.T) { 63 | defer func() { 64 | if r := recover(); r == nil { 65 | t.Errorf("The code did not panic") 66 | } 67 | }() 68 | Address := getNextServerAddress() 69 | 70 | testID := id.NewIdFromString("test", id.Node, t) 71 | 72 | _ = StartNode(testID, Address, 0, NewImplementation(), 73 | []byte("bad cert"), []byte("bad key")) 74 | } 75 | -------------------------------------------------------------------------------- /publicAddress/override.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package publicAddress 9 | 10 | import ( 11 | "github.com/pkg/errors" 12 | "net" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | // GetIpOverride returns the public IP of the caller or the override IP, if it 18 | // is supplied. If overrideIP is empty, then the caller's IP is looked up, 19 | // joined with the port, and returned. If the overrideIP is supplied, then it is 20 | // joined with the port and returned. If the overrideIP has a port, then it is 21 | // returned as is. 22 | func GetIpOverride(overrideIP string, port int) (string, error) { 23 | return getIpOverride(overrideIP, port, lookupServices) 24 | } 25 | 26 | // getIpOverride returns the public IP address. If an override address is 27 | // provided, it is returned instead with the provided port unless it already 28 | // includes a port. 29 | func getIpOverride(overrideIP string, port int, services []Service) (string, error) { 30 | // If an override address was not supplied, then lookup gateway's public IP 31 | // and combine it with the supplied port; otherwise, use the override IP 32 | if overrideIP == "" { 33 | publicIp, err := GetIP(services, DefaultPollTimeout) 34 | if err != nil { 35 | return "", errors.Errorf("failed to lookup public IP address: %v", err) 36 | } 37 | 38 | return net.JoinHostPort(publicIp, strconv.Itoa(port)), nil 39 | } else { 40 | return JoinIpPort(overrideIP, port) 41 | } 42 | } 43 | 44 | // JoinIpPort joins the ip and port together. If the ip already has a port, it 45 | // is returned as is. 46 | func JoinIpPort(ip string, port int) (string, error) { 47 | if ip == "" { 48 | return ip, nil 49 | } 50 | 51 | _, _, err := net.SplitHostPort(ip) 52 | if err != nil { 53 | // If it does not have a port, then append the supplied port 54 | if strings.Contains(err.Error(), "missing port") { 55 | return net.JoinHostPort(ip, strconv.Itoa(port)), nil 56 | } else { 57 | return "", errors.Errorf("failed to parse public IP address "+ 58 | "override \"%s\": %v", ip, err) 59 | } 60 | } 61 | 62 | return ip, nil 63 | } 64 | -------------------------------------------------------------------------------- /clientregistrar/clientregistrar_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package clientregistrar 9 | 10 | import ( 11 | "crypto/tls" 12 | "fmt" 13 | "sync" 14 | "testing" 15 | 16 | "gitlab.com/elixxir/comms/client" 17 | pb "gitlab.com/elixxir/comms/mixmessages" 18 | "gitlab.com/elixxir/comms/testkeys" 19 | "gitlab.com/xx_network/comms/connect" 20 | "gitlab.com/xx_network/primitives/id" 21 | ) 22 | 23 | var serverPortLock sync.Mutex 24 | var serverPort = 16900 // Changed from 5900 to avoid conflicts 25 | 26 | func getNextServerAddress() string { 27 | serverPortLock.Lock() 28 | defer func() { 29 | serverPort++ 30 | serverPortLock.Unlock() 31 | }() 32 | return fmt.Sprintf("localhost:%d", serverPort) 33 | } 34 | 35 | // Tests whether the server can be connected to and run an RPC with TLS enabled 36 | func TestTLS(t *testing.T) { 37 | RegAddress := getNextServerAddress() 38 | 39 | keyPath := testkeys.GetNodeKeyPath() 40 | keyData := testkeys.LoadFromPath(keyPath) 41 | certPath := testkeys.GetNodeCertPath() 42 | certData := testkeys.LoadFromPath(certPath) 43 | testId := id.NewIdFromString("test", id.Generic, t) 44 | rg := StartClientRegistrarServer(testId, RegAddress, 45 | NewImplementation(), 46 | certData, keyData) 47 | // Well, client shouldn't have a server type because it's not a server 48 | // It's a client 49 | // So, we need some way to add a connection to the manager for the client 50 | defer rg.Shutdown() 51 | keypair, err := tls.X509KeyPair(certData, keyData) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | err = rg.ServeHttps(keypair) 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | var c client.Comms 61 | manager := connect.NewManagerTesting(t) 62 | 63 | params := connect.GetDefaultHostParams() 64 | params.AuthEnabled = false 65 | host, err := manager.AddHost(testId, RegAddress, certData, params) 66 | if err != nil { 67 | t.Errorf("Unable to call NewHost: %+v", err) 68 | } 69 | 70 | _, err = c.SendRegistrationMessage(host, &pb.ClientRegistration{}) 71 | if err != nil { 72 | t.Errorf("RegistrationMessage: Error received: %s", err) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /network/dataStructures/roundData.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Store a list of the most recent rounds, holding the most recent update for each 9 | 10 | package dataStructures 11 | 12 | import ( 13 | "github.com/pkg/errors" 14 | "gitlab.com/elixxir/comms/mixmessages" 15 | "gitlab.com/xx_network/primitives/id" 16 | "gitlab.com/xx_network/ring" 17 | ) 18 | 19 | const RoundInfoBufLen = 1500 20 | 21 | // ID numbers can overwrite 22 | type Data struct { 23 | rounds *ring.Buff 24 | } 25 | 26 | // Initialize a new Data object 27 | func NewData() *Data { 28 | // We want data using the round ID as its primary 29 | 30 | return &Data{ 31 | rounds: ring.NewBuff(RoundInfoBufLen), 32 | } 33 | } 34 | 35 | // Upsert a round into the ring bugger 36 | func (d *Data) UpsertRound(r *Round) error { 37 | //find the round location 38 | //check the new state is newer then the current 39 | //replace the round info object 40 | return d.rounds.UpsertById(int(r.info.ID), r) 41 | } 42 | 43 | // Get a given round id from the ring buffer as a roundInfo 44 | func (d *Data) GetRound(id int) (*mixmessages.RoundInfo, error) { 45 | val, err := d.rounds.GetById(id) 46 | if err != nil { 47 | return nil, errors.Wrapf(err, "Failed to get round by id with "+ 48 | "%d", id) 49 | } 50 | 51 | if val == nil { 52 | return nil, errors.Errorf("Failed to get round by id with %d, "+ 53 | "got nil round", id) 54 | } 55 | 56 | return val.(*Round).Get(), nil 57 | } 58 | 59 | // Get a given round id from the ring buffer as a round object 60 | func (d *Data) GetWrappedRound(id int) (*Round, error) { 61 | val, err := d.rounds.GetById(id) 62 | if err != nil { 63 | return nil, errors.Wrapf(err, "Failed to get update with id %d", id) 64 | } 65 | var rtn *Round 66 | if val != nil { 67 | rtn = val.(*Round) 68 | } 69 | return rtn, nil 70 | } 71 | 72 | // Get the ID of the newest round in the buffer 73 | func (d *Data) GetLastRoundID() id.Round { 74 | return id.Round(d.rounds.GetNewestId()) 75 | } 76 | 77 | // Gets the ID of the oldest roundd in the buffer 78 | func (d *Data) GetOldestRoundID() id.Round { 79 | return id.Round(d.rounds.GetOldestId()) 80 | } 81 | -------------------------------------------------------------------------------- /gateway/authorizer.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | "github.com/golang/protobuf/ptypes" 12 | "github.com/golang/protobuf/ptypes/any" 13 | jww "github.com/spf13/jwalterweatherman" 14 | pb "gitlab.com/elixxir/comms/mixmessages" 15 | "gitlab.com/xx_network/comms/connect" 16 | "gitlab.com/xx_network/comms/messages" 17 | ) 18 | 19 | // SendAuthorizerCertRequest sends a request for an https certificate to the authorizer 20 | func (g *Comms) SendAuthorizerCertRequest(host *connect.Host, msg *pb.AuthorizerCertRequest) (*messages.Ack, error) { 21 | // Create the send function 22 | f := func(conn connect.Connection) (*any.Any, error) { 23 | // Set up the context 24 | ctx, cancel := host.GetMessagingContext() 25 | defer cancel() 26 | 27 | // Send the message 28 | resp, err := pb.NewAuthorizerClient(conn.GetGrpcConn()). 29 | RequestCert(ctx, msg) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return ptypes.MarshalAny(resp) 34 | } 35 | 36 | // Execute the Send function 37 | jww.TRACE.Printf("Sending certificate request to authorizer: %s", msg) 38 | resp, err := g.Send(host, f) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | result := &messages.Ack{} 44 | return result, ptypes.UnmarshalAny(resp, result) 45 | } 46 | 47 | // SendEABCredentialRequest sends a request for ACME authorization to the authorizer 48 | func (g *Comms) SendEABCredentialRequest(host *connect.Host, msg *pb.EABCredentialRequest) (*pb.EABCredentialResponse, error) { 49 | // Create the send function 50 | f := func(conn connect.Connection) (*any.Any, error) { 51 | // Set up the context 52 | ctx, cancel := host.GetMessagingContext() 53 | defer cancel() 54 | 55 | // Send the message 56 | resp, err := pb.NewAuthorizerClient(conn.GetGrpcConn()). 57 | RequestEABCredentials(ctx, msg) 58 | if err != nil { 59 | return nil, err 60 | } 61 | return ptypes.MarshalAny(resp) 62 | } 63 | 64 | // Execute the Send function 65 | jww.TRACE.Printf("Sending certificate request to authorizer: %s", msg) 66 | resp, err := g.Send(host, f) 67 | if err != nil { 68 | return nil, err 69 | } 70 | 71 | result := &pb.EABCredentialResponse{} 72 | return result, ptypes.UnmarshalAny(resp, result) 73 | } 74 | -------------------------------------------------------------------------------- /network/dataStructures/ipOverride_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | import ( 11 | "gitlab.com/xx_network/primitives/id" 12 | "testing" 13 | ) 14 | 15 | // tests that NewIpOverrideList returns a properly formatted override list 16 | func TestNewIpOverrideList(t *testing.T) { 17 | nol := NewIpOverrideList() 18 | 19 | if nol.ipOverride == nil { 20 | t.Errorf("Ip Override List should have a map") 21 | } 22 | } 23 | 24 | // tests that creating an override works 25 | func TestIpOverrideList_Override(t *testing.T) { 26 | iol := &IpOverrideList{ 27 | ipOverride: make(map[id.ID]string), 28 | } 29 | 30 | testID := id.NewIdFromUInt(42, id.Node, t) 31 | testIP := "woop" 32 | 33 | iol.Override(testID, testIP) 34 | 35 | if len(iol.ipOverride) != 1 { 36 | t.Errorf("IP override has the wrong length") 37 | } 38 | 39 | resultIP, exist := iol.ipOverride[*testID] 40 | 41 | if !exist { 42 | t.Errorf("could not find override in the map") 43 | } 44 | 45 | if resultIP != testIP { 46 | t.Errorf("the ip returned from the map is not as expected"+ 47 | " expected: %s, recieved: %s", testIP, resultIP) 48 | } 49 | } 50 | 51 | // tests that the old IP is passed through when there is no override 52 | func TestIpOverrideList_CheckOverride_NoOverride(t *testing.T) { 53 | iol := &IpOverrideList{ 54 | ipOverride: make(map[id.ID]string), 55 | } 56 | 57 | testID := id.NewIdFromUInt(42, id.Node, t) 58 | testIP := "woop" 59 | 60 | dummyID := id.NewIdFromUInt(69, id.Node, t) 61 | iol.Override(dummyID, "blarg") 62 | 63 | resultIP := iol.CheckOverride(testID, testIP) 64 | if resultIP != testIP { 65 | t.Errorf("The returned ip is not as expected; "+ 66 | "Expected: %s, Returned: %s", testIP, resultIP) 67 | } 68 | } 69 | 70 | // tests that the old IP overwritten through when there is an override 71 | func TestIpOverrideList_CheckOverride_ValidOverride(t *testing.T) { 72 | iol := &IpOverrideList{ 73 | ipOverride: make(map[id.ID]string), 74 | } 75 | 76 | testID := id.NewIdFromUInt(42, id.Node, t) 77 | testIP := "woop" 78 | 79 | iol.Override(testID, testIP) 80 | 81 | resultIP := iol.CheckOverride(testID, "blarg") 82 | if resultIP != testIP { 83 | t.Errorf("The returned ip is not as expected; "+ 84 | "Expected: %s, Returned: %s", testIP, resultIP) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /gateway/registration_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | pb "gitlab.com/elixxir/comms/mixmessages" 12 | "gitlab.com/elixxir/comms/node" 13 | "gitlab.com/xx_network/comms/connect" 14 | "gitlab.com/xx_network/comms/gossip" 15 | "gitlab.com/xx_network/comms/messages" 16 | "gitlab.com/xx_network/primitives/id" 17 | "testing" 18 | ) 19 | 20 | // Smoke test SendRequestClientKeyMessage 21 | func TestSendRequestNonceMessage(t *testing.T) { 22 | GatewayAddress := getNextGatewayAddress() 23 | ServerAddress := getNextServerAddress() 24 | testID := id.NewIdFromString("test", id.Generic, t) 25 | gateway := StartGateway(testID, GatewayAddress, NewImplementation(), nil, 26 | nil, gossip.DefaultManagerFlags()) 27 | server := node.StartNode(testID, ServerAddress, 0, node.NewImplementation(), 28 | nil, nil) 29 | defer gateway.Shutdown() 30 | defer server.Shutdown() 31 | manager := connect.NewManagerTesting(t) 32 | 33 | params := connect.GetDefaultHostParams() 34 | params.AuthEnabled = false 35 | host, err := manager.AddHost(testID, ServerAddress, nil, params) 36 | if err != nil { 37 | t.Errorf("Unable to call NewHost: %+v", err) 38 | } 39 | 40 | RSASignature := &messages.RSASignature{ 41 | Signature: []byte("test"), 42 | } 43 | 44 | _, err = gateway.SendRequestClientKeyMessage(host, 45 | &pb.SignedClientKeyRequest{ClientKeyRequestSignature: RSASignature}) 46 | if err != nil { 47 | t.Errorf("SendRequestClientKeyMessage: Error received: %s", err) 48 | } 49 | } 50 | 51 | func TestPoll(t *testing.T) { 52 | GatewayAddress := getNextGatewayAddress() 53 | ServerAddress := getNextServerAddress() 54 | 55 | testID := id.NewIdFromString("test", id.Generic, t) 56 | gateway := StartGateway(testID, GatewayAddress, NewImplementation(), nil, 57 | nil, gossip.DefaultManagerFlags()) 58 | server := node.StartNode(testID, ServerAddress, 0, node.NewImplementation(), 59 | nil, nil) 60 | defer gateway.Shutdown() 61 | defer server.Shutdown() 62 | manager := connect.NewManagerTesting(t) 63 | 64 | params := connect.GetDefaultHostParams() 65 | params.AuthEnabled = false 66 | host, err := manager.AddHost(testID, ServerAddress, nil, params) 67 | if err != nil { 68 | t.Errorf("Unable to call NewHost: %+v", err) 69 | } 70 | 71 | _, err = gateway.SendPoll(host, &pb.ServerPoll{}) 72 | if err != nil { 73 | t.Errorf("TestDemandNdf: Error received: %s", err) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /client/notificationBot.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains client -> notificationBot functionality 9 | 10 | package client 11 | 12 | import ( 13 | "github.com/golang/protobuf/ptypes" 14 | "github.com/golang/protobuf/ptypes/any" 15 | "github.com/pkg/errors" 16 | jww "github.com/spf13/jwalterweatherman" 17 | pb "gitlab.com/elixxir/comms/mixmessages" 18 | "gitlab.com/xx_network/comms/connect" 19 | "gitlab.com/xx_network/comms/messages" 20 | ) 21 | 22 | // Client -> NotificationBot 23 | func (c *Comms) RegisterForNotifications(host *connect.Host, 24 | message *pb.NotificationRegisterRequest) (*messages.Ack, error) { 25 | // Create the Send Function 26 | f := func(conn connect.Connection) (*any.Any, error) { 27 | // Set up the context 28 | ctx, cancel := host.GetMessagingContext() 29 | defer cancel() 30 | 31 | // Send the message 32 | resultMsg, err := pb.NewNotificationBotClient(conn.GetGrpcConn()). 33 | RegisterForNotifications(ctx, message) 34 | if err != nil { 35 | return nil, errors.New(err.Error()) 36 | } 37 | return ptypes.MarshalAny(resultMsg) 38 | } 39 | 40 | // Execute the Send function 41 | jww.TRACE.Printf("Sending RegisterForNotification message: %+v", message) 42 | resultMsg, err := c.Send(host, f) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | // Marshall the result 48 | result := &messages.Ack{} 49 | return result, ptypes.UnmarshalAny(resultMsg, result) 50 | 51 | } 52 | 53 | // Client -> NotificationBot 54 | func (c *Comms) UnregisterForNotifications(host *connect.Host, message *pb.NotificationUnregisterRequest) (*messages.Ack, error) { 55 | // Create the Send Function 56 | f := func(conn connect.Connection) (*any.Any, error) { 57 | // Set up the context 58 | ctx, cancel := host.GetMessagingContext() 59 | defer cancel() 60 | 61 | // Send the message 62 | resultMsg, err := pb.NewNotificationBotClient(conn.GetGrpcConn()). 63 | UnregisterForNotifications(ctx, message) 64 | if err != nil { 65 | return nil, errors.New(err.Error()) 66 | } 67 | return ptypes.MarshalAny(resultMsg) 68 | } 69 | 70 | // Execute the Send function 71 | jww.TRACE.Printf("Sending UnregisterForNotification message: %+v", message) 72 | resultMsg, err := c.Send(host, f) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | // Marshall the result 78 | result := &messages.Ack{} 79 | return result, ptypes.UnmarshalAny(resultMsg, result) 80 | 81 | } 82 | -------------------------------------------------------------------------------- /authorizer/authorizer_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package authorizer 9 | 10 | import ( 11 | "errors" 12 | "fmt" 13 | "sync" 14 | "testing" 15 | 16 | pb "gitlab.com/elixxir/comms/mixmessages" 17 | "gitlab.com/elixxir/comms/node" 18 | "gitlab.com/elixxir/comms/testkeys" 19 | "gitlab.com/xx_network/comms/connect" 20 | "gitlab.com/xx_network/primitives/id" 21 | ) 22 | 23 | var serverPortLock sync.Mutex 24 | var serverPort = 15900 // Changed from 5900 to avoid conflicts 25 | 26 | func getNextServerAddress() string { 27 | serverPortLock.Lock() 28 | defer func() { 29 | serverPort++ 30 | serverPortLock.Unlock() 31 | }() 32 | return fmt.Sprintf("localhost:%d", serverPort) 33 | } 34 | 35 | // Tests whether the server can be connected to and run an RPC with TLS enabled 36 | func TestTLS(t *testing.T) { 37 | RegAddress := getNextServerAddress() 38 | 39 | keyPath := testkeys.GetNodeKeyPath() 40 | keyData := testkeys.LoadFromPath(keyPath) 41 | certPath := testkeys.GetNodeCertPath() 42 | certData := testkeys.LoadFromPath(certPath) 43 | testId := id.NewIdFromString("test", id.Generic, t) 44 | 45 | impl := NewImplementation() 46 | impl.Functions.Authorize = func(auth *pb.AuthorizerAuth, ipAddr string) (err error) { 47 | return errors.New("This function ran") 48 | } 49 | 50 | rg := StartAuthorizerServer(testId, RegAddress, 51 | impl, certData, keyData) 52 | // Well, client shouldn't have a server type because it's not a server 53 | // It's a client 54 | // So, we need some way to add a connection to the manager for the client 55 | defer rg.Shutdown() 56 | var c node.Comms 57 | manager := connect.NewManagerTesting(t) 58 | 59 | params := connect.GetDefaultHostParams() 60 | params.AuthEnabled = false 61 | host, err := manager.AddHost(testId, RegAddress, certData, params) 62 | if err != nil { 63 | t.Errorf("Unable to call NewHost: %+v", err) 64 | } 65 | 66 | _, err = c.SendAuthorizerAuth(host, &pb.AuthorizerAuth{}) 67 | if err == nil && err.Error() != "This function ran" { 68 | t.Errorf("RegistrationMessage: Error received: %s", err) 69 | } 70 | } 71 | 72 | func TestBadCerts(t *testing.T) { 73 | defer func() { 74 | if r := recover(); r == nil { 75 | t.Errorf("The code did not panic") 76 | } 77 | }() 78 | RegAddress := getNextServerAddress() 79 | testId := id.NewIdFromString("test", id.Generic, t) 80 | 81 | _ = StartAuthorizerServer(testId, RegAddress, NewImplementation(), 82 | []byte("bad cert"), []byte("bad key")) 83 | } 84 | -------------------------------------------------------------------------------- /udb/endpoint.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains user discovery server gRPC endpoint wrappers 9 | // When you add the udb server to mixmessages/mixmessages.proto and add the 10 | // first function, a version of that goes here which calls the "handler" 11 | // version of the function, with any mappings/wrappings necessary. 12 | 13 | package udb 14 | 15 | import ( 16 | "context" 17 | jww "github.com/spf13/jwalterweatherman" 18 | pb "gitlab.com/elixxir/comms/mixmessages" 19 | "gitlab.com/xx_network/comms/messages" 20 | ) 21 | 22 | // Handles validation of reverse-authentication tokens 23 | func (u *Comms) AuthenticateToken(ctx context.Context, 24 | msg *messages.AuthenticatedMessage) (*messages.Ack, error) { 25 | err := u.ValidateToken(msg) 26 | if err != nil { 27 | jww.ERROR.Printf("Unable to authenticate token: %+v", err) 28 | } 29 | return &messages.Ack{}, err 30 | } 31 | 32 | // Handles reception of reverse-authentication token requests 33 | func (u *Comms) RequestToken(context.Context, *messages.Ping) (*messages.AssignToken, error) { 34 | token, err := u.GenerateToken() 35 | return &messages.AssignToken{ 36 | Token: token, 37 | }, err 38 | } 39 | 40 | func (u *Comms) RegisterUser(ctx context.Context, msg *pb.UDBUserRegistration) (*messages.Ack, error) { 41 | return u.handler.RegisterUser(msg) 42 | } 43 | 44 | func (u *Comms) RemoveUser(ctx context.Context, msg *pb.FactRemovalRequest) (*messages.Ack, error) { 45 | return u.handler.RemoveUser(msg) 46 | } 47 | 48 | func (u *Comms) RegisterFact(ctx context.Context, msg *pb.FactRegisterRequest) (*pb.FactRegisterResponse, error) { 49 | return u.handler.RegisterFact(msg) 50 | } 51 | 52 | func (u *Comms) ConfirmFact(ctx context.Context, msg *pb.FactConfirmRequest) (*messages.Ack, error) { 53 | return u.handler.ConfirmFact(msg) 54 | } 55 | 56 | func (u *Comms) RemoveFact(ctx context.Context, msg *pb.FactRemovalRequest) (*messages.Ack, error) { 57 | return u.handler.RemoveFact(msg) 58 | } 59 | 60 | func (u *Comms) RequestChannelLease(ctx context.Context, msg *pb.ChannelLeaseRequest) (*pb.ChannelLeaseResponse, error) { 61 | return u.handler.RequestChannelLease(msg) 62 | } 63 | 64 | // ValidateUsername validates that a user owns a username by signing the contents of the 65 | // mixmessages.UsernameValidationRequest. 66 | func (u *Comms) ValidateUsername( 67 | ctx context.Context, request *pb.UsernameValidationRequest) (*pb.UsernameValidation, error) { 68 | return u.handler.ValidateUsername(request) 69 | } 70 | -------------------------------------------------------------------------------- /gateway/notifications_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | jww "github.com/spf13/jwalterweatherman" 12 | pb "gitlab.com/elixxir/comms/mixmessages" 13 | "gitlab.com/elixxir/comms/notificationBot" 14 | "gitlab.com/xx_network/comms/connect" 15 | "gitlab.com/xx_network/comms/gossip" 16 | "gitlab.com/xx_network/primitives/id" 17 | "testing" 18 | "time" 19 | ) 20 | 21 | // Happy path. 22 | func TestComms_SendNotificationBatch(t *testing.T) { 23 | jww.SetLogThreshold(jww.LevelTrace) 24 | jww.SetStdoutThreshold(jww.LevelTrace) 25 | 26 | // Set up gateway 27 | gwAddr := getNextGatewayAddress() 28 | gwID := id.NewIdFromString("TestGatewayID", id.Gateway, t) 29 | gateway := StartGateway(gwID, gwAddr, NewImplementation(), nil, nil, gossip.DefaultManagerFlags()) 30 | defer gateway.Shutdown() 31 | 32 | // Set up notification bot 33 | nbAddr := getNextServerAddress() 34 | nbID := &id.NotificationBot 35 | impl := notificationBot.NewImplementation() 36 | receiveChan := make(chan *pb.NotificationBatch) 37 | impl.Functions.ReceiveNotificationBatch = func(notifBatch *pb.NotificationBatch, auth *connect.Auth) error { 38 | go func() { receiveChan <- notifBatch }() 39 | return nil 40 | } 41 | notifications := notificationBot.StartNotificationBot(nbID, nbAddr, impl, nil, nil) 42 | defer notifications.Shutdown() 43 | 44 | // Create manager and add notification bot as host 45 | manager := connect.NewManagerTesting(t) 46 | params := connect.GetDefaultHostParams() 47 | params.AuthEnabled = false 48 | host, err := manager.AddHost(nbID, nbAddr, nil, params) 49 | if err != nil { 50 | t.Errorf("Failed to add host: %+v", err) 51 | } 52 | 53 | // Generate message to send 54 | notifBatch := &pb.NotificationBatch{ 55 | RoundID: 42, 56 | Notifications: []*pb.NotificationData{ 57 | { 58 | EphemeralID: 42, 59 | IdentityFP: []byte("IdentityFP"), 60 | MessageHash: []byte("MessageHash"), 61 | }, 62 | }, 63 | // XXX_sizecache: 31, 64 | } 65 | 66 | // Send NotificationBatch to notification bot 67 | err = gateway.SendNotificationBatch(host, notifBatch) 68 | if err != nil { 69 | t.Errorf("SendNotificationBatch() returned an error: %+v", err) 70 | } 71 | 72 | select { 73 | case result := <-receiveChan: 74 | if notifBatch.String() != result.String() { 75 | t.Errorf("Failed to receive the expected NotificationData."+ 76 | "\nexpected: %s\nreceived: %s", notifBatch, result) 77 | } 78 | case <-time.NewTimer(50 * time.Millisecond).C: 79 | t.Error("Timed out while waiting to receive the NotificationData.") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /network/dataStructures/round.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | import ( 11 | jww "github.com/spf13/jwalterweatherman" 12 | pb "gitlab.com/elixxir/comms/mixmessages" 13 | "gitlab.com/elixxir/primitives/states" 14 | "gitlab.com/xx_network/comms/signature" 15 | "gitlab.com/xx_network/crypto/signature/ec" 16 | "gitlab.com/xx_network/crypto/signature/rsa" 17 | "sync/atomic" 18 | "time" 19 | ) 20 | 21 | // Structure wraps a round info object with the 22 | // key to verify the protobuff's signature 23 | // and a state track for verifying 24 | type Round struct { 25 | info *pb.RoundInfo 26 | needsValidation *uint32 27 | rsaPubKey *rsa.PublicKey 28 | ecPubKey *ec.PublicKey 29 | startTime time.Time 30 | } 31 | 32 | // Constructor of a Round object. 33 | func NewRound(ri *pb.RoundInfo, rsaPubKey *rsa.PublicKey, ecPubKey *ec.PublicKey) *Round { 34 | validationDefault := uint32(0) 35 | return &Round{ 36 | info: ri, 37 | needsValidation: &validationDefault, 38 | rsaPubKey: rsaPubKey, 39 | ecPubKey: ecPubKey, 40 | startTime: time.Unix(0, int64(ri.Timestamps[states.QUEUED])), 41 | } 42 | } 43 | 44 | // Constructor of an already verified round object 45 | // Intended for use by round creator. 46 | func NewVerifiedRound(ri *pb.RoundInfo, pubkey *rsa.PublicKey) *Round { 47 | // Set validation to done on creation 48 | validationDefault := uint32(1) 49 | return &Round{ 50 | info: ri, 51 | needsValidation: &validationDefault, 52 | rsaPubKey: pubkey, 53 | startTime: time.Unix(0, int64(ri.Timestamps[states.QUEUED])), 54 | } 55 | } 56 | 57 | // Get returns the round info object. If we have not 58 | // validated the signature before, we then verify. 59 | // Later calls will not need validation 60 | func (r *Round) Get() *pb.RoundInfo { 61 | if atomic.LoadUint32(r.needsValidation) == 0 { 62 | if r.rsaPubKey != nil { 63 | // Check the sig, panic if failure 64 | err := signature.VerifyRsa(r.info, r.rsaPubKey) 65 | if err != nil { 66 | jww.FATAL.Panicf("Could not validate "+ 67 | "the roundInfo signature: %+v: %v", r.info, err) 68 | } 69 | } else { 70 | // Check the sig, panic if failure 71 | err := signature.VerifyEddsa(r.info, r.ecPubKey) 72 | if err != nil { 73 | jww.FATAL.Panicf("Could not validate "+ 74 | "the roundInfo signature: %+v: %v", r.info, err) 75 | } 76 | } 77 | 78 | atomic.StoreUint32(r.needsValidation, 1) 79 | } 80 | return r.info 81 | } 82 | 83 | func (r *Round) StartTime() time.Time { 84 | return r.startTime 85 | } 86 | -------------------------------------------------------------------------------- /gateway/gateway_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | "fmt" 12 | "gitlab.com/elixxir/comms/node" 13 | "gitlab.com/elixxir/comms/testkeys" 14 | "gitlab.com/xx_network/comms/connect" 15 | "gitlab.com/xx_network/comms/gossip" 16 | "gitlab.com/xx_network/primitives/id" 17 | "sync" 18 | "testing" 19 | ) 20 | 21 | var serverPortLock sync.Mutex 22 | var serverPort = 15500 // Changed from 5500 to avoid conflicts 23 | 24 | func getNextServerAddress() string { 25 | serverPortLock.Lock() 26 | defer func() { 27 | serverPort++ 28 | serverPortLock.Unlock() 29 | }() 30 | return fmt.Sprintf("0.0.0.0:%d", serverPort) 31 | } 32 | 33 | var gatewayPortLock sync.Mutex 34 | var gatewayPort = 15600 // Changed from 5600 to avoid conflicts 35 | 36 | func getNextGatewayAddress() string { 37 | gatewayPortLock.Lock() 38 | defer func() { 39 | gatewayPort++ 40 | gatewayPortLock.Unlock() 41 | }() 42 | return fmt.Sprintf("0.0.0.0:%d", gatewayPort) 43 | } 44 | 45 | // Tests whether the gateway can be connected to and run an RPC with TLS enabled 46 | func TestTLS(t *testing.T) { 47 | keyPath := testkeys.GetNodeKeyPath() 48 | keyData := testkeys.LoadFromPath(keyPath) 49 | certPath := testkeys.GetNodeCertPath() 50 | certData := testkeys.LoadFromPath(certPath) 51 | 52 | GatewayAddress := getNextGatewayAddress() 53 | testID := id.NewIdFromString("test", id.Gateway, t) 54 | gateway := StartGateway(testID, GatewayAddress, NewImplementation(), 55 | certData, keyData, gossip.DefaultManagerFlags()) 56 | defer gateway.Shutdown() 57 | ServerAddress := getNextServerAddress() 58 | testNodeID := id.NewIdFromString("test", id.Node, t) 59 | server := node.StartNode(testNodeID, ServerAddress, 0, node.NewImplementation(), 60 | certData, keyData) 61 | defer server.Shutdown() 62 | manager := connect.NewManagerTesting(t) 63 | 64 | testId := id.NewIdFromString("test", id.Node, t) 65 | params := connect.GetDefaultHostParams() 66 | params.AuthEnabled = false 67 | host, err := manager.AddHost(testId, ServerAddress, certData, params) 68 | if err != nil { 69 | t.Errorf("Unable to call NewHost: %+v", err) 70 | } 71 | 72 | _, err = gateway.GetRoundBufferInfo(host) 73 | if err != nil { 74 | t.Error(err) 75 | } 76 | } 77 | 78 | func TestBadCerts(t *testing.T) { 79 | defer func() { 80 | if r := recover(); r == nil { 81 | t.Errorf("The code did not panic") 82 | } 83 | }() 84 | Address := getNextServerAddress() 85 | 86 | testID := id.NewIdFromString("test", id.Node, t) 87 | _ = StartGateway(testID, Address, NewImplementation(), 88 | []byte("bad cert"), []byte("bad key"), gossip.DefaultManagerFlags()) 89 | } 90 | -------------------------------------------------------------------------------- /gateway/registration.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains gateway -> server registration functionality 9 | 10 | package gateway 11 | 12 | import ( 13 | "github.com/golang/protobuf/ptypes" 14 | "github.com/golang/protobuf/ptypes/any" 15 | "github.com/pkg/errors" 16 | jww "github.com/spf13/jwalterweatherman" 17 | pb "gitlab.com/elixxir/comms/mixmessages" 18 | "gitlab.com/xx_network/comms/connect" 19 | ) 20 | 21 | // Gateway -> Server Send Function 22 | func (g *Comms) SendRequestClientKeyMessage(host *connect.Host, 23 | message *pb.SignedClientKeyRequest) (*pb.SignedKeyResponse, error) { 24 | 25 | // Create the Send Function 26 | f := func(conn connect.Connection) (*any.Any, error) { 27 | // Set up the context 28 | ctx, cancel := host.GetMessagingContext() 29 | defer cancel() 30 | // Pack the message for server 31 | authMsg, err := g.PackAuthenticatedMessage(message, host, false) 32 | if err != nil { 33 | return nil, errors.New(err.Error()) 34 | } 35 | // Send the message 36 | resultMsg, err := pb.NewNodeClient(conn.GetGrpcConn()). 37 | RequestClientKey(ctx, authMsg) 38 | if err != nil { 39 | return nil, errors.New(err.Error()) 40 | } 41 | 42 | return ptypes.MarshalAny(resultMsg) 43 | } 44 | 45 | // Execute the Send function 46 | jww.TRACE.Printf("Sending Request Nonce message: %+v", message) 47 | resultMsg, err := g.Send(host, f) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | // Marshall the result 53 | result := &pb.SignedKeyResponse{} 54 | return result, ptypes.UnmarshalAny(resultMsg, result) 55 | } 56 | 57 | // Gateway -> Server Send Function 58 | func (g *Comms) SendPoll(host *connect.Host, 59 | message *pb.ServerPoll) (*pb.ServerPollResponse, error) { 60 | 61 | // Create the Send Function 62 | f := func(conn connect.Connection) (*any.Any, error) { 63 | // Set up the context 64 | ctx, cancel := host.GetMessagingContext() 65 | defer cancel() 66 | // Pack the message for server 67 | authMsg, err := g.PackAuthenticatedMessage(message, host, false) 68 | if err != nil { 69 | return nil, errors.New(err.Error()) 70 | } 71 | 72 | // Send the message 73 | resultMsg, err := pb.NewNodeClient(conn.GetGrpcConn()). 74 | Poll(ctx, authMsg) 75 | if err != nil { 76 | return nil, errors.New(err.Error()) 77 | } 78 | return ptypes.MarshalAny(resultMsg) 79 | } 80 | 81 | // Execute the Send function 82 | jww.TRACE.Printf("Sending Poll message...") 83 | resultMsg, err := g.Send(host, f) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | // Marshall the result 89 | result := &pb.ServerPollResponse{} 90 | return result, ptypes.UnmarshalAny(resultMsg, result) 91 | } 92 | -------------------------------------------------------------------------------- /client/notificationBot_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package client 9 | 10 | import ( 11 | pb "gitlab.com/elixxir/comms/mixmessages" 12 | "gitlab.com/elixxir/comms/notificationBot" 13 | "gitlab.com/xx_network/comms/connect" 14 | "gitlab.com/xx_network/primitives/id" 15 | "testing" 16 | ) 17 | 18 | // Smoke test for RegisterForNotifications 19 | func TestRegisterForNotifications(t *testing.T) { 20 | testId := id.NewIdFromString("test", id.Generic, t) 21 | clientId := id.NewIdFromString("client", id.Generic, t) 22 | 23 | // Start notification bot 24 | nbAddress := getNextAddress() 25 | notificationBot := notificationBot.StartNotificationBot(testId, nbAddress, 26 | notificationBot.NewImplementation(), nil, nil) 27 | defer notificationBot.Shutdown() 28 | 29 | // Create client's comms object 30 | c, err := NewClientComms(clientId, nil, nil, nil) 31 | if err != nil { 32 | t.Errorf("Can't create client comms: %+v", err) 33 | } 34 | manager := connect.NewManagerTesting(t) 35 | 36 | // Add notification bot to comm's manager 37 | params := connect.GetDefaultHostParams() 38 | params.AuthEnabled = false 39 | host, err := manager.AddHost(testId, nbAddress, nil, params) 40 | if err != nil { 41 | t.Errorf("Unable to call NewHost: %+v", err) 42 | } 43 | 44 | // Register client with notification bot 45 | _, err = c.RegisterForNotifications(host, &pb.NotificationRegisterRequest{}) 46 | if err != nil { 47 | t.Errorf("RegistrationMessage: Error received: %s", err) 48 | } 49 | 50 | } 51 | 52 | // Smoke test for UnregisterForNotifications 53 | func TestUnregisterForNotifications(t *testing.T) { 54 | testId := id.NewIdFromString("test", id.Generic, t) 55 | clientId := id.NewIdFromString("client", id.Generic, t) 56 | 57 | // Start notification bot 58 | nbAddress := getNextAddress() 59 | nb := notificationBot.StartNotificationBot(testId, nbAddress, 60 | notificationBot.NewImplementation(), nil, nil) 61 | defer nb.Shutdown() 62 | 63 | // Create client's comms object 64 | c, err := NewClientComms(clientId, nil, nil, nil) 65 | if err != nil { 66 | t.Errorf("Can't create client comms: %+v", err) 67 | } 68 | manager := connect.NewManagerTesting(t) 69 | 70 | // Add notification bot to comm's manager 71 | params := connect.GetDefaultHostParams() 72 | params.AuthEnabled = false 73 | host, err := manager.AddHost(testId, nbAddress, nil, params) 74 | if err != nil { 75 | t.Errorf("Unable to call NewHost: %+v", err) 76 | } 77 | 78 | // Unregister client with notification bot 79 | _, err = c.UnregisterForNotifications(host, &pb.NotificationUnregisterRequest{}) 80 | if err != nil { 81 | t.Errorf("RegistrationMessage: Error received: %s", err) 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /publicAddress/ipLookupServices_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package publicAddress 9 | 10 | import ( 11 | "net/url" 12 | "testing" 13 | ) 14 | 15 | // // Tests that each lookup Service returns a valid IPv4 address. 16 | // // Note: keep this test disabled so we don't spam the servers. 17 | // func Test_lookupServices_ValidURLs(t *testing.T) { 18 | // var myLastIp string 19 | // var lastIpSet bool 20 | // for _, address := range lookupServices { 21 | // ip, err := getIP(address.url, connectionTimeout) 22 | // if err != nil { 23 | // t.Errorf("%s failed to return an IP: %+v", address, err) 24 | // continue 25 | // } 26 | // 27 | // if net.ParseIP(ip) == nil { 28 | // t.Errorf("The IP returned by %s is invalid: %s", address, ip) 29 | // continue 30 | // } 31 | // 32 | // if myLastIp != ip && lastIpSet { 33 | // t.Errorf("The IP returned by %s is does not match other IPs returned."+ 34 | // "\nexpected: %s\nreceived: %s", address, myLastIp, ip) 35 | // } else { 36 | // lastIpSet = true 37 | // myLastIp = ip 38 | // } 39 | // } 40 | // } 41 | 42 | // Tests that there is no duplicate URLs in the lookupServices list. 43 | func Test_lookupServices_Duplicate(t *testing.T) { 44 | hostMap := map[string]string{} 45 | for _, service := range lookupServices { 46 | u, err := url.Parse(service.url) 47 | if err != nil { 48 | t.Errorf("Failed to parse address: %+v", err) 49 | } 50 | 51 | if _, exists := hostMap[u.Hostname()]; exists { 52 | t.Errorf("Address with hostname %s already exists."+ 53 | "\nexists: %s\ncurrent: %s", 54 | u.Hostname(), hostMap[u.Hostname()], service.url) 55 | } else { 56 | hostMap[u.Hostname()] = service.url 57 | } 58 | } 59 | } 60 | 61 | // Tests that MakeTestLookupService creates a test server that responds with the 62 | // expected IP address. 63 | func TestMakeTestLookupService(t *testing.T) { 64 | expectedIp := "0.0.0.0" 65 | list, ts := MakeTestLookupService(expectedIp, t) 66 | defer ts.Close() 67 | 68 | testIp, err := getIP(list[0].url, connectionTimeout) 69 | if err != nil { 70 | t.Errorf("Failed to get IP from test server.") 71 | } 72 | 73 | if expectedIp != testIp { 74 | t.Errorf("Test server did not return the expected IP."+ 75 | "\nexpected: %s\nreceived: %s", expectedIp, testIp) 76 | } 77 | } 78 | 79 | // Panic path: tests that MakeTestLookupService panics when the provided 80 | // interface is not for testing. 81 | func TestMakeTestLookupService_InterfaceTypePanic(t *testing.T) { 82 | defer func() { 83 | if r := recover(); r == nil { 84 | t.Errorf("Failed to panic when provided interface is not for testing.") 85 | } 86 | }() 87 | 88 | var i interface{} 89 | _, _ = MakeTestLookupService("", i) 90 | } 91 | -------------------------------------------------------------------------------- /mixmessages/utils.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains utils functions for comms 9 | 10 | package mixmessages 11 | 12 | import ( 13 | "github.com/golang/protobuf/proto" 14 | jww "github.com/spf13/jwalterweatherman" 15 | ) 16 | 17 | // Headers for streaming 18 | const ( 19 | PostPhaseHeader = "batchinfo" 20 | UnmixedBatchHeader = "unmixedbatchinfo" 21 | MixedBatchHeader = "mixedBatchInfo" 22 | FinishRealtimeHeader = "finishRealtimeRoundInfo" 23 | PrecompTestBatchHeader = "precompTestBatch" 24 | ) 25 | 26 | const NoStreamingHeaderErr = "Streaming header has no information from %s" 27 | 28 | // ChunkSize is the size of a streaming chunk in bytes. 29 | const ChunkSize = 1250 30 | 31 | // ChunkHeader is the header used for by a gateway 32 | // streaming its response for client poll. This is used for streaming 33 | // the amount of chunks the response has been split into. 34 | const ChunkHeader = "totalChunks" 35 | 36 | // SplitResponseIntoChunks is a function which takes in a message and splits 37 | // the serialized message into ChunkSize chunks. . 38 | func SplitResponseIntoChunks(message proto.Message) ([]*StreamChunk, error) { 39 | data, err := proto.Marshal(message) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | // Go will round down on integer division, the arithmetic below 45 | // ensures the division rounds up 46 | chunks := make([]*StreamChunk, 0, (len(data)+ChunkSize-1)/ChunkSize) 47 | for loc := 0; len(data) > loc; loc += ChunkSize { 48 | end := loc + ChunkSize 49 | if end > len(data) { 50 | end = len(data) 51 | } 52 | chunks = append(chunks, &StreamChunk{Datum: data[loc:end]}) 53 | } 54 | 55 | return chunks, nil 56 | } 57 | 58 | // AssembleChunksIntoResponse takes a list of StreamChunk's and assembles 59 | // the datum into the message type expected by the caller. 60 | // This functions acts as the inverse of SplitResponseIntoChunks. 61 | func AssembleChunksIntoResponse(chunks []*StreamChunk, response proto.Message) error { 62 | // Get the length of the last chunk packet 63 | lastChunkLen := len(chunks[len(chunks)-1].Datum) 64 | 65 | // Calculate the total data of all chunks 66 | totalData := ChunkSize*(len(chunks)-1) + lastChunkLen 67 | 68 | // Allocate a byte slice with that data 69 | data := make([]byte, 0, totalData) 70 | 71 | // Populate the data slice with the chunk data 72 | for _, chunk := range chunks { 73 | data = append(data, chunk.Datum...) 74 | } 75 | 76 | return proto.Unmarshal(data, response) 77 | } 78 | 79 | func DebugMode() { 80 | jww.SetLogThreshold(jww.LevelDebug) 81 | jww.SetStdoutThreshold(jww.LevelDebug) 82 | } 83 | 84 | func TraceMode() { 85 | jww.SetLogThreshold(jww.LevelTrace) 86 | jww.SetStdoutThreshold(jww.LevelTrace) 87 | } 88 | -------------------------------------------------------------------------------- /udb/udb_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package udb 9 | 10 | import ( 11 | "fmt" 12 | "sync" 13 | "testing" 14 | ) 15 | 16 | var serverPortLock sync.Mutex 17 | var serverPort = 5950 18 | 19 | func getNextServerAddress() string { 20 | serverPortLock.Lock() 21 | defer func() { 22 | serverPort++ 23 | serverPortLock.Unlock() 24 | }() 25 | return fmt.Sprintf("localhost:%d", serverPort) 26 | } 27 | 28 | // Tests whether the server can be connected to and run an RPC with TLS enabled 29 | func TestTLS(t *testing.T) { 30 | /* 31 | RegAddress := getNextServerAddress() 32 | 33 | keyPath := testkeys.GetNodeKeyPath() 34 | keyData := testkeys.LoadFromPath(keyPath) 35 | certPath := testkeys.GetNodeCertPath() 36 | certData := testkeys.LoadFromPath(certPath) 37 | testId := id.NewIdFromString("test", id.Generic, t) 38 | 39 | rg := StartServer(testId, RegAddress, 40 | NewImplementation(), 41 | certData, keyData) 42 | // Well, client shouldn't have a server type because it's not a server 43 | // It's a client 44 | // So, we need some way to add a connection to the manager for the client 45 | defer rg.Shutdown() 46 | var c client.Comms 47 | manager := connect.NewManagerTesting(t) 48 | 49 | params := connect.GetDefaultHostParams() 50 | params.AuthEnabled = false 51 | host, err := manager.AddHost(testId, RegAddress, certData, params) 52 | if err != nil { 53 | t.Errorf("Unable to call NewHost: %+v", err) 54 | } 55 | // Now call something... note that client call would need to be 56 | // implemented inside client. 57 | _, err = c.SendClientCall(host, &pb.UserRegistration{}) 58 | if err != nil { 59 | t.Errorf("RegistrationMessage: Error received: %s", err) 60 | } 61 | */ 62 | } 63 | 64 | // This test doesn't start the server, and instead tests the endpoint through to 65 | // the handler 66 | func TestTwo(t *testing.T) { 67 | /* 68 | impl := NewImplementation() 69 | comms := &Comms{ 70 | handler: impl, 71 | } 72 | 73 | IWasCalled := false 74 | clientCall := func(msg *pb.PermissioningPoll, 75 | auth *connect.Auth, 76 | serverAddress string) ( 77 | *pb.PermissionPollResponse, error) { 78 | IWasCalled = true 79 | return &pb.PermissionPollResponse{}, nil 80 | } 81 | impl.Functions.ClientCall = clientCall 82 | 83 | // Calls ClientCall inside endpoint.go, which calls impl.ClientCall, 84 | // which then calls impl.Functions.ClientCall which is the dummy call 85 | // or the one you would replace right above. 86 | // note that this will technically fail because you would need a valid 87 | // protocomms object (not usually true for these..) 88 | comms.ClientCall(context.Context{}, nil) 89 | 90 | if IWasCalled != true { 91 | t.Errorf("ClientCall not called as expected") 92 | } 93 | */ 94 | } 95 | -------------------------------------------------------------------------------- /publicAddress/ipLookupServices.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package publicAddress 9 | 10 | import ( 11 | "fmt" 12 | jww "github.com/spf13/jwalterweatherman" 13 | "net/http" 14 | "net/http/httptest" 15 | "testing" 16 | ) 17 | 18 | const ( 19 | ipv4Address = "ipv4" 20 | ipv6Address = "ipv6" 21 | ) 22 | 23 | // Service structure contains the URL to an IP lookup Service and which IP 24 | // version it returns. 25 | type Service struct { 26 | v string // Which IP version the lookup Service returns 27 | url string // URL of the Service 28 | } 29 | 30 | // List of URLs that respond with our public IP address. 31 | var lookupServices = []Service{ 32 | {ipv4Address, "https://4.echoip.de"}, 33 | {ipv4Address, "https://api.ipify.org"}, 34 | {ipv4Address, "https://checkip.amazonaws.com"}, 35 | {ipv4Address, "https://ipv4.icanhazip.com"}, 36 | {ipv4Address, "https://ifconfig.me/ip"}, 37 | {ipv4Address, "https://ip-addr.es"}, 38 | {ipv4Address, "https://ip4.seeip.org"}, 39 | {ipv4Address, "https://ipaddr.site"}, 40 | {ipv4Address, "https://ipaddress.sh"}, 41 | {ipv4Address, "https://ipcalf.com/?format=text"}, 42 | {ipv4Address, "https://ipecho.net/plain"}, 43 | {ipv4Address, "https://ipinfo.io/ip"}, 44 | {ipv4Address, "https://l2.io/ip"}, 45 | {ipv4Address, "https://myexternalip.com/raw"}, 46 | {ipv4Address, "https://myip.dnsomatic.com"}, 47 | {ipv4Address, "https://sfml-dev.org/ip-provider.php"}, 48 | {ipv4Address, "https://v4.ident.me"}, 49 | {ipv4Address, "https://v4.ipv6-test.com/api/myip.php"}, 50 | {ipv6Address, "https://ifconfig.co/ip"}, 51 | {ipv6Address, "https://curlmyip.net"}, 52 | {ipv6Address, "https://diagnostic.opendns.com/myip"}, 53 | {ipv6Address, "https://ip.tyk.nu"}, 54 | {ipv6Address, "https://wgetip.com"}, 55 | {ipv6Address, "https://bot.whatismyipaddress.com/"}, 56 | {ipv6Address, "https://ipof.in/txt"}, 57 | } 58 | 59 | // MakeTestLookupService creates a test server and service list containing the 60 | // IP of that server. The server will respond with the provided address. This 61 | // function is intended for testing only so that tests do not need to reach an 62 | // external service to function. Once the returned server is used, it should be 63 | // closed using ts.Close(). 64 | func MakeTestLookupService(ip string, i interface{}) ([]Service, *httptest.Server) { 65 | switch i.(type) { 66 | case *testing.T, *testing.M, *testing.B, *testing.PB: 67 | break 68 | default: 69 | jww.FATAL.Panicf("Provided interface is not for testing: %T", i) 70 | } 71 | 72 | ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 73 | if _, err := fmt.Fprint(w, ip); err != nil { 74 | jww.FATAL.Panicf("Failed to write to response writer: %+v", err) 75 | } 76 | })) 77 | 78 | return []Service{{ipv4Address, ts.URL}, {ipv4Address, ts.URL}}, ts 79 | } 80 | -------------------------------------------------------------------------------- /network/securedNdf_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package network 9 | 10 | import ( 11 | "fmt" 12 | pb "gitlab.com/elixxir/comms/mixmessages" 13 | ds "gitlab.com/elixxir/comms/network/dataStructures" 14 | "gitlab.com/elixxir/comms/testutils" 15 | "gitlab.com/xx_network/comms/signature" 16 | "gitlab.com/xx_network/crypto/signature/rsa" 17 | "gitlab.com/xx_network/primitives/ndf" 18 | "math/rand" 19 | "testing" 20 | ) 21 | 22 | func setup() *ds.Ndf { 23 | msg := &pb.NDF{ 24 | Ndf: testutils.ExampleNDF, 25 | } 26 | netDef := &ds.Ndf{} 27 | 28 | _ = netDef.Update(msg) 29 | return netDef 30 | } 31 | 32 | func TestNewSecuredNdf(t *testing.T) { 33 | d, _ := ndf.Unmarshal(testutils.ExampleNDF) 34 | sndf, err := NewSecuredNdf(d) 35 | if err != nil { 36 | t.Error(err) 37 | } 38 | if sndf == nil { 39 | t.Errorf("Internal ndf object is nil") 40 | } 41 | } 42 | 43 | func TestSecuredNdf_update(t *testing.T) { 44 | src := rand.New(rand.NewSource(42)) 45 | privKey, err := rsa.GenerateKey(src, 768) 46 | if err != nil { 47 | t.Errorf("Could not generate rsa key: %s", err) 48 | } 49 | 50 | badSrc := rand.New(rand.NewSource(33)) 51 | badPriv, err := rsa.GenerateKey(badSrc, 768) 52 | if err != nil { 53 | t.Errorf("Could not generate rsa key: %s", err) 54 | } 55 | badPub := badPriv.GetPublic() 56 | fmt.Println(badPub) 57 | 58 | f := pb.NDF{} 59 | 60 | baseNDF := ndf.NetworkDefinition{} 61 | f.Ndf, err = baseNDF.Marshal() 62 | 63 | if err != nil { 64 | t.Errorf("Could not generate serialized ndf: %s", err) 65 | } 66 | 67 | err = signature.SignRsa(&f, privKey) 68 | 69 | if err != nil { 70 | t.Errorf("Could not sign serialized ndf: %s", err) 71 | } 72 | 73 | sndf, err := NewSecuredNdf(testutils.NDF) 74 | if err != nil { 75 | t.Errorf("Failed to secure ndf: %+v", err) 76 | } 77 | err = sndf.update(&f, privKey.GetPublic()) 78 | 79 | if err != nil { 80 | t.Errorf("Could not update ndf: %s", err) 81 | } 82 | 83 | err = sndf.update(&f, badPub) 84 | // Fixme 85 | /* if err == nil { 86 | t.Errorf("should have received bad key error") 87 | }*/ 88 | 89 | } 90 | 91 | func TestSecuredNdf_Get(t *testing.T) { 92 | sn := SecuredNdf{f: setup()} 93 | if sn.Get() == nil { 94 | t.Error("Should have received ndf") 95 | } 96 | } 97 | 98 | func TestSecuredNdf_GetPb(t *testing.T) { 99 | sn := SecuredNdf{f: setup()} 100 | if sn.GetPb() == nil { 101 | t.Error("Should have received pb") 102 | } 103 | } 104 | 105 | func TestSecuredNdf_GetHash(t *testing.T) { 106 | sn := SecuredNdf{f: setup()} 107 | if sn.GetHash() == nil { 108 | t.Error("Should have received hash") 109 | } 110 | } 111 | 112 | func TestSecuredNdf_CompareHash(t *testing.T) { 113 | sn := SecuredNdf{f: setup()} 114 | b := sn.CompareHash(sn.f.GetHash()) 115 | if !b { 116 | t.Error("Should have received true for comparison") 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /network/dataStructures/ndf.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Handles basic operations on different forms of network definitions 9 | 10 | package dataStructures 11 | 12 | import ( 13 | "bytes" 14 | "github.com/pkg/errors" 15 | pb "gitlab.com/elixxir/comms/mixmessages" 16 | "gitlab.com/xx_network/primitives/ndf" 17 | "golang.org/x/crypto/blake2b" 18 | "sync" 19 | ) 20 | 21 | // Ndf encapsulates all data from an NDF. 22 | type Ndf struct { 23 | f *ndf.NetworkDefinition 24 | pb *pb.NDF 25 | hash []byte 26 | sync.RWMutex 27 | } 28 | 29 | // NewNdf initializes a Ndf object from a primitives ndf.NetworkDefinition. 30 | func NewNdf(definition *ndf.NetworkDefinition) (*Ndf, error) { 31 | h, err := GenerateNDFHash(nil) 32 | if err != nil { 33 | return nil, errors.WithMessage(err, "Failed to hash NDF") 34 | } 35 | 36 | return &Ndf{ 37 | f: definition, 38 | pb: nil, 39 | hash: h, 40 | }, nil 41 | } 42 | 43 | // Update to a new NDF if the passed NDF is valid. 44 | func (file *Ndf) Update(m *pb.NDF) error { 45 | 46 | // Build the ndf object 47 | decoded, err := ndf.Unmarshal(m.Ndf) 48 | if err != nil { 49 | return errors.WithMessage(err, "Could not decode the NDF") 50 | } 51 | 52 | file.Lock() 53 | defer file.Unlock() 54 | 55 | file.pb = m 56 | file.f = decoded 57 | 58 | file.hash, err = GenerateNDFHash(file.pb) 59 | 60 | return err 61 | } 62 | 63 | // Get returns the NDF object. 64 | // FIXME: return a copy instead to ensure edits to not impact the original version 65 | func (file *Ndf) Get() *ndf.NetworkDefinition { 66 | file.RLock() 67 | defer file.RUnlock() 68 | 69 | return file.f 70 | } 71 | 72 | // GetHash returns the NDF hash. 73 | func (file *Ndf) GetHash() []byte { 74 | file.RLock() 75 | defer file.RUnlock() 76 | 77 | rtn := make([]byte, len(file.hash)) 78 | copy(rtn, file.hash) 79 | return rtn 80 | } 81 | 82 | // GetPb returns the NDF message. 83 | // FIXME: return a copy instead to ensure edits to not impact the original version 84 | func (file *Ndf) GetPb() *pb.NDF { 85 | file.RLock() 86 | defer file.RUnlock() 87 | 88 | return file.pb 89 | } 90 | 91 | // CompareHash evaluates if the passed NDF hash is the same as the stored one. 92 | func (file *Ndf) CompareHash(h []byte) bool { 93 | file.RLock() 94 | defer file.RUnlock() 95 | 96 | // Return whether the hashes are different 97 | return bytes.Equal(file.hash, h) 98 | } 99 | 100 | // GenerateNDFHash generates a hash of the unmarshalled NDF bytes in the comms 101 | // message. If the message or NDF bytes is nil, zeroes are returned. 102 | func GenerateNDFHash(msg *pb.NDF) ([]byte, error) { 103 | if msg == nil || msg.GetNdf() == nil { 104 | return make([]byte, 32), nil 105 | } 106 | // Create new BLAKE2b hash 107 | hash, err := blake2b.New256(nil) 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | hash.Write(msg.Ndf) 113 | 114 | return hash.Sum(nil), nil 115 | } 116 | -------------------------------------------------------------------------------- /clientregistrar/handler.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains callback interface for registration functionality 9 | 10 | package clientregistrar 11 | 12 | import ( 13 | "runtime/debug" 14 | 15 | jww "github.com/spf13/jwalterweatherman" 16 | pb "gitlab.com/elixxir/comms/mixmessages" 17 | "gitlab.com/xx_network/comms/connect" 18 | "gitlab.com/xx_network/comms/messages" 19 | "gitlab.com/xx_network/primitives/id" 20 | ) 21 | 22 | // Registration object used to implement 23 | // endpoints and top-level comms functionality 24 | type Comms struct { 25 | *connect.ProtoComms 26 | handler Handler 27 | *pb.UnimplementedClientRegistrarServer 28 | *messages.UnimplementedGenericServer 29 | } 30 | 31 | // Starts a new server on the address:port specified by localServer 32 | // and a callback interface for server operations 33 | // with given path to public and private key for TLS connection 34 | func StartClientRegistrarServer(id *id.ID, localServer string, handler Handler, 35 | certPEMblock, keyPEMblock []byte) *Comms { 36 | 37 | pc, err := connect.StartCommServer(id, localServer, 38 | certPEMblock, keyPEMblock, nil) 39 | if err != nil { 40 | jww.FATAL.Panicf("Unable to start comms server: %+v", err) 41 | } 42 | 43 | clientRegistrarServer := Comms{ 44 | ProtoComms: pc, 45 | handler: handler, 46 | } 47 | pb.RegisterClientRegistrarServer(clientRegistrarServer.GetServer(), &clientRegistrarServer) 48 | messages.RegisterGenericServer(clientRegistrarServer.GetServer(), &clientRegistrarServer) 49 | 50 | pc.ServeWithWeb() 51 | return &clientRegistrarServer 52 | } 53 | 54 | type Handler interface { 55 | RegisterUser(msg *pb.ClientRegistration) (confirmation *pb.SignedClientRegistrationConfirmations, err error) 56 | } 57 | 58 | type implementationFunctions struct { 59 | RegisterUser func(msg *pb.ClientRegistration) (confirmation *pb.SignedClientRegistrationConfirmations, err error) 60 | } 61 | 62 | // Implementation allows users of the client library to set the 63 | // functions that implement the node functions 64 | type Implementation struct { 65 | Functions implementationFunctions 66 | } 67 | 68 | // NewImplementation returns a Implementation struct with all of the 69 | // function pointers returning nothing and printing an error. 70 | func NewImplementation() *Implementation { 71 | um := "UNIMPLEMENTED FUNCTION!" 72 | warn := func(msg string) { 73 | jww.WARN.Printf(msg) 74 | jww.WARN.Printf("%s", debug.Stack()) 75 | } 76 | return &Implementation{ 77 | Functions: implementationFunctions{ 78 | 79 | RegisterUser: func(msg *pb.ClientRegistration) (confirmation *pb.SignedClientRegistrationConfirmations, err error) { 80 | warn(um) 81 | return &pb.SignedClientRegistrationConfirmations{}, nil 82 | }, 83 | }, 84 | } 85 | } 86 | 87 | // Registers a user and returns a signed public key 88 | func (s *Implementation) RegisterUser(msg *pb.ClientRegistration) (confirmation *pb.SignedClientRegistrationConfirmations, err error) { 89 | return s.Functions.RegisterUser(msg) 90 | } 91 | -------------------------------------------------------------------------------- /notificationBot/endpoint.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains notificationBot gRPC endpoints 9 | 10 | package notificationBot 11 | 12 | import ( 13 | "github.com/golang/protobuf/ptypes" 14 | "github.com/pkg/errors" 15 | jww "github.com/spf13/jwalterweatherman" 16 | pb "gitlab.com/elixxir/comms/mixmessages" 17 | "gitlab.com/xx_network/comms/messages" 18 | "golang.org/x/net/context" 19 | ) 20 | 21 | // Handles validation of reverse-authentication tokens 22 | func (nb *Comms) AuthenticateToken(_ context.Context, 23 | msg *messages.AuthenticatedMessage) (*messages.Ack, error) { 24 | err := nb.ValidateToken(msg) 25 | if err != nil { 26 | jww.ERROR.Printf("Unable to authenticate token: %+v", err) 27 | } 28 | return &messages.Ack{}, err 29 | } 30 | 31 | // Handles reception of reverse-authentication token requests 32 | func (nb *Comms) RequestToken(context.Context, *messages.Ping) (*messages.AssignToken, error) { 33 | token, err := nb.GenerateToken() 34 | return &messages.AssignToken{ 35 | Token: token, 36 | }, err 37 | } 38 | 39 | // RegisterForNotifications event handler which registers a client with the notification bot 40 | func (nb *Comms) RegisterForNotifications(_ context.Context, msg *pb.NotificationRegisterRequest) (*messages.Ack, error) { 41 | err := nb.handler.RegisterForNotifications(msg) 42 | 43 | // Return the confirmation message 44 | return &messages.Ack{}, err 45 | } 46 | 47 | // UnregisterForNotifications event handler which unregisters a client with the notification bot 48 | func (nb *Comms) UnregisterForNotifications(_ context.Context, msg *pb.NotificationUnregisterRequest) (*messages.Ack, error) { 49 | err := nb.handler.UnregisterForNotifications(msg) 50 | 51 | // Return the confirmation message 52 | return &messages.Ack{}, err 53 | } 54 | 55 | func (nb *Comms) ReceiveNotificationBatch(ctx context.Context, msg *messages.AuthenticatedMessage) (*messages.Ack, error) { 56 | // Check the authState of the message 57 | authState, err := nb.AuthenticatedReceiver(msg, ctx) 58 | if err != nil { 59 | return nil, errors.Errorf("Failed to handle reception of AuthenticatedMessage: %+v", err) 60 | } 61 | 62 | // Unmarshal notification data 63 | notificationBatch := &pb.NotificationBatch{} 64 | err = ptypes.UnmarshalAny(msg.Message, notificationBatch) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | err = nb.handler.ReceiveNotificationBatch(notificationBatch, authState) 70 | 71 | return &messages.Ack{}, err 72 | } 73 | 74 | func (nb *Comms) RegisterToken(ctx context.Context, msg *pb.RegisterTokenRequest) (*messages.Ack, error) { 75 | return &messages.Ack{}, nb.handler.RegisterToken(msg) 76 | } 77 | 78 | func (nb *Comms) UnregisterToken(ctx context.Context, msg *pb.UnregisterTokenRequest) (*messages.Ack, error) { 79 | return &messages.Ack{}, nb.handler.UnregisterToken(msg) 80 | } 81 | 82 | func (nb *Comms) RegisterTrackedID(ctx context.Context, msg *pb.RegisterTrackedIdRequest) (*messages.Ack, error) { 83 | return &messages.Ack{}, nb.handler.RegisterTrackedID(msg) 84 | } 85 | 86 | func (nb *Comms) UnregisterTrackedID(ctx context.Context, msg *pb.UnregisterTrackedIdRequest) (*messages.Ack, error) { 87 | return &messages.Ack{}, nb.handler.UnregisterTrackedID(msg) 88 | } 89 | -------------------------------------------------------------------------------- /notificationBot/registerNotification_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package notificationBot 9 | 10 | import ( 11 | "gitlab.com/elixxir/comms/gateway" 12 | "gitlab.com/elixxir/comms/mixmessages" 13 | "gitlab.com/elixxir/comms/testkeys" 14 | "gitlab.com/elixxir/comms/testutils" 15 | "gitlab.com/xx_network/comms/connect" 16 | "gitlab.com/xx_network/comms/gossip" 17 | "gitlab.com/xx_network/primitives/id" 18 | "testing" 19 | ) 20 | 21 | // Happy path 22 | func TestRegisterForNotifications(t *testing.T) { 23 | // Get keys and certs 24 | keyPath := testkeys.GetNodeKeyPath() 25 | keyData := testkeys.LoadFromPath(keyPath) 26 | certPath := testkeys.GetNodeCertPath() 27 | certData := testkeys.LoadFromPath(certPath) 28 | // Get ID 29 | testId := id.NewIdFromString("test", id.Generic, t) 30 | // Get available port 31 | notificationBotAddress := getNextAddress() 32 | 33 | //Init Notification bot 34 | notificationBot := StartNotificationBot(testId, notificationBotAddress, 35 | NewImplementation(), certData, keyData) 36 | defer notificationBot.Shutdown() 37 | //Init Gateway 38 | gw := gateway.StartGateway(testId, getNextAddress(), 39 | gateway.NewImplementation(), nil, nil, 40 | gossip.DefaultManagerFlags()) 41 | defer gw.Shutdown() 42 | 43 | ctx, cancel := testutils.NewContextTesting(t) 44 | defer cancel() 45 | defer ctx.Done() 46 | 47 | //Init host and manager 48 | manager := connect.NewManagerTesting(t) 49 | _, err := manager.AddHost(testId, notificationBotAddress, 50 | certData, connect.GetDefaultHostParams()) 51 | if err != nil { 52 | t.Errorf("Unable to call NewHost: %+v", err) 53 | } 54 | 55 | // Create message and pack it 56 | msg := &mixmessages.NotificationRegisterRequest{} 57 | 58 | // Run comm 59 | _, err = notificationBot.RegisterForNotifications(ctx, msg) 60 | if err != nil { 61 | t.Errorf("Failed to unregister: %+v", err) 62 | } 63 | } 64 | 65 | // Happy path 66 | func TestUnRegisterForNotifications(t *testing.T) { 67 | // Get keys and certs 68 | keyPath := testkeys.GetNodeKeyPath() 69 | keyData := testkeys.LoadFromPath(keyPath) 70 | certPath := testkeys.GetNodeCertPath() 71 | certData := testkeys.LoadFromPath(certPath) 72 | // Get Id 73 | testId := id.NewIdFromString("test", id.Generic, t) 74 | // Get available port 75 | notificationBotAddress := getNextAddress() 76 | 77 | //Init Notification bot 78 | notificationBot := StartNotificationBot(testId, notificationBotAddress, 79 | NewImplementation(), certData, keyData) 80 | defer notificationBot.Shutdown() 81 | ctx, cancel := testutils.NewContextTesting(t) 82 | defer cancel() 83 | defer ctx.Done() 84 | 85 | //Init host and manager 86 | manager := connect.NewManagerTesting(t) 87 | _, err := manager.AddHost(testId, notificationBotAddress, 88 | certData, connect.GetDefaultHostParams()) 89 | if err != nil { 90 | t.Errorf("Unable to call NewHost: %+v", err) 91 | } 92 | 93 | // Create message and pack it 94 | msg := &mixmessages.NotificationUnregisterRequest{} 95 | 96 | // Run comm 97 | _, err = notificationBot.UnregisterForNotifications(ctx, msg) 98 | if err != nil { 99 | t.Errorf("Failed to unregister: %+v", err) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /network/dataStructures/group.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | // Define the network.Instance's group object and methods for creation and updating 11 | 12 | import ( 13 | "encoding/json" 14 | "github.com/pkg/errors" 15 | "gitlab.com/elixxir/crypto/cyclic" 16 | "gitlab.com/xx_network/crypto/large" 17 | "gitlab.com/xx_network/primitives/ndf" 18 | "sync" 19 | "testing" 20 | ) 21 | 22 | // Struct that handles and updates cyclic.Groups 23 | type Group struct { 24 | groupString string 25 | cyclicGroup *cyclic.Group 26 | *sync.RWMutex 27 | } 28 | 29 | // NewGroup creates a ds.Group with a cyclic.Group and a mutex 30 | func NewGroup() *Group { 31 | return &Group{ 32 | RWMutex: &sync.RWMutex{}, 33 | } 34 | } 35 | 36 | // Get returns the ds.Groups's cyclic group 37 | func (g *Group) Get() *cyclic.Group { 38 | return g.cyclicGroup 39 | } 40 | 41 | // Get returns the ds.Groups's cyclic group string 42 | func (g *Group) GetString() string { 43 | return g.groupString 44 | } 45 | 46 | // Update sets the group's string and cyclic.Group object 47 | // If these values have not been set yet, we set these two values 48 | // If these values are set and the newGroup is different, it errors 49 | // 50 | // as the group should be immutable after being set 51 | func (g *Group) Update(newGroup string) error { 52 | g.Lock() 53 | defer g.Unlock() 54 | // Check if groupString has not been set 55 | if g.groupString == "" { 56 | // If so initialize these values 57 | g.groupString = newGroup 58 | 59 | // Create cyclic.Group object 60 | grp, err := toGroup(newGroup) 61 | if err != nil { 62 | return errors.Errorf("Unable to update group: %+v", err) 63 | } 64 | // Set value 65 | g.cyclicGroup = grp 66 | } else if g.groupString != newGroup { 67 | // If they have already been set and the newGroup is a different value, 68 | return errors.Errorf("Attempt to modify an already initialized group") 69 | } 70 | 71 | return nil 72 | } 73 | 74 | // Utility function for NewInstanceTesting that directly sets cyclic.Group object 75 | // USED FOR TESTING PURPOSED ONLY 76 | func (g Group) UpdateCyclicGroupTesting(group *cyclic.Group, i interface{}) { 77 | switch i.(type) { 78 | case *testing.T: 79 | break 80 | case *testing.M: 81 | break 82 | case *testing.B: 83 | break 84 | default: 85 | panic("Should not be able to directly set cyclic group outside of testing purposes") 86 | } 87 | 88 | g.cyclicGroup = group 89 | } 90 | 91 | // toGroup is a helper function which converts a string representing a cyclic.Group 92 | // into a cyclic.Group object 93 | func toGroup(gprString string) (*cyclic.Group, error) { 94 | // Convert it into an ndf group 95 | tmpNdf := &ndf.Group{} 96 | err := json.Unmarshal([]byte(gprString), tmpNdf) 97 | if err != nil { 98 | return nil, errors.Errorf("Unable to marshal new group: %+v", err) 99 | } 100 | 101 | // Pull out the prime and generator values from the ndf.Group object 102 | prime := large.NewIntFromString(tmpNdf.Prime, 16) 103 | generator := large.NewIntFromString(tmpNdf.Generator, 16) 104 | 105 | // Create the group with the above values 106 | return cyclic.NewGroup(prime, generator), nil 107 | } 108 | -------------------------------------------------------------------------------- /node/register_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package node 9 | 10 | import ( 11 | pb "gitlab.com/elixxir/comms/mixmessages" 12 | "gitlab.com/elixxir/comms/registration" 13 | "gitlab.com/xx_network/comms/connect" 14 | "gitlab.com/xx_network/primitives/id" 15 | "testing" 16 | ) 17 | 18 | // Smoke test SendNodeRegistration 19 | func TestSendNodeRegistration(t *testing.T) { 20 | RegAddress := getNextServerAddress() 21 | testId := id.NewIdFromString("test", id.Generic, t) 22 | server := StartNode(testId, getNextServerAddress(), 0, NewImplementation(), 23 | nil, nil) 24 | reg := registration.StartRegistrationServer(testId, RegAddress, registration.NewImplementation(), nil, nil, nil) 25 | defer server.Shutdown() 26 | defer reg.Shutdown() 27 | manager := connect.NewManagerTesting(t) 28 | 29 | params := connect.GetDefaultHostParams() 30 | params.AuthEnabled = false 31 | host, err := manager.AddHost(testId, RegAddress, nil, params) 32 | if err != nil { 33 | t.Errorf("Unable to call NewHost: %+v", err) 34 | } 35 | 36 | msgs := &pb.NodeRegistration{Salt: testId.Bytes()} 37 | err = server.SendNodeRegistration(host, msgs) 38 | if err != nil { 39 | t.Errorf("SendNodeTopology: Error received: %s", err) 40 | } 41 | } 42 | 43 | // Smoke test 44 | func TestComms_SendPoll(t *testing.T) { 45 | RegAddress := getNextServerAddress() 46 | testId := id.NewIdFromString("test", id.Generic, t) 47 | server := StartNode(testId, getNextServerAddress(), 0, NewImplementation(), 48 | nil, nil) 49 | reg := registration.StartRegistrationServer(testId, RegAddress, registration.NewImplementation(), nil, nil, nil) 50 | defer server.Shutdown() 51 | defer reg.Shutdown() 52 | manager := connect.NewManagerTesting(t) 53 | 54 | params := connect.GetDefaultHostParams() 55 | params.AuthEnabled = false 56 | host, err := manager.AddHost(testId, RegAddress, nil, params) 57 | if err != nil { 58 | t.Errorf("Unable to call NewHost: %+v", err) 59 | } 60 | 61 | msgs := &pb.PermissioningPoll{ 62 | Full: &pb.NDFHash{ 63 | Hash: make([]byte, 0), 64 | }, 65 | Partial: &pb.NDFHash{ 66 | Hash: make([]byte, 0), 67 | }, 68 | } 69 | 70 | _, err = server.SendPoll(host, msgs) 71 | if err != nil { 72 | t.Errorf("SendPoll: Error received: %+v", err) 73 | } 74 | } 75 | 76 | func TestComms_SendRegistrationCheck(t *testing.T) { 77 | RegAddress := getNextServerAddress() 78 | testId := id.NewIdFromString("blah", id.Generic, t) 79 | server := StartNode(testId, getNextServerAddress(), 0, NewImplementation(), 80 | nil, nil) 81 | reg := registration.StartRegistrationServer(testId, RegAddress, registration.NewImplementation(), nil, nil, nil) 82 | defer server.Shutdown() 83 | defer reg.Shutdown() 84 | manager := connect.NewManagerTesting(t) 85 | 86 | params := connect.GetDefaultHostParams() 87 | params.AuthEnabled = false 88 | host, err := manager.AddHost(testId, RegAddress, nil, params) 89 | if err != nil { 90 | t.Errorf("Unable to call NewHost: %+v", err) 91 | } 92 | 93 | msgs := &pb.RegisteredNodeCheck{ 94 | ID: testId.Bytes(), 95 | } 96 | 97 | _, err = server.SendRegistrationCheck(host, msgs) 98 | if err != nil { 99 | t.Errorf("SendRegistrationCheck: Error received: %+v", err) 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /network/dataStructures/round_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | import ( 11 | "bytes" 12 | "gitlab.com/elixxir/comms/mixmessages" 13 | "gitlab.com/elixxir/comms/testutils" 14 | "gitlab.com/elixxir/primitives/states" 15 | "testing" 16 | ) 17 | 18 | // Smoke test for constructor 19 | func TestNewRound(t *testing.T) { 20 | pubKey, _ := testutils.LoadPublicKeyTesting(t) 21 | ecKey, _ := testutils.LoadEllipticPublicKey(t) 22 | 23 | ri := &mixmessages.RoundInfo{ID: uint64(1), UpdateID: uint64(1), Timestamps: make([]uint64, states.NUM_STATES)} 24 | 25 | rnd := NewRound(ri, pubKey, ecKey.GetPublic()) 26 | 27 | // Check that values in object match inputted values 28 | if rnd.info != ri || rnd.rsaPubKey != pubKey || !bytes.Equal(rnd.ecPubKey.Marshal(), ecKey.GetPublic().Marshal()) { 29 | t.Errorf("Initial round values from constructor are not expected."+ 30 | "\n\tExpected round info: %v"+ 31 | "\n\tReceived round info: %v"+ 32 | "\n\tExpected rsa public key: %v"+ 33 | "\n\tReceived rsa public key: %v"+ 34 | "\n\tExpected EC public key: %v"+ 35 | "\n\tReceived EC public key: %v", 36 | ri, rnd.info, pubKey, rnd.rsaPubKey, ecKey.GetPublic().Marshal(), rnd.ecPubKey.Marshal()) 37 | } 38 | 39 | } 40 | 41 | // Smoke test for other constructor 42 | func TestNewVerifiedRound(t *testing.T) { 43 | pubKey, _ := testutils.LoadPublicKeyTesting(t) 44 | ri := &mixmessages.RoundInfo{ID: uint64(1), UpdateID: uint64(1), Timestamps: make([]uint64, states.NUM_STATES)} 45 | 46 | rnd := NewVerifiedRound(ri, pubKey) 47 | 48 | // Check that values in object match inputted values 49 | if rnd.info != ri || rnd.rsaPubKey != pubKey || *rnd.needsValidation != 1 { 50 | t.Errorf("Initial round values from constructor are not expected."+ 51 | "\n\tExpected round info: %v"+ 52 | "\n\tReceived round info: %v"+ 53 | "\n\tExpected public key: %v"+ 54 | "\n\tReceived public key: %v"+ 55 | "\n\tExpected needsValidation: %v"+ 56 | "\n\tReceived needsValidation: %v", 57 | ri, rnd.info, pubKey, rnd.rsaPubKey, rnd.needsValidation, 1) 58 | } 59 | 60 | } 61 | 62 | // Unit test of Get() 63 | func TestNewRound_Get(t *testing.T) { 64 | pubKey, _ := testutils.LoadPublicKeyTesting(t) 65 | ri := &mixmessages.RoundInfo{ID: uint64(1), UpdateID: uint64(1), Timestamps: make([]uint64, states.NUM_STATES)} 66 | // Mock signature of roundInfo as it will be verified in codepath 67 | testutils.SignRoundInfoRsa(ri, t) 68 | ecPubKey, _ := testutils.LoadEllipticPublicKey(t) 69 | testutils.SignRoundInfoEddsa(ri, ecPubKey, t) 70 | 71 | rnd := NewRound(ri, pubKey, ecPubKey.GetPublic()) 72 | 73 | // Check the initial value of the atomic value (lazily) 74 | if *rnd.needsValidation != 0 { 75 | t.Errorf("Validation value is not default value!") 76 | } 77 | 78 | // Check that the returned round info matches inputted value 79 | retrievedRI := rnd.Get() 80 | if retrievedRI != ri { 81 | t.Errorf("RoundInfo from Get() not expected."+ 82 | "\n\tExpected: %v"+ 83 | "\n\tReceived: %v", ri, retrievedRI) 84 | } 85 | 86 | // Check the atomic value has been incremented after a Get() call 87 | if *rnd.needsValidation != 1 { 88 | t.Errorf("Validation value is not set after Get() call!") 89 | } 90 | 91 | // Check the atomic value has not changed after a second Get() call 92 | _ = rnd.Get() 93 | if *rnd.needsValidation != 1 { 94 | t.Errorf("Validation value has been modified after a second Get() call!") 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /network/dataStructures/roundData_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | import ( 11 | "gitlab.com/elixxir/comms/mixmessages" 12 | "gitlab.com/elixxir/comms/testutils" 13 | "gitlab.com/elixxir/primitives/states" 14 | "reflect" 15 | "testing" 16 | ) 17 | 18 | func TestData_UpsertRound(t *testing.T) { 19 | d := NewData() 20 | 21 | // Construct a mock round object 22 | ri := &mixmessages.RoundInfo{ 23 | ID: 0, 24 | UpdateID: 0, 25 | Timestamps: make([]uint64, states.NUM_STATES), 26 | } 27 | 28 | pubKey, err := testutils.LoadPublicKeyTesting(t) 29 | if err != nil { 30 | t.Errorf("Failed to load public key: %v", err) 31 | t.FailNow() 32 | } 33 | rnd := NewRound(ri, pubKey, nil) 34 | err = d.UpsertRound(rnd) 35 | if err != nil { 36 | t.Errorf("Failed to upsert round: %+v", err) 37 | } 38 | } 39 | 40 | func TestData_GetRound(t *testing.T) { 41 | d := NewData() 42 | 43 | // Construct a mock round object 44 | ri := &mixmessages.RoundInfo{ 45 | ID: 0, 46 | UpdateID: 0, 47 | Timestamps: make([]uint64, states.NUM_STATES), 48 | } 49 | testutils.SignRoundInfoRsa(ri, t) 50 | 51 | pubKey, err := testutils.LoadPublicKeyTesting(t) 52 | if err != nil { 53 | t.Errorf("Failed to load public key: %v", err) 54 | t.FailNow() 55 | } 56 | rnd := NewRound(ri, pubKey, nil) 57 | 58 | _ = d.UpsertRound(rnd) 59 | _, err = d.GetRound(0) 60 | if err != nil { 61 | t.Errorf("Failed to get roundinfo with proper id") 62 | } 63 | } 64 | 65 | func TestData_GetWrappedRound(t *testing.T) { 66 | d := NewData() 67 | 68 | // Construct a mock round object 69 | ri := &mixmessages.RoundInfo{ 70 | ID: 0, 71 | UpdateID: 0, 72 | Timestamps: make([]uint64, states.NUM_STATES), 73 | } 74 | testutils.SignRoundInfoRsa(ri, t) 75 | 76 | pubKey, err := testutils.LoadPublicKeyTesting(t) 77 | if err != nil { 78 | t.Errorf("Failed to load public key: %v", err) 79 | t.FailNow() 80 | } 81 | rnd := NewRound(ri, pubKey, nil) 82 | 83 | _ = d.UpsertRound(rnd) 84 | retrieved, err := d.GetWrappedRound(0) 85 | if err != nil { 86 | t.Errorf("Failed to get roundinfo with proper id") 87 | } 88 | 89 | if !reflect.DeepEqual(rnd, retrieved) { 90 | t.Errorf("Retrieved value did not match expected!"+ 91 | "\n\tExpected: %v"+ 92 | "\n\tReceived: %v", rnd, retrieved) 93 | } 94 | } 95 | 96 | func TestData_ComparisonFunc(t *testing.T) { 97 | d := NewData() 98 | 99 | // Construct a mock round object 100 | roundInfoOne := &mixmessages.RoundInfo{ 101 | ID: 2, 102 | UpdateID: 3, 103 | Timestamps: make([]uint64, states.NUM_STATES), 104 | } 105 | testutils.SignRoundInfoRsa(roundInfoOne, t) 106 | 107 | pubKey, err := testutils.LoadPublicKeyTesting(t) 108 | if err != nil { 109 | t.Errorf("Failed to load public key: %v", err) 110 | t.FailNow() 111 | } 112 | roundOne := NewRound(roundInfoOne, pubKey, nil) 113 | _ = d.UpsertRound(roundOne) 114 | 115 | // Construct a mock round object 116 | roundInfoTwo := &mixmessages.RoundInfo{ 117 | ID: 2, 118 | UpdateID: 4, 119 | Timestamps: make([]uint64, states.NUM_STATES), 120 | } 121 | testutils.SignRoundInfoRsa(roundInfoTwo, t) 122 | 123 | roundTwo := NewRound(roundInfoTwo, pubKey, nil) 124 | _ = d.UpsertRound(roundTwo) 125 | r, err := d.GetRound(2) 126 | if err != nil { 127 | t.Errorf("Failed to get round: %+v", err) 128 | } 129 | if r.UpdateID != 4 { 130 | t.Error("Round did not properly upsert") 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /client/mockServer_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains a dummy/mock server instance for testing purposes 9 | 10 | package client 11 | 12 | import ( 13 | "fmt" 14 | "github.com/pkg/errors" 15 | pb "gitlab.com/elixxir/comms/mixmessages" 16 | "gitlab.com/elixxir/comms/testutils" 17 | "gitlab.com/xx_network/comms/connect" 18 | "gitlab.com/xx_network/primitives/ndf" 19 | "sync" 20 | ) 21 | 22 | // ------------------------- Testing globals ------------------------------------- 23 | 24 | var GetHostErrBool = true 25 | var RequestNdfErr error = nil 26 | var NdfToreturn = pb.NDF{Ndf: []byte(testutils.ExampleJSON)} 27 | 28 | const RegistrationAddr = "0.0.0.0:5558" 29 | 30 | var ExampleBadNdfJSON = "badNDF" 31 | var RegistrationHandler = &MockRegistration{} 32 | 33 | const RegistrationAddrErr = "0.0.0.0:5559" 34 | 35 | var portLock sync.Mutex 36 | var port = 5800 37 | var RegistrationError = &MockRegistrationError{} 38 | var Retries = 0 39 | 40 | // Utility function to avoid address collisions in testing suite 41 | func getNextAddress() string { 42 | portLock.Lock() 43 | defer func() { 44 | port++ 45 | portLock.Unlock() 46 | }() 47 | return fmt.Sprintf("0.0.0.0:%d", port) 48 | } 49 | 50 | // ------------------------- Mock Registration Server Handler --------------------------- 51 | 52 | type MockRegistration struct { 53 | } 54 | 55 | func (s *MockRegistration) RegisterNode(salt []byte, serverAddr, serverTlsCert, gatewayAddr, 56 | gatewayTlsCert, registrationCode string) error { 57 | return nil 58 | } 59 | 60 | func (s *MockRegistration) PollNdf(clientNdfHash []byte) (*pb.NDF, error) { 61 | return &pb.NDF{ 62 | Ndf: []byte(testutils.ExampleJSON), 63 | }, nil 64 | } 65 | 66 | func (s *MockRegistration) Poll(*pb.PermissioningPoll, *connect.Auth) (*pb.PermissionPollResponse, error) { 67 | return &pb.PermissionPollResponse{}, nil 68 | } 69 | 70 | // Registers a user and returns a signed public key 71 | func (s *MockRegistration) RegisterUser(registration *pb.ClientRegistration) (*pb.SignedClientRegistrationConfirmations, error) { 72 | return nil, nil 73 | } 74 | 75 | func (s *MockRegistration) CheckRegistration(msg *pb.RegisteredNodeCheck) (*pb.RegisteredNodeConfirmation, error) { 76 | return nil, nil 77 | } 78 | 79 | // ------------------------- Mock Error Registration Server Handler --------------------------- 80 | 81 | type MockRegistrationError struct { 82 | } 83 | 84 | func (s *MockRegistrationError) RegisterNode(salt []byte, serverAddr, serverTlsCert, gatewayAddr, 85 | gatewayTlsCert, registrationCode string) error { 86 | return nil 87 | } 88 | 89 | func (s *MockRegistrationError) PollNdf(clientNdfHash []byte) (*pb.NDF, error) { 90 | if Retries < 5 { 91 | Retries++ 92 | return nil, errors.New(ndf.NO_NDF) 93 | } 94 | return &pb.NDF{ 95 | Ndf: []byte(testutils.ExampleJSON), 96 | }, nil 97 | } 98 | 99 | func (s *MockRegistrationError) Poll(*pb.PermissioningPoll, *connect.Auth) (*pb.PermissionPollResponse, error) { 100 | if Retries < 5 { 101 | Retries++ 102 | return nil, errors.New(ndf.NO_NDF) 103 | } 104 | return &pb.PermissionPollResponse{}, nil 105 | } 106 | 107 | // Registers a user and returns a signed public key 108 | func (s *MockRegistrationError) RegisterUser(confirmation *pb.ClientRegistration) (*pb.SignedClientRegistrationConfirmations, error) { 109 | return nil, nil 110 | } 111 | 112 | func (s *MockRegistrationError) CheckRegistration(msg *pb.RegisteredNodeCheck) (*pb.RegisteredNodeConfirmation, error) { 113 | return nil, nil 114 | } 115 | -------------------------------------------------------------------------------- /mixmessages/digesttesthelper.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package mixmessages 9 | 10 | import ( 11 | "bytes" 12 | "fmt" 13 | "gitlab.com/elixxir/crypto/hash" 14 | "gitlab.com/xx_network/comms/signature" 15 | "gitlab.com/xx_network/primitives/netTime" 16 | "math/rand" 17 | "reflect" 18 | "strings" 19 | "testing" 20 | ) 21 | 22 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 23 | 24 | func RandStringRunes(n int) string { 25 | b := make([]rune, n) 26 | for i := range b { 27 | b[i] = letterRunes[rand.Intn(len(letterRunes))] 28 | } 29 | return string(b) 30 | } 31 | 32 | func checkdigest(t *testing.T, gs signature.GenericRsaSignable) { 33 | r := reflect.ValueOf(gs).Elem() 34 | 35 | h, err := hash.NewCMixHash() 36 | if err != nil { 37 | t.Errorf("Error creating CMix hash: %s", err) 38 | } 39 | 40 | // Get the old digest 41 | oldDigest := gs.Digest([]byte{}, h) 42 | 43 | // Setup RNG to fill fields with 44 | now := netTime.Now() 45 | rand.Seed(now.Unix()) 46 | 47 | // For every value in the passed in struct 48 | for i := 0; i < r.NumField(); i++ { 49 | h.Reset() 50 | 51 | // Get the value and type of the field 52 | valField := r.Field(i) 53 | typeField := r.Type().Field(i) 54 | 55 | if typeField.Name == "EccSignature" || typeField.Name == "Signature" || typeField.Name == "Errors" || typeField.Name == "state" || typeField.Name == "sizeCache" || typeField.Name == "unknownFields" || strings.Contains(typeField.Name, "XXX") { 56 | fmt.Printf("Skipping field.\n") 57 | continue 58 | } 59 | 60 | // Replace the value with something random 61 | switch valField.Interface().(type) { 62 | case []byte: 63 | randomVal := make([]byte, 4) 64 | rand.Read(randomVal) 65 | valField.SetBytes(randomVal) 66 | 67 | case uint32: 68 | valField.SetUint(uint64(rand.Uint32())) 69 | 70 | case uint64: 71 | valField.SetUint(rand.Uint64()) 72 | 73 | case string: 74 | valField.SetString(RandStringRunes(4)) 75 | 76 | case [][]uint8: 77 | arr := [][]uint8{ 78 | {uint8(rand.Int()), uint8(rand.Int())}, 79 | {uint8(rand.Int()), uint8(rand.Int())}, 80 | } 81 | v := reflect.ValueOf(arr) 82 | valField.Set(v) 83 | 84 | case []uint64: 85 | arr := []uint64{rand.Uint64(), rand.Uint64(), rand.Uint64()} 86 | v := reflect.ValueOf(arr) 87 | valField.Set(v) 88 | 89 | case []*ClientError: 90 | randClientId := make([]byte, 33) 91 | rand.Read(randClientId) 92 | randClientId2 := make([]byte, 33) 93 | rand.Read(randClientId2) 94 | rea := []*ClientError{ 95 | { 96 | ClientId: randClientId, 97 | Error: RandStringRunes(4), 98 | }, 99 | { 100 | ClientId: randClientId2, 101 | Error: RandStringRunes(4), 102 | }, 103 | } 104 | v := reflect.ValueOf(rea) 105 | valField.Set(v) 106 | 107 | default: 108 | t.Errorf("checkdigest doesn't know how to handle type %s\n", reflect.TypeOf(valField.Interface())) 109 | } 110 | 111 | fmt.Printf("| Field Name: %s,\n| Field Value: %v,\n| Field Type: %s\n", typeField.Name, valField.Interface(), typeField.Type) 112 | 113 | // Get the new signature 114 | newDigest := gs.Digest([]byte{}, h) 115 | 116 | // Compare them to make sure the signature doesn't match 117 | if bytes.Compare(oldDigest, newDigest) == 0 { 118 | t.Errorf("Digests matched\n") 119 | fmt.Printf("^^^ FAILED DIGEST CHECK ^^^\n\n\n") 120 | } else { 121 | fmt.Printf("Digests did not match, field passed check!\n\n\n") 122 | } 123 | 124 | oldDigest = newDigest 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /publicAddress/override_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package publicAddress 9 | 10 | import ( 11 | "net" 12 | "strconv" 13 | "strings" 14 | "testing" 15 | ) 16 | 17 | // Happy path. 18 | func TestGetIpOverride(t *testing.T) { 19 | host, port := "0.0.0.0", 11420 20 | expectedIP := net.JoinHostPort(host, strconv.Itoa(port)) 21 | 22 | testIP, err := GetIpOverride(host, port) 23 | if err != nil { 24 | t.Errorf("GetIpOverride() returned an error: %+v", err) 25 | } 26 | 27 | if expectedIP != testIP { 28 | t.Errorf("GetIpOverride() did not return the expected IP."+ 29 | "\nexpected: %s\nreceived: %s", expectedIP, testIP) 30 | } 31 | } 32 | 33 | // Happy path: no override IP is provided so it is looked up and combined with 34 | // the given port. 35 | func Test_getIpOverride_IpLookup(t *testing.T) { 36 | ip, port := "0.0.0.0", 11420 37 | expectedIP := net.JoinHostPort(ip, strconv.Itoa(port)) 38 | testServices, ts := MakeTestLookupService(ip, t) 39 | defer ts.Close() 40 | 41 | testIP, err := getIpOverride("", port, testServices) 42 | if err != nil { 43 | t.Errorf("getIpOverride() returned an error: %+v", err) 44 | } 45 | 46 | if expectedIP != testIP { 47 | t.Errorf("getIpOverride() did not return the expected IP."+ 48 | "\nexpected: %s\nreceived: %s", expectedIP, testIP) 49 | } 50 | } 51 | 52 | // Error path: IP address lookup fails. 53 | func Test_getIpOverride_IpLookupError(t *testing.T) { 54 | testServices, ts := MakeTestLookupService("invalid IP", t) 55 | defer ts.Close() 56 | 57 | _, err := getIpOverride("", 11420, testServices) 58 | if err == nil || !strings.Contains(err.Error(), "lookup public IP address") { 59 | t.Errorf("getIpOverride() did not return an error for an invalid IP "+ 60 | "response: %+v", err) 61 | } 62 | } 63 | 64 | // Happy path: an override IP is provided without a port so it is combined with 65 | // the given port. 66 | func TestJoinIpPort_IpOverride(t *testing.T) { 67 | ip, port := "1.1.1.1", 11420 68 | expectedIP := net.JoinHostPort(ip, strconv.Itoa(port)) 69 | testIP, err := JoinIpPort(ip, port) 70 | if err != nil { 71 | t.Errorf("JoinIpPort() returned an error: %+v", err) 72 | } 73 | 74 | if expectedIP != testIP { 75 | t.Errorf("JoinIpPort() did not return the expected IP."+ 76 | "\nexpected: %s\nreceived: %s", expectedIP, testIP) 77 | } 78 | } 79 | 80 | // Happy path: an override IP is provided with a port so it is returned as is. 81 | func TestJoinIpPort_IpPortOverride(t *testing.T) { 82 | expectedIP := net.JoinHostPort("1.1.1.1", strconv.Itoa(22840)) 83 | 84 | testIP, err := JoinIpPort(expectedIP, 11420) 85 | if err != nil { 86 | t.Errorf("JoinIpPort() returned an error: %+v", err) 87 | } 88 | 89 | if expectedIP != testIP { 90 | t.Errorf("JoinIpPort() did not return the expected IP."+ 91 | "\nexpected: %s\nreceived: %s", expectedIP, testIP) 92 | } 93 | } 94 | 95 | // Error path: override IP is invalid. 96 | func TestJoinIpPort_OverrideIpError(t *testing.T) { 97 | _, err := JoinIpPort("0.0.0.0::100", 11420) 98 | if err == nil || !strings.Contains(err.Error(), "parse public IP address") { 99 | t.Errorf("JoinIpPort() did not return an error for an invalid "+ 100 | "override IP : %+v", err) 101 | } 102 | } 103 | 104 | // Happy path: supplying an empty IP results in an empty return. 105 | func TestJoinIpPort_NoIp(t *testing.T) { 106 | ip, err := JoinIpPort("", 11420) 107 | if err != nil { 108 | t.Errorf("JoinIpPort() returned an error: %+v", err) 109 | } 110 | 111 | if ip != "" { 112 | t.Errorf("JoinIpPort() did not return the expected IP."+ 113 | "\nexpected: %s\nreceived: %s", "", ip) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /registration/endpoint.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains registration server gRPC endpoints 9 | 10 | package registration 11 | 12 | import ( 13 | "fmt" 14 | "github.com/golang/protobuf/ptypes" 15 | "github.com/pkg/errors" 16 | jww "github.com/spf13/jwalterweatherman" 17 | pb "gitlab.com/elixxir/comms/mixmessages" 18 | "gitlab.com/xx_network/comms/connect" 19 | "gitlab.com/xx_network/comms/messages" 20 | "golang.org/x/net/context" 21 | ) 22 | 23 | // Handles validation of reverse-authentication tokens 24 | func (r *Comms) AuthenticateToken(ctx context.Context, 25 | msg *messages.AuthenticatedMessage) (*messages.Ack, error) { 26 | err := r.ValidateToken(msg) 27 | if err != nil { 28 | jww.ERROR.Printf("Unable to authenticate token: %+v", err) 29 | } 30 | return &messages.Ack{}, err 31 | } 32 | 33 | // Handles reception of reverse-authentication token requests 34 | func (r *Comms) RequestToken(context.Context, *messages.Ping) (*messages.AssignToken, error) { 35 | token, err := r.GenerateToken() 36 | return &messages.AssignToken{ 37 | Token: token, 38 | }, err 39 | } 40 | 41 | // RegisterUser event handler which registers a user with the platform 42 | func (r *Comms) RegisterUser(ctx context.Context, msg *pb.ClientRegistration) ( 43 | *pb.SignedClientRegistrationConfirmations, error) { 44 | // Obtain the signed key by passing to registration server 45 | confirmationMessage, err := r.handler.RegisterUser(msg) 46 | // Obtain the error message, if any 47 | errMsg := "" 48 | if err != nil { 49 | errMsg = err.Error() 50 | err = errors.New(err.Error()) 51 | } 52 | confirmationMessage.Error = errMsg 53 | // Return the confirmation message 54 | return confirmationMessage, err 55 | } 56 | 57 | // Handle a node registration event 58 | func (r *Comms) RegisterNode(ctx context.Context, msg *pb.NodeRegistration) ( 59 | *messages.Ack, error) { 60 | 61 | // Infer peer IP address (do not use msg.GetServerAddress()) 62 | ip, _, err := connect.GetAddressFromContext(ctx) 63 | if err != nil { 64 | return &messages.Ack{}, err 65 | } 66 | 67 | port := msg.GetServerPort() 68 | address := fmt.Sprintf("%s:%d", ip, port) 69 | 70 | gwAddress := fmt.Sprintf("%s:%d", msg.GetGatewayAddress(), 71 | msg.GetGatewayPort()) 72 | 73 | // Pass information for Node registration 74 | err = r.handler.RegisterNode(msg.GetSalt(), address, 75 | msg.GetServerTlsCert(), 76 | gwAddress, msg.GetGatewayTlsCert(), 77 | msg.GetRegistrationCode()) 78 | return &messages.Ack{}, err 79 | } 80 | 81 | // Handles incoming requests for the NDF 82 | func (r *Comms) PollNdf(ctx context.Context, ndfHash *pb.NDFHash) (*pb.NDF, error) { 83 | return r.handler.PollNdf(ndfHash.Hash) 84 | } 85 | 86 | // Server -> Permissioning unified polling 87 | func (r *Comms) Poll(ctx context.Context, msg *messages.AuthenticatedMessage) (*pb.PermissionPollResponse, error) { 88 | // Create an auth object 89 | authState, err := r.AuthenticatedReceiver(msg, ctx) 90 | if err != nil { 91 | return nil, errors.Errorf("Unable handles reception of AuthenticatedMessage: %+v", err) 92 | } 93 | 94 | // Unmarshall the any message to the message type needed 95 | pollMsg := &pb.PermissioningPoll{} 96 | err = ptypes.UnmarshalAny(msg.Message, pollMsg) 97 | if err != nil { 98 | return nil, err 99 | } 100 | 101 | //Return the new ndf 102 | return r.handler.Poll(pollMsg, authState) 103 | } 104 | 105 | // Server -> Permissioning unified polling 106 | func (r *Comms) CheckRegistration(ctx context.Context, msg *pb.RegisteredNodeCheck) (*pb.RegisteredNodeConfirmation, error) { 107 | 108 | //Return the new ndf 109 | return r.handler.CheckRegistration(msg) 110 | } 111 | -------------------------------------------------------------------------------- /network/dataStructures/roundUpdates_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package dataStructures 9 | 10 | import ( 11 | "gitlab.com/elixxir/comms/mixmessages" 12 | "gitlab.com/elixxir/comms/testutils" 13 | "gitlab.com/elixxir/primitives/states" 14 | "testing" 15 | ) 16 | 17 | // Unit test 18 | func TestUpdates_AddRound(t *testing.T) { 19 | u := NewUpdates() 20 | // Construct a mock round object 21 | ri := &mixmessages.RoundInfo{ 22 | ID: 0, 23 | UpdateID: 0, 24 | Timestamps: make([]uint64, states.NUM_STATES), 25 | } 26 | pubKey, err := testutils.LoadPublicKeyTesting(t) 27 | if err != nil { 28 | t.Errorf("Failed to load public key: %v", err) 29 | t.FailNow() 30 | } 31 | ecKey, _ := testutils.LoadEllipticPublicKey(t) 32 | 33 | rnd := NewRound(ri, pubKey, ecKey.GetPublic()) 34 | err = u.AddRound(rnd) 35 | if err != nil { 36 | t.Errorf("Failed to add round: %+v", err) 37 | } 38 | } 39 | 40 | // Unit test 41 | func TestUpdates_GetUpdate(t *testing.T) { 42 | u := NewUpdates() 43 | updateID := 3 44 | // Construct a mock round object 45 | ri := &mixmessages.RoundInfo{ 46 | ID: 0, 47 | UpdateID: uint64(updateID), 48 | Timestamps: make([]uint64, states.NUM_STATES), 49 | } 50 | if err := testutils.SignRoundInfoRsa(ri, t); err != nil { 51 | t.Errorf("Failed to sign mock round info: %v", err) 52 | } 53 | pubKey, err := testutils.LoadPublicKeyTesting(t) 54 | if err != nil { 55 | t.Errorf("Failed to load public key: %v", err) 56 | t.FailNow() 57 | } 58 | 59 | ecKey, _ := testutils.LoadEllipticPublicKey(t) 60 | if err := testutils.SignRoundInfoEddsa(ri, ecKey, t); err != nil { 61 | t.Errorf("Failed to sign mock round info: %v", err) 62 | } 63 | 64 | rnd := NewRound(ri, pubKey, ecKey.GetPublic()) 65 | _ = u.AddRound(rnd) 66 | _, err = u.GetUpdate(updateID) 67 | if err != nil { 68 | t.Errorf("Failed to get update: %+v", err) 69 | } 70 | } 71 | 72 | // Unit test 73 | func TestUpdates_GetUpdates(t *testing.T) { 74 | u := NewUpdates() 75 | 76 | pubKey, err := testutils.LoadPublicKeyTesting(t) 77 | if err != nil { 78 | t.Errorf("Failed to load public key: %v", err) 79 | t.FailNow() 80 | } 81 | ecKey, _ := testutils.LoadEllipticPublicKey(t) 82 | 83 | updateID := 3 84 | // Construct a mock round object 85 | roundInfoOne := &mixmessages.RoundInfo{ 86 | ID: 0, 87 | UpdateID: uint64(updateID), 88 | Timestamps: make([]uint64, states.NUM_STATES), 89 | } 90 | 91 | // Sign the round on the keys 92 | if err := testutils.SignRoundInfoRsa(roundInfoOne, t); err != nil { 93 | t.Errorf("Failed to sign mock round info: %v", err) 94 | } 95 | if err := testutils.SignRoundInfoEddsa(roundInfoOne, ecKey, t); err != nil { 96 | t.Errorf("Failed to sign mock round info: %v", err) 97 | } 98 | 99 | roundOne := NewRound(roundInfoOne, pubKey, ecKey.GetPublic()) 100 | 101 | // Construct a second eound 102 | roundInfoTwo := &mixmessages.RoundInfo{ 103 | ID: 0, 104 | UpdateID: uint64(updateID + 1), 105 | Timestamps: make([]uint64, states.NUM_STATES), 106 | } 107 | if err := testutils.SignRoundInfoRsa(roundInfoTwo, t); err != nil { 108 | t.Errorf("Failed to sign mock round info: %v", err) 109 | } 110 | if err := testutils.SignRoundInfoEddsa(roundInfoTwo, ecKey, t); err != nil { 111 | t.Errorf("Failed to sign mock round info: %v", err) 112 | } 113 | 114 | roundTwo := NewRound(roundInfoTwo, pubKey, ecKey.GetPublic()) 115 | 116 | _ = u.AddRound(roundOne) 117 | // Add second round twice (shouldn't duplicate) 118 | _ = u.AddRound(roundTwo) 119 | _ = u.AddRound(roundTwo) 120 | l := u.GetUpdates(2) 121 | if len(l) != 2 { 122 | t.Error("Something went wrong, didn't get all results") 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /authorizer/handler.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains callback interface for authorizer functionality 9 | 10 | package authorizer 11 | 12 | import ( 13 | "runtime/debug" 14 | 15 | jww "github.com/spf13/jwalterweatherman" 16 | pb "gitlab.com/elixxir/comms/mixmessages" 17 | "gitlab.com/xx_network/comms/connect" 18 | "gitlab.com/xx_network/comms/messages" 19 | "gitlab.com/xx_network/primitives/id" 20 | ) 21 | 22 | // Authorizer object used to implement 23 | // endpoints and top-level comms functionality 24 | type Comms struct { 25 | *connect.ProtoComms 26 | handler Handler 27 | *pb.UnimplementedAuthorizerServer 28 | *messages.UnimplementedGenericServer 29 | } 30 | 31 | // Starts a new server on the address:port specified by localServer 32 | // and a callback interface for server operations 33 | // with given path to public and private key for TLS connection 34 | func StartAuthorizerServer(id *id.ID, localServer string, handler Handler, 35 | certPEMblock, keyPEMblock []byte) *Comms { 36 | 37 | pc, err := connect.StartCommServer(id, localServer, 38 | certPEMblock, keyPEMblock, nil) 39 | if err != nil { 40 | jww.FATAL.Panicf("Unable to start comms server: %+v", err) 41 | } 42 | 43 | authorizerServer := Comms{ 44 | ProtoComms: pc, 45 | handler: handler, 46 | } 47 | pb.RegisterAuthorizerServer(authorizerServer.GetServer(), &authorizerServer) 48 | messages.RegisterGenericServer(authorizerServer.GetServer(), &authorizerServer) 49 | 50 | pc.Serve() 51 | return &authorizerServer 52 | } 53 | 54 | type Handler interface { 55 | Authorize(auth *pb.AuthorizerAuth, ipAddr string) (err error) 56 | RequestCert(msg *pb.AuthorizerCertRequest) (*messages.Ack, error) 57 | RequestEABCredentials(msg *pb.EABCredentialRequest) (*pb.EABCredentialResponse, error) 58 | } 59 | 60 | type implementationFunctions struct { 61 | Authorize func(auth *pb.AuthorizerAuth, ipAddr string) (err error) 62 | RequestCert func(msg *pb.AuthorizerCertRequest) (*messages.Ack, error) 63 | RequestEABCredentials func(msg *pb.EABCredentialRequest) (*pb.EABCredentialResponse, error) 64 | } 65 | 66 | // Implementation allows users of the client library to set the 67 | // functions that implement the node functions 68 | type Implementation struct { 69 | Functions implementationFunctions 70 | } 71 | 72 | // NewImplementation returns a Implementation struct with all of the 73 | // function pointers returning nothing and printing an error. 74 | func NewImplementation() *Implementation { 75 | um := "UNIMPLEMENTED FUNCTION!" 76 | warn := func(msg string) { 77 | jww.WARN.Printf(msg) 78 | jww.WARN.Printf("%s", debug.Stack()) 79 | } 80 | return &Implementation{ 81 | Functions: implementationFunctions{ 82 | 83 | Authorize: func(auth *pb.AuthorizerAuth, ipAddr string) (err error) { 84 | warn(um) 85 | return nil 86 | }, 87 | RequestCert: func(msg *pb.AuthorizerCertRequest) (*messages.Ack, error) { 88 | warn(um) 89 | return &messages.Ack{}, nil 90 | }, 91 | RequestEABCredentials: func(msg *pb.EABCredentialRequest) (*pb.EABCredentialResponse, error) { 92 | warn(um) 93 | return &pb.EABCredentialResponse{}, nil 94 | }, 95 | }, 96 | } 97 | } 98 | 99 | // Authorizes a node to talk to permissioning 100 | func (s *Implementation) Authorize(auth *pb.AuthorizerAuth, ipAddr string) (err error) { 101 | return s.Functions.Authorize(auth, ipAddr) 102 | } 103 | 104 | // Request a signed certificate for HTTPS 105 | func (s *Implementation) RequestCert(msg *pb.AuthorizerCertRequest) (*messages.Ack, error) { 106 | return s.Functions.RequestCert(msg) 107 | } 108 | 109 | // Request ACME key for HTTPS 110 | func (s *Implementation) RequestEABCredentials(msg *pb.EABCredentialRequest) (*pb.EABCredentialResponse, error) { 111 | return s.Functions.RequestEABCredentials(msg) 112 | } 113 | -------------------------------------------------------------------------------- /mixmessages/roundInfo.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains functions to make the RoundInfo message type conform to a generic 9 | // signing interface 10 | 11 | package mixmessages 12 | 13 | import ( 14 | "crypto" 15 | "encoding/binary" 16 | "gitlab.com/elixxir/primitives/states" 17 | "gitlab.com/xx_network/comms/messages" 18 | "gitlab.com/xx_network/primitives/id" 19 | "hash" 20 | ) 21 | 22 | // GetSig returns the RSA signature. 23 | // IF none exists, it creates it, adds it to the object, then returns it. 24 | func (m *RoundInfo) GetSig() *messages.RSASignature { 25 | if m.Signature != nil { 26 | return m.Signature 27 | } 28 | 29 | m.Signature = new(messages.RSASignature) 30 | 31 | return m.Signature 32 | } 33 | 34 | func (m *RoundInfo) GetEccSig() *messages.ECCSignature { 35 | if m.EccSignature != nil { 36 | return m.EccSignature 37 | } 38 | 39 | m.EccSignature = new(messages.ECCSignature) 40 | 41 | return m.EccSignature 42 | } 43 | 44 | // Digest hashes the contents of the message in a repeatable manner 45 | // using the provided cryptographic hash. It includes the nonce in the hash 46 | func (m *RoundInfo) Digest(nonce []byte, h hash.Hash) []byte { 47 | h.Reset() 48 | 49 | // Serialize and hash RoundId 50 | h.Write(serializeUin64(m.ID)) 51 | 52 | // Serialize and hash UpdateId 53 | h.Write(serializeUin64(m.UpdateID)) 54 | 55 | // Serialize and hash state 56 | h.Write(serializeUin32(m.State)) 57 | 58 | // Serialize and hash batch size 59 | h.Write(serializeUin32(m.BatchSize)) 60 | 61 | // Serialize and hash resource queue timeout 62 | h.Write(serializeUin32(m.ResourceQueueTimeoutMillis)) 63 | 64 | // Serialize and hash address space size 65 | h.Write(serializeUin32(m.AddressSpaceSize)) 66 | 67 | // Hash the topology 68 | for _, node := range m.Topology { 69 | h.Write(node) 70 | } 71 | 72 | // Serialize and hash the timestamps 73 | for _, timeStamp := range m.Timestamps { 74 | h.Write(serializeUin64(timeStamp)) 75 | } 76 | 77 | // Hash ClientErrors 78 | for _, clientError := range m.ClientErrors { 79 | sha := crypto.SHA256.New() 80 | data := clientError.Digest(nonce, sha) 81 | h.Write(data) 82 | } 83 | 84 | // Hash nonce 85 | h.Write(nonce) 86 | 87 | // Return the hash 88 | return h.Sum(nil) 89 | } 90 | 91 | // serializeUin64 is a helper function which serializes 92 | // any uint64 data into a byte slice for hashing purposes 93 | func serializeUin64(data uint64) []byte { 94 | serializedData := make([]byte, 8) 95 | binary.LittleEndian.PutUint64(serializedData, data) 96 | return serializedData 97 | } 98 | 99 | // serializeUin32 is a helper function which serializes 100 | // any uint32 data into a byte slice for hashing purposes 101 | func serializeUin32(data uint32) []byte { 102 | serializedData := make([]byte, 4) 103 | binary.LittleEndian.PutUint32(serializedData, data) 104 | return serializedData 105 | } 106 | 107 | // GetActivity gets the state of the node 108 | func (m *RoundInfo) GetRoundState() states.Round { 109 | return states.Round(m.State) 110 | } 111 | 112 | func (m *RoundInfo) GetRoundId() id.Round { 113 | return id.Round(m.ID) 114 | } 115 | 116 | func (m *RoundInfo) DeepCopy() *RoundInfo { 117 | return &RoundInfo{ 118 | ID: m.ID, 119 | UpdateID: m.UpdateID, 120 | State: m.State, 121 | BatchSize: m.BatchSize, 122 | Topology: m.Topology, 123 | Timestamps: m.Timestamps, 124 | Errors: m.Errors, 125 | ClientErrors: m.ClientErrors, 126 | ResourceQueueTimeoutMillis: m.ResourceQueueTimeoutMillis, 127 | Signature: m.Signature, 128 | AddressSpaceSize: m.AddressSpaceSize, 129 | EccSignature: m.EccSignature, 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /network/slotDigest_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package network 9 | 10 | import ( 11 | "encoding/binary" 12 | "gitlab.com/elixxir/comms/mixmessages" 13 | "reflect" 14 | "testing" 15 | ) 16 | 17 | // Tests that GenerateSlotDigest outputs a byte slice the length of the sum 18 | // of its serialized components. Also checks if output matches precanned data 19 | func TestGenerateSlotDigest(t *testing.T) { 20 | senderID := []byte("senderId") 21 | payloadA := []byte("payloadA") 22 | payloadB := []byte("payloadB") 23 | roundId := uint64(11420) 24 | kmacs := [][]byte{[]byte("kmac1"), []byte("kmac2")} 25 | 26 | // Craft message 1 27 | msg := &mixmessages.Slot{ 28 | PayloadA: payloadA, 29 | PayloadB: payloadB, 30 | KMACs: kmacs, 31 | SenderID: senderID, 32 | } 33 | 34 | gwSlot := &mixmessages.GatewaySlot{ 35 | Message: msg, 36 | RoundID: roundId, 37 | } 38 | 39 | gwDigest := GenerateSlotDigest(gwSlot) 40 | 41 | roundIdBytes := make([]byte, 8) 42 | binary.BigEndian.PutUint64(roundIdBytes, roundId) 43 | 44 | expectedLen := len(senderID) + len(payloadA) + len(payloadB) + len(roundIdBytes) 45 | for _, kmac := range kmacs { 46 | expectedLen += len(kmac) 47 | } 48 | 49 | if len(gwDigest) != expectedLen { 50 | t.Errorf("GenerateSlotDigest failed length test."+ 51 | "\n\tExpected length: %v"+ 52 | "\n\tReceived length: %v", expectedLen, len(gwDigest)) 53 | } 54 | 55 | if !reflect.DeepEqual(gwDigest, precannedGatewayDigest) { 56 | t.Errorf("GenerateSlotDigest did not match expected."+ 57 | "\n\tExpected: %v"+ 58 | "\n\tReceived: %v", precannedGatewayDigest, gwDigest) 59 | } 60 | 61 | } 62 | 63 | // Test that GenerateSlotDigest generates the same output with the same input 64 | func TestGenerateSlotDigest_Consistency(t *testing.T) { 65 | senderID := []byte("senderId") 66 | payloadA := []byte("payloadA") 67 | payloadB := []byte("payloadB") 68 | roundId := uint64(42) 69 | kmacs := [][]byte{[]byte("kmac1"), []byte("kmac2")} 70 | 71 | // Craft message 1 72 | msg := &mixmessages.Slot{ 73 | PayloadA: payloadA, 74 | PayloadB: payloadB, 75 | KMACs: kmacs, 76 | SenderID: senderID, 77 | } 78 | 79 | gwSlot := &mixmessages.GatewaySlot{ 80 | Message: msg, 81 | RoundID: roundId, 82 | } 83 | 84 | gwDigest1 := GenerateSlotDigest(gwSlot) 85 | gwDigest2 := GenerateSlotDigest(gwSlot) 86 | 87 | if !reflect.DeepEqual(gwDigest1, gwDigest2) { 88 | t.Errorf("GenerateSlotDigest outputted different results with identical input."+ 89 | "\n\tPrimary output: %v"+ 90 | "\n\tSecondary output: %v", gwDigest1, gwDigest2) 91 | } 92 | 93 | } 94 | 95 | // Tests that GenerateSlotDigest produces different output with different input 96 | func TestGenerateSlotDigest_Inconsistency(t *testing.T) { 97 | senderID := []byte("senderId") 98 | payloadA := []byte("payloadA") 99 | payloadB := []byte("payloadB") 100 | roundId := uint64(42) 101 | kmacs := [][]byte{[]byte("kmac1"), []byte("kmac2")} 102 | 103 | // Craft message 1 104 | msg1 := &mixmessages.Slot{ 105 | PayloadA: payloadA, 106 | PayloadB: payloadB, 107 | KMACs: kmacs, 108 | SenderID: senderID, 109 | } 110 | 111 | gwSlot1 := &mixmessages.GatewaySlot{ 112 | Message: msg1, 113 | RoundID: roundId, 114 | } 115 | 116 | // Craft message 2 with swapped payloads 117 | msg2 := &mixmessages.Slot{ 118 | PayloadA: payloadB, 119 | PayloadB: payloadA, 120 | KMACs: kmacs, 121 | SenderID: senderID, 122 | } 123 | 124 | gwSlot2 := &mixmessages.GatewaySlot{ 125 | Message: msg2, 126 | RoundID: roundId, 127 | } 128 | 129 | // Generate slot digest 130 | gwDigest1 := GenerateSlotDigest(gwSlot1) 131 | gwDigest2 := GenerateSlotDigest(gwSlot2) 132 | 133 | if reflect.DeepEqual(gwDigest1, gwDigest2) { 134 | t.Errorf("GenerateSlotDigest outputted identical results with different input."+ 135 | "\n\tPrimary output: %v"+ 136 | "\n\tSecondary output: %v", gwDigest1, gwDigest2) 137 | } 138 | } 139 | 140 | var precannedGatewayDigest = []byte{115, 101, 110, 100, 101, 114, 73, 100, 112, 97, 121, 108, 111, 97, 100, 65, 112, 97, 121, 108, 111, 97, 100, 66, 107, 109, 97, 99, 49, 107, 109, 97, 99, 50, 0, 0, 0, 0, 0, 0, 44, 156} 141 | -------------------------------------------------------------------------------- /gateway/authorizer_test.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | jww "github.com/spf13/jwalterweatherman" 5 | "gitlab.com/elixxir/comms/authorizer" 6 | pb "gitlab.com/elixxir/comms/mixmessages" 7 | "gitlab.com/xx_network/comms/connect" 8 | "gitlab.com/xx_network/comms/gossip" 9 | "gitlab.com/xx_network/comms/messages" 10 | "gitlab.com/xx_network/primitives/id" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | // Happy path. 16 | func TestComms_SendAuthorizerCertRequest(t *testing.T) { 17 | jww.SetLogThreshold(jww.LevelTrace) 18 | jww.SetStdoutThreshold(jww.LevelTrace) 19 | 20 | // Set up gateway 21 | gwAddr := getNextGatewayAddress() 22 | gwID := id.NewIdFromString("TestGatewayID", id.Gateway, t) 23 | gateway := StartGateway(gwID, gwAddr, NewImplementation(), nil, nil, gossip.DefaultManagerFlags()) 24 | defer gateway.Shutdown() 25 | 26 | // Set up authorizer 27 | authAddr := getNextServerAddress() 28 | authID := &id.Authorizer 29 | impl := authorizer.NewImplementation() 30 | receiveChan := make(chan *pb.AuthorizerCertRequest) 31 | impl.Functions.RequestCert = func(notifBatch *pb.AuthorizerCertRequest) (*messages.Ack, error) { 32 | go func() { receiveChan <- notifBatch }() 33 | return &messages.Ack{}, nil 34 | } 35 | authServer := authorizer.StartAuthorizerServer(authID, authAddr, impl, nil, nil) 36 | defer authServer.Shutdown() 37 | 38 | // Create manager and add authorizer as host 39 | manager := connect.NewManagerTesting(t) 40 | params := connect.GetDefaultHostParams() 41 | params.AuthEnabled = false 42 | host, err := manager.AddHost(authID, authAddr, nil, params) 43 | if err != nil { 44 | t.Errorf("Failed to add host: %+v", err) 45 | } 46 | 47 | // Generate message to send 48 | msg := &pb.AuthorizerCertRequest{ 49 | Timestamp: int64(54321), 50 | } 51 | 52 | // Send auth cert request to authorizer 53 | resp, err := gateway.SendAuthorizerCertRequest(host, msg) 54 | if err != nil { 55 | t.Errorf("SendAuthorizerCertRequest() returned an error: %+v", err) 56 | } 57 | if resp == nil { 58 | t.Errorf("SendAuthorizerCertRequest() did not respond with an AuthorizerCert") 59 | } 60 | 61 | select { 62 | case result := <-receiveChan: 63 | if msg.String() != result.String() { 64 | t.Errorf("Failed to receive the expected Authorizer Cert."+ 65 | "\nexpected: %s\nreceived: %s", msg, result) 66 | } 67 | case <-time.NewTimer(50 * time.Millisecond).C: 68 | t.Error("Timed out while waiting to receive the Authorizer Cert.") 69 | } 70 | } 71 | 72 | // Happy path. 73 | func TestComms_SendEABCredentialRequest(t *testing.T) { 74 | jww.SetLogThreshold(jww.LevelTrace) 75 | jww.SetStdoutThreshold(jww.LevelTrace) 76 | 77 | // Set up gateway 78 | gwAddr := getNextGatewayAddress() 79 | gwID := id.NewIdFromString("TestGatewayID", id.Gateway, t) 80 | gateway := StartGateway(gwID, gwAddr, NewImplementation(), nil, nil, gossip.DefaultManagerFlags()) 81 | defer gateway.Shutdown() 82 | 83 | // Set up authorizer 84 | authAddr := getNextServerAddress() 85 | authID := &id.Authorizer 86 | impl := authorizer.NewImplementation() 87 | receiveChan := make(chan *pb.EABCredentialRequest) 88 | impl.Functions.RequestEABCredentials = func(notifBatch *pb.EABCredentialRequest) (*pb.EABCredentialResponse, error) { 89 | go func() { receiveChan <- notifBatch }() 90 | return &pb.EABCredentialResponse{}, nil 91 | } 92 | authServer := authorizer.StartAuthorizerServer(authID, authAddr, impl, nil, nil) 93 | defer authServer.Shutdown() 94 | 95 | // Create manager and add authorizer as host 96 | manager := connect.NewManagerTesting(t) 97 | params := connect.GetDefaultHostParams() 98 | params.AuthEnabled = false 99 | host, err := manager.AddHost(authID, authAddr, nil, params) 100 | if err != nil { 101 | t.Errorf("Failed to add host: %+v", err) 102 | } 103 | 104 | // Generate message to send 105 | msg := &pb.EABCredentialRequest{} 106 | 107 | // Send auth EABCredential request to authorizer 108 | resp, err := gateway.SendEABCredentialRequest(host, msg) 109 | if err != nil { 110 | t.Errorf("SendEABCredentialRequest() returned an error: %+v", err) 111 | } 112 | if resp == nil { 113 | t.Errorf("SendEABCredentialRequest() did not respond with an EABCredentialResponse") 114 | } 115 | 116 | select { 117 | case result := <-receiveChan: 118 | if msg.String() != result.String() { 119 | t.Errorf("Failed to receive the expected Authorizer EABCredential response."+ 120 | "\nexpected: %s\nreceived: %s", msg, result) 121 | } 122 | case <-time.NewTimer(50 * time.Millisecond).C: 123 | t.Error("Timed out while waiting to receive the Authorizer EAB credential response.") 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /node/register.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package node 9 | 10 | import ( 11 | "github.com/golang/protobuf/ptypes" 12 | "github.com/golang/protobuf/ptypes/any" 13 | "github.com/pkg/errors" 14 | jww "github.com/spf13/jwalterweatherman" 15 | pb "gitlab.com/elixxir/comms/mixmessages" 16 | "gitlab.com/xx_network/comms/connect" 17 | "gitlab.com/xx_network/comms/messages" 18 | ) 19 | 20 | // Server -> Registration Send Function 21 | func (s *Comms) SendNodeRegistration(host *connect.Host, 22 | message *pb.NodeRegistration) error { 23 | 24 | // Create the Send Function 25 | f := func(conn connect.Connection) (*any.Any, error) { 26 | // Set up the context 27 | ctx, cancel := host.GetMessagingContext() 28 | defer cancel() 29 | 30 | // Send the message 31 | _, err := pb.NewRegistrationClient(conn.GetGrpcConn()). 32 | RegisterNode(ctx, message) 33 | if err != nil { 34 | err = errors.New(err.Error()) 35 | } 36 | return nil, err 37 | } 38 | 39 | // Execute the Send function 40 | jww.TRACE.Printf("Sending Node Registration message: %+v", message) 41 | _, err := s.Send(host, f) 42 | return err 43 | } 44 | 45 | // Server -> Registration Send Function 46 | func (s *Comms) SendPoll(host *connect.Host, 47 | message *pb.PermissioningPoll) (*pb.PermissionPollResponse, error) { 48 | 49 | // Create the Send Function 50 | f := func(conn connect.Connection) (*any.Any, error) { 51 | // Set up the context 52 | ctx, cancel := host.GetMessagingContext() 53 | defer cancel() 54 | // Pack the message for server 55 | authMsg, err := s.PackAuthenticatedMessage(message, host, false) 56 | if err != nil { 57 | return nil, errors.New(err.Error()) 58 | } 59 | 60 | // Send the message 61 | resultMsg, err := pb.NewRegistrationClient(conn.GetGrpcConn()). 62 | Poll(ctx, authMsg) 63 | if err != nil { 64 | return nil, errors.New(err.Error()) 65 | } 66 | return ptypes.MarshalAny(resultMsg) 67 | } 68 | 69 | // Execute the Send function 70 | jww.TRACE.Printf("Sending Poll message...") 71 | resultMsg, err := s.Send(host, f) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | // Marshall the result 77 | result := &pb.PermissionPollResponse{} 78 | return result, ptypes.UnmarshalAny(resultMsg, result) 79 | } 80 | 81 | // Server -> Registration Send Function 82 | func (s *Comms) SendRegistrationCheck(host *connect.Host, 83 | message *pb.RegisteredNodeCheck) (*pb.RegisteredNodeConfirmation, error) { 84 | // Create the Send Function 85 | f := func(conn connect.Connection) (*any.Any, error) { 86 | // Set up the context 87 | ctx, cancel := host.GetMessagingContext() 88 | defer cancel() 89 | 90 | // Send the message 91 | resultMsg, err := pb.NewRegistrationClient(conn.GetGrpcConn()). 92 | CheckRegistration(ctx, message) 93 | if err != nil { 94 | return nil, errors.New(err.Error()) 95 | } 96 | return ptypes.MarshalAny(resultMsg) 97 | 98 | } 99 | 100 | // Execute the Send function 101 | jww.TRACE.Printf("Sending Node Registration Check message: %+v", message) 102 | resultMsg, err := s.Send(host, f) 103 | if err != nil { 104 | return nil, err 105 | } 106 | 107 | // Marshall the result 108 | result := &pb.RegisteredNodeConfirmation{} 109 | return result, ptypes.UnmarshalAny(resultMsg, result) 110 | } 111 | 112 | // Server -> Authorizer Send Function 113 | func (s *Comms) SendAuthorizerAuth(host *connect.Host, 114 | message *pb.AuthorizerAuth) (*messages.Ack, error) { 115 | // Create the Send Function 116 | f := func(conn connect.Connection) (*any.Any, error) { 117 | // Set up the context 118 | ctx, cancel := host.GetMessagingContext() 119 | defer cancel() 120 | 121 | // Send the message 122 | resultMsg, err := pb.NewAuthorizerClient(conn.GetGrpcConn()). 123 | Authorize(ctx, message) 124 | if err != nil { 125 | return nil, errors.New(err.Error()) 126 | } 127 | return ptypes.MarshalAny(resultMsg) 128 | 129 | } 130 | 131 | // Execute the Send function 132 | jww.TRACE.Printf("Sending Authorizer Authorize message: %+v", message) 133 | resultMsg, err := s.Send(host, f) 134 | if err != nil { 135 | return nil, err 136 | } 137 | 138 | // Marshall the result 139 | result := &messages.Ack{} 140 | return result, ptypes.UnmarshalAny(resultMsg, result) 141 | } 142 | -------------------------------------------------------------------------------- /gateway/proxy_test.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package gateway 9 | 10 | import ( 11 | pb "gitlab.com/elixxir/comms/mixmessages" 12 | "gitlab.com/xx_network/comms/connect" 13 | "gitlab.com/xx_network/comms/gossip" 14 | "gitlab.com/xx_network/primitives/id" 15 | "testing" 16 | "time" 17 | ) 18 | 19 | // Smoke test. 20 | func TestComms_SendPutMessage(t *testing.T) { 21 | gwAddress1 := getNextGatewayAddress() 22 | gwAddress2 := getNextGatewayAddress() 23 | testID1 := id.NewIdFromString("test1", id.Gateway, t) 24 | testID2 := id.NewIdFromString("test2", id.Gateway, t) 25 | gw1 := StartGateway(testID1, gwAddress1, NewImplementation(), nil, nil, 26 | gossip.DefaultManagerFlags()) 27 | gw2 := StartGateway(testID2, gwAddress2, NewImplementation(), nil, nil, 28 | gossip.DefaultManagerFlags()) 29 | defer gw1.Shutdown() 30 | defer gw2.Shutdown() 31 | manager := connect.NewManagerTesting(t) 32 | 33 | params := connect.GetDefaultHostParams() 34 | params.AuthEnabled = false 35 | host, err := manager.AddHost(testID1, gwAddress2, nil, params) 36 | if err != nil { 37 | t.Fatalf("Failed to add host to manager: %+v", err) 38 | } 39 | 40 | _, err = gw1.SendPutMessageProxy(host, &pb.GatewaySlot{}, 2*time.Minute) 41 | if err != nil { 42 | t.Errorf("SendPutMessage produced an error: %+v", err) 43 | } 44 | } 45 | 46 | // Smoke test. 47 | func TestComms_SendPutManyMessages(t *testing.T) { 48 | gwAddress1 := getNextGatewayAddress() 49 | gwAddress2 := getNextGatewayAddress() 50 | testID1 := id.NewIdFromString("test1", id.Gateway, t) 51 | testID2 := id.NewIdFromString("test2", id.Gateway, t) 52 | gw1 := StartGateway(testID1, gwAddress1, NewImplementation(), nil, nil, 53 | gossip.DefaultManagerFlags()) 54 | gw2 := StartGateway(testID2, gwAddress2, NewImplementation(), nil, nil, 55 | gossip.DefaultManagerFlags()) 56 | defer gw1.Shutdown() 57 | defer gw2.Shutdown() 58 | manager := connect.NewManagerTesting(t) 59 | 60 | params := connect.GetDefaultHostParams() 61 | params.AuthEnabled = false 62 | host, err := manager.AddHost(testID1, gwAddress2, nil, params) 63 | if err != nil { 64 | t.Fatalf("Failed to add host to manager: %+v", err) 65 | } 66 | 67 | _, err = gw1.SendPutManyMessagesProxy(host, &pb.GatewaySlots{}, 2*time.Minute) 68 | if err != nil { 69 | t.Errorf("SendPutMessage produced an error: %+v", err) 70 | } 71 | } 72 | 73 | // Smoke test. 74 | func TestComms_SendRequestNonce(t *testing.T) { 75 | gwAddress1 := getNextGatewayAddress() 76 | gwAddress2 := getNextGatewayAddress() 77 | testID1 := id.NewIdFromString("test1", id.Gateway, t) 78 | testID2 := id.NewIdFromString("test2", id.Gateway, t) 79 | gw1 := StartGateway(testID1, gwAddress1, NewImplementation(), nil, nil, 80 | gossip.DefaultManagerFlags()) 81 | gw2 := StartGateway(testID2, gwAddress2, NewImplementation(), nil, nil, 82 | gossip.DefaultManagerFlags()) 83 | defer gw1.Shutdown() 84 | defer gw2.Shutdown() 85 | manager := connect.NewManagerTesting(t) 86 | 87 | params := connect.GetDefaultHostParams() 88 | params.AuthEnabled = false 89 | host, err := manager.AddHost(testID1, gwAddress2, nil, params) 90 | if err != nil { 91 | t.Fatalf("Failed to add host to manager: %+v", err) 92 | } 93 | 94 | _, err = gw1.SendRequestClientKey(host, &pb.SignedClientKeyRequest{}, 2*time.Minute) 95 | if err != nil { 96 | t.Errorf("SendRequestNonce produced an error: %+v", err) 97 | } 98 | } 99 | 100 | // Smoke test. 101 | func TestComms_SendRequestMessages(t *testing.T) { 102 | gwAddress1 := getNextGatewayAddress() 103 | gwAddress2 := getNextGatewayAddress() 104 | testID1 := id.NewIdFromString("test1", id.Gateway, t) 105 | testID2 := id.NewIdFromString("test2", id.Gateway, t) 106 | gw1 := StartGateway(testID1, gwAddress1, NewImplementation(), nil, nil, 107 | gossip.DefaultManagerFlags()) 108 | gw2 := StartGateway(testID2, gwAddress2, NewImplementation(), nil, nil, 109 | gossip.DefaultManagerFlags()) 110 | defer gw1.Shutdown() 111 | defer gw2.Shutdown() 112 | manager := connect.NewManagerTesting(t) 113 | 114 | params := connect.GetDefaultHostParams() 115 | params.AuthEnabled = false 116 | host, err := manager.AddHost(testID1, gwAddress2, nil, params) 117 | if err != nil { 118 | t.Fatalf("Failed to add host to manager: %+v", err) 119 | } 120 | 121 | _, err = gw1.SendRequestMessages(host, &pb.GetMessages{}, 2*time.Minute) 122 | if err != nil { 123 | t.Errorf("SendRequestMessages produced an error: %+v", err) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /client/notificationsV2_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | pb "gitlab.com/elixxir/comms/mixmessages" 5 | "gitlab.com/elixxir/comms/notificationBot" 6 | "gitlab.com/xx_network/comms/connect" 7 | "gitlab.com/xx_network/primitives/id" 8 | "testing" 9 | ) 10 | 11 | // Smoke test for UnregisterForNotifications 12 | func TestComms_RegisterToken(t *testing.T) { 13 | testId := id.NewIdFromString("test", id.Generic, t) 14 | clientId := id.NewIdFromString("client", id.Generic, t) 15 | 16 | // Start notification bot 17 | nbAddress := getNextAddress() 18 | nb := notificationBot.StartNotificationBot(testId, nbAddress, 19 | notificationBot.NewImplementation(), nil, nil) 20 | defer nb.Shutdown() 21 | 22 | // Create client's comms object 23 | c, err := NewClientComms(clientId, nil, nil, nil) 24 | if err != nil { 25 | t.Errorf("Can't create client comms: %+v", err) 26 | } 27 | manager := connect.NewManagerTesting(t) 28 | 29 | // Add notification bot to comm's manager 30 | params := connect.GetDefaultHostParams() 31 | params.AuthEnabled = false 32 | host, err := manager.AddHost(testId, nbAddress, nil, params) 33 | if err != nil { 34 | t.Errorf("Unable to call NewHost: %+v", err) 35 | } 36 | 37 | // Unregister client with notification bot 38 | _, err = c.RegisterToken(host, &pb.RegisterTokenRequest{}) 39 | if err != nil { 40 | t.Errorf("RegistrationMessage: Error received: %s", err) 41 | } 42 | } 43 | 44 | // Smoke test for UnregisterForNotifications 45 | func TestComms_RegisterTrackedID(t *testing.T) { 46 | testId := id.NewIdFromString("test", id.Generic, t) 47 | clientId := id.NewIdFromString("client", id.Generic, t) 48 | 49 | // Start notification bot 50 | nbAddress := getNextAddress() 51 | nb := notificationBot.StartNotificationBot(testId, nbAddress, 52 | notificationBot.NewImplementation(), nil, nil) 53 | defer nb.Shutdown() 54 | 55 | // Create client's comms object 56 | c, err := NewClientComms(clientId, nil, nil, nil) 57 | if err != nil { 58 | t.Errorf("Can't create client comms: %+v", err) 59 | } 60 | manager := connect.NewManagerTesting(t) 61 | 62 | // Add notification bot to comm's manager 63 | params := connect.GetDefaultHostParams() 64 | params.AuthEnabled = false 65 | host, err := manager.AddHost(testId, nbAddress, nil, params) 66 | if err != nil { 67 | t.Errorf("Unable to call NewHost: %+v", err) 68 | } 69 | 70 | // Unregister client with notification bot 71 | _, err = c.RegisterTrackedID(host, &pb.RegisterTrackedIdRequest{}) 72 | if err != nil { 73 | t.Errorf("RegistrationMessage: Error received: %s", err) 74 | } 75 | } 76 | 77 | // Smoke test for UnregisterForNotifications 78 | func TestComms_UnregisterToken(t *testing.T) { 79 | testId := id.NewIdFromString("test", id.Generic, t) 80 | clientId := id.NewIdFromString("client", id.Generic, t) 81 | 82 | // Start notification bot 83 | nbAddress := getNextAddress() 84 | nb := notificationBot.StartNotificationBot(testId, nbAddress, 85 | notificationBot.NewImplementation(), nil, nil) 86 | defer nb.Shutdown() 87 | 88 | // Create client's comms object 89 | c, err := NewClientComms(clientId, nil, nil, nil) 90 | if err != nil { 91 | t.Errorf("Can't create client comms: %+v", err) 92 | } 93 | manager := connect.NewManagerTesting(t) 94 | 95 | // Add notification bot to comm's manager 96 | params := connect.GetDefaultHostParams() 97 | params.AuthEnabled = false 98 | host, err := manager.AddHost(testId, nbAddress, nil, params) 99 | if err != nil { 100 | t.Errorf("Unable to call NewHost: %+v", err) 101 | } 102 | 103 | // Unregister client with notification bot 104 | _, err = c.UnregisterToken(host, &pb.UnregisterTokenRequest{}) 105 | if err != nil { 106 | t.Errorf("RegistrationMessage: Error received: %s", err) 107 | } 108 | } 109 | 110 | // Smoke test for UnregisterForNotifications 111 | func TestComms_UnregisterTrackedID(t *testing.T) { 112 | testId := id.NewIdFromString("test", id.Generic, t) 113 | clientId := id.NewIdFromString("client", id.Generic, t) 114 | 115 | // Start notification bot 116 | nbAddress := getNextAddress() 117 | nb := notificationBot.StartNotificationBot(testId, nbAddress, 118 | notificationBot.NewImplementation(), nil, nil) 119 | defer nb.Shutdown() 120 | 121 | // Create client's comms object 122 | c, err := NewClientComms(clientId, nil, nil, nil) 123 | if err != nil { 124 | t.Errorf("Can't create client comms: %+v", err) 125 | } 126 | manager := connect.NewManagerTesting(t) 127 | 128 | // Add notification bot to comm's manager 129 | params := connect.GetDefaultHostParams() 130 | params.AuthEnabled = false 131 | host, err := manager.AddHost(testId, nbAddress, nil, params) 132 | if err != nil { 133 | t.Errorf("Unable to call NewHost: %+v", err) 134 | } 135 | 136 | // Unregister client with notification bot 137 | _, err = c.UnregisterTrackedID(host, &pb.UnregisterTrackedIdRequest{}) 138 | if err != nil { 139 | t.Errorf("RegistrationMessage: Error received: %s", err) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /testutils/utils.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | package testutils 9 | 10 | import ( 11 | "context" 12 | "crypto/rand" 13 | "github.com/pkg/errors" 14 | jww "github.com/spf13/jwalterweatherman" 15 | pb "gitlab.com/elixxir/comms/mixmessages" 16 | "gitlab.com/elixxir/comms/testkeys" 17 | "gitlab.com/xx_network/comms/signature" 18 | "gitlab.com/xx_network/crypto/signature/ec" 19 | "gitlab.com/xx_network/crypto/signature/rsa" 20 | "google.golang.org/grpc/peer" 21 | "net" 22 | "testing" 23 | "time" 24 | ) 25 | 26 | const privKeyEncoded = `uVAt6d+y3XW699L3THlcoTA2utw2dhoqnX6821x6OcnOliwX84eajmp45IZ+STw0dUl8uJtZwDKDuHVX6ZpGzg==` 27 | 28 | func LoadPublicKeyTesting(i interface{}) (*rsa.PublicKey, error) { 29 | switch i.(type) { 30 | case *testing.T: 31 | break 32 | case *testing.M: 33 | break 34 | case *testing.B: 35 | break 36 | default: 37 | jww.FATAL.Panicf("SignRoundInfoRsa is restricted to testing only. Got %T", i) 38 | } 39 | 40 | privKey, err := LoadPrivateKeyTesting(i) 41 | if err != nil { 42 | return nil, errors.Errorf("Could not load private key: %v", err) 43 | } 44 | 45 | return privKey.GetPublic(), nil 46 | } 47 | 48 | func LoadPrivateKeyTesting(i interface{}) (*rsa.PrivateKey, error) { 49 | switch i.(type) { 50 | case *testing.T: 51 | break 52 | case *testing.M: 53 | break 54 | case *testing.B: 55 | break 56 | default: 57 | jww.FATAL.Panicf("SignRoundInfoRsa is restricted to testing only. Got %T", i) 58 | } 59 | 60 | keyPath := testkeys.GetNodeKeyPath() 61 | keyData := testkeys.LoadFromPath(keyPath) 62 | 63 | privKey, err := rsa.LoadPrivateKeyFromPem(keyData) 64 | if err != nil { 65 | return nil, errors.Errorf("Could not load public key: %v", err) 66 | } 67 | 68 | return privKey, nil 69 | 70 | } 71 | 72 | func LoadEllipticPublicKey(i interface{}) (*ec.PrivateKey, error) { 73 | switch i.(type) { 74 | case *testing.T: 75 | break 76 | case *testing.M: 77 | break 78 | case *testing.B: 79 | break 80 | default: 81 | jww.FATAL.Panicf("SignRoundInfoRsa is restricted to testing only. Got %T", i) 82 | } 83 | 84 | ecKey, err := ec.NewKeyPair(rand.Reader) 85 | if err != nil { 86 | return nil, errors.Errorf("Failed to generate new keypair: %v", err) 87 | } 88 | err = ecKey.UnmarshalText(privKeyEncoded) 89 | if err != nil { 90 | return nil, errors.Errorf("Failed to unmarshal private key: %v", err) 91 | } 92 | return ecKey, nil 93 | 94 | } 95 | 96 | // Utility function which signs a round info message 97 | func SignRoundInfoRsa(ri *pb.RoundInfo, i interface{}) error { 98 | switch i.(type) { 99 | case *testing.T: 100 | break 101 | case *testing.M: 102 | break 103 | case *testing.B: 104 | break 105 | default: 106 | jww.FATAL.Panicf("SignRoundInfoRsa is restricted to testing only. Got %T", i) 107 | } 108 | 109 | keyPath := testkeys.GetNodeKeyPath() 110 | keyData := testkeys.LoadFromPath(keyPath) 111 | 112 | privKey, err := rsa.LoadPrivateKeyFromPem(keyData) 113 | if err != nil { 114 | return errors.Errorf("Could not load public key: %v", err) 115 | } 116 | 117 | err = signature.SignRsa(ri, privKey) 118 | if err != nil { 119 | return errors.Errorf("Could not sign round info: %+v", err) 120 | } 121 | return nil 122 | } 123 | 124 | func SignRoundInfoEddsa(ri *pb.RoundInfo, key *ec.PrivateKey, i interface{}) error { 125 | switch i.(type) { 126 | case *testing.T: 127 | break 128 | case *testing.M: 129 | break 130 | case *testing.B: 131 | break 132 | default: 133 | jww.FATAL.Panicf("SignRoundInfoEddsa is restricted to testing only. Got %T", i) 134 | } 135 | err := signature.SignEddsa(ri, key) 136 | if err != nil { 137 | return errors.Errorf("Could not sign round info: %+v", err) 138 | } 139 | return nil 140 | 141 | } 142 | 143 | // NewContextTesting constructs a context.Context object on 144 | // the local Unix default domain (UDP) port 145 | func NewContextTesting(i interface{}) (context.Context, context.CancelFunc) { 146 | switch i.(type) { 147 | case *testing.T: 148 | break 149 | case *testing.M: 150 | break 151 | case *testing.B: 152 | break 153 | default: 154 | jww.FATAL.Panicf("SignRoundInfoEddsa is restricted to testing only. Got %T", i) 155 | } 156 | 157 | protoCtx, cancel := context.WithTimeout(context.Background(), 158 | time.Second) 159 | timeout := 1 * time.Second 160 | conn, err := net.DialTimeout("udp", "0.0.0.0:53", timeout) 161 | if err != nil { 162 | jww.FATAL.Fatalf("Failed to get a conn object in setup: %v", err) 163 | } 164 | p := &peer.Peer{ 165 | Addr: conn.RemoteAddr(), 166 | } 167 | 168 | return peer.NewContext(protoCtx, p), cancel 169 | } 170 | -------------------------------------------------------------------------------- /node/phase.go: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright © 2024 xx foundation // 3 | // // 4 | // Use of this source code is governed by a license that can be found in the // 5 | // LICENSE file. // 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | // Contains server -> server functionality for precomputation operations 9 | 10 | package node 11 | 12 | import ( 13 | "context" 14 | "encoding/base64" 15 | "github.com/golang/protobuf/proto" 16 | "github.com/golang/protobuf/ptypes" 17 | "github.com/golang/protobuf/ptypes/any" 18 | "github.com/pkg/errors" 19 | jww "github.com/spf13/jwalterweatherman" 20 | pb "gitlab.com/elixxir/comms/mixmessages" 21 | "gitlab.com/xx_network/comms/connect" 22 | "gitlab.com/xx_network/comms/messages" 23 | "google.golang.org/grpc/metadata" 24 | ) 25 | 26 | // Server -> Server Send Function 27 | func (s *Comms) SendPostPhase(host *connect.Host, 28 | message *pb.Batch) (*messages.Ack, error) { 29 | 30 | // Create the Send Function 31 | f := func(conn connect.Connection) (*any.Any, error) { 32 | // Set up the context 33 | ctx, cancel := host.GetMessagingContext() 34 | defer cancel() 35 | // Format to authenticated message type 36 | authMsg, err := s.PackAuthenticatedMessage(message, host, false) 37 | if err != nil { 38 | return nil, errors.New(err.Error()) 39 | } 40 | // Send the message 41 | resultMsg, err := pb.NewNodeClient(conn.GetGrpcConn()). 42 | PostPhase(ctx, authMsg) 43 | if err != nil { 44 | return nil, errors.New(err.Error()) 45 | } 46 | return ptypes.MarshalAny(resultMsg) 47 | } 48 | 49 | // Execute the Send function 50 | jww.TRACE.Printf("Sending Post Phase message: %+v", message) 51 | resultMsg, err := s.Send(host, f) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | // Marshall the result 57 | result := &messages.Ack{} 58 | return result, ptypes.UnmarshalAny(resultMsg, result) 59 | } 60 | 61 | // GetPostPhaseStreamClient gets the streaming client 62 | // using a header and returns the stream and the cancel context 63 | // if there are no connection errors 64 | func (s *Comms) GetPostPhaseStreamClient(host *connect.Host, 65 | header pb.BatchInfo) (pb.Node_StreamPostPhaseClient, context.CancelFunc, error) { 66 | 67 | ctx, cancel := s.getPostPhaseStreamContext(&header) 68 | 69 | streamClient, err := s.getPostPhaseStream(host, ctx) 70 | if err != nil { 71 | return nil, nil, err 72 | } 73 | 74 | return streamClient, cancel, nil 75 | } 76 | 77 | // getPostPhaseStreamContext is given batchInfo PostPhase header 78 | // and creates a streaming context, adds the header to the context 79 | // and returns the context with the header and a cancel func 80 | func (s *Comms) getPostPhaseStreamContext(batchInfo *pb.BatchInfo) ( 81 | context.Context, context.CancelFunc) { 82 | 83 | // Create streaming context so you can close stream later 84 | ctx, cancel := connect.StreamingContext() 85 | 86 | encodedStr := base64.StdEncoding.EncodeToString([]byte(batchInfo.String())) 87 | 88 | // Add batch information to streaming context 89 | ctx = metadata.AppendToOutgoingContext(ctx, pb.PostPhaseHeader, encodedStr) 90 | 91 | return ctx, cancel 92 | } 93 | 94 | // getPostPhaseStream uses an id and streaming context to retrieve 95 | // a Node_StreamPostPhaseClient object otherwise it returns 96 | // an error if the connection is unavailable 97 | func (s *Comms) getPostPhaseStream(host *connect.Host, 98 | ctx context.Context) (pb.Node_StreamPostPhaseClient, error) { 99 | 100 | // Create the Stream Function 101 | f := func(conn connect.Connection) (interface{}, error) { 102 | 103 | // Add authentication information to streaming context 104 | ctx = s.PackAuthenticatedContext(host, ctx) 105 | 106 | // Get the stream client 107 | streamClient, err := pb.NewNodeClient(conn.GetGrpcConn()). 108 | StreamPostPhase(ctx) 109 | if err != nil { 110 | return nil, errors.New(err.Error()) 111 | } 112 | return streamClient, nil 113 | } 114 | 115 | // Execute the Stream function 116 | resultClient, err := s.Stream(host, f) 117 | if err != nil { 118 | return nil, err 119 | } 120 | 121 | // Marshall the result 122 | result := resultClient.(pb.Node_StreamPostPhaseClient) 123 | return result, nil 124 | } 125 | 126 | // Gets the header in the metadata from the server stream 127 | // and returns it or an error if it fails. 128 | func GetPostPhaseStreamHeader(stream pb.Node_StreamPostPhaseServer) (*pb.BatchInfo, error) { 129 | 130 | // Obtain the headers from server metadata 131 | md, ok := metadata.FromIncomingContext(stream.Context()) 132 | if !ok { 133 | return nil, errors.New("unable to retrieve meta data / header") 134 | } 135 | 136 | // Unmarshall the header into a message 137 | 138 | marshledBatch, err := base64.StdEncoding.DecodeString(md.Get(pb.PostPhaseHeader)[0]) 139 | if err != nil { 140 | return nil, err 141 | } 142 | batchInfo := &pb.BatchInfo{} 143 | err = proto.UnmarshalText(string(marshledBatch), batchInfo) 144 | if err != nil { 145 | return nil, err 146 | } 147 | 148 | return batchInfo, nil 149 | } 150 | --------------------------------------------------------------------------------