diff --git a/api/client.go b/api/client.go index d053f66d7b4b0793946e164b64dbcbe5d71c9eed..2e5934be9b4db2784476bd7a8d87a44b2132edcf 100644 --- a/api/client.go +++ b/api/client.go @@ -7,38 +7,46 @@ package api import ( + "bufio" + "crypto" + "crypto/sha256" + "encoding/base64" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/context" + "gitlab.com/elixxir/client/context/stoppable" "gitlab.com/elixxir/client/context/switchboard" - "gitlab.com/elixxir/client/network" "gitlab.com/elixxir/client/storage" + pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/crypto/tls" + "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/ndf" - - "github.com/pkg/errors" - jww "github.com/spf13/jwalterweatherman" + "strings" ) type Client struct { storage *storage.Session ctx *context.Context switchboard *switchboard.Switchboard - network *network.Network + network context.NetworkManager } // NewClient creates client storage, generates keys, connects, and registers // with the network. Note that this does not register a username/identity, but // merely creates a new cryptographic identity for adding such information // at a later date. -func NewClient(network, storageDir string, password []byte) (Client, error) { +func NewClient(network, storageDir string, password []byte) (*Client, error) { if clientStorageExists(storageDir) { - return errors.New("client already exists at %s", + return nil, errors.Errorf("client already exists at %s", storageDir) } // Parse the NDF - ndf, err := parseNDF(network) - if err != nil { - return nil, err - } + //ndf, err := parseNDF(network) + //if err != nil { + // return nil, err + //} // Create Storage @@ -48,7 +56,7 @@ func NewClient(network, storageDir string, password []byte) (Client, error) { // Register with network - client = Client{ + client := &Client{ storage: nil, ctx: nil, switchboard: nil, @@ -58,9 +66,9 @@ func NewClient(network, storageDir string, password []byte) (Client, error) { } // LoadClient initalizes a client object from existing storage. -func LoadClient(storageDir string, password []byte) (Client, error) { +func LoadClient(storageDir string, password []byte) (*Client, error) { if !clientStorageExists(storageDir) { - return errors.New("client does not exist at %s", + return nil, errors.Errorf("client does not exist at %s", storageDir) } @@ -68,7 +76,7 @@ func LoadClient(storageDir string, password []byte) (Client, error) { // Load and create network, context, switchboard - client = Client{ + client := &Client{ storage: nil, ctx: nil, switchboard: nil, @@ -81,16 +89,16 @@ func LoadClient(storageDir string, password []byte) (Client, error) { // RegisterListener registers a listener callback function that is called // every time a new message matches the specified parameters. -func (c Client) RegisterListener(uid id.ID, msgType int, username string, +func (c *Client) RegisterListenerCb(uid id.ID, msgType int, username string, listenerCb func(msg Message)) { - jww.INFO.Printf("RegisterListener(%s, %d, %s, %v)", uid, msgType, - username, listenerCb) + jww.INFO.Printf("RegisterListener(%s, %d, %s, func())", uid, msgType, + username) } // SendE2E sends an end-to-end payload to the provided recipient with // the provided msgType. Returns the list of rounds in which parts of // the message were sent or an error if it fails. -func (c Client) SendE2E(payload []byte, recipient id.ID, msgType int) ( +func (c *Client) SendE2E(payload []byte, recipient id.ID, msgType int) ( []int, error) { jww.INFO.Printf("SendE2E(%s, %s, %d)", payload, recipient, msgType) @@ -102,7 +110,7 @@ func (c Client) SendE2E(payload []byte, recipient id.ID, msgType int) ( // of the message were sent or an error if it fails. // NOTE: Do not use this function unless you know what you are doing. // This function always produces an error message in client logging. -func (c Client) SendUnsafe(payload []byte, recipient id.ID, msgType int) ([]int, +func (c *Client) SendUnsafe(payload []byte, recipient id.ID, msgType int) ([]int, error) { jww.INFO.Printf("SendUnsafe(%s, %s, %d)", payload, recipient, msgType) @@ -113,9 +121,8 @@ func (c Client) SendUnsafe(payload []byte, recipient id.ID, msgType int) ([]int, // recipient. Note that both SendE2E and SendUnsafe call SendCMIX. // Returns the round ID of the round the payload was sent or an error // if it fails. -func (c Client) SendCMIX(payload []byte, recipient id.ID) (int, error) { - jww.INFO.Printf("SendCMIX(%s, %s)", payload, recipient, - msgType) +func (c *Client) SendCMIX(payload []byte, recipient id.ID) (int, error) { + jww.INFO.Printf("SendCMIX(%s, %s)", payload, recipient) return 0, nil } @@ -125,14 +132,45 @@ func (c Client) SendCMIX(payload []byte, recipient id.ID) (int, error) { // especially as these rely on third parties (i.e., Firebase *cough* // *cough* google's palantir *cough*) that may represent a security // risk to the user. -func (c Client) RegisterForNotifications(token []byte) error { +func (c *Client) RegisterForNotifications(token []byte) error { jww.INFO.Printf("RegisterForNotifications(%s)", token) + // // Pull the host from the manage + // notificationBotHost, ok := cl.receptionManager.Comms.GetHost(&id.NotificationBot) + // if !ok { + // return errors.New("Failed to retrieve host for notification bot") + // } + + // // Send the register message + // _, err := cl.receptionManager.Comms.RegisterForNotifications(notificationBotHost, + // &mixmessages.NotificationToken{ + // Token: notificationToken, + // }) + // if err != nil { + // err := errors.Errorf( + // "RegisterForNotifications: Unable to register for notifications! %s", err) + // return err + // } + return nil } // UnregisterForNotifications turns of notifications for this client -func (c Client) UnregisterForNotifications() error { +func (c *Client) UnregisterForNotifications() error { jww.INFO.Printf("UnregisterForNotifications()") + // // Pull the host from the manage + // notificationBotHost, ok := cl.receptionManager.Comms.GetHost(&id.NotificationBot) + // if !ok { + // return errors.New("Failed to retrieve host for notification bot") + // } + + // // Send the unregister message + // _, err := cl.receptionManager.Comms.UnregisterForNotifications(notificationBotHost) + // if err != nil { + // err := errors.Errorf( + // "RegisterForNotifications: Unable to register for notifications! %s", err) + // return err + // } + return nil } @@ -142,16 +180,15 @@ func (c Client) UnregisterForNotifications() error { // out of band methods to exchange cryptographic identities // (e.g., QR codes), but failing to be registered precludes usage // of the user discovery mechanism (this may be preferred by user). -func (c Client) IsRegistered() bool { - jww.INFO.Printf("IsRegistered(%s, %s, %d)", payload, recipient, - msgType) +func (c *Client) IsRegistered() bool { + jww.INFO.Printf("IsRegistered()") return false } // RegisterIdentity registers an arbitrary username with the user // discovery protocol. Returns an error when it cannot connect or // the username is already registered. -func (c Client) RegisterIdentity(username string) error { +func (c *Client) RegisterIdentity(username string) error { jww.INFO.Printf("RegisterIdentity(%s)", username) return nil } @@ -159,7 +196,7 @@ func (c Client) RegisterIdentity(username string) error { // RegisterEmail makes the users email searchable after confirmation. // It returns a registration confirmation token to be used with // ConfirmRegistration or an error on failure. -func (c Client) RegisterEmail(email string) ([]byte, error) { +func (c *Client) RegisterEmail(email string) ([]byte, error) { jww.INFO.Printf("RegisterEmail(%s)", email) return nil, nil } @@ -167,7 +204,7 @@ func (c Client) RegisterEmail(email string) ([]byte, error) { // RegisterPhone makes the users phone searchable after confirmation. // It returns a registration confirmation token to be used with // ConfirmRegistration or an error on failure. -func (c Client) RegisterPhone(phone string) ([]byte, error) { +func (c *Client) RegisterPhone(phone string) ([]byte, error) { jww.INFO.Printf("RegisterPhone(%s)", phone) return nil, nil } @@ -175,28 +212,28 @@ func (c Client) RegisterPhone(phone string) ([]byte, error) { // ConfirmRegistration sends the user discovery agent a confirmation // token (from Register Email/Phone) and code (string sent via Email // or SMS to confirm ownership) to confirm ownership. -func (c Client) ConfirmRegistration(token, code []byte) error { +func (c *Client) ConfirmRegistration(token, code []byte) error { jww.INFO.Printf("ConfirmRegistration(%s, %s)", token, code) return nil } // GetUser returns the current user Identity for this client. This // can be serialized into a byte stream for out-of-band sharing. -func (c Client) GetUser() (Contact, error) { +func (c *Client) GetUser() (Contact, error) { jww.INFO.Printf("GetUser()") return Contact{}, nil } // MakeContact creates a contact from a byte stream (i.e., unmarshal's a // Contact object), allowing out-of-band import of identities. -func (c Client) MakeContact(contactBytes []byte) (Contact, error) { +func (c *Client) MakeContact(contactBytes []byte) (Contact, error) { jww.INFO.Printf("MakeContact(%s)", contactBytes) return Contact{}, nil } // GetContact returns a Contact object for the given user id, or // an error -func (c Client) GetContact(uid []byte) (Contact, error) { +func (c *Client) GetContact(uid []byte) (Contact, error) { jww.INFO.Printf("GetContact(%s)", uid) return Contact{}, nil } @@ -204,19 +241,19 @@ func (c Client) GetContact(uid []byte) (Contact, error) { // Search accepts a "separator" separated list of search elements with // an associated list of searchTypes. It returns a ContactList which // allows you to iterate over the found contact objects. -func (c Client) Search(data, separator string, searchTypes []byte) []Contact { +func (c *Client) Search(data, separator string, searchTypes []byte) []Contact { jww.INFO.Printf("Search(%s, %s, %s)", data, separator, searchTypes) return nil } // SearchWithHandler is a non-blocking search that also registers // a callback interface for user disovery events. -func (c Client) SearchWithCallback(data, separator string, searchTypes []byte, +func (c *Client) SearchWithCallback(data, separator string, searchTypes []byte, cb func(results []Contact)) { resultCh := make(chan []Contact, 1) go func(out chan []Contact, data, separator string, srchTypes []byte) { out <- c.Search(data, separator, srchTypes) - out.Close() + close(out) }(resultCh, data, separator, searchTypes) go func(in chan []Contact, cb func(results []Contact)) { @@ -232,23 +269,23 @@ func (c Client) SearchWithCallback(data, separator string, searchTypes []byte, // so this user can send messages to the desired recipient Contact. // To receive confirmation from the remote user, clients must // register a listener to do that. -func (c Client) CreateAuthenticatedChannel(recipient Contact, +func (c *Client) CreateAuthenticatedChannel(recipient Contact, payload []byte) error { - jww.INFO.Printf("CreateAuthenticatedChannel(%s, %s)", + jww.INFO.Printf("CreateAuthenticatedChannel(%v, %v)", recipient, payload) return nil } // RegisterAuthConfirmationCb registers a callback for channel // authentication confirmation events. -func (c Client) RegisterAuthConfirmationCb(cb func(contact Contact, +func (c *Client) RegisterAuthConfirmationCb(cb func(contact Contact, payload []byte)) { jww.INFO.Printf("RegisterAuthConfirmationCb(...)") } // RegisterAuthRequestCb registers a callback for channel // authentication request events. -func (c Client) RegisterAuthRequestCb(cb func(contact Contact, +func (c *Client) RegisterAuthRequestCb(cb func(contact Contact, payload []byte)) { jww.INFO.Printf("RegisterAuthRequestCb(...)") } @@ -257,13 +294,15 @@ func (c Client) RegisterAuthRequestCb(cb func(contact Contact, // and returns an object for checking state and stopping those threads. // Call this when returning from sleep and close when going back to // sleep. -func (c Client) StartNetworkRunner() NetworkRunner { +func (c *Client) StartNetworkRunner() stoppable.Stoppable { jww.INFO.Printf("StartNetworkRunner()") + return nil } // RegisterRoundEventsCb registers a callback for round // events. -func (c Client) RegisterRoundEventsCb(cb func(re RoundEvent)) { +func (c *Client) RegisterRoundEventsCb( + cb func(re *pb.RoundInfo, timedOut bool)) { jww.INFO.Printf("RegisterRoundEventsCb(...)") } @@ -314,19 +353,19 @@ func parseNDF(ndfString string) (*ndf.NetworkDefinition, error) { } // Load the TLS cert given to us, and from that get the RSA public key - cert, err := tls.LoadCertificate(ndf.NdfPub) + cert, err := tls.LoadCertificate(ndf.Registration.TlsCertificate) if err != nil { return nil, err } - pubKey := &rsa.PublicKey{PublicKey: *cert.PublicKey.(*gorsa.PublicKey)} + pubKey := cert.PublicKey.(*rsa.PublicKey) // Hash NDF JSON rsaHash := sha256.New() rsaHash.Write(ndfData) // Verify signature - err = rsa.Verify( - pubKey, crypto.SHA256, rsaHash.Sum(nil), ndfSignature, nil) + err = rsa.Verify(pubKey, crypto.SHA256, + rsaHash.Sum(nil), ndfSignature, nil) if err != nil { return nil, err } diff --git a/api/contact.go b/api/contact.go index b0604b743854a7538bff45a6ea2f25b488af123c..07f3e6ec0463e08d738cfceb8ce6881b66264d2f 100644 --- a/api/contact.go +++ b/api/contact.go @@ -6,6 +6,11 @@ package api +import ( + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" +) + // Contact implements the Contact interface defined in bindings/interfaces.go, type Contact struct { ID id.ID @@ -22,7 +27,7 @@ func (c Contact) GetID() []byte { // GetPublicKey returns the publickey bytes for this user. func (c Contact) GetPublicKey() []byte { - return c.PubKey.Bytes() + return rsa.CreatePublicKeyPem(&c.PubKey) } // GetSalt returns the salt used to initiate an authenticated channel diff --git a/api/interfaces.go b/api/interfaces.go new file mode 100644 index 0000000000000000000000000000000000000000..5d8c04419cde6972ac3e253bcce8924a7f6d6e68 --- /dev/null +++ b/api/interfaces.go @@ -0,0 +1,45 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +package api + +import ( + "gitlab.com/xx_network/primitives/id" +) + +// Message is a message received from the cMix network in the clear +// or that has been decrypted using established E2E keys. +type Message interface { + // Returns the message's sender ID, if available + GetSender() id.ID + GetSenderBytes() []byte + + // Returns the message payload/contents + // Parse this with protobuf/whatever according to the message type + GetPayload() []byte + + // Returns the message's recipient ID + // This is usually your userID but could be an ephemeral/group ID + GetRecipient() id.ID + GetRecipientBytes() []byte + + // Returns the message's type + GetMessageType() int32 + + // Returns the message's timestamp in seconds since unix epoc + GetTimestamp() int64 + // Returns the message's timestamp in ns since unix epoc + GetTimestampNano() int64 +} + +// RoundEvent contains event information for a given round. +// TODO: This is a half-baked interface and will be filled out later. +type RoundEvent interface { + // GetID returns the round ID for this round. + GetID() int + // GetStatus returns the status of this round. + GetStatus() int +} diff --git a/api/ndf_test.go b/api/ndf_test.go deleted file mode 100644 index 82be0622881d5f778263e1516389fa8ab77627ef..0000000000000000000000000000000000000000 --- a/api/ndf_test.go +++ /dev/null @@ -1,159 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2019 Privategrity Corporation / -// / -// All rights reserved. / -//////////////////////////////////////////////////////////////////////////////// - -package api - -import ( - "crypto" - "crypto/rand" - "encoding/base64" - "fmt" - "gitlab.com/xx_network/crypto/signature/rsa" - "gitlab.com/xx_network/primitives/ndf" - "reflect" - "testing" -) - -var ExampleJSON = `{"Timestamp": "2019-06-04T20:48:48-07:00", "gateways": [{"Address": "52.25.135.52", "Tls_certificate": "-----BEGIN CERTIFICATE-----\nMIIDgTCCAmmgAwIBAgIJAKLdZ8UigIAeMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEaMBgGA1UEAwwRZ2F0ZXdheSou\nY21peC5yaXAwHhcNMTkwMzA1MTgzNTU0WhcNMjkwMzAyMTgzNTU0WjBvMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ2xhcmVtb250\nMRswGQYDVQQKDBJQcml2YXRlZ3JpdHkgQ29ycC4xGjAYBgNVBAMMEWdhdGV3YXkq\nLmNtaXgucmlwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9+AaxwDP\nxHbhLmn4HoZu0oUM48Qufc6T5XEZTrpMrqJAouXk+61Jc0EFH96/sbj7VyvnXPRo\ngIENbk2Y84BkB9SkRMIXya/gh9dOEDSgnvj/yg24l3bdKFqBMKiFg00PYB30fU+A\nbe3OI/le0I+v++RwH2AV0BMq+T6PcAGjCC1Q1ZB0wP9/VqNMWq5lbK9wD46IQiSi\n+SgIQeE7HoiAZXrGO0Y7l9P3+VRoXjRQbqfn3ETNL9ZvQuarwAYC9Ix5MxUrS5ag\nOmfjc8bfkpYDFAXRXmdKNISJmtCebX2kDrpP8Bdasx7Fzsx59cEUHCl2aJOWXc7R\n5m3juOVL1HUxjQIDAQABoyAwHjAcBgNVHREEFTATghFnYXRld2F5Ki5jbWl4LnJp\ncDANBgkqhkiG9w0BAQUFAAOCAQEAMu3xoc2LW2UExAAIYYWEETggLNrlGonxteSu\njuJjOR+ik5SVLn0lEu22+z+FCA7gSk9FkWu+v9qnfOfm2Am+WKYWv3dJ5RypW/hD\nNXkOYxVJNYFxeShnHohNqq4eDKpdqSxEcuErFXJdLbZP1uNs4WIOKnThgzhkpuy7\ntZRosvOF1X5uL1frVJzHN5jASEDAa7hJNmQ24kh+ds/Ge39fGD8pK31CWhnIXeDo\nvKD7wivi/gSOBtcRWWLvU8SizZkS3hgTw0lSOf5geuzvasCEYlqrKFssj6cTzbCB\nxy3ra3WazRTNTW4TmkHlCUC9I3oWTTxw5iQxF/I2kQQnwR7L3w==\n-----END CERTIFICATE-----"}, {"Address": "52.25.219.38", "Tls_certificate": "-----BEGIN CERTIFICATE-----\nMIIDgTCCAmmgAwIBAgIJAKLdZ8UigIAeMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEaMBgGA1UEAwwRZ2F0ZXdheSou\nY21peC5yaXAwHhcNMTkwMzA1MTgzNTU0WhcNMjkwMzAyMTgzNTU0WjBvMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ2xhcmVtb250\nMRswGQYDVQQKDBJQcml2YXRlZ3JpdHkgQ29ycC4xGjAYBgNVBAMMEWdhdGV3YXkq\nLmNtaXgucmlwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9+AaxwDP\nxHbhLmn4HoZu0oUM48Qufc6T5XEZTrpMrqJAouXk+61Jc0EFH96/sbj7VyvnXPRo\ngIENbk2Y84BkB9SkRMIXya/gh9dOEDSgnvj/yg24l3bdKFqBMKiFg00PYB30fU+A\nbe3OI/le0I+v++RwH2AV0BMq+T6PcAGjCC1Q1ZB0wP9/VqNMWq5lbK9wD46IQiSi\n+SgIQeE7HoiAZXrGO0Y7l9P3+VRoXjRQbqfn3ETNL9ZvQuarwAYC9Ix5MxUrS5ag\nOmfjc8bfkpYDFAXRXmdKNISJmtCebX2kDrpP8Bdasx7Fzsx59cEUHCl2aJOWXc7R\n5m3juOVL1HUxjQIDAQABoyAwHjAcBgNVHREEFTATghFnYXRld2F5Ki5jbWl4LnJp\ncDANBgkqhkiG9w0BAQUFAAOCAQEAMu3xoc2LW2UExAAIYYWEETggLNrlGonxteSu\njuJjOR+ik5SVLn0lEu22+z+FCA7gSk9FkWu+v9qnfOfm2Am+WKYWv3dJ5RypW/hD\nNXkOYxVJNYFxeShnHohNqq4eDKpdqSxEcuErFXJdLbZP1uNs4WIOKnThgzhkpuy7\ntZRosvOF1X5uL1frVJzHN5jASEDAa7hJNmQ24kh+ds/Ge39fGD8pK31CWhnIXeDo\nvKD7wivi/gSOBtcRWWLvU8SizZkS3hgTw0lSOf5geuzvasCEYlqrKFssj6cTzbCB\nxy3ra3WazRTNTW4TmkHlCUC9I3oWTTxw5iQxF/I2kQQnwR7L3w==\n-----END CERTIFICATE-----"}, {"Address": "52.41.80.104", "Tls_certificate": "-----BEGIN CERTIFICATE-----\nMIIDgTCCAmmgAwIBAgIJAKLdZ8UigIAeMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQx\nGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEaMBgGA1UEAwwRZ2F0ZXdheSou\nY21peC5yaXAwHhcNMTkwMzA1MTgzNTU0WhcNMjkwMzAyMTgzNTU0WjBvMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJQ2xhcmVtb250\nMRswGQYDVQQKDBJQcml2YXRlZ3JpdHkgQ29ycC4xGjAYBgNVBAMMEWdhdGV3YXkq\nLmNtaXgucmlwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9+AaxwDP\nxHbhLmn4HoZu0oUM48Qufc6T5XEZTrpMrqJAouXk+61Jc0EFH96/sbj7VyvnXPRo\ngIENbk2Y84BkB9SkRMIXya/gh9dOEDSgnvj/yg24l3bdKFqBMKiFg00PYB30fU+A\nbe3OI/le0I+v++RwH2AV0BMq+T6PcAGjCC1Q1ZB0wP9/VqNMWq5lbK9wD46IQiSi\n+SgIQeE7HoiAZXrGO0Y7l9P3+VRoXjRQbqfn3ETNL9ZvQuarwAYC9Ix5MxUrS5ag\nOmfjc8bfkpYDFAXRXmdKNISJmtCebX2kDrpP8Bdasx7Fzsx59cEUHCl2aJOWXc7R\n5m3juOVL1HUxjQIDAQABoyAwHjAcBgNVHREEFTATghFnYXRld2F5Ki5jbWl4LnJp\ncDANBgkqhkiG9w0BAQUFAAOCAQEAMu3xoc2LW2UExAAIYYWEETggLNrlGonxteSu\njuJjOR+ik5SVLn0lEu22+z+FCA7gSk9FkWu+v9qnfOfm2Am+WKYWv3dJ5RypW/hD\nNXkOYxVJNYFxeShnHohNqq4eDKpdqSxEcuErFXJdLbZP1uNs4WIOKnThgzhkpuy7\ntZRosvOF1X5uL1frVJzHN5jASEDAa7hJNmQ24kh+ds/Ge39fGD8pK31CWhnIXeDo\nvKD7wivi/gSOBtcRWWLvU8SizZkS3hgTw0lSOf5geuzvasCEYlqrKFssj6cTzbCB\nxy3ra3WazRTNTW4TmkHlCUC9I3oWTTxw5iQxF/I2kQQnwR7L3w==\n-----END CERTIFICATE-----"}], "nodes": [{"Id": [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNDCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBAERwUmUlL9YP\nq6MSn+bUr6qNZPsVYoQAo8nTjZWiuSjJa2XWnh7sftnISWkwkiiRxo7qfq3sAiD5\nB8+tM6kONeICBXukldXJerxoVBspYa+RiPuDWy2pwGRDBpfty3QqJOpu5g2ThYFJ\nD5Xu0yCuX8ZJRj33nliI8dQgKdQQva6p2VuXzyRT8LwXMfRwLuSB6Schc9mF8C\nkWCb4m0ujlEKe1xKoKt2zG9b1o7XyaVhxguSUAuEznifMzsEUfuONJOy+XoQELex\nF0wvLzNzABcyxkM3lx52uG41mKgJiV6Z0ZyuBRvt+V3VL/38tPn9lsTaFi8N6/IH\nRyy0bWP5s44=\n-----END PUBLIC KEY-----\n", "Address": "18.237.147.105", "Tls_certificate": "-----BEGIN CERTIFICATE-----MIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDAeFwOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDhDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfsWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSEtJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uAm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9bJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEAAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEAneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIfU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2qvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4cyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1RtgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E56m52PyzMNV+2N21IPppKwA==-----END CERTIFICATE-----"}, {"Id": [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNDCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBAFbADcqA8KQh\nxzgylW6VS1dYYelO5DjPZVVSjfdcbj1twu4ZHDNZLOexpv4nGY8xS6vesELXcVOR\n/CHXgh/3byBZYm0zkrBi/FsJJ3nP2uZ1+QCRldI2KzqcLOWH/CAYj8koork9k1Dp\nFq7rMSDgw4pktqvFj9Eev8dSZuRnoCfZbt/6vxi1r30AYAjDYOwcysqcVyUa1tPa\nLEh3JksttXUCd5cvfqatWedTs5Vxo7ICW1toGBHABYvSJkwK0YFfi5RLw+Oda1sA\njJ+aLcIxQjrpoRC2alXCdwmZXVb+O6zluQctw6LJjt4J704ueSvR4VNNhr0uLYGW\nk7e+WoQCS98=\n-----END PUBLIC KEY-----\n", "Address": "52.11.136.238", "Tls_certificate": "-----BEGIN CERTIFICATE-----MIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDAeFwOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDhDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfsWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSEtJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uAm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9bJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEAAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEAneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIfU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2qvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4cyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1RtgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E56m52PyzMNV+2N21IPppKwA==-----END CERTIFICATE-----"}, {"Id": [3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNTCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBAQCN19tTnkS3\nitBQXXR/h8OKl+rliFBLgO6h6GvZL4yQDZFtBAOmkrs3wLoDroJRGCeqz/IUb+JF\njslEr/mpm2kcmK77hr535dq7HsWz1fFl9YyGTaOH055FLSV9QEPAV9j3zWADdQ1v\nuSQll+QfWi6lIibWV4HNQ2ywRFoOY8OBLCJB90UXLeJpaPanpqiM8hjda2VGRDbi\nIixEE2lCOWITydiz2DmvXrLhVGF49+g5MDwbWO65dmasCe//Ff6Z4bJ6n049xv\nVtac8nX6FO3eBsV5d+rG6HZXSG3brCKRCSKYCTX1IkTSiutYxYqvwaluoCjOakh0\nKkqvQ8IeVZ+B\n-----END PUBLIC KEY-----\n", "Address": "34.213.79.31", "Tls_certificate": "-----BEGIN CERTIFICATE-----MIIDbDCCAlSgAwIBAgIJAOUNtZneIYECMA0GCSqGSIb3DQEBBQUAMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDAeFwOTAzMDUxODM1NDNaFw0yOTAzMDIxODM1NDNaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjETMBEGA1UEAwwKKi5jbWl4LnJpcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPP0WyVkfZA/CEd2DgKpcudn0oDhDwsjmx8LBDWsUgQzyLrFiVigfUmUefknUH3dTJjmiJtGqLsayCnWdqWLHPJYvFfsWYW0IGF93UG/4N5UAWO4okC3CYgKSi4ekpfw2zgZq0gmbzTnXcHF9gfmQ7jJUKSEtJPSNzXq+PZeJTC9zJAb4Lj8QzH18rDM8DaL2y1ns0Y2Hu0edBFn/OqavBJKb/uAm3AEjqeOhC7EQUjVamWlTBPt40+B/6aFJX5BYm2JFkRsGBIyBVL46MvC02MgzTT9bJIJfwqmBaTruwemNgzGu7Jk03hqqS1TUEvSI6/x8bVoba3orcKkf9HsDjECAwEAAaMZMBcwFQYDVR0RBA4wDIIKKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEAneUocN4AbcQAC1+b3To8u5UGdaGxhcGyZBlAoenRVdjXK3lTjsMdMWb4QctgNfIfU/zuUn2mxTmF/ekP0gCCgtleZr9+DYKU5hlXk8K10uKxGD6EvoiXZzlfeUuotgp2qvI3ysOm/hvCfyEkqhfHtbxjV7j7v7eQFPbvNaXbLa0yr4C4vMK/Z09Ui9JrZ/Z4cyIkxfC6/rOqAirSdIp09EGiw7GM8guHyggE4IiZrDslT8V3xIl985cbCxSxeW1RtgH4rdEXuVe9+31oJhmXOE9ux2jCop9tEJMgWg7HStrJ5plPbb+HmjoX3nBO04E56m52PyzMNV+2N21IPppKwA==-----END CERTIFICATE-----"}], "registration": {"Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNDCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBAAlELnrXLG0s\n4yAAn7IsVWwY7swDnbBcsIF2cnef4tjm/nNwrFKp5AxYqgeXCiJM8VkyJrotWG50\nnXQwMCR6BsvYrlVt/RmQvR8BSrir62uSLK7hMKm7dXnFvtyFtjp91UwTRbIjxhUQ\nGYnhAzrkCDWo1m54ysqXEGlrVwvRXrCAXiLKPiTEIS+B4GFH9W26SwBxhFLNYSUk\nZZ7+4qwMf9aTu7kIpXTP3hNIyRCjtuZvo5SnymtbLARwTP943hW8MOj0+Ege+m1P\ntey6rkMUGQ86cgK9/7+7Jb+EwW5UxdQtFPUFeNKdQ6zDPS6qbliecUrsc12tdgeg\nhQyuMbyKUuo=\n-----END PUBLIC KEY-----\n", "Address": "registration.default.cmix.rip", "Tls_certificate": "-----BEGIN CERTIFICATE-----MIIDkDCCAnigAwIBAgIJAJnjosuSsP7gMA0GCSqGSIb3DQEBBQUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEfMB0GA1UEAwwWcmVnaXN0cmF0aW9uKi5jbWl4LnJpcDAeFwOTAzMDUyMTQ5NTZaFw0yOTAzMDIyMTQ5NTZaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlDbGFyZW1vbnQxGzAZBgNVBAoMElByaXZhdGVncml0eSBDb3JwLjEfMB0GA1UEAwwWcmVnaXN0cmF0aW9uKi5jbWl4LnJpcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOQKvqjdh35o+MECBhCwopJzPlQNmq2iPbewRNtI02bUNK3kLQUbFlYdzNGZS4GYXGc5O+jdi8Slx82r1kdjz5PPCNFBARIsOP/L8r3DGeW+yeJdgBZjm1s3ylkamt4Ajiq/bNjysS6L/WSOp+sVumDxtBEzO/UTU1O6QRnzUphLaiWENmErGvsH0CZVq38Ia58k/QjCAzpUcYi4j2l1fb07xqFcQD8H6SmUM297UyQosDrp8ukdIo31Koxr4XDnnNNsYStC26tzHMeKuJ2Wl+3YzsSyflfM2YEcKE31sqB9DS36UkJ8J84eLsHNImGg3WodFAviDB67+jXDbB30NkMCAwEAAaMlMCMwIQYDVR0RBBowGIIWcmVnaXN0cmF0aW9uKi5jbWl4LnJpcDANBgkqhkiG9w0BAQUFAAOCAQEAF9mNzk+g+o626Rllt3f3/1qIyYQrYJ0BjSWCKYEFMCgZ4JibAJjAvIajhVYERtltffM+YKcdE2kTpdzJ0YJuUnRfuv6sVnXlVVugUUnd4IOigmjbCdM32k170CYMm0aiwGxl4FrNa8ei7AIax/s1n+sqWq3HeW5LXjnoVb+s3HeCWIuLfcgrurfye8FnNhy14HFzxVYYefIKmL+DPlcGGGm/PPYt3u4a2+rP3xaihc65dTa0u5tf/XPXtPxTDPFj2JeQDFxo7QRREbPD89CtYnwuP937CrkvCKrL0GkW1FViXKqZY9F5uhxrvLIpzhbNrs/EbtweY35XGLDCCMkg==-----END CERTIFICATE-----"}, "udb": {"Id": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3], "Dsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIDNDCCAiwCggEBAJ22+1lRtmu2/h4UDx0s5VAjdBYf1lON8WSCGGQvC1xIyPek\nGq36GHMkuHZ0+hgisA8ez4E2lD18VXVyZOWhpE/+AS6ZNuAMHT6TELAcfReYBdMF\niyqfS7b5cWv+YRfGtbPMTZvjQRBK1KgK1slOAF9LmT4U8JHrUXQ78zBQw43iNVZ+\nGzTD1qXAzqoaDzaCE8PRmEPQtLCdy5/HLTnI3kHxvxTUu0Vjyig3FiHK0zJLai05\nIUW+v6x0iAUjb1yi/pK4cc2PnDbTKStVCcqMqneirfx7/XfdpvcRJadFb+oVPkMy\nVqImHGoG7TaTeX55lfrVqrvPvj7aJ0HjdUBK4lsCIQDywxGTdM52yTVpkLRlN0oX\n8j+e01CJvZafYcbd6ZmMHwKCAQBcf/awb48UP+gohDNJPkdpxNmIrOW+JaDiSAln\nBxbGE9ewzuaTL4+qfETSyyRSPaU/vk9uw1lYktGqWMQyigbEahVmLn6qcDod7Pi7\nstBdvi65VsFCozhmHRBGHA0TVHIIUFfzSUMJ/6c8YR94syrbtXQMNhyfNb6QmX2y\nAU4u9apheC9Sq+uL1kMsTdCXvFQjsoXa+2DcNk6BYfSio1rKOhCxxNIDzHakcKM6\n/cvdkpWYWavYtW4XJSUteOrGbnG6muPx3SSHGZh0OTzU2DIYaABlR2Dh40wJ5NFV\nF5+ewNxEc/mWvc5u7Ryr7YtvEW962c9QXfD5mONKsnUUsP/nAoIBACvR2lUslz3D\nB/MUo0rHVIHVkhVJCxNjtgTOYgJ9ckArSXQbYzr/fcigcNGjUO2LbK5NFp9GK43C\nrLxMUnJ9nkyIVPaWvquJFZItjcDK3NiNGyD4XyM0eRj4dYeSxQM48hvFbmtbjlXn\n9SQTnGIlr1XnTI4RVHZSQOL6kFJIaLw6wYrQ4w08Ng+p45brp5ercAHnLiftNUWP\nqROhQkdSEpS9LEwfotUSY1jP2AhQfaIMxaeXsZuTU1IYvdhMFRL3DR0r5Ww2Upf8\ng0Ace0mtnsUQ2OG+7MTh2jYIEWRjvuoe3RCz603ujW6g7BfQ1H7f4YFwc5xOOJ3u\nr4dj49dCCjc=\n-----END PUBLIC KEY-----\n"}, "E2e": {"Prime": "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", "Small_prime": "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD9128A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9EE1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36B3861AA7255E4C0278BA3604650C10BE19482F23171B671DF1CF3B960C074301CD93C1D17603D147DAE2AEF837A62964EF15E5FB4AAC0B8C1CCAA4BE754AB5728AE9130C4C7D02880AB9472D455655347FFFFFFFFFFFFFFF", "Generator": "02"}, "CMIX": {"Prime": "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", "Small_prime": "7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD9128A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9EE1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36B3861AA7255E4C0278BA3604650C10BE19482F23171B671DF1CF3B960C074301CD93C1D17603D147DAE2AEF837A62964EF15E5FB4AAC0B8C1CCAA4BE754AB5728AE9130C4C7D02880AB9472D455655347FFFFFFFFFFFFFFF", "Generator": "02"}}` - -// FIXME: This test is breaking grep in turn breaking our pipeline. we need to write a ticket to fix this. -// Tests that VerifyNDF() correctly verifies the NDF signature. -func TestVerifyNDF(t *testing.T) { - // Load tls private key - privKey, err := rsa.LoadPrivateKeyFromPem([]byte("-----BEGIN PRIVATE KEY-----\n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\nOaRA5ZbdE7g7vxKRV36jT3wvD7W+\n-----END PRIVATE KEY-----\n")) - if err != nil || privKey == nil { - t.Error("Failed to load privKey\n") - } - // Sign the NDF - ndfJSON, _, _ := ndf.DecodeNDF(ExampleJSON + "\n") - rsaHash := crypto.SHA256.New() - rsaHash.Write([]byte(ExampleJSON)) - signature, _ := rsa.Sign( - rand.Reader, privKey, crypto.SHA256, rsaHash.Sum(nil), nil) - - // Print error on panic - defer func() { - if r := recover(); r != nil { - t.Errorf("VerifyNDF() panicked when it was not supposed to"+ - "\n\treceived: %#v\n\texpected: %#v", r, nil) - } - }() - - // Compose network definition string - ndfString := ExampleJSON + "\n" + base64.StdEncoding.EncodeToString(signature) + "\n" - - // Run VerifyNDF() - fmt.Println(ndfString) - ndfJSONOutput := VerifyNDF(ndfString, "-----BEGIN CERTIFICATE-----\n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\n-----END CERTIFICATE-----\n") - - // Check that the output is the expected NetworkDefinition structure - if !reflect.DeepEqual(ndfJSONOutput, ndfJSON) { - t.Errorf("VerifyNDF() did not output the correct "+ - "NetworkDefinition structure"+ - "\n\treceived: %#v\n\texpected: %#v", - ndfJSONOutput, ndfJSON) - } -} - -// Tests that VerifyNDF() panics when given the incorrect RSA public key. -func TestVerifyNDF_ErrPublicKey(t *testing.T) { - // Generate RSA private key and fake RSA public key - // Size of 768 is unsafe, but allows the test to run faster - privateKey, _ := rsa.GenerateKey(rand.Reader, 768) - - privateKey2, _ := rsa.GenerateKey(rand.Reader, 768) - publicKey := &rsa.PublicKey{PublicKey: privateKey2.PublicKey} - publicKeyBytes := rsa.CreatePublicKeyPem(publicKey) - - // Sign the NDF - ndfJSON, _, _ := ndf.DecodeNDF(ExampleJSON + "\n") - opts := rsa.NewDefaultOptions() - rsaHash := opts.Hash.New() - rsaHash.Write(ndfJSON.Serialize()) - signature, _ := rsa.Sign( - rand.Reader, privateKey, opts.Hash, rsaHash.Sum(nil), nil) - - // Print error on no panic - defer func() { - if r := recover(); r == nil { - t.Errorf("VerifyNDF() did not panic when expected when the "+ - "public key is invalid"+ - "\n\treceived: %#v\n\texpected: %#v", - r, "Could not verify NDF: crypto/rsa: verification error") - } - }() - - // Compose network definition string - ndfString := ExampleJSON + "\n" + base64.StdEncoding.EncodeToString(signature) - - // Run VerifyNDF() - VerifyNDF(ndfString, string(publicKeyBytes)) -} - -// Tests that VerifyNDF() panics when given an invalid NDF string. -func TestVerifyNDF_ErrInvalidNDF(t *testing.T) { - // Generate RSA private key and fake RSA public key - // Size of 768 is unsafe, but allows the test to run faster - privateKey, _ := rsa.GenerateKey(rand.Reader, 768) - - privateKey2, _ := rsa.GenerateKey(rand.Reader, 768) - publicKey := &rsa.PublicKey{PublicKey: privateKey2.PublicKey} - publicKeyBytes := rsa.CreatePublicKeyPem(publicKey) - - // Sign the NDF - ndfJSON, _, _ := ndf.DecodeNDF(ExampleJSON + "\n") - opts := rsa.NewDefaultOptions() - rsaHash := opts.Hash.New() - rsaHash.Write(ndfJSON.Serialize()) - signature, _ := rsa.Sign( - rand.Reader, privateKey, opts.Hash, rsaHash.Sum(nil), nil) - - // Print error on no panic - defer func() { - if r := recover(); r == nil { - t.Errorf("VerifyNDF() did not panic when expected when given "+ - "invalid NDF"+ - "\n\treceived: %#v\n\texpected: %#v", - r, "Could not decode NDF: unexpected end of JSON input") - } - }() - - // Compose network definition string - ndfString := " \n" + base64.StdEncoding.EncodeToString(signature) - - // Run VerifyNDF() - VerifyNDF(ndfString, string(publicKeyBytes)) -} - -// Tests that VerifyNDF() correctly outputs a NetworkDefinition structure and -// skips verifying the signature when the public key is empty. -func TestVerifyNDF_EmptyPublicKey(t *testing.T) { - // Generate RSA private and public keys - // Size of 768 is unsafe, but allows the test to run faster - privateKey, _ := rsa.GenerateKey(rand.Reader, 768) - - // Sign the NDF - ndfJSON, _, _ := ndf.DecodeNDF(ExampleJSON + "\n") - opts := rsa.NewDefaultOptions() - rsaHash := opts.Hash.New() - rsaHash.Write(ndfJSON.Serialize()) - signature, _ := rsa.Sign( - rand.Reader, privateKey, opts.Hash, rsaHash.Sum(nil), nil) - - // Compose network definition string - ndfString := ExampleJSON + "\n" + base64.StdEncoding.EncodeToString(signature) - - // Run VerifyNDF() - ndfJSONOutput := VerifyNDF(ndfString, "") - - // Check that the output is the expected NetworkDefinition structure - if !reflect.DeepEqual(ndfJSONOutput, ndfJSON) { - t.Errorf("VerifyNDF() did not output the correct "+ - "NetworkDefinition structure"+ - "\n\treceived: %#v\n\texpected: %#v", - ndfJSONOutput, ndfJSON) - } -} diff --git a/api/notifications.go b/api/notifications.go deleted file mode 100644 index a7587f4f80c632995dd0cb4c885a5a6189bc7d2e..0000000000000000000000000000000000000000 --- a/api/notifications.go +++ /dev/null @@ -1,52 +0,0 @@ -package api - -import ( - "github.com/pkg/errors" - "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/xx_network/primitives/id" -) - -// RegisterForNotifications sends a message to notification bot indicating it -// is registering for notifications -func (cl *Client) RegisterForNotifications(notificationToken []byte) error { - // Pull the host from the manage - notificationBotHost, ok := cl.receptionManager.Comms.GetHost(&id.NotificationBot) - if !ok { - return errors.New("Failed to retrieve host for notification bot") - } - - // Send the register message - _, err := cl.receptionManager.Comms.RegisterForNotifications(notificationBotHost, - &mixmessages.NotificationToken{ - Token: notificationToken, - }) - if err != nil { - err := errors.Errorf( - "RegisterForNotifications: Unable to register for notifications! %s", err) - return err - } - - return nil - -} - -// UnregisterForNotifications sends a message to notification bot indicating it -// no longer wants to be registered for notifications -func (cl *Client) UnregisterForNotifications() error { - // Pull the host from the manage - notificationBotHost, ok := cl.receptionManager.Comms.GetHost(&id.NotificationBot) - if !ok { - return errors.New("Failed to retrieve host for notification bot") - } - - // Send the unregister message - _, err := cl.receptionManager.Comms.UnregisterForNotifications(notificationBotHost) - if err != nil { - err := errors.Errorf( - "RegisterForNotifications: Unable to register for notifications! %s", err) - return err - } - - return nil - -} diff --git a/api/notifications_test.go b/api/notifications_test.go deleted file mode 100644 index f283f7e558fc28d41a0749a97ba2a5794d707b09..0000000000000000000000000000000000000000 --- a/api/notifications_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package api - -import ( - "gitlab.com/xx_network/crypto/signature/rsa" - "testing" -) - -var dummy_key = `-----BEGIN PRIVATE KEY----- -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 -jR+QSAa9eEozCngV6LUagC0YYWDZ ------END PRIVATE KEY-----` - -// Happy path -func TestClient_RegisterForNotifications(t *testing.T) { - // Initialize client with dummy storage - storage := DummyStorage{LocationA: ".ekv-registernotifications/a", StoreA: []byte{'a', 'b', 'c'}} - client, err := NewClient(&storage, ".ekv-registernotifications/a", "", def) - if err != nil { - t.Errorf("Failed to initialize dummy client: %s", err.Error()) - return - } - - privKey, err := rsa.LoadPrivateKeyFromPem([]byte(dummy_key)) - if err != nil { - t.Errorf("Failed to load private key: %+v", err) - } - - // InitNetwork to gateways and reg server - err = client.InitNetwork() - - if err != nil { - t.Errorf("Client failed of connect: %+v", err) - } - - err = client.GenerateKeys(privKey, "test") - if err != nil { - t.Errorf("Failed to properly set up keys: %+v", err) - } - - token := make([]byte, 32) - - err = client.RegisterForNotifications(token) - if err != nil { - t.Errorf("Expected happy path, received error: %+v", err) - } -} - -// Happy path -func TestClient_UnregisterForNotifications(t *testing.T) { - // Initialize client with dummy storage - storage := DummyStorage{LocationA: ".ekv-unregisternotifications/a", StoreA: []byte{'a', 'b', 'c'}} - client, err := NewClient(&storage, ".ekv-unregisternotifications/a", "", def) - if err != nil { - t.Errorf("Failed to initialize dummy client: %s", err.Error()) - } - - privKey, err := rsa.LoadPrivateKeyFromPem([]byte(dummy_key)) - if err != nil { - t.Errorf("Failed to load private key: %+v", err) - return - } - err = client.GenerateKeys(privKey, "test") - if err != nil { - t.Errorf("Failed to properly set up keys: %+v", err) - return - } - // InitNetwork to gateways and reg server - err = client.InitNetwork() - - if err != nil { - t.Errorf("Client failed of connect: %+v", err) - } - - err = client.UnregisterForNotifications() - if err != nil { - t.Errorf("Expected happy path, received error: %+v", err) - } -} diff --git a/api/permissioning.go b/api/permissioning.go index 8573366306087791f71ea806bd3110691037ce77..2a7444d7e94421e59ac510a493b00a82f3e28456 100644 --- a/api/permissioning.go +++ b/api/permissioning.go @@ -8,17 +8,14 @@ package api import ( "github.com/pkg/errors" - "gitlab.com/elixxir/client/context" "gitlab.com/elixxir/client/globals" - "gitlab.com/elixxir/client/network/permissioning" "gitlab.com/elixxir/client/storage" - "gitlab.com/elixxir/comms/client" ) // Returns an error if registration fails. func (c *Client) RegisterWithPermissioning(registrationCode string) error { - instance := ctx.Manager.GetInstance() - instance.GetPartialNdf() + ctx := c.ctx + netman := ctx.Manager //Check the regState is in proper state for registration regState := c.storage.GetRegistrationStatus() @@ -29,8 +26,8 @@ func (c *Client) RegisterWithPermissioning(registrationCode string) error { userData := ctx.Session.User() // Register with the permissioning server and generate user information - regValidationSignature, err := permissioning.RegisterWithPermissioning(&comms, - userData.GetCryptographicIdentity().GetRSA().GetPublic(), registrationCode) + regValidationSignature, err := netman.RegisterWithPermissioning( + registrationCode) if err != nil { globals.Log.INFO.Printf(err.Error()) return err diff --git a/bindings/api.go b/bindings/api.go index d0a0fc133d6f6c769009cc3ad82fe940e4cdf611..b8351406a78d4b37e6b10553e7bf30384fbfe553 100644 --- a/bindings/api.go +++ b/bindings/api.go @@ -10,6 +10,12 @@ import ( "gitlab.com/elixxir/client/api" ) +// BindingsClient wraps the api.Client, implementing additional functions +// to support the gomobile Client interface +type BindingsClient struct { + api.Client +} + // NewClient connects and registers to the network using a json encoded // network information string and then creates a new client at the specified // storageDir using the specified password. This function will fail @@ -29,7 +35,12 @@ import ( // Users of this function should delete the storage directory on error. func NewClient(network, storageDir string, password []byte) (Client, error) { // TODO: This should wrap the bindings ClientImpl, when available. - return api.NewClient(network, storageDir, password) + client, err := api.NewClient(network, storageDir, password) + if err != nil { + return nil, err + } + bindingsClient := &BindingsClient{*client} + return bindingsClient, nil } // LoadClient will load an existing client from the storageDir @@ -41,5 +52,66 @@ func NewClient(network, storageDir string, password []byte) (Client, error) { // starts subprocesses to perform network operations. func LoadClient(storageDir string, password []byte) (Client, error) { // TODO: This should wrap the bindings ClientImpl, when available. - return api.LoadClient(storageDir, password) + client, err := api.LoadClient(storageDir, password) + if err != nil { + return nil, err + } + bindingsClient := &BindingsClient{*client} + return bindingsClient, nil +} + +// RegisterListener records and installs a listener for messages +// matching specific uid, msgType, and/or username +func (b *BindingsClient) RegisterListener(uid []byte, msgType int, + username string, listener Listener) { +} + +// SearchWithHandler is a non-blocking search that also registers +// a callback interface for user disovery events. +func (b *BindingsClient) SearchWithHandler(data, separator string, + searchTypes []byte, hdlr UserDiscoveryHandler) { +} + +// RegisterAuthEventsHandler registers a callback interface for channel +// authentication events. +func (b *BindingsClient) RegisterAuthEventsHandler(hdlr AuthEventHandler) { +} + +// RegisterRoundEventsHandler registers a callback interface for round +// events. +func (b *BindingsClient) RegisterRoundEventsHandler(hdlr RoundEventHandler) { +} + +// SendE2E sends an end-to-end payload to the provided recipient with +// the provided msgType. Returns the list of rounds in which parts of +// the message were sent or an error if it fails. +func (b *BindingsClient) SendE2E(payload, recipient []byte, + msgType int) (RoundList, error) { + return nil, nil +} + +// SendUnsafe sends an unencrypted payload to the provided recipient +// with the provided msgType. Returns the list of rounds in which parts +// of the message were sent or an error if it fails. +// NOTE: Do not use this function unless you know what you are doing. +// This function always produces an error message in client logging. +func (b *BindingsClient) SendUnsafe(payload, recipient []byte, + msgType int) (RoundList, error) { + return nil, nil +} + +// SendCMIX sends a "raw" CMIX message payload to the provided +// recipient. Note that both SendE2E and SendUnsafe call SendCMIX. +// Returns the round ID of the round the payload was sent or an error +// if it fails. +func (b *BindingsClient) SendCMIX(payload, recipient []byte) (int, error) { + return 0, nil +} + +// Search accepts a "separator" separated list of search elements with +// an associated list of searchTypes. It returns a ContactList which +// allows you to iterate over the found contact objects. +func (b *BindingsClient) Search(data, separator string, + searchTypes []byte) ContactList { + return nil } diff --git a/bindings/interfaces.go b/bindings/interfaces.go index a3300b2d711b468f95c7b1d45b77adce65f3a3f9..5156dd6b896f10a18637b23e9929f80100c5f982 100644 --- a/bindings/interfaces.go +++ b/bindings/interfaces.go @@ -7,7 +7,8 @@ package bindings import ( - "gitlab.com/elixxir/primitives/switchboard" + "gitlab.com/elixxir/client/api" + "gitlab.com/elixxir/client/context/stoppable" ) // Client is defined inside the api package. At minimum, it implements all of @@ -84,13 +85,13 @@ type Client interface { // GetUser returns the current user Identity for this client. This // can be serialized into a byte stream for out-of-band sharing. - GetUser() (Contact, error) + GetUser() (api.Contact, error) // MakeContact creates a contact from a byte stream (i.e., unmarshal's a // Contact object), allowing out-of-band import of identities. - MakeContact(contactBytes []byte) (Contact, error) + MakeContact(contactBytes []byte) (api.Contact, error) // GetContact returns a Contact object for the given user id, or // an error - GetContact(uid []byte) (Contact, error) + GetContact(uid []byte) (api.Contact, error) // ----- User Discovery ----- @@ -109,7 +110,7 @@ type Client interface { // so this user can send messages to the desired recipient Contact. // To receive confirmation from the remote user, clients must // register a listener to do that. - CreateAuthenticatedChannel(recipient Contact, payload []byte) error + CreateAuthenticatedChannel(recipient api.Contact, payload []byte) error // RegierAuthEventsHandler registers a callback interface for channel // authentication events. RegisterAuthEventsHandler(hdlr AuthEventHandler) @@ -120,68 +121,19 @@ type Client interface { // and returns an object for checking state and stopping those threads. // Call this when returning from sleep and close when going back to // sleep. - StartNetworkRunner() NetworkRunner + StartNetworkRunner() stoppable.Stoppable // RegisterRoundEventsHandler registers a callback interface for round // events. RegisterRoundEventsHandler(hdlr RoundEventHandler) } -// Message is a message received from the cMix network in the clear -// or that has been decrypted using established E2E keys. -type Message interface { - // Returns the message's sender ID, if available - GetSender() []byte - // Returns the message payload/contents - // Parse this with protobuf/whatever according to the message type - GetPayload() []byte - // Returns the message's recipient ID - // This is usually your userID but could be an ephemeral/group ID - GetRecipient() []byte - // Returns the message's type - GetMessageType() int32 - // Returns the message's timestamp in seconds since unix epoc - GetTimestamp() int64 - // Returns the message's timestamp in ns since unix epoc - GetTimestampNano() int64 -} - -// RoundEvent contains event information for a given round. -// TODO: This is a half-baked interface and will be filled out later. -type RoundEvent interface { - // GetID returns the round ID for this round. - GetID() int - // GetStatus returns the status of this round. - GetStatus() int -} - // ContactList contains a list of contacts type ContactList interface { // GetLen returns the number of contacts in the list GetLen() int // GetContact returns the contact at index i - GetContact(i int) Contact -} - -// Contact contains the contacts information. Note that this object -// is a copy of the contact at the time it was generated, so api users -// cannot rely on this object being updated once it has been received. -type Contact interface { - // GetID returns the user ID for this user. - GetID() []byte - // GetPublicKey returns the publickey bytes for this user. - GetPublicKey() []byte - // GetSalt returns the salt used to initiate an authenticated channel - GetSalt() []byte - // IsAuthenticated returns true if an authenticated channel exists for - // this user so we can begin to send messages. - IsAuthenticated() bool - // IsConfirmed returns true if the user has confirmed the authenticated - // channel on their end. - IsConfirmed() bool - // Marshal creates a serialized representation of a contact for - // out-of-band contact exchange. - Marshal() ([]byte, error) + GetContact(i int) api.Contact } // ----- Callback interfaces ----- @@ -192,7 +144,7 @@ type Contact interface { // time. type Listener interface { // Hear is called to receive a message in the UI - Hear(msg Message) + Hear(msg api.Message) } // AuthEventHandler handles authentication requests initiated by @@ -202,13 +154,30 @@ type AuthEventHandler interface { // the client has called CreateAuthenticatedChannel for // the provided contact. Payload is typically empty but // may include a small introductory message. - HandleConfirmation(contact Contact, payload []byte) + HandleConfirmation(contact api.Contact, payload []byte) // HandleRequest handles AuthEvents received before // the client has called CreateAuthenticatedChannel for // the provided contact. It should prompt the user to accept // the channel creation "request" and, if approved, // call CreateAuthenticatedChannel for this Contact. - HandleRequest(contact Contact, payload []byte) + HandleRequest(contact api.Contact, payload []byte) +} + +// RoundList contains a list of contacts +type RoundList interface { + // GetLen returns the number of contacts in the list + GetLen() int + // GetRoundID returns the round ID at index i + GetRoundID(i int) int +} + +// RoundEvent contains event information for a given round. +// TODO: This is a half-baked interface and will be filled out later. +type RoundEvent interface { + // GetID returns the round ID for this round. + GetID() int + // GetStatus returns the status of this round. + GetStatus() int } // RoundEventHandler handles round events happening on the cMix network. diff --git a/cmd/root.go b/cmd/root.go index 4fe59246c05ee9ac47df68cff958e0c3b22e0fb5..fb328b155b826d8433cc0b990927041aa0ab6a7d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -8,23 +8,15 @@ package cmd import ( - "encoding/base64" "encoding/binary" "fmt" - "github.com/golang/protobuf/proto" "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" - "gitlab.com/elixxir/primitives/switchboard" - "gitlab.com/elixxir/primitives/utils" - "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" "io/ioutil" "os" "strconv" - "strings" - "sync/atomic" - "time" ) var verbose bool @@ -63,319 +55,116 @@ func Execute() { } } -func sessionInitialization() (*id.ID, string, *api.Client) { - var err error - register := false - - var client *api.Client - - // Read in the network definition file and save as string - ndfBytes, err := utils.ReadFile(ndfPath) - if err != nil { - globals.Log.FATAL.Panicf("Could not read network definition file: %v", err) - } - - // Check if the NDF verify flag is set - if skipNDFVerification { - ndfPubKey = "" - globals.Log.WARN.Println("Skipping NDF verification") - } else { - pkFile, err := os.Open(ndfPubKey) - if err != nil { - globals.Log.FATAL.Panicf("Could not open cert file: %v", - err) - } - - pkBytes, err := ioutil.ReadAll(pkFile) - if err != nil { - globals.Log.FATAL.Panicf("Could not read cert file: %v", - err) - } - ndfPubKey = string(pkBytes) - } - - // Verify the signature - globals.Log.DEBUG.Println("Verifying NDF...") - ndfJSON := api.VerifyNDF(string(ndfBytes), ndfPubKey) - globals.Log.DEBUG.Printf(" NDF Verified") - - //If no session file is passed initialize with RAM Storage - if sessionFile == "" { - client, err = api.NewClient(&globals.RamStorage{}, "", "", ndfJSON) - if err != nil { - globals.Log.ERROR.Printf("Could Not Initialize Ram Storage: %s\n", - err.Error()) - return &id.ZeroUser, "", nil - } - globals.Log.INFO.Println("Initialized Ram Storage") - register = true - } else { - - var sessionA, sessionB string - - locs := strings.Split(sessionFile, ",") - - if len(locs) == 2 { - sessionA = locs[0] - sessionB = locs[1] - } else { - sessionA = sessionFile - sessionB = sessionFile + "-2" - } - - //If a session file is passed, check if it's valid - _, err1 := os.Stat(sessionA) - _, err2 := os.Stat(sessionB) - - if err1 != nil && err2 != nil { - //If the file does not exist, register a new user - if os.IsNotExist(err1) && os.IsNotExist(err2) { - register = true - } else { - //Fail if any other error is received - globals.Log.ERROR.Printf("Error with file paths: %s %s", - err1, err2) - return &id.ZeroUser, "", nil - } - } - //Initialize client with OS Storage - client, err = api.NewClient(nil, sessionA, sessionB, ndfJSON) - if err != nil { - globals.Log.ERROR.Printf("Could Not Initialize OS Storage: %s\n", err.Error()) - return &id.ZeroUser, "", nil - } - globals.Log.INFO.Println("Initialized OS Storage") - - } - - if noBlockingTransmission { - globals.Log.INFO.Println("Disabling Blocking Transmissions") - client.DisableBlockingTransmission() - } - - // Handle parsing gateway addresses from the config file - - //REVIEWER NOTE: Possibly need to remove/rearrange this, - // now that client may not know gw's upon client creation - /*gateways := client.GetNDF().Gateways - // If gwAddr was not passed via command line, check config file - if len(gateways) < 1 { - // No gateways in config file or passed via command line - globals.Log.ERROR.Printf("Error: No gateway specified! Add to" + - " configuration file or pass via command line using -g!\n") - return &id.ZeroUser, "", nil - }*/ - - if noTLS { - client.DisableTls() - } - - // InitNetwork to gateways, notificationBot and reg server - err = client.InitNetwork() - if err != nil { - globals.Log.FATAL.Panicf("Could not call connect on client: %+v", err) - } - - client.SetRateLimiting(rateLimiting) - - // Holds the User ID - var uid *id.ID - - // Register a new user if requested - if register { - globals.Log.INFO.Println("Registering...") - - regCode := registrationCode - // If precanned user, use generated code instead - if userId != 0 { - precanned = true - uid := new(id.ID) - binary.BigEndian.PutUint64(uid[:], userId) - uid.SetType(id.User) - regCode = userRegistry.RegistrationCode(uid) - } - - globals.Log.INFO.Printf("Building keys...") - - var privKey *rsa.PrivateKey - - if privateKeyPath != "" { - privateKeyBytes, err := utils.ReadFile(privateKeyPath) - if err != nil { - globals.Log.FATAL.Panicf("Could not load user private key PEM from "+ - "path %s: %+v", privateKeyPath, err) - } - - privKey, err = rsa.LoadPrivateKeyFromPem(privateKeyBytes) - if err != nil { - globals.Log.FATAL.Panicf("Could not load private key from PEM bytes: %+v", err) - } - } - - //Generate keys for registration - err := client.GenerateKeys(privKey, sessFilePassword) - if err != nil { - globals.Log.FATAL.Panicf("%+v", err) - } - - globals.Log.INFO.Printf("Attempting to register with code %s...", regCode) - - errRegister := fmt.Errorf("") - - //Attempt to register user with same keys until a success occurs - for errRegister != nil { - _, errRegister = client.RegisterWithPermissioning(precanned, regCode) - if errRegister != nil { - globals.Log.FATAL.Panicf("Could Not Register User: %s", - errRegister.Error()) - } - } - - err = client.RegisterWithNodes() - if err != nil { - globals.Log.FATAL.Panicf("Could Not Register User with nodes: %s", - err.Error()) - } - - uid = client.GetCurrentUser() - - userbase64 := base64.StdEncoding.EncodeToString(uid[:]) - globals.Log.INFO.Printf("Registered as user (uid, the var) %v", uid) - globals.Log.INFO.Printf("Registered as user (userID, the global) %v", userId) - globals.Log.INFO.Printf("Successfully registered user %s!", userbase64) - - } else { - // hack for session persisting with cmd line - // doesn't support non pre canned users - uid := new(id.ID) - binary.BigEndian.PutUint64(uid[:], userId) - uid.SetType(id.User) - globals.Log.INFO.Printf("Skipped Registration, user: %v", uid) - } - - if !precanned { - // If we are sending to a non precanned user we retrieve the uid from the session returned by client.login - uid, err = client.Login(sessFilePassword) - } else { - _, err = client.Login(sessFilePassword) - } - - if err != nil { - globals.Log.FATAL.Panicf("Could not login: %v", err) - } - return uid, client.GetUsername(), client -} - -func setKeyParams(client *api.Client) { - globals.Log.DEBUG.Printf("Trying to parse key parameters...") - minKeys, err := strconv.Atoi(keyParams[0]) - if err != nil { - return - } - - maxKeys, err := strconv.Atoi(keyParams[1]) - if err != nil { - return - } - - numRekeys, err := strconv.Atoi(keyParams[2]) - if err != nil { - return - } - - ttlScalar, err := strconv.ParseFloat(keyParams[3], 64) - if err != nil { - return - } - - minNumKeys, err := strconv.Atoi(keyParams[4]) - if err != nil { - return - } - - globals.Log.DEBUG.Printf("Setting key generation parameters: %d, %d, %d, %f, %d", - minKeys, maxKeys, numRekeys, ttlScalar, minNumKeys) - - params := client.GetKeyParams() - params.MinKeys = uint16(minKeys) - params.MaxKeys = uint16(maxKeys) - params.NumRekeys = uint16(numRekeys) - params.TTLScalar = ttlScalar - params.MinNumKeys = uint16(minNumKeys) -} - -type FallbackListener struct { - MessagesReceived int64 -} - -func (l *FallbackListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...interface{}) { - if !isHeardElsewhere { - message := item.(*parse.Message) - sender, ok := userRegistry.Users.GetUser(message.Sender) - var senderNick string - if !ok { - globals.Log.ERROR.Printf("Couldn't get sender %v", message.Sender) - } else { - senderNick = sender.Username - } - atomic.AddInt64(&l.MessagesReceived, 1) - globals.Log.INFO.Printf("Message of type %v from %q, %v received with fallback: %s\n", - message.MessageType, printIDNice(message.Sender), senderNick, - string(message.Body)) - } -} - -type TextListener struct { - MessagesReceived int64 -} - -func (l *TextListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...interface{}) { - message := item.(*parse.Message) - globals.Log.INFO.Println("Hearing a text message") - result := cmixproto.TextMessage{} - err := proto.Unmarshal(message.Body, &result) - if err != nil { - globals.Log.ERROR.Printf("Error unmarshaling text message: %v\n", - err.Error()) - } - - sender, ok := userRegistry.Users.GetUser(message.Sender) - var senderNick string - if !ok { - globals.Log.INFO.Printf("First message from sender %v", printIDNice(message.Sender)) - u := userRegistry.Users.NewUser(message.Sender, base64.StdEncoding.EncodeToString(message.Sender[:])) - userRegistry.Users.UpsertUser(u) - senderNick = u.Username - } else { - senderNick = sender.Username - } - logMsg := fmt.Sprintf("Message from %v, %v Received: %s\n", - printIDNice(message.Sender), - senderNick, result.Message) - globals.Log.INFO.Printf("%s -- Timestamp: %s\n", logMsg, - message.Timestamp.String()) - fmt.Printf(logMsg) - - atomic.AddInt64(&l.MessagesReceived, 1) -} - -type userSearcher struct { - foundUserChan chan []byte -} - -func newUserSearcher() api.SearchCallback { - us := userSearcher{} - us.foundUserChan = make(chan []byte) - return &us -} - -func (us *userSearcher) Callback(userID, pubKey []byte, err error) { - if err != nil { - globals.Log.ERROR.Printf("Could not find searched user: %+v", err) - } else { - us.foundUserChan <- userID - } -} +// func setKeyParams(client *api.Client) { +// jww.DEBUG.Printf("Trying to parse key parameters...") +// minKeys, err := strconv.Atoi(keyParams[0]) +// if err != nil { +// return +// } + +// maxKeys, err := strconv.Atoi(keyParams[1]) +// if err != nil { +// return +// } + +// numRekeys, err := strconv.Atoi(keyParams[2]) +// if err != nil { +// return +// } + +// ttlScalar, err := strconv.ParseFloat(keyParams[3], 64) +// if err != nil { +// return +// } + +// minNumKeys, err := strconv.Atoi(keyParams[4]) +// if err != nil { +// return +// } + +// jww.DEBUG.Printf("Setting key generation parameters: %d, %d, %d, %f, %d", +// minKeys, maxKeys, numRekeys, ttlScalar, minNumKeys) + +// params := client.GetKeyParams() +// params.MinKeys = uint16(minKeys) +// params.MaxKeys = uint16(maxKeys) +// params.NumRekeys = uint16(numRekeys) +// params.TTLScalar = ttlScalar +// params.MinNumKeys = uint16(minNumKeys) +// } + +// type FallbackListener struct { +// MessagesReceived int64 +// } + +// func (l *FallbackListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...interface{}) { +// if !isHeardElsewhere { +// message := item.(*parse.Message) +// sender, ok := userRegistry.Users.GetUser(message.Sender) +// var senderNick string +// if !ok { +// jww.ERROR.Printf("Couldn't get sender %v", message.Sender) +// } else { +// senderNick = sender.Username +// } +// atomic.AddInt64(&l.MessagesReceived, 1) +// jww.INFO.Printf("Message of type %v from %q, %v received with fallback: %s\n", +// message.MessageType, printIDNice(message.Sender), senderNick, +// string(message.Body)) +// } +// } + +// type TextListener struct { +// MessagesReceived int64 +// } + +// func (l *TextListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...interface{}) { +// message := item.(*parse.Message) +// jww.INFO.Println("Hearing a text message") +// result := cmixproto.TextMessage{} +// err := proto.Unmarshal(message.Body, &result) +// if err != nil { +// jww.ERROR.Printf("Error unmarshaling text message: %v\n", +// err.Error()) +// } + +// sender, ok := userRegistry.Users.GetUser(message.Sender) +// var senderNick string +// if !ok { +// jww.INFO.Printf("First message from sender %v", printIDNice(message.Sender)) +// u := userRegistry.Users.NewUser(message.Sender, base64.StdEncoding.EncodeToString(message.Sender[:])) +// userRegistry.Users.UpsertUser(u) +// senderNick = u.Username +// } else { +// senderNick = sender.Username +// } +// logMsg := fmt.Sprintf("Message from %v, %v Received: %s\n", +// printIDNice(message.Sender), +// senderNick, result.Message) +// jww.INFO.Printf("%s -- Timestamp: %s\n", logMsg, +// message.Timestamp.String()) +// fmt.Printf(logMsg) + +// atomic.AddInt64(&l.MessagesReceived, 1) +// } + +// type userSearcher struct { +// foundUserChan chan []byte +// } + +// func newUserSearcher() api.SearchCallback { +// us := userSearcher{} +// us.foundUserChan = make(chan []byte) +// return &us +// } + +// func (us *userSearcher) Callback(userID, pubKey []byte, err error) { +// if err != nil { +// jww.ERROR.Printf("Could not find searched user: %+v", err) +// } else { +// us.foundUserChan <- userID +// } +// } // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ @@ -399,7 +188,7 @@ var rootCmd = &cobra.Command{ } jww.SetLogOutput(logOutput) if verbose { - jww.SetLogThreshold(verbose) + jww.SetLogThreshold(jww.LevelTrace) } }, } @@ -412,7 +201,7 @@ func isValidUser(usr []byte) (bool, *id.ID) { if b != 0 { uid, err := id.Unmarshal(usr) if err != nil { - globals.Log.WARN.Printf("Could not unmarshal user: %s", err) + jww.WARN.Printf("Could not unmarshal user: %s", err) return false, nil } return true, uid diff --git a/cmd/udb.go b/cmd/udb.go index 9935a5382ce96f36434576c641a4a18f25aa11de..8a3ffd4d8ef1e6d512df4dd6f7169ada6b7c1258 100644 --- a/cmd/udb.go +++ b/cmd/udb.go @@ -11,7 +11,7 @@ import ( "gitlab.com/elixxir/client/globals" "gitlab.com/xx_network/primitives/id" "strings" - "time" + //"time" ) type callbackSearch struct{} @@ -46,7 +46,7 @@ func parseUdbMessage(msg string, client *api.Client) { keyword := args[0] // Case-insensitive match the keyword to a command if strings.EqualFold(keyword, "SEARCH") { - client.SearchForUser(args[2], searchCallback, 2*time.Minute) + //client.SearchForUser(args[2], searchCallback, 2*time.Minute) } else if strings.EqualFold(keyword, "REGISTER") { globals.Log.ERROR.Printf("UDB REGISTER not allowed, it is already done during user registration") } else { diff --git a/network/message/sendE2E.go b/network/message/sendE2E.go index 840595e09f504c88d5cb215de8b9f62f0f59f412..76370c30262cffc2f9f158c6d2be62e09c00eaa4 100644 --- a/network/message/sendE2E.go +++ b/network/message/sendE2E.go @@ -37,7 +37,7 @@ func (m *Manager) SendE2E(msg message.Send, param params.E2E) ([]id.Round, error partner, err := m.Session.E2e().GetPartner(msg.Recipient) if err != nil { return nil, errors.WithMessagef(err, "Could not send End to End encrypted "+ - "message, no relationship found with %s", partner) + "message, no relationship found with %v", partner) } wg := sync.WaitGroup{} diff --git a/network/node/register_test.go b/network/node/register_test.go index 48709d252b0153a324bed017b25812de0aacb70d..c2bb1a538e83a09ae280ffccee3b62b1dab4da40 100644 --- a/network/node/register_test.go +++ b/network/node/register_test.go @@ -41,6 +41,18 @@ func (nm *MockNetworkManager) GetHealthTracker() context.HealthTracker { return nil } +func (nm *MockNetworkManager) GetRemoteVersion() (string, error) { + return "", nil +} + +func (nm *MockNetworkManager) GetStoppable() stoppable.Stoppable { + return nil +} + +func (nm *MockNetworkManager) RegisterWithPermissioning(string) ([]byte, error) { + return nil, nil +} + // Mock client comms object type MockClientComms struct { request chan bool diff --git a/network/rounds/retreive.go b/network/rounds/retreive.go index dcb08c525f7560425de26bc424409dc3dbe4ee68..b6b45e7a35383e9f3dabe116733c77de12887db4 100644 --- a/network/rounds/retreive.go +++ b/network/rounds/retreive.go @@ -1,14 +1,15 @@ package rounds import ( + "encoding/binary" "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/network/message" + pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/primitives/id" - pb "gitlab.com/elixxir/comms/mixmessages" - jww "github.com/spf13/jwalterweatherman" ) type messageRetrievalComms interface { @@ -39,6 +40,13 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, } } +// TODO: remove me when api fixed +func uint64ToBytes(i uint64) []byte { + bs := make([]byte, 8) + binary.LittleEndian.PutUint64(bs, 31415926) + return bs +} + func (m *Manager) getMessagesFromGateway(roundInfo *pb.RoundInfo, comms messageRetrievalComms) (message.Bundle, error) { @@ -55,7 +63,7 @@ func (m *Manager) getMessagesFromGateway(roundInfo *pb.RoundInfo, msgReq := &pb.GetMessages{ ClientID: m.Uid.Marshal(), //TODO: fix this, should not be a byte slice - RoundID: uint64(rid), + RoundID: uint64ToBytes(uint64(rid)), } msgResp, err := comms.RequestMessages(gwHost, msgReq) // Fail the round if an error occurs so it can be tried again later