Skip to content
Snippets Groups Projects
Commit f67e3ad0 authored by Benjamin Wenger's avatar Benjamin Wenger
Browse files

finished the first pass implementation of the client API

parent 0f1ce957
No related branches found
No related tags found
No related merge requests found
package api
import jww "github.com/spf13/jwalterweatherman"
// CreateAuthenticatedChannel creates a 1-way authenticated channel
// 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,
payload []byte) error {
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,
payload []byte)) {
jww.INFO.Printf("RegisterAuthConfirmationCb(...)")
}
// RegisterAuthRequestCb registers a callback for channel
// authentication request events.
func (c *Client) RegisterAuthRequestCb(cb func(contact Contact,
payload []byte)) {
jww.INFO.Printf("RegisterAuthRequestCb(...)")
}
......@@ -18,13 +18,11 @@ import (
"gitlab.com/elixxir/client/storage"
"gitlab.com/elixxir/client/switchboard"
"gitlab.com/elixxir/comms/client"
pb "gitlab.com/elixxir/comms/mixmessages"
"gitlab.com/elixxir/crypto/csprng"
"gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/fastRNG"
"gitlab.com/elixxir/crypto/large"
"gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id"
"gitlab.com/xx_network/primitives/ndf"
"time"
)
......@@ -218,7 +216,13 @@ func loadClient(session *storage.Session, rngStreamGen *fastRNG.StreamGenerator)
// - Garbled Messages (/network/message/garbled.go)
// Can be signaled to check all recent messages which could be be decoded
// Uses a message store on disk for persistence
// - Critical Messages (/network/message/critical.go)
// Ensures all protocol layer mandatory messages are sent
// Uses a message store on disk for persistence
// - KeyExchange Trigger (/keyExchange/trigger.go)
// Responds to sent rekeys and executes them
// - KeyExchange Confirm (/keyExchange/confirm.go)
// Responds to confirmations of successful rekey operations
func (c *Client) StartNetworkFollower() error {
jww.INFO.Printf("StartNetworkFollower()")
......@@ -271,217 +275,20 @@ func (c *Client) NetworkFollowerStatus() Status {
return c.status.get()
}
// 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) (
[]int, error) {
jww.INFO.Printf("SendE2E(%s, %s, %d)", payload, recipient,
msgType)
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 (c *Client) SendUnsafe(payload []byte, recipient id.ID, msgType int) ([]int,
error) {
jww.INFO.Printf("SendUnsafe(%s, %s, %d)", payload, recipient,
msgType)
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 (c *Client) SendCMIX(payload []byte, recipient id.ID) (int, error) {
jww.INFO.Printf("SendCMIX(%s, %s)", payload, recipient)
return 0, nil
}
// RegisterListener registers a listener callback function that is called
// every time a new message matches the specified parameters.
func (c *Client) RegisterListenerCb(uid id.ID, msgType int, username string,
listenerCb func(msg Message)) {
jww.INFO.Printf("RegisterListener(%s, %d, %s, func())", uid, msgType,
username)
}
// RegisterForNotifications allows a client to register for push
// notifications.
// Note that clients are not required to register for push notifications
// 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 {
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 {
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
}
// Returns true if the cryptographic identity has been registered with
// the CMIX user discovery agent.
// Note that clients do not need to perform this step if they use
// 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()")
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 {
jww.INFO.Printf("RegisterIdentity(%s)", username)
return nil
}
// 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) {
jww.INFO.Printf("RegisterEmail(%s)", email)
return nil, nil
}
// 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) {
jww.INFO.Printf("RegisterPhone(%s)", phone)
return nil, nil
}
// 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 {
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) {
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) {
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) {
jww.INFO.Printf("GetContact(%s)", uid)
return Contact{}, 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 (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,
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)
close(out)
}(resultCh, data, separator, searchTypes)
go func(in chan []Contact, cb func(results []Contact)) {
select {
case contacts := <-in:
cb(contacts)
//TODO: Timer
}
}(resultCh, cb)
}
// CreateAuthenticatedChannel creates a 1-way authenticated channel
// 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,
payload []byte) error {
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,
payload []byte)) {
jww.INFO.Printf("RegisterAuthConfirmationCb(...)")
// Returns the switchboard for Registration
func (c *Client) GetSwitchboard() interfaces.Switchboard {
return c.switchboard
}
// RegisterAuthRequestCb registers a callback for channel
// authentication request events.
func (c *Client) RegisterAuthRequestCb(cb func(contact Contact,
payload []byte)) {
jww.INFO.Printf("RegisterAuthRequestCb(...)")
// Returns the health tracker for registration and polling
func (c *Client) GetHealth() interfaces.HealthTracker {
return c.network.GetHealthTracker()
}
// RegisterRoundEventsCb registers a callback for round
// events.
func (c *Client) RegisterRoundEventsCb(
cb func(re *pb.RoundInfo, timedOut bool)) {
jww.INFO.Printf("RegisterRoundEventsCb(...)")
func (c *Client) GetRoundEvents() interfaces.RoundEvents {
return c.network.GetInstance().GetRoundEvents()
}
// ----- Utility Functions -----
......
......@@ -6,11 +6,35 @@
package api
import jww "github.com/spf13/jwalterweatherman"
import (
"gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id"
)
// 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) {
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) {
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) {
jww.INFO.Printf("GetContact(%s)", uid)
return Contact{}, nil
}
// Contact implements the Contact interface defined in bindings/interfaces.go,
type Contact struct {
ID id.ID
......
////////////////////////////////////////////////////////////////////////////////
// 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
}
package api
import jww "github.com/spf13/jwalterweatherman"
// RegisterForNotifications allows a client to register for push
// notifications.
// Note that clients are not required to register for push notifications
// 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 {
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 {
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
}
package api
import (
"gitlab.com/elixxir/client/interfaces/message"
"gitlab.com/elixxir/client/interfaces/params"
"gitlab.com/elixxir/primitives/format"
"gitlab.com/xx_network/primitives/id"
jww "github.com/spf13/jwalterweatherman"
)
//This holds all functions to send messages over the network
// 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(m message.Send, param params.E2E) ([]id.Round, error) {
jww.INFO.Printf("SendE2E(%s, %d. %v)", m.Recipient,
m.MessageType, m.Payload)
return c.network.SendE2E(m, param)
}
// 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 (c *Client) SendUnsafe(m message.Send, param params.Unsafe) ([]id.Round,
error) {
jww.INFO.Printf("SendUnsafe(%s, %d. %v)", m.Recipient,
m.MessageType, m.Payload)
return c.network.SendUnsafe(m, param)
}
// 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 (c *Client) SendCMIX(msg format.Message, param params.CMIX) (id.Round,
error) {
jww.INFO.Printf("SendCMIX(%v)", msg)
return c.network.SendCMIX(msg, param)
}
package api
import jww "github.com/spf13/jwalterweatherman"
// Returns true if the cryptographic identity has been registered with
// the CMIX user discovery agent.
// Note that clients do not need to perform this step if they use
// 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()")
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 {
jww.INFO.Printf("RegisterIdentity(%s)", username)
return nil
}
// 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) {
jww.INFO.Printf("RegisterEmail(%s)", email)
return nil, nil
}
// 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) {
jww.INFO.Printf("RegisterPhone(%s)", phone)
return nil, nil
}
// 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 {
jww.INFO.Printf("ConfirmRegistration(%s, %s)", token, code)
return 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 (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,
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)
close(out)
}(resultCh, data, separator, searchTypes)
go func(in chan []Contact, cb func(results []Contact)) {
select {
case contacts := <-in:
cb(contacts)
//TODO: Timer
}
}(resultCh, cb)
}
package interfaces
import (
ds "gitlab.com/elixxir/comms/network/dataStructures"
"gitlab.com/elixxir/primitives/states"
"gitlab.com/xx_network/primitives/id"
"time"
)
// The round events interface allows the registration of an event which triggers
// when a round reaches one or more states
type RoundEvents interface {
// designates a callback to call on the specified event
// rid is the id of the round the event occurs on
// callback is the callback the event is triggered on
// timeout is the amount of time before an error event is returned
// valid states are the states which the event should trigger on
AddRoundEvent(rid id.Round, callback ds.RoundEventCallback,
timeout time.Duration, validStates ...states.Round) *ds.EventCallback
// designates a go channel to signal the specified event
// rid is the id of the round the event occurs on
// eventChan is the channel the event is triggered on
// timeout is the amount of time before an error event is returned
// valid states are the states which the event should trigger on
AddRoundEventChan(rid id.Round, eventChan chan ds.EventReturn,
timeout time.Duration, validStates ...states.Round) *ds.EventCallback
//Allows the un-registration of a round event before it triggers
Remove(rid id.Round, e *ds.EventCallback)
}
package interfaces
import (
"gitlab.com/elixxir/client/interfaces/message"
"gitlab.com/elixxir/client/switchboard"
"gitlab.com/xx_network/primitives/id"
)
// public switchboard interface which only allows registration and does not
// allow speaking messages
type Switchboard interface {
// Registers a new listener. Returns the ID of the new listener.
// Keep this around if you want to be able to delete the listener later.
//
// name is used for debug printing and not checked for uniqueness
//
// user: 0 for all, or any user ID to listen for messages from a particular
// user. 0 can be id.ZeroUser or id.ZeroID
// messageType: 0 for all, or any message type to listen for messages of
// that type. 0 can be switchboard.AnyType
// newListener: something implementing the Listener interface. Do not
// pass nil to this.
//
// If a message matches multiple listeners, all of them will hear the
// message.
RegisterListener(user *id.ID, messageType message.Type,
newListener switchboard.Listener) switchboard.ListenerID
// Registers a new listener built around the passed function.
// Returns the ID of the new listener.
// Keep this around if you want to be able to delete the listener later.
//
// name is used for debug printing and not checked for uniqueness
//
// user: 0 for all, or any user ID to listen for messages from a particular
// user. 0 can be id.ZeroUser or id.ZeroID
// messageType: 0 for all, or any message type to listen for messages of
// that type. 0 can be switchboard.AnyType
// newListener: a function implementing the ListenerFunc function type.
// Do not pass nil to this.
//
// If a message matches multiple listeners, all of them will hear the
// message.
RegisterFunc(name string, user *id.ID, messageType message.Type,
newListener switchboard.ListenerFunc) switchboard.ListenerID
// Registers a new listener built around the passed channel.
// Returns the ID of the new listener.
// Keep this around if you want to be able to delete the listener later.
//
// name is used for debug printing and not checked for uniqueness
//
// user: 0 for all, or any user ID to listen for messages from a particular
// user. 0 can be id.ZeroUser or id.ZeroID
// messageType: 0 for all, or any message type to listen for messages of
// that type. 0 can be switchboard.AnyType
// newListener: an item channel.
// Do not pass nil to this.
//
// If a message matches multiple listeners, all of them will hear the
// message.
RegisterChannel(name string, user *id.ID, messageType message.Type,
newListener chan message.Receive) switchboard.ListenerID
// Unregister removes the listener with the specified ID so it will no
// longer get called
Unregister(listenerID switchboard.ListenerID)
}
......@@ -87,6 +87,14 @@ func NewManager(session *storage.Session, switchboard *switchboard.Switchboard,
}
// StartRunners kicks off all network reception goroutines ("threads").
// Started Threads are:
// - Network Follower (/network/follow.go)
// - Historical Round Retrieval (/network/rounds/historical.go)
// - Message Retrieval Worker Group (/network/rounds/retrieve.go)
// - Message Handling Worker Group (/network/message/handle.go)
// - Health Tracker (/network/health)
// - Garbled Messages (/network/message/garbled.go)
// - Critical Messages (/network/message/critical.go)
func (m *manager) Follow() (stoppable.Stoppable, error) {
if !atomic.CompareAndSwapUint32(m.running, 0, 1) {
......
......@@ -10,6 +10,14 @@ import (
"time"
)
// Critical Messages are protocol layer communications that must succeed. These
// are added to the persistent critical messages store. This thread waits for
// network access to move from unhealthy to healthy and the sends all critical
// messages.
// Health is tracked by registering with the Health
// Tracker (/network/Health/Tracker.g0)
//Thread loop for processing critical messages
func (m *Manager) processCriticalMessages(quitCh <-chan struct{}) {
done := false
for !done {
......@@ -24,6 +32,7 @@ func (m *Manager) processCriticalMessages(quitCh <-chan struct{}) {
}
}
// processes all critical messages
func (m *Manager) criticalMessages() {
critMsgs := m.Session.GetCriticalMessages()
//try to send every message in the critical messages buffer in paralell
......
......@@ -13,6 +13,9 @@ import (
"time"
)
// WARNING: Potentially Unsafe
// Payloads send are not End to End encrypted, MetaData is NOT protected with
// this call, see SendE2E for End to End encryption and full privacy protection
// Internal SendCmix which bypasses the network check, will attempt to send to
// the network without checking state. It has a built in retry system which can
// be configured through the params object.
......
......@@ -11,6 +11,15 @@ import (
"time"
)
// WARNING: Unsafe
// Payloads are not End to End encrypted, MetaData is NOT protected with
// this call, see SendE2E for End to End encryption and full privacy protection
// Internal SendUnsafe which bypasses the network check, will attempt to send to
// the network without checking state.
// This partitions payloads into multi-part messages but does NOT end to encrypt
// them
// Sends using SendCMIX and returns a list of rounds the messages are in. Will
// return an error if a single part of the message fails to send.
func (m *Manager) SendUnsafe(msg message.Send, param params.Unsafe) ([]id.Round, error) {
//timestamp the message
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment