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

started implmeenting client

parent 09e882e1
No related branches found
No related tags found
No related merge requests found
......@@ -11,6 +11,7 @@ import (
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/params"
"gitlab.com/elixxir/client/interfaces/user"
"gitlab.com/elixxir/client/keyExchange"
"gitlab.com/elixxir/client/network"
"gitlab.com/elixxir/client/permissioning"
......@@ -70,9 +71,8 @@ func NewClient(ndfJSON, storageDir string, password []byte, registrationCode str
// Create Storage
passwordStr := string(password)
storageSess, err := storage.New(storageDir, passwordStr,
protoUser.UID, protoUser.Salt, protoUser.RSAKey, protoUser.IsPrecanned,
protoUser.CMixKey, protoUser.E2EKey, cmixGrp, e2eGrp, rngStreamGen)
storageSess, err := storage.New(storageDir, passwordStr, protoUser,
cmixGrp, e2eGrp, rngStreamGen)
if err != nil {
return nil, err
}
......@@ -108,9 +108,8 @@ func NewPrecannedClient(precannedID uint, defJSON, storageDir string, password [
// Create Storage
passwordStr := string(password)
storageSess, err := storage.New(storageDir, passwordStr,
protoUser.UID, protoUser.Salt, protoUser.RSAKey, protoUser.IsPrecanned,
protoUser.CMixKey, protoUser.E2EKey, cmixGrp, e2eGrp, rngStreamGen)
storageSess, err := storage.New(storageDir, passwordStr, protoUser,
cmixGrp, e2eGrp, rngStreamGen)
if err != nil {
return nil, err
}
......@@ -202,6 +201,8 @@ func loadClient(session *storage.Session, rngStreamGen *fastRNG.StreamGenerator)
// state and stopping those threads.
// Call this when returning from sleep and close when going back to
// sleep.
// These threads may become a significant drain on battery when offline, ensure
// they are stopped if there is no internet access
// Threads Started:
// - Network Follower (/network/follow.go)
// tracks the network events and hands them off to workers for handling
......@@ -270,27 +271,43 @@ func (c *Client) StopNetworkFollower(timeout time.Duration) error {
return nil
}
//gets the state of the network follower
// Gets the state of the network follower. Returns:
// Stopped - 0
// Starting - 1000
// Running - 2000
// Stopping - 3000
func (c *Client) NetworkFollowerStatus() Status {
jww.INFO.Printf("NetworkFollowerStatus()")
return c.status.get()
}
// Returns the switchboard for Registration
func (c *Client) GetSwitchboard() interfaces.Switchboard {
return c.switchboard
}
// Returns the health tracker for registration and polling
func (c *Client) GetHealth() interfaces.HealthTracker {
jww.INFO.Printf("GetHealth()")
return c.network.GetHealthTracker()
}
// RegisterRoundEventsCb registers a callback for round
// events.
func (c *Client) GetRoundEvents() interfaces.RoundEvents {
jww.INFO.Printf("GetRoundEvents()")
return c.network.GetInstance().GetRoundEvents()
}
// Returns the switchboard for Registration
func (c *Client) GetSwitchboard() interfaces.Switchboard {
jww.INFO.Printf("GetSwitchboard()")
return c.switchboard
}
// 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() user.User {
jww.INFO.Printf("GetUser()")
return c.storage.GetUser()
}
// ----- Utility Functions -----
// parseNDF parses the initial ndf string for the client. do not check the
// signature, it is deprecated.
......
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2019 Privategrity Corporation /
// /
// All rights reserved. /
////////////////////////////////////////////////////////////////////////////////
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
}
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2019 Privategrity Corporation /
// /
// All rights reserved. /
////////////////////////////////////////////////////////////////////////////////
package api
import (
"encoding/binary"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces/user"
"gitlab.com/elixxir/crypto/csprng"
"gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/xx"
"gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id"
jww "github.com/spf13/jwalterweatherman"
)
type user struct {
UID *id.ID
Salt []byte
RSAKey *rsa.PrivateKey
CMixKey *cyclic.Int
E2EKey *cyclic.Int
IsPrecanned bool
}
const (
// SaltSize size of user salts
SaltSize = 32
)
// createNewUser generates an identity for cMix
func createNewUser(rng csprng.Source, cmix, e2e *cyclic.Group) user {
func createNewUser(rng csprng.Source, cmix, e2e *cyclic.Group) user.User {
// RSA Keygen (4096 bit defaults)
rsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen)
if err != nil {
......@@ -70,19 +56,19 @@ func createNewUser(rng csprng.Source, cmix, e2e *cyclic.Group) user {
jww.FATAL.Panicf(err.Error())
}
return user{
UID: userID,
return user.User{
ID: userID.DeepCopy(),
Salt: salt,
RSAKey: rsaKey,
CMixKey: cmix.NewIntFromBytes(cMixKeyBytes),
E2EKey: e2e.NewIntFromBytes(e2eKeyBytes),
IsPrecanned: false,
RSA: rsaKey,
Precanned: false,
CmixDhPrivateKey: cmix.NewIntFromBytes(cMixKeyBytes),
E2eDhPrivateKey: cmix.NewIntFromBytes(e2eKeyBytes),
}
}
// TODO: Add precanned user code structures here.
// creates a precanned user
func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic.Group) user {
func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic.Group) user.User {
// DH Keygen
// FIXME: Why 256 bits? -- this is spec but not explained, it has
// to do with optimizing operations on one side and still preserves
......@@ -99,12 +85,10 @@ func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic.
binary.BigEndian.PutUint64(userID[:], uint64(precannedID))
userID.SetType(id.User)
return user{
UID: &userID,
return user.User{
ID: userID.DeepCopy(),
Salt: salt,
RSAKey: &rsa.PrivateKey{},
CMixKey: cmix.NewInt(1),
E2EKey: e2e.NewIntFromBytes(e2eKeyBytes),
IsPrecanned: true,
Precanned: false,
E2eDhPrivateKey: cmix.NewIntFromBytes(e2eKeyBytes),
}
}
......@@ -8,15 +8,16 @@ package bindings
import (
"gitlab.com/elixxir/client/api"
"time"
)
// BindingsClient wraps the api.Client, implementing additional functions
// to support the gomobile Client interface
type BindingsClient struct {
api.Client
api api.Client
}
// NewClient connects and registers to the network using a json encoded
/*// 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
// when:
......@@ -58,7 +59,48 @@ func LoadClient(storageDir string, password []byte) (Client, error) {
}
bindingsClient := &BindingsClient{*client}
return bindingsClient, nil
}*/
func (b *BindingsClient) StartNetworkFollower() error {
return b.api.StartNetworkFollower()
}
func (b *BindingsClient) StopNetworkFollower(timeoutMS int) error {
timeout := time.Duration(timeoutMS) * time.Millisecond
return b.api.StopNetworkFollower(timeout)
}
func (b *BindingsClient) NetworkFollowerStatus() int {
return int(b.api.NetworkFollowerStatus())
}
func (b *BindingsClient) IsNetworkHealthy() bool {
return b.api.GetHealth().IsHealthy()
}
func (b *BindingsClient) RegisterNetworkHealthCB(cb func(bool)) {
b.api.GetHealth().AddFunc(cb)
}
// RegisterListener records and installs a listener for messages
// matching specific uid, msgType, and/or username
......
......@@ -7,7 +7,7 @@
package bindings
import (
"gitlab.com/elixxir/client/api"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/xx_network/primitives/id"
)
......@@ -15,7 +15,41 @@ import (
// functionality defined here. A Client handles all network connectivity, key
// generation, and storage for a given cryptographic identity on the cmix
// network.
// These threads may become a significant drain on battery when offline, ensure
// they are stopped if there is no internet access
type Client interface {
// ----- Network -----
// StartNetworkFollower kicks off the tracking of the network. It starts
// long running network client threads and returns an object for checking
// state and stopping those threads.
// Call this when returning from sleep and close when going back to
// sleep.
StartNetworkFollower() error
// StopNetworkFollower stops the network follower if it is running.
// It returns errors if the Follower is in the wrong status to stop or if it
// fails to stop it.
// if the network follower is running and this fails, the client object will
// most likely be in an unrecoverable state and need to be trashed.
StopNetworkFollower(timeoutMS int) error
// NetworkFollowerStatus gets the state of the network follower.
// Returns:
// Stopped - 0
// Starting - 1000
// Running - 2000
// Stopping - 3000
NetworkFollowerStatus() int
// Returns true if the following of the network is in a state where messages
// can be sent, false otherwise
IsNetworkHealthy() bool
// Registers a callback which gets triggered every time network health
// changes
RegisterNetworkHealthCB(func(bool))
RegisterRoundEventCallback(rid int, hdlr RoundEventHandler, )
// ----- Reception -----
......@@ -85,13 +119,7 @@ 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() (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) (api.Contact, error)
// GetContact returns a Contact object for the given user id, or
// an error
GetContact(uid []byte) (api.Contact, error)
GetUser() (interfaces.Contact, error)
// ----- User Discovery -----
......@@ -110,22 +138,16 @@ 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 api.Contact, payload []byte) error
CreateAuthenticatedChannel(recipient interfaces.Contact, payload []byte) error
// RegierAuthEventsHandler registers a callback interface for channel
// authentication events.
RegisterAuthEventsHandler(hdlr AuthEventHandler)
// ----- Network -----
// StartNetworkRunner kicks off the longrunning network client threads
// and returns an object for checking state and stopping those threads.
// Call this when returning from sleep and close when going back to
// sleep.
StartNetworkFollower() error
// RegisterRoundEventsHandler registers a callback interface for round
// events.
RegisterRoundEventsHandler(hdlr RoundEventHandler)
RegisterRoundEventsHandler()
}
// ContactList contains a list of contacts
......@@ -133,7 +155,7 @@ type ContactList interface {
// GetLen returns the number of contacts in the list
GetLen() int
// GetContact returns the contact at index i
GetContact(i int) api.Contact
GetContact(i int) interfaces.Contact
}
// ----- Callback interfaces -----
......@@ -154,13 +176,13 @@ 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 api.Contact, payload []byte)
HandleConfirmation(contact interfaces.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 api.Contact, payload []byte)
HandleRequest(contact interfaces.Contact, payload []byte)
}
// RoundList contains a list of contacts
......@@ -171,18 +193,10 @@ type RoundList interface {
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.
type RoundEventHandler interface {
HandleEvent(re RoundEvent)
HandleEvent(id int, state byte)
}
// UserDiscoveryHandler handles search results against the user discovery agent.
......
......@@ -13,7 +13,7 @@ import (
// in go, the structure is meant to be edited directly, the functions are for
// bindings compatibility
type Contact struct {
ID id.ID
ID *id.ID
DhPubKey *cyclic.Int
Facts []fact.Fact
}
......
package interfaces
type User interface {
GetID() []byte
GetSalt() []byte
GetRSAPrivateKeyPem() []byte
GetRSAPublicKeyPem() []byte
IsPrecanned() bool
GetCmixDhPrivateKey() []byte
GetCmixDhPublicKey() []byte
GetE2EDhPrivateKey() []byte
GetE2EDhPublicKey() []byte
GetContact() Contact
}
package user
import (
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/contact"
"gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id"
)
type User struct {
//General Identity
ID *id.ID
Salt []byte
RSA *rsa.PrivateKey
Precanned bool
//cmix Identity
CmixDhPrivateKey *cyclic.Int
CmixDhPublicKey *cyclic.Int
//e2e Identity
E2eDhPrivateKey *cyclic.Int
E2eDhPublicKey *cyclic.Int
}
func (u User) GetID() []byte {
return u.ID.Marshal()
}
func (u User) GetSalt() []byte {
return u.Salt
}
func (u User) GetRSAPrivateKeyPem() []byte {
return rsa.CreatePrivateKeyPem(u.RSA)
}
func (u User) GetRSAPublicKeyPem() []byte {
return rsa.CreatePublicKeyPem(u.RSA.GetPublic())
}
func (u User) IsPrecanned() bool {
return u.Precanned
}
func (u User) GetCmixDhPrivateKey() []byte {
return u.CmixDhPrivateKey.Bytes()
}
func (u User) GetCmixDhPublicKey() []byte {
return u.CmixDhPublicKey.Bytes()
}
func (u User) GetE2EDhPrivateKey() []byte {
return u.E2eDhPrivateKey.Bytes()
}
func (u User) GetE2EDhPublicKey() []byte {
return u.E2eDhPublicKey.Bytes()
}
func (u User) GetContact() interfaces.Contact {
return contact.Contact{
ID: u.ID.DeepCopy(),
DhPubKey: u.E2eDhPublicKey,
Facts: nil,
}
}
package stoppable
import "time"
type Bindings interface {
Close(timeoutMS int) error
IsRunning() bool
Name() string
}
func WrapForBindings(s Stoppable) Bindings {
return &bindingsStoppable{s: s}
}
type bindingsStoppable struct {
s Stoppable
}
func (bs *bindingsStoppable) Close(timeoutMS int) error {
timeout := time.Duration(timeoutMS) * time.Millisecond
return bs.s.Close(timeout)
}
func (bs *bindingsStoppable) IsRunning() bool {
return bs.s.IsRunning()
}
func (bs *bindingsStoppable) Name() string {
return bs.s.Name()
}
......@@ -27,6 +27,7 @@ import (
"gitlab.com/xx_network/primitives/ndf"
"sync"
"testing"
userInterface "gitlab.com/elixxir/client/interfaces/user"
)
// Number of rounds to store in the CheckedRound buffer
......@@ -69,8 +70,8 @@ func initStore(baseDir, password string) (*Session, error) {
}
// Creates new UserData in the session
func New(baseDir, password string, uid *id.ID, salt []byte, rsaKey *rsa.PrivateKey,
isPrecanned bool, cmixDHPrivKey, e2eDHPrivKey *cyclic.Int, cmixGrp,
func New(baseDir, password string, u userInterface.User, cmixGrp,
e2eGrp *cyclic.Group, rng *fastRNG.StreamGenerator) (*Session, error) {
s, err := initStore(baseDir, password)
......@@ -84,17 +85,17 @@ func New(baseDir, password string, uid *id.ID, salt []byte, rsaKey *rsa.PrivateK
"Create new session")
}
s.user, err = user.NewUser(s.kv, uid, salt, rsaKey, isPrecanned)
s.user, err = user.NewUser(s.kv, u.ID, u.Salt, u.RSA, u.Precanned)
if err != nil {
return nil, errors.WithMessage(err, "Failed to create session")
}
s.cmix, err = cmix.NewStore(cmixGrp, s.kv, cmixDHPrivKey)
s.cmix, err = cmix.NewStore(cmixGrp, s.kv, u.CmixDhPrivateKey)
if err != nil {
return nil, errors.WithMessage(err, "Failed to create session")
}
s.e2e, err = e2e.NewStore(e2eGrp, s.kv, e2eDHPrivKey, rng)
s.e2e, err = e2e.NewStore(e2eGrp, s.kv, u.E2eDhPrivateKey, rng)
if err != nil {
return nil, errors.WithMessage(err, "Failed to create session")
}
......
package storage
import "gitlab.com/elixxir/client/interfaces/user"
func (s *Session) GetUser() user.User {
s.mux.RLock()
defer s.mux.RUnlock()
ci := s.user.GetCryptographicIdentity()
return user.User{
ID: ci.GetUserID().DeepCopy(),
Salt: copySlice(ci.GetSalt()),
RSA: ci.GetRSA(),
Precanned: ci.IsPrecanned(),
CmixDhPrivateKey: s.cmix.GetDHPrivateKey().DeepCopy(),
CmixDhPublicKey: s.cmix.GetDHPublicKey().DeepCopy(),
E2eDhPrivateKey: s.e2e.GetDHPrivateKey().DeepCopy(),
E2eDhPublicKey: s.e2e.GetDHPublicKey().DeepCopy(),
}
}
func copySlice(s []byte) []byte {
n := make([]byte, len(s))
copy(n, s)
return n
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment