Skip to content
Snippets Groups Projects
Commit d77d1b48 authored by Jono Wenger's avatar Jono Wenger
Browse files

Fix comments and formatting

parent 0d6a5e86
No related branches found
No related tags found
2 merge requests!510Release,!278Fix comments and formatting
...@@ -8,6 +8,10 @@ package xxdk ...@@ -8,6 +8,10 @@ package xxdk
import "sync" import "sync"
// TriggerBackup function is called to start a backup. The reason is used for
// logging purposes and should describe the event that triggered a backup.
//
// For example, the reason can say "contact added" when a new contact is saved.
type TriggerBackup func(reason string) type TriggerBackup func(reason string)
// Container contains the trigger to call to initiate a backup. // Container contains the trigger to call to initiate a backup.
......
This diff is collapsed.
This diff is collapsed.
...@@ -25,8 +25,8 @@ import ( ...@@ -25,8 +25,8 @@ import (
"gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id"
) )
// E2e object bundles a ReceptionIdentity with a Cmix // E2e object bundles a ReceptionIdentity with a Cmix object and can be used for
// and can be used for high level operations such as connections // high-level operations, such as connections.
type E2e struct { type E2e struct {
*Cmix *Cmix
auth auth.State auth auth.State
...@@ -35,8 +35,8 @@ type E2e struct { ...@@ -35,8 +35,8 @@ type E2e struct {
e2eIdentity ReceptionIdentity e2eIdentity ReceptionIdentity
} }
// AuthCallbacks is an adapter for the auth.Callbacks interface // AuthCallbacks is an adapter for the auth.Callbacks interface that allows for
// that allows for initializing an E2e object without an E2e-dependant auth.Callbacks // initializing an E2e object without an E2e-dependant auth.Callbacks.
type AuthCallbacks interface { type AuthCallbacks interface {
Request(partner contact.Contact, receptionID receptionID.EphemeralIdentity, Request(partner contact.Contact, receptionID receptionID.EphemeralIdentity,
round rounds.Round, messenger *E2e) round rounds.Round, messenger *E2e)
...@@ -46,9 +46,9 @@ type AuthCallbacks interface { ...@@ -46,9 +46,9 @@ type AuthCallbacks interface {
round rounds.Round, messenger *E2e) round rounds.Round, messenger *E2e)
} }
// Login creates a new E2e backed by the xxdk.Cmix persistent versioned.KV // Login creates a new E2e backed by the xxdk.Cmix persistent versioned.KV. It
// It bundles a Cmix object with a ReceptionIdentity object // bundles a Cmix object with a ReceptionIdentity object and initializes the
// and initializes the auth.State and e2e.Handler objects // auth.State and e2e.Handler objects.
func Login(net *Cmix, callbacks AuthCallbacks, func Login(net *Cmix, callbacks AuthCallbacks,
identity ReceptionIdentity, params E2EParams) (m *E2e, err error) { identity ReceptionIdentity, params E2EParams) (m *E2e, err error) {
...@@ -63,15 +63,15 @@ func Login(net *Cmix, callbacks AuthCallbacks, ...@@ -63,15 +63,15 @@ func Login(net *Cmix, callbacks AuthCallbacks,
return login(net, callbacks, identity, net.GetStorage().GetKV(), params) return login(net, callbacks, identity, net.GetStorage().GetKV(), params)
} }
// LoginEphemeral creates a new E2e backed by a totally ephemeral versioned.KV // LoginEphemeral creates a new E2e backed by a totally ephemeral versioned.KV.
func LoginEphemeral(net *Cmix, callbacks AuthCallbacks, func LoginEphemeral(net *Cmix, callbacks AuthCallbacks,
identity ReceptionIdentity, params E2EParams) (m *E2e, err error) { identity ReceptionIdentity, params E2EParams) (m *E2e, err error) {
return login(net, callbacks, identity, return login(net, callbacks, identity,
versioned.NewKV(ekv.MakeMemstore()), params) versioned.NewKV(ekv.MakeMemstore()), params)
} }
// loginLegacy creates a new E2e backed by the xxdk.Cmix persistent versioned.KV // loginLegacy creates a new E2e backed by the xxdk.Cmix persistent
// Uses the pre-generated transmission ID used by xxdk.Cmix. // versioned.KV. It uses the pre-generated transmission ID used by xxdk.Cmix.
// This function is designed to maintain backwards compatibility with previous // This function is designed to maintain backwards compatibility with previous
// xx messenger designs and should not be used for other purposes. // xx messenger designs and should not be used for other purposes.
func loginLegacy(net *Cmix, callbacks AuthCallbacks, func loginLegacy(net *Cmix, callbacks AuthCallbacks,
...@@ -90,8 +90,7 @@ func loginLegacy(net *Cmix, callbacks AuthCallbacks, ...@@ -90,8 +90,7 @@ func loginLegacy(net *Cmix, callbacks AuthCallbacks,
err = net.AddService(m.e2e.StartProcesses) err = net.AddService(m.e2e.StartProcesses)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "Failed to add "+ return nil, errors.WithMessage(err, "Failed to add the e2e processes")
"the e2e processies")
} }
m.auth, err = auth.NewState(net.GetStorage().GetKV(), net.GetCmix(), m.auth, err = auth.NewState(net.GetStorage().GetKV(), net.GetCmix(),
...@@ -111,7 +110,7 @@ func loginLegacy(net *Cmix, callbacks AuthCallbacks, ...@@ -111,7 +110,7 @@ func loginLegacy(net *Cmix, callbacks AuthCallbacks,
return m, err return m, err
} }
// login creates a new xxdk.E2e backed by the given versioned.KV // login creates a new xxdk.E2e backed by the given versioned.KV.
func login(net *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity, func login(net *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity,
kv *versioned.KV, params E2EParams) (m *E2e, err error) { kv *versioned.KV, params E2EParams) (m *E2e, err error) {
...@@ -125,7 +124,8 @@ func login(net *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity, ...@@ -125,7 +124,8 @@ func login(net *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity,
return nil, err return nil, err
} }
if !generatedId.Cmp(identity.ID) { if !generatedId.Cmp(identity.ID) {
return nil, errors.Errorf("Given identity %s is invalid, generated ID does not match", return nil, errors.Errorf(
"Given identity %s is invalid, generated ID does not match",
identity.ID.String()) identity.ID.String())
} }
...@@ -139,63 +139,58 @@ func login(net *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity, ...@@ -139,63 +139,58 @@ func login(net *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity,
return nil, err return nil, err
} }
// load or init the new e2e storage // Load or init the new e2e storage
e2eGrp := net.GetStorage().GetE2EGroup() e2eGrp := net.GetStorage().GetE2EGroup()
m.e2e, err = e2e.Load(kv, m.e2e, err = e2e.Load(kv, net.GetCmix(), identity.ID, e2eGrp, net.GetRng(),
net.GetCmix(), identity.ID, e2eGrp, net.GetRng(),
net.GetEventReporter()) net.GetEventReporter())
if err != nil { if err != nil {
//initialize the e2e storage // Initialize the e2e storage
err = e2e.Init(kv, identity.ID, dhPrivKey, e2eGrp, err = e2e.Init(kv, identity.ID, dhPrivKey, e2eGrp, params.Rekey)
params.Rekey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
//load the new e2e storage // Load the new e2e storage
m.e2e, err = e2e.Load(kv, m.e2e, err = e2e.Load(kv, net.GetCmix(), identity.ID, e2eGrp,
net.GetCmix(), identity.ID, e2eGrp, net.GetRng(), net.GetRng(), net.GetEventReporter())
net.GetEventReporter())
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "Failed to load a "+ return nil, errors.WithMessage(
"newly created e2e store") err, "Failed to load a newly created e2e store")
} }
} }
err = net.AddService(m.e2e.StartProcesses) err = net.AddService(m.e2e.StartProcesses)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "Failed to add "+ return nil, errors.WithMessage(err, "Failed to add the e2e processes")
"the e2e processies")
} }
m.auth, err = auth.NewState(kv, net.GetCmix(), m.auth, err = auth.NewState(kv, net.GetCmix(), m.e2e, net.GetRng(),
m.e2e, net.GetRng(), net.GetEventReporter(), net.GetEventReporter(), params.Auth, params.Session,
params.Auth, params.Session,
MakeAuthCallbacksAdapter(callbacks, m), m.backup.TriggerBackup) MakeAuthCallbacksAdapter(callbacks, m), m.backup.TriggerBackup)
if err != nil { if err != nil {
return nil, err return nil, err
} }
net.network.AddIdentity(identity.ID, time.Time{}, true) net.network.AddIdentity(identity.ID, time.Time{}, true)
jww.INFO.Printf("Client logged in: \n\tReceptionID: %s", jww.INFO.Printf("Client logged in: \n\tReceptionID: %s", identity.ID)
identity.ID)
return m, err return m, err
} }
// loadOrInitE2eLegacy loads the e2e handler or makes a new one, generating a new // loadOrInitE2eLegacy loads the e2e.Handler or makes a new one, generating a
// e2e private key. It attempts to load via a legacy construction, then tries // new E2E private key. It attempts to load via a legacy construction, then
// to load the modern one, creating a new modern ID if neither can be found // tries to load the modern one, creating a new modern ID if neither can be
// found.
func loadOrInitE2eLegacy(identity ReceptionIdentity, net *Cmix) (e2e.Handler, error) { func loadOrInitE2eLegacy(identity ReceptionIdentity, net *Cmix) (e2e.Handler, error) {
e2eGrp := net.GetStorage().GetE2EGroup() e2eGrp := net.GetStorage().GetE2EGroup()
kv := net.GetStorage().GetKV() kv := net.GetStorage().GetKV()
//try to load a legacy e2e handler // Try to load a legacy e2e handler
e2eHandler, err := e2e.LoadLegacy(kv, e2eHandler, err := e2e.LoadLegacy(kv,
net.GetCmix(), identity.ID, e2eGrp, net.GetRng(), net.GetCmix(), identity.ID, e2eGrp, net.GetRng(),
net.GetEventReporter(), rekey.GetDefaultParams()) net.GetEventReporter(), rekey.GetDefaultParams())
if err != nil { if err != nil {
jww.DEBUG.Printf("e2e.LoadLegacy error: %v", err) jww.DEBUG.Printf("e2e.LoadLegacy error: %v", err)
//if no legacy e2e handler exists, try to load a new one // If no legacy e2e handler exists, try to load a new one
e2eHandler, err = e2e.Load(kv, e2eHandler, err = e2e.Load(kv,
net.GetCmix(), identity.ID, e2eGrp, net.GetRng(), net.GetCmix(), identity.ID, e2eGrp, net.GetRng(),
net.GetEventReporter()) net.GetEventReporter())
...@@ -203,7 +198,7 @@ func loadOrInitE2eLegacy(identity ReceptionIdentity, net *Cmix) (e2e.Handler, er ...@@ -203,7 +198,7 @@ func loadOrInitE2eLegacy(identity ReceptionIdentity, net *Cmix) (e2e.Handler, er
jww.WARN.Printf("Failed to load e2e instance for %s, "+ jww.WARN.Printf("Failed to load e2e instance for %s, "+
"creating a new one: %v", identity.ID, err) "creating a new one: %v", identity.ID, err)
//initialize the e2e storage // Initialize the e2e storage
privKey, err := identity.GetDHKeyPrivate() privKey, err := identity.GetDHKeyPrivate()
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -214,39 +209,38 @@ func loadOrInitE2eLegacy(identity ReceptionIdentity, net *Cmix) (e2e.Handler, er ...@@ -214,39 +209,38 @@ func loadOrInitE2eLegacy(identity ReceptionIdentity, net *Cmix) (e2e.Handler, er
return nil, err return nil, err
} }
//load the new e2e storage // Load the new e2e storage
e2eHandler, err = e2e.Load(kv, e2eHandler, err = e2e.Load(kv,
net.GetCmix(), identity.ID, e2eGrp, net.GetRng(), net.GetCmix(), identity.ID, e2eGrp, net.GetRng(),
net.GetEventReporter()) net.GetEventReporter())
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "Failed to load a "+ return nil, errors.WithMessage(err,
"newly created e2e store") "Failed to load a newly created e2e store")
} }
} else { } else {
jww.INFO.Printf("Loaded a modern e2e instance for %s", jww.INFO.Printf("Loaded a modern e2e instance for %s", identity.ID)
identity.ID)
} }
} else { } else {
jww.INFO.Printf("Loaded a legacy e2e instance for %s", jww.INFO.Printf("Loaded a legacy e2e instance for %s", identity.ID)
identity.ID)
} }
return e2eHandler, nil return e2eHandler, nil
} }
// GetReceptionIdentity returns a safe copy of the E2e ReceptionIdentity // GetReceptionIdentity returns a safe copy of the E2e ReceptionIdentity.
func (m *E2e) GetReceptionIdentity() ReceptionIdentity { func (m *E2e) GetReceptionIdentity() ReceptionIdentity {
return m.e2eIdentity.DeepCopy() return m.e2eIdentity.DeepCopy()
} }
// ConstructProtoUserFile is a helper function which is used for proto // ConstructProtoUserFile is a helper function that is used for proto client
// client testing. This is used for development testing. // testing. This is used for development testing.
func (m *E2e) ConstructProtoUserFile() ([]byte, error) { func (m *E2e) ConstructProtoUserFile() ([]byte, error) {
// load the registration code // load the registration code
regCode, err := m.GetStorage().GetRegCode() regCode, err := m.GetStorage().GetRegCode()
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "failed to register with "+ return nil, errors.WithMessage(err,
"permissioning") "failed to register with permissioning")
} }
transIdentity := m.Cmix.GetTransmissionIdentity() transIdentity := m.Cmix.GetTransmissionIdentity()
...@@ -276,26 +270,29 @@ func (m *E2e) ConstructProtoUserFile() ([]byte, error) { ...@@ -276,26 +270,29 @@ func (m *E2e) ConstructProtoUserFile() ([]byte, error) {
jsonBytes, err := json.Marshal(Usr) jsonBytes, err := json.Marshal(Usr)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "failed to register with "+ return nil, errors.WithMessage(err,
"permissioning") "failed to register with permissioning")
} }
return jsonBytes, nil return jsonBytes, nil
} }
// GetAuth returns the auth.State.
func (m *E2e) GetAuth() auth.State { func (m *E2e) GetAuth() auth.State {
return m.auth return m.auth
} }
// GetE2E returns the e2e.Handler.
func (m *E2e) GetE2E() e2e.Handler { func (m *E2e) GetE2E() e2e.Handler {
return m.e2e return m.e2e
} }
// GetBackupContainer returns the backup Container.
func (m *E2e) GetBackupContainer() *Container { func (m *E2e) GetBackupContainer() *Container {
return m.backup return m.backup
} }
// DeleteContact is a function which removes a partner from E2e's storage // DeleteContact removes a partner from E2e's storage.
func (m *E2e) DeleteContact(partnerId *id.ID) error { func (m *E2e) DeleteContact(partnerId *id.ID) error {
jww.DEBUG.Printf("Deleting contact with ID %s", partnerId) jww.DEBUG.Printf("Deleting contact with ID %s", partnerId)
...@@ -322,7 +319,7 @@ func (m *E2e) DeleteContact(partnerId *id.ID) error { ...@@ -322,7 +319,7 @@ func (m *E2e) DeleteContact(partnerId *id.ID) error {
return nil return nil
} }
// MakeAuthCallbacksAdapter creates an authCallbacksAdapter // MakeAuthCallbacksAdapter creates an authCallbacksAdapter.
func MakeAuthCallbacksAdapter(ac AuthCallbacks, e2e *E2e) *authCallbacksAdapter { func MakeAuthCallbacksAdapter(ac AuthCallbacks, e2e *E2e) *authCallbacksAdapter {
return &authCallbacksAdapter{ return &authCallbacksAdapter{
ac: ac, ac: ac,
...@@ -331,12 +328,13 @@ func MakeAuthCallbacksAdapter(ac AuthCallbacks, e2e *E2e) *authCallbacksAdapter ...@@ -331,12 +328,13 @@ func MakeAuthCallbacksAdapter(ac AuthCallbacks, e2e *E2e) *authCallbacksAdapter
} }
// authCallbacksAdapter is an adapter type to make the AuthCallbacks type // authCallbacksAdapter is an adapter type to make the AuthCallbacks type
// compatible with the auth.Callbacks type // compatible with the auth.Callbacks type.
type authCallbacksAdapter struct { type authCallbacksAdapter struct {
ac AuthCallbacks ac AuthCallbacks
e2e *E2e e2e *E2e
} }
// MakeAuthCB generates a new auth.Callbacks with the given AuthCallbacks.
func MakeAuthCB(e2e *E2e, cbs AuthCallbacks) auth.Callbacks { func MakeAuthCB(e2e *E2e, cbs AuthCallbacks) auth.Callbacks {
return &authCallbacksAdapter{ return &authCallbacksAdapter{
ac: cbs, ac: cbs,
...@@ -344,33 +342,36 @@ func MakeAuthCB(e2e *E2e, cbs AuthCallbacks) auth.Callbacks { ...@@ -344,33 +342,36 @@ func MakeAuthCB(e2e *E2e, cbs AuthCallbacks) auth.Callbacks {
} }
} }
// Request will be called when an auth Request message is processed.
func (aca *authCallbacksAdapter) Request(partner contact.Contact, func (aca *authCallbacksAdapter) Request(partner contact.Contact,
receptionID receptionID.EphemeralIdentity, round rounds.Round) { receptionID receptionID.EphemeralIdentity, round rounds.Round) {
aca.ac.Request(partner, receptionID, round, aca.e2e) aca.ac.Request(partner, receptionID, round, aca.e2e)
} }
// Confirm will be called when an auth Confirm message is processed.
func (aca *authCallbacksAdapter) Confirm(partner contact.Contact, func (aca *authCallbacksAdapter) Confirm(partner contact.Contact,
receptionID receptionID.EphemeralIdentity, round rounds.Round) { receptionID receptionID.EphemeralIdentity, round rounds.Round) {
aca.ac.Confirm(partner, receptionID, round, aca.e2e) aca.ac.Confirm(partner, receptionID, round, aca.e2e)
} }
// Reset will be called when an auth Reset operation occurs.
func (aca *authCallbacksAdapter) Reset(partner contact.Contact, func (aca *authCallbacksAdapter) Reset(partner contact.Contact,
receptionID receptionID.EphemeralIdentity, round rounds.Round) { receptionID receptionID.EphemeralIdentity, round rounds.Round) {
aca.ac.Reset(partner, receptionID, round, aca.e2e) aca.ac.Reset(partner, receptionID, round, aca.e2e)
} }
// DefaultAuthCallbacks is a simple structure for providing a default Callbacks implementation // DefaultAuthCallbacks is a simple structure for providing a default
// It should generally not be used. // AuthCallbacks implementation. It should generally not be used.
type DefaultAuthCallbacks struct{} type DefaultAuthCallbacks struct{}
// Confirm will be called when an auth Confirm message is processed. // Request will be called when an auth Request message is processed.
func (a DefaultAuthCallbacks) Confirm(contact.Contact, func (a DefaultAuthCallbacks) Request(contact.Contact,
receptionID.EphemeralIdentity, rounds.Round, *E2e) { receptionID.EphemeralIdentity, rounds.Round, *E2e) {
jww.ERROR.Printf("No valid auth callback assigned!") jww.ERROR.Printf("No valid auth callback assigned!")
} }
// Request will be called when an auth Request message is processed. // Confirm will be called when an auth Confirm message is processed.
func (a DefaultAuthCallbacks) Request(contact.Contact, func (a DefaultAuthCallbacks) Confirm(contact.Contact,
receptionID.EphemeralIdentity, rounds.Round, *E2e) { receptionID.EphemeralIdentity, rounds.Round, *E2e) {
jww.ERROR.Printf("No valid auth callback assigned!") jww.ERROR.Printf("No valid auth callback assigned!")
} }
......
...@@ -5,21 +5,18 @@ import ( ...@@ -5,21 +5,18 @@ import (
) )
// ReportEvent reports an event from the client to api users, providing a // ReportEvent reports an event from the client to api users, providing a
// priority, category, eventType, and details // priority, category, eventType, and details.
func (c *Cmix) ReportEvent(priority int, category, evtType, details string) { func (c *Cmix) ReportEvent(priority int, category, evtType, details string) {
c.events.Report(priority, category, evtType, details) c.events.Report(priority, category, evtType, details)
} }
// RegisterEventCallback records the given function to receive // RegisterEventCallback records the given function to receive ReportableEvent
// ReportableEvent objects. It returns the internal index // objects.
// of the callback so that it can be deleted later. func (c *Cmix) RegisterEventCallback(name string, myFunc event.Callback) error {
func (c *Cmix) RegisterEventCallback(name string,
myFunc event.Callback) error {
return c.events.RegisterEventCallback(name, myFunc) return c.events.RegisterEventCallback(name, myFunc)
} }
// UnregisterEventCallback deletes the callback identified by the // UnregisterEventCallback deletes the callback identified by the name.
// index. It returns an error if it fails.
func (c *Cmix) UnregisterEventCallback(name string) { func (c *Cmix) UnregisterEventCallback(name string) {
c.events.UnregisterEventCallback(name) c.events.UnregisterEventCallback(name)
} }
...@@ -23,8 +23,8 @@ import ( ...@@ -23,8 +23,8 @@ import (
const idVersion = 0 const idVersion = 0
// ReceptionIdentity is used by the E2e object for managing // ReceptionIdentity is used by the E2e object for managing identities used for
// identities used for message pickup // message pickup.
type ReceptionIdentity struct { type ReceptionIdentity struct {
ID *id.ID ID *id.ID
RSAPrivatePem []byte RSAPrivatePem []byte
...@@ -33,9 +33,10 @@ type ReceptionIdentity struct { ...@@ -33,9 +33,10 @@ type ReceptionIdentity struct {
E2eGrp []byte E2eGrp []byte
} }
// StoreReceptionIdentity stores the given identity in Cmix storage with the given key // StoreReceptionIdentity stores the given identity in Cmix storage with the
// This is the ideal way to securely store identities, as the caller of this function // given key. This is the ideal way to securely store identities, as the caller
// is only required to store the given key separately rather than the keying material // of this function is only required to store the given key separately rather
// than the keying material.
func StoreReceptionIdentity(key string, identity ReceptionIdentity, net *Cmix) error { func StoreReceptionIdentity(key string, identity ReceptionIdentity, net *Cmix) error {
marshalledIdentity, err := identity.Marshal() marshalledIdentity, err := identity.Marshal()
if err != nil { if err != nil {
...@@ -49,7 +50,8 @@ func StoreReceptionIdentity(key string, identity ReceptionIdentity, net *Cmix) e ...@@ -49,7 +50,8 @@ func StoreReceptionIdentity(key string, identity ReceptionIdentity, net *Cmix) e
}) })
} }
// LoadReceptionIdentity loads the given identity in Cmix storage with the given key // LoadReceptionIdentity loads the given identity in Cmix storage with the given
// key.
func LoadReceptionIdentity(key string, net *Cmix) (ReceptionIdentity, error) { func LoadReceptionIdentity(key string, net *Cmix) (ReceptionIdentity, error) {
storageObj, err := net.GetStorage().Get(key) storageObj, err := net.GetStorage().Get(key)
if err != nil { if err != nil {
...@@ -59,62 +61,58 @@ func LoadReceptionIdentity(key string, net *Cmix) (ReceptionIdentity, error) { ...@@ -59,62 +61,58 @@ func LoadReceptionIdentity(key string, net *Cmix) (ReceptionIdentity, error) {
return UnmarshalReceptionIdentity(storageObj.Data) return UnmarshalReceptionIdentity(storageObj.Data)
} }
// Marshal returns the JSON representation of a ReceptionIdentity // Marshal returns the JSON representation of a ReceptionIdentity.
func (r ReceptionIdentity) Marshal() ([]byte, error) { func (r ReceptionIdentity) Marshal() ([]byte, error) {
return json.Marshal(&r) return json.Marshal(&r)
} }
// UnmarshalReceptionIdentity takes in a marshalled ReceptionIdentity // UnmarshalReceptionIdentity takes in a marshalled ReceptionIdentity and
// and converts it to an object // converts it to an object.
func UnmarshalReceptionIdentity(marshaled []byte) (ReceptionIdentity, error) { func UnmarshalReceptionIdentity(marshaled []byte) (ReceptionIdentity, error) {
newIdentity := ReceptionIdentity{} newIdentity := ReceptionIdentity{}
return newIdentity, json.Unmarshal(marshaled, &newIdentity) return newIdentity, json.Unmarshal(marshaled, &newIdentity)
} }
// GetDHKeyPrivate returns the DHKeyPrivate in go format // GetDHKeyPrivate returns the DHKeyPrivate.
func (r ReceptionIdentity) GetDHKeyPrivate() (*cyclic.Int, error) { func (r ReceptionIdentity) GetDHKeyPrivate() (*cyclic.Int, error) {
dhKeyPriv := &cyclic.Int{} dhKeyPriv := &cyclic.Int{}
err := dhKeyPriv.UnmarshalJSON(r.DHKeyPrivate) err := dhKeyPriv.UnmarshalJSON(r.DHKeyPrivate)
return dhKeyPriv, err return dhKeyPriv, err
} }
// GetRSAPrivatePem returns the RSAPrivatePem in go format // GetRSAPrivatePem returns the RSAPrivatePem.
func (r ReceptionIdentity) GetRSAPrivatePem() (*rsa.PrivateKey, error) { func (r ReceptionIdentity) GetRSAPrivatePem() (*rsa.PrivateKey, error) {
return rsa.LoadPrivateKeyFromPem(r.RSAPrivatePem) return rsa.LoadPrivateKeyFromPem(r.RSAPrivatePem)
} }
// GetGroup returns the cyclic.Group in go format // GetGroup returns the cyclic.Group.
func (r ReceptionIdentity) GetGroup() (*cyclic.Group, error) { func (r ReceptionIdentity) GetGroup() (*cyclic.Group, error) {
grp := &cyclic.Group{} grp := &cyclic.Group{}
return grp, grp.UnmarshalJSON(r.E2eGrp) return grp, grp.UnmarshalJSON(r.E2eGrp)
} }
// MakeReceptionIdentity generates a new cryptographic identity // MakeReceptionIdentity generates a new cryptographic identity for receiving
// for receiving messages. // messages.
func MakeReceptionIdentity(net *Cmix) (ReceptionIdentity, error) { func MakeReceptionIdentity(net *Cmix) (ReceptionIdentity, error) {
rng := net.GetRng().GetStream() rng := net.GetRng().GetStream()
defer rng.Close() defer rng.Close()
grp := net.GetStorage().GetE2EGroup() grp := net.GetStorage().GetE2EGroup()
//make RSA Key // Make RSA Key
rsaKey, err := rsa.GenerateKey(rng, rsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen)
rsa.DefaultRSABitLen)
if err != nil { if err != nil {
return ReceptionIdentity{}, err return ReceptionIdentity{}, err
} }
//make salt // Make salt
salt := make([]byte, 32) salt := make([]byte, 32)
_, err = rng.Read(salt) _, err = rng.Read(salt)
//make dh private key // Make DH private key
privKey := diffieHellman.GeneratePrivateKey( privKey := diffieHellman.GeneratePrivateKey(len(grp.GetPBytes()), grp, rng)
len(grp.GetPBytes()),
grp, rng)
// make the ID // make the ID
newId, err := xx.NewID(rsaKey.GetPublic(), newId, err := xx.NewID(rsaKey.GetPublic(), salt, id.User)
salt, id.User)
if err != nil { if err != nil {
return ReceptionIdentity{}, err return ReceptionIdentity{}, err
} }
...@@ -129,7 +127,7 @@ func MakeReceptionIdentity(net *Cmix) (ReceptionIdentity, error) { ...@@ -129,7 +127,7 @@ func MakeReceptionIdentity(net *Cmix) (ReceptionIdentity, error) {
return ReceptionIdentity{}, err return ReceptionIdentity{}, err
} }
//create the identity object // Create the identity object
rsaPem := rsa.CreatePrivateKeyPem(rsaKey) rsaPem := rsa.CreatePrivateKeyPem(rsaKey)
I := ReceptionIdentity{ I := ReceptionIdentity{
ID: newId, ID: newId,
...@@ -142,15 +140,15 @@ func MakeReceptionIdentity(net *Cmix) (ReceptionIdentity, error) { ...@@ -142,15 +140,15 @@ func MakeReceptionIdentity(net *Cmix) (ReceptionIdentity, error) {
return I, nil return I, nil
} }
// MakeLegacyReceptionIdentity generates the cryptographic identity // MakeLegacyReceptionIdentity generates the cryptographic identity for
// for receiving messages based on the extant stored user.Info // receiving messages based on the extant stored user.Info.
func MakeLegacyReceptionIdentity(net *Cmix) (ReceptionIdentity, error) { func MakeLegacyReceptionIdentity(net *Cmix) (ReceptionIdentity, error) {
userInfo := net.GetStorage().PortableUserInfo() userInfo := net.GetStorage().PortableUserInfo()
return buildReceptionIdentity(userInfo.ReceptionID, userInfo.ReceptionSalt, return buildReceptionIdentity(userInfo.ReceptionID, userInfo.ReceptionSalt,
userInfo.ReceptionRSA, net.GetStorage().GetE2EGroup(), userInfo.E2eDhPrivateKey) userInfo.ReceptionRSA, net.GetStorage().GetE2EGroup(), userInfo.E2eDhPrivateKey)
} }
// DeepCopy produces a safe copy of a ReceptionIdentity // DeepCopy produces a safe copy of the ReceptionIdentity.
func (r ReceptionIdentity) DeepCopy() ReceptionIdentity { func (r ReceptionIdentity) DeepCopy() ReceptionIdentity {
saltCopy := make([]byte, len(r.Salt)) saltCopy := make([]byte, len(r.Salt))
copy(saltCopy, r.Salt) copy(saltCopy, r.Salt)
...@@ -169,7 +167,7 @@ func (r ReceptionIdentity) DeepCopy() ReceptionIdentity { ...@@ -169,7 +167,7 @@ func (r ReceptionIdentity) DeepCopy() ReceptionIdentity {
} }
} }
// GetContact accepts a xxdk.ReceptionIdentity object and returns a contact.Contact object // GetContact returns a contact.Contact object of the reception identity.
func (r ReceptionIdentity) GetContact() contact.Contact { func (r ReceptionIdentity) GetContact() contact.Contact {
grp, _ := r.GetGroup() grp, _ := r.GetGroup()
dhKeyPriv, _ := r.GetDHKeyPrivate() dhKeyPriv, _ := r.GetDHKeyPrivate()
...@@ -183,10 +181,11 @@ func (r ReceptionIdentity) GetContact() contact.Contact { ...@@ -183,10 +181,11 @@ func (r ReceptionIdentity) GetContact() contact.Contact {
return ct return ct
} }
// buildReceptionIdentity creates a new ReceptionIdentity // buildReceptionIdentity creates a new ReceptionIdentity from the given
// from the given user.Info // user.Info.
func buildReceptionIdentity(receptionId *id.ID, receptionSalt []byte, receptionRsa *rsa.PrivateKey, func buildReceptionIdentity(receptionId *id.ID, receptionSalt []byte,
e2eGrp *cyclic.Group, dHPrivkey *cyclic.Int) (ReceptionIdentity, error) { receptionRsa *rsa.PrivateKey, e2eGrp *cyclic.Group, dHPrivkey *cyclic.Int) (
ReceptionIdentity, error) {
saltCopy := make([]byte, len(receptionSalt)) saltCopy := make([]byte, len(receptionSalt))
copy(saltCopy, receptionSalt) copy(saltCopy, receptionSalt)
...@@ -208,17 +207,18 @@ func buildReceptionIdentity(receptionId *id.ID, receptionSalt []byte, receptionR ...@@ -208,17 +207,18 @@ func buildReceptionIdentity(receptionId *id.ID, receptionSalt []byte, receptionR
}, nil }, nil
} }
// TransmissionIdentity represents the identity // TransmissionIdentity represents the identity used to transmit over the
// used to transmit over the network via a specific Cmix object // network via a specific Cmix object.
type TransmissionIdentity struct { type TransmissionIdentity struct {
ID *id.ID ID *id.ID
RSAPrivatePem *rsa.PrivateKey RSAPrivatePem *rsa.PrivateKey
Salt []byte Salt []byte
// Timestamp in which user has registered with the network
// Timestamp of when the user has registered with the network
RegistrationTimestamp int64 RegistrationTimestamp int64
} }
// DeepCopy produces a safe copy of a TransmissionIdentity // DeepCopy produces a safe copy of a TransmissionIdentity.
func (t TransmissionIdentity) DeepCopy() TransmissionIdentity { func (t TransmissionIdentity) DeepCopy() TransmissionIdentity {
saltCopy := make([]byte, len(t.Salt)) saltCopy := make([]byte, len(t.Salt))
copy(saltCopy, t.Salt) copy(saltCopy, t.Salt)
...@@ -230,8 +230,8 @@ func (t TransmissionIdentity) DeepCopy() TransmissionIdentity { ...@@ -230,8 +230,8 @@ func (t TransmissionIdentity) DeepCopy() TransmissionIdentity {
} }
} }
// buildTransmissionIdentity creates a new TransmissionIdentity // buildTransmissionIdentity creates a new TransmissionIdentity from the given
// from the given user.Info // user.Info.
func buildTransmissionIdentity(userInfo user.Info) TransmissionIdentity { func buildTransmissionIdentity(userInfo user.Info) TransmissionIdentity {
saltCopy := make([]byte, len(userInfo.TransmissionSalt)) saltCopy := make([]byte, len(userInfo.TransmissionSalt))
copy(saltCopy, userInfo.TransmissionSalt) copy(saltCopy, userInfo.TransmissionSalt)
......
...@@ -10,6 +10,7 @@ package xxdk ...@@ -10,6 +10,7 @@ package xxdk
import ( import (
"encoding/base64" "encoding/base64"
"github.com/pkg/errors" "github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman"
pb "gitlab.com/elixxir/comms/mixmessages" pb "gitlab.com/elixxir/comms/mixmessages"
"gitlab.com/xx_network/comms/signature" "gitlab.com/xx_network/comms/signature"
"gitlab.com/xx_network/crypto/tls" "gitlab.com/xx_network/crypto/tls"
...@@ -19,33 +20,37 @@ import ( ...@@ -19,33 +20,37 @@ import (
) )
// DownloadAndVerifySignedNdfWithUrl retrieves the NDF from a specified URL. // DownloadAndVerifySignedNdfWithUrl retrieves the NDF from a specified URL.
// The NDF is processed into a protobuf containing a signature which // The NDF is processed into a protobuf containing a signature that is verified
// is verified using the cert string passed in. The NDF is returned as marshaled // using the cert string passed in. The NDF is returned as marshaled byte data
// byte data which may be used to start a client. // that may be used to start a client.
func DownloadAndVerifySignedNdfWithUrl(url, cert string) ([]byte, error) { func DownloadAndVerifySignedNdfWithUrl(url, cert string) ([]byte, error) {
// Build a request for the file // Build a request for the file
resp, err := http.Get(url) resp, err := http.Get(url)
if err != nil { if err != nil {
return nil, errors.WithMessagef(err, "Failed to retrieve "+ return nil, errors.WithMessagef(
"NDF from %s", url) err, "Failed to retrieve NDF from %s", url)
} }
defer resp.Body.Close() defer func() {
if err = resp.Body.Close(); err != nil {
jww.ERROR.Printf("Failed to close http response body: %+v", err)
}
}()
// Download contents of the file // Download contents of the file
signedNdfEncoded, err := ioutil.ReadAll(resp.Body) signedNdfEncoded, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "Failed to read signed "+ return nil, errors.WithMessage(
"NDF response request") err, "Failed to read signed NDF response request")
} }
// Process the download NDF and return the marshaled NDF // Process the download NDF and return the marshaled NDF
return processAndVerifySignedNdf(signedNdfEncoded, cert) return processAndVerifySignedNdf(signedNdfEncoded, cert)
} }
// processAndVerifySignedNdf is a helper function which parses the downloaded NDF // processAndVerifySignedNdf is a helper function that parses the downloaded NDF
// into a protobuf containing a signature. The signature is verified using the // into a protobuf containing a signature. The signature is verified using the
// passed in cert. Upon successful parsing and verification, the NDF is // passed in cert. Upon successful parsing and verification, the NDF is returned
// returned as byte data. // as byte data.
func processAndVerifySignedNdf(signedNdfEncoded []byte, cert string) ([]byte, error) { func processAndVerifySignedNdf(signedNdfEncoded []byte, cert string) ([]byte, error) {
// Base64 decode the signed NDF // Base64 decode the signed NDF
signedNdfMarshaled, err := base64.StdEncoding.DecodeString( signedNdfMarshaled, err := base64.StdEncoding.DecodeString(
...@@ -58,26 +63,29 @@ func processAndVerifySignedNdf(signedNdfEncoded []byte, cert string) ([]byte, er ...@@ -58,26 +63,29 @@ func processAndVerifySignedNdf(signedNdfEncoded []byte, cert string) ([]byte, er
signedNdfMsg := &pb.NDF{} signedNdfMsg := &pb.NDF{}
err = proto.Unmarshal(signedNdfMarshaled, signedNdfMsg) err = proto.Unmarshal(signedNdfMarshaled, signedNdfMsg)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "Failed to unmarshal "+ return nil, errors.WithMessage(err,
"signed NDF into protobuf") "Failed to unmarshal signed NDF into protobuf")
} }
// Load the certificate from it's PEM contents // Load the certificate from it's PEM contents
schedulingCert, err := tls.LoadCertificate(cert) schedulingCert, err := tls.LoadCertificate(cert)
if err != nil { if err != nil {
return nil, errors.WithMessagef(err, "Failed to parse scheduling cert (%s)", cert) return nil, errors.WithMessagef(err,
"Failed to parse scheduling cert (%s)", cert)
} }
// Extract the public key from the cert // Extract the public key from the cert
schedulingPubKey, err := tls.ExtractPublicKey(schedulingCert) schedulingPubKey, err := tls.ExtractPublicKey(schedulingCert)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "Failed to extract public key from cert") return nil, errors.WithMessage(err,
"Failed to extract public key from cert")
} }
// Verify signed NDF message // Verify signed NDF message
err = signature.VerifyRsa(signedNdfMsg, schedulingPubKey) err = signature.VerifyRsa(signedNdfMsg, schedulingPubKey)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "Failed to verify signed NDF message") return nil, errors.WithMessage(err,
"Failed to verify signed NDF message")
} }
return signedNdfMsg.Ndf, nil return signedNdfMsg.Ndf, nil
......
...@@ -16,18 +16,17 @@ import ( ...@@ -16,18 +16,17 @@ import (
"gitlab.com/xx_network/primitives/id/ephemeral" "gitlab.com/xx_network/primitives/id/ephemeral"
) )
// RegisterForNotifications allows a client to register for push // RegisterForNotifications allows a client to register for push notifications.
// notifications. // Note that clients are not required 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*
// especially as these rely on third parties (i.e., Firebase *cough* // Google's palantir *cough*) that may represent a security risk to the user.
// *cough* google's palantir *cough*) that may represent a security
// risk to the user.
func (m *E2e) RegisterForNotifications(token string) error { func (m *E2e) RegisterForNotifications(token string) error {
jww.INFO.Printf("RegisterForNotifications(%s)", token) jww.INFO.Printf("RegisterForNotifications(%s)", token)
// Pull the host from the manage // Pull the host from the manage
notificationBotHost, ok := m.GetComms().GetHost(&id.NotificationBot) notificationBotHost, ok := m.GetComms().GetHost(&id.NotificationBot)
if !ok { if !ok {
return errors.New("RegisterForNotifications: Failed to retrieve host for notification bot") return errors.New("RegisterForNotifications: " +
"Failed to retrieve host for notification bot")
} }
intermediaryReceptionID, sig, err := m.getIidAndSig() intermediaryReceptionID, sig, err := m.getIidAndSig()
if err != nil { if err != nil {
...@@ -59,7 +58,7 @@ func (m *E2e) RegisterForNotifications(token string) error { ...@@ -59,7 +58,7 @@ func (m *E2e) RegisterForNotifications(token string) error {
return nil return nil
} }
// UnregisterForNotifications turns of notifications for this client // UnregisterForNotifications turns off notifications for this client.
func (m *E2e) UnregisterForNotifications() error { func (m *E2e) UnregisterForNotifications() error {
jww.INFO.Printf("UnregisterForNotifications()") jww.INFO.Printf("UnregisterForNotifications()")
// Pull the host from the manage // Pull the host from the manage
...@@ -71,8 +70,9 @@ func (m *E2e) UnregisterForNotifications() error { ...@@ -71,8 +70,9 @@ func (m *E2e) UnregisterForNotifications() error {
if err != nil { if err != nil {
return err return err
} }
// Send the unregister message // Sends the unregister message
_, err = m.GetComms().UnregisterForNotifications(notificationBotHost, &mixmessages.NotificationUnregisterRequest{ _, err = m.GetComms().UnregisterForNotifications(notificationBotHost,
&mixmessages.NotificationUnregisterRequest{
IntermediaryId: intermediaryReceptionID, IntermediaryId: intermediaryReceptionID,
IIDTransmissionRsaSig: sig, IIDTransmissionRsaSig: sig,
}) })
...@@ -86,17 +86,21 @@ func (m *E2e) UnregisterForNotifications() error { ...@@ -86,17 +86,21 @@ func (m *E2e) UnregisterForNotifications() error {
} }
func (m *E2e) getIidAndSig() ([]byte, []byte, error) { func (m *E2e) getIidAndSig() ([]byte, []byte, error) {
intermediaryReceptionID, err := ephemeral.GetIntermediaryId(m.GetStorage().GetReceptionID()) intermediaryReceptionID, err := ephemeral.GetIntermediaryId(
m.GetStorage().GetReceptionID())
if err != nil { if err != nil {
return nil, nil, errors.WithMessage(err, "RegisterForNotifications: Failed to form intermediary ID") return nil, nil, errors.WithMessage(err,
"RegisterForNotifications: Failed to form intermediary ID")
} }
h, err := hash.NewCMixHash() h, err := hash.NewCMixHash()
if err != nil { if err != nil {
return nil, nil, errors.WithMessage(err, "RegisterForNotifications: Failed to create cmix hash") return nil, nil, errors.WithMessage(err,
"RegisterForNotifications: Failed to create cMix hash")
} }
_, err = h.Write(intermediaryReceptionID) _, err = h.Write(intermediaryReceptionID)
if err != nil { if err != nil {
return nil, nil, errors.WithMessage(err, "RegisterForNotifications: Failed to write intermediary ID to hash") return nil, nil, errors.WithMessage(err,
"RegisterForNotifications: Failed to write intermediary ID to hash")
} }
stream := m.GetRng().GetStream() stream := m.GetRng().GetStream()
...@@ -104,7 +108,8 @@ func (m *E2e) getIidAndSig() ([]byte, []byte, error) { ...@@ -104,7 +108,8 @@ func (m *E2e) getIidAndSig() ([]byte, []byte, error) {
m.GetStorage().GetTransmissionRSA(), m.GetStorage().GetTransmissionRSA(),
hash.CMixHash, h.Sum(nil), nil) hash.CMixHash, h.Sum(nil), nil)
if err != nil { if err != nil {
return nil, nil, errors.WithMessage(err, "RegisterForNotifications: Failed to sign intermediary ID") return nil, nil, errors.WithMessage(err,
"RegisterForNotifications: Failed to sign intermediary ID")
} }
stream.Close() stream.Close()
return intermediaryReceptionID, sig, nil return intermediaryReceptionID, sig, nil
......
...@@ -20,8 +20,8 @@ import ( ...@@ -20,8 +20,8 @@ import (
"gitlab.com/elixxir/client/e2e/rekey" "gitlab.com/elixxir/client/e2e/rekey"
) )
// CMIXParams contains the parameters for Network tracking and for // CMIXParams contains the parameters for Network tracking and for specific CMIX
// specific CMIX messaging settings. // messaging settings.
// //
// FIXME: this breakdown could be cleaner and is an unfortunate side effect of // FIXME: this breakdown could be cleaner and is an unfortunate side effect of
// several refactors of the codebase. // several refactors of the codebase.
...@@ -32,8 +32,8 @@ type CMIXParams struct { ...@@ -32,8 +32,8 @@ type CMIXParams struct {
// E2EParams holds all the settings for e2e and it's various submodules. // E2EParams holds all the settings for e2e and it's various submodules.
// //
// NOTE: "Base" wraps cmix.CMIXParams to control message send params, // Note that Base wraps cmix.CMIXParams to control message send params, so that
// so xxdk library users should copy the desired settings to both. // xxdk library users should copy the desired settings to both.
// FIXME: this should not wrap a copy of cmix.CMIXParams. // FIXME: this should not wrap a copy of cmix.CMIXParams.
type E2EParams struct { type E2EParams struct {
Session session.Params Session session.Params
...@@ -43,10 +43,11 @@ type E2EParams struct { ...@@ -43,10 +43,11 @@ type E2EParams struct {
Auth auth.Params Auth auth.Params
} }
//////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// -- CMix Params Helper Functions -- // // CMix Params Helper Functions //
//////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// GetDefaultCMixParams returns a new CMIXParams with the default parameters.
func GetDefaultCMixParams() CMIXParams { func GetDefaultCMixParams() CMIXParams {
return CMIXParams{ return CMIXParams{
Network: cmix.GetDefaultParams(), Network: cmix.GetDefaultParams(),
...@@ -54,20 +55,22 @@ func GetDefaultCMixParams() CMIXParams { ...@@ -54,20 +55,22 @@ func GetDefaultCMixParams() CMIXParams {
} }
} }
// Unmarshal fills an empty object with the deserialized contents of jsonData // Unmarshal fills an empty object with the deserialized contents of the JSON
// data.
func (p *CMIXParams) Unmarshal(jsonData []byte) error { func (p *CMIXParams) Unmarshal(jsonData []byte) error {
return json.Unmarshal(jsonData, p) return json.Unmarshal(jsonData, p)
} }
// Marshal creates json data of the object // Marshal creates JSON data of the object.
func (p *CMIXParams) Marshal() ([]byte, error) { func (p *CMIXParams) Marshal() ([]byte, error) {
return json.Marshal(p) return json.Marshal(p)
} }
//////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// -- E2E Params Helper Functions -- // // E2E Params Helper Functions //
//////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// GetDefaultE2EParams returns a new E2EParams with the default parameters.
func GetDefaultE2EParams() E2EParams { func GetDefaultE2EParams() E2EParams {
return E2EParams{ return E2EParams{
Session: session.GetDefaultParams(), Session: session.GetDefaultParams(),
...@@ -78,12 +81,13 @@ func GetDefaultE2EParams() E2EParams { ...@@ -78,12 +81,13 @@ func GetDefaultE2EParams() E2EParams {
} }
} }
// Unmarshal fills an empty object with the deserialized contents of jsonData // Unmarshal fills an empty object with the deserialized contents of the JSON
// data.
func (p *E2EParams) Unmarshal(jsonData []byte) error { func (p *E2EParams) Unmarshal(jsonData []byte) error {
return json.Unmarshal(jsonData, p) return json.Unmarshal(jsonData, p)
} }
// Marshal creates json data of the object // Marshal creates JSON data of the object.
func (p *E2EParams) Marshal() ([]byte, error) { func (p *E2EParams) Marshal() ([]byte, error) {
return json.Marshal(p) return json.Marshal(p)
} }
...@@ -13,8 +13,8 @@ import ( ...@@ -13,8 +13,8 @@ import (
"testing" "testing"
) )
// Tests that no data is lost when marshaling and // Tests that no data is lost when marshaling and unmarshalling the CMIXParams
// unmarshaling the Params object. // object.
func TestParams_MarshalUnmarshal(t *testing.T) { func TestParams_MarshalUnmarshal(t *testing.T) {
// Construct a set of params // Construct a set of params
p := GetDefaultCMixParams() p := GetDefaultCMixParams()
...@@ -42,9 +42,8 @@ func TestParams_MarshalUnmarshal(t *testing.T) { ...@@ -42,9 +42,8 @@ func TestParams_MarshalUnmarshal(t *testing.T) {
t.Logf("%s", string(data2)) t.Logf("%s", string(data2))
// Check that they match (it is done this way to avoid // Check that they match (it is done this way to avoid false failures when
// false failures with the reflect.DeepEqual function and // using the reflect.DeepEqual function and pointers)
// pointers)
if !bytes.Equal(data, data2) { if !bytes.Equal(data, data2) {
t.Fatalf("Data was lost in marshal/unmarshal.") t.Fatalf("Data was lost in marshal/unmarshal.")
} }
......
...@@ -14,26 +14,24 @@ import ( ...@@ -14,26 +14,24 @@ import (
"gitlab.com/elixxir/client/storage/user" "gitlab.com/elixxir/client/storage/user"
) )
// Returns an error if registration fails. // registerWithPermissioning returns an error if registration fails.
func (c *Cmix) registerWithPermissioning() error { func (c *Cmix) registerWithPermissioning() error {
//get the users public key // Get the users public key
transmissionPubKey := c.storage.GetTransmissionRSA().GetPublic() transmissionPubKey := c.storage.GetTransmissionRSA().GetPublic()
receptionPubKey := c.storage.GetReceptionRSA().GetPublic() receptionPubKey := c.storage.GetReceptionRSA().GetPublic()
//load the registration code // Load the registration code
regCode, err := c.storage.GetRegCode() regCode, err := c.storage.GetRegCode()
if err != nil { if err != nil {
return errors.WithMessage(err, "failed to register with "+ return errors.WithMessage(err, "failed to register with permissioning")
"permissioning")
} }
//register with registration // Register with registration
transmissionRegValidationSignature, receptionRegValidationSignature, transmissionRegValidationSignature, receptionRegValidationSignature,
registrationTimestamp, err := c.permissioning.Register( registrationTimestamp, err := c.permissioning.Register(
transmissionPubKey, receptionPubKey, regCode) transmissionPubKey, receptionPubKey, regCode)
if err != nil { if err != nil {
return errors.WithMessage(err, "failed to register with "+ return errors.WithMessage(err, "failed to register with permissioning")
"permissioning")
} }
// store the signature // store the signature
...@@ -43,7 +41,7 @@ func (c *Cmix) registerWithPermissioning() error { ...@@ -43,7 +41,7 @@ func (c *Cmix) registerWithPermissioning() error {
receptionRegValidationSignature) receptionRegValidationSignature)
c.storage.SetRegistrationTimestamp(registrationTimestamp) c.storage.SetRegistrationTimestamp(registrationTimestamp)
//update the registration state // Update the registration state
err = c.storage.ForwardRegistrationStatus(storage.PermissioningComplete) err = c.storage.ForwardRegistrationStatus(storage.PermissioningComplete)
if err != nil { if err != nil {
return errors.WithMessage(err, "failed to update local state "+ return errors.WithMessage(err, "failed to update local state "+
...@@ -52,15 +50,14 @@ func (c *Cmix) registerWithPermissioning() error { ...@@ -52,15 +50,14 @@ func (c *Cmix) registerWithPermissioning() error {
return nil return nil
} }
// ConstructProtoUserFile is a helper function which is used for proto // ConstructProtoUserFile is a helper function that is used for proto client
// client testing. This is used for development testing. // testing. This is used for development testing.
func (c *Cmix) ConstructProtoUserFile() ([]byte, error) { func (c *Cmix) ConstructProtoUserFile() ([]byte, error) {
//load the registration code // Load the registration code
regCode, err := c.storage.GetRegCode() regCode, err := c.storage.GetRegCode()
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "failed to register with "+ return nil, errors.WithMessage(err, "failed to get registration code")
"permissioning")
} }
userInfo := c.GetStorage().PortableUserInfo() userInfo := c.GetStorage().PortableUserInfo()
...@@ -82,8 +79,7 @@ func (c *Cmix) ConstructProtoUserFile() ([]byte, error) { ...@@ -82,8 +79,7 @@ func (c *Cmix) ConstructProtoUserFile() ([]byte, error) {
jsonBytes, err := json.Marshal(Usr) jsonBytes, err := json.Marshal(Usr)
if err != nil { if err != nil {
return nil, errors.WithMessage(err, "failed to register with "+ return nil, errors.WithMessage(err, "failed to JSON marshal user.Proto")
"permissioning")
} }
return jsonBytes, nil return jsonBytes, nil
......
...@@ -22,11 +22,11 @@ import ( ...@@ -22,11 +22,11 @@ import (
"gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/crypto/csprng"
) )
// NewPrecannedClient creates an insecure user with predetermined keys // NewPrecannedClient creates an insecure user with predetermined keys with
// with nodes It creates client storage, generates keys, connects, and // nodes. It creates client storage, generates keys, connects, and registers
// registers with the network. Note that this does not register a // with the network. Note that this does not register a username/identity, but
// username/identity, but merely creates a new cryptographic identity // merely creates a new cryptographic identity for adding such information at a
// for adding such information at a later date. // later date.
func NewPrecannedClient(precannedID uint, defJSON, storageDir string, func NewPrecannedClient(precannedID uint, defJSON, storageDir string,
password []byte) error { password []byte) error {
jww.INFO.Printf("NewPrecannedClient()") jww.INFO.Printf("NewPrecannedClient()")
...@@ -57,7 +57,8 @@ func NewPrecannedClient(precannedID uint, defJSON, storageDir string, ...@@ -57,7 +57,8 @@ func NewPrecannedClient(precannedID uint, defJSON, storageDir string,
return err return err
} }
// MakePrecannedAuthenticatedChannel creates an insecure e2e relationship with a precanned user // MakePrecannedAuthenticatedChannel creates an insecure E2E relationship with a
// precanned user.
func (m *E2e) MakePrecannedAuthenticatedChannel(precannedID uint) ( func (m *E2e) MakePrecannedAuthenticatedChannel(precannedID uint) (
contact.Contact, error) { contact.Contact, error) {
...@@ -98,14 +99,14 @@ func (m *E2e) MakePrecannedAuthenticatedChannel(precannedID uint) ( ...@@ -98,14 +99,14 @@ func (m *E2e) MakePrecannedAuthenticatedChannel(precannedID uint) (
} }
mySIDHPrivKey.GeneratePublicKey(mySIDHPubKey) mySIDHPrivKey.GeneratePublicKey(mySIDHPubKey)
// add the precanned user as a e2e contact // Add the precanned user as a e2e contact
// FIXME: these params need to be threaded through... // FIXME: these params need to be threaded through...
sesParam := session.GetDefaultParams() sesParam := session.GetDefaultParams()
_, err = m.e2e.AddPartner(precanContact.ID, precanContact.DhPubKey, _, err = m.e2e.AddPartner(precanContact.ID, precanContact.DhPubKey,
m.e2e.GetHistoricalDHPrivkey(), theirSIDHPubKey, m.e2e.GetHistoricalDHPrivkey(), theirSIDHPubKey,
mySIDHPrivKey, sesParam, sesParam) mySIDHPrivKey, sesParam, sesParam)
// check garbled messages in case any messages arrived before creating // Check garbled messages in case any messages arrived before creating
// the channel // the channel
m.GetCmix().CheckInProgressMessages() m.GetCmix().CheckInProgressMessages()
......
...@@ -7,8 +7,8 @@ import ( ...@@ -7,8 +7,8 @@ import (
"time" "time"
) )
// a service process starts itself in a new thread, returning from the // Service is a service process that starts itself in a new thread, returning
// originator a stopable to control it // from the originator a stoppable to control it.
type Service func() (stoppable.Stoppable, error) type Service func() (stoppable.Stoppable, error)
type services struct { type services struct {
...@@ -18,8 +18,8 @@ type services struct { ...@@ -18,8 +18,8 @@ type services struct {
mux sync.Mutex mux sync.Mutex
} }
// newServiceProcessiesList creates a new services list which will add its // newServices creates a new services list that will add its services to the
// services to the passed mux // passed mux.
func newServices() *services { func newServices() *services {
return &services{ return &services{
services: make([]Service, 0), services: make([]Service, 0),
...@@ -28,8 +28,8 @@ func newServices() *services { ...@@ -28,8 +28,8 @@ func newServices() *services {
} }
} }
// Add adds the service process to the list and adds it to the multi-stopable. // add appends the service process to the list and adds it to the multi-
// Start running it if services are running // stoppable. Start running it if services are running.
func (s *services) add(sp Service) error { func (s *services) add(sp Service) error {
s.mux.Lock() s.mux.Lock()
defer s.mux.Unlock() defer s.mux.Unlock()
...@@ -48,9 +48,9 @@ func (s *services) add(sp Service) error { ...@@ -48,9 +48,9 @@ func (s *services) add(sp Service) error {
return nil return nil
} }
// Runs all services. If they are in the process of stopping, // start runs all services. If they are in the process of stopping, it will wait
// it will wait for the stop to complete or the timeout to ellapse // for the stop to complete or the timeout to elapse. Will error if already
// Will error if already running // running.
func (s *services) start(timeout time.Duration) error { func (s *services) start(timeout time.Duration) error {
s.mux.Lock() s.mux.Lock()
defer s.mux.Unlock() defer s.mux.Unlock()
...@@ -69,10 +69,10 @@ func (s *services) start(timeout time.Duration) error { ...@@ -69,10 +69,10 @@ func (s *services) start(timeout time.Duration) error {
} }
} }
//create a new stopable // Create a new stoppable
s.stoppable = stoppable.NewMulti(followerStoppableName) s.stoppable = stoppable.NewMulti(followerStoppableName)
//start all services and register with the stoppable // Start all services and register with the stoppable
for _, sp := range s.services { for _, sp := range s.services {
stop, err := sp() stop, err := sp()
if err != nil { if err != nil {
...@@ -86,8 +86,8 @@ func (s *services) start(timeout time.Duration) error { ...@@ -86,8 +86,8 @@ func (s *services) start(timeout time.Duration) error {
return nil return nil
} }
// Stops all currently running services. Will return an // stop closes all currently running services. It returns an error if the stat
// error if the state is not "running" // is not Running.
func (s *services) stop() error { func (s *services) stop() error {
s.mux.Lock() s.mux.Lock()
defer s.mux.Unlock() defer s.mux.Unlock()
...@@ -108,7 +108,7 @@ func (s *services) stop() error { ...@@ -108,7 +108,7 @@ func (s *services) stop() error {
return nil return nil
} }
// returns the current state of services // status returns the current state of services.
func (s *services) status() Status { func (s *services) status() Status {
s.mux.Lock() s.mux.Lock()
defer s.mux.Unlock() defer s.mux.Unlock()
......
...@@ -8,15 +8,15 @@ ...@@ -8,15 +8,15 @@
package xxdk package xxdk
import ( import (
"errors" "github.com/pkg/errors"
"gitlab.com/elixxir/client/stoppable" "gitlab.com/elixxir/client/stoppable"
"reflect" "reflect"
"testing" "testing"
"time" "time"
) )
// Unit test // Unit test of newServices.
func TestNewServices(t *testing.T) { func Test_newServices(t *testing.T) {
expected := &services{ expected := &services{
services: make([]Service, 0), services: make([]Service, 0),
stoppable: stoppable.NewMulti("services"), stoppable: stoppable.NewMulti("services"),
...@@ -32,8 +32,8 @@ func TestNewServices(t *testing.T) { ...@@ -32,8 +32,8 @@ func TestNewServices(t *testing.T) {
} }
} }
// Unit test // Unit test of services.add.
func TestServices_Add(t *testing.T) { func Test_services_add(t *testing.T) {
mockService := func() (stoppable.Stoppable, error) { mockService := func() (stoppable.Stoppable, error) {
return nil, nil return nil, nil
} }
...@@ -42,7 +42,7 @@ func TestServices_Add(t *testing.T) { ...@@ -42,7 +42,7 @@ func TestServices_Add(t *testing.T) {
err := mockServices.add(mockService) err := mockServices.add(mockService)
if err != nil { if err != nil {
t.Fatalf("Failed to add mock service to services: %v", err) t.Fatalf("Failed to add mock service to the services list: %v", err)
} }
err = mockServices.start(500 * time.Millisecond) err = mockServices.start(500 * time.Millisecond)
...@@ -62,7 +62,8 @@ func TestServices_Add(t *testing.T) { ...@@ -62,7 +62,8 @@ func TestServices_Add(t *testing.T) {
} }
} }
func TestServices_Start(t *testing.T) { // Unit test of services.start.
func Test_services_start(t *testing.T) {
mockService := func() (stoppable.Stoppable, error) { mockService := func() (stoppable.Stoppable, error) {
return nil, nil return nil, nil
} }
...@@ -71,7 +72,7 @@ func TestServices_Start(t *testing.T) { ...@@ -71,7 +72,7 @@ func TestServices_Start(t *testing.T) {
err := mockServices.add(mockService) err := mockServices.add(mockService)
if err != nil { if err != nil {
t.Fatalf("Failed to add mock service to services: %v", err) t.Fatalf("Failed to add mock service to the services list: %v", err)
} }
err = mockServices.start(500) err = mockServices.start(500)
...@@ -86,7 +87,8 @@ func TestServices_Start(t *testing.T) { ...@@ -86,7 +87,8 @@ func TestServices_Start(t *testing.T) {
} }
} }
func TestServices_Stop(t *testing.T) { // Unit test of services.stop.
func Test_services_stop(t *testing.T) {
mockService := func() (stoppable.Stoppable, error) { mockService := func() (stoppable.Stoppable, error) {
return stoppable.NewSingle("test"), nil return stoppable.NewSingle("test"), nil
} }
...@@ -95,7 +97,7 @@ func TestServices_Stop(t *testing.T) { ...@@ -95,7 +97,7 @@ func TestServices_Stop(t *testing.T) {
err := mockServices.add(mockService) err := mockServices.add(mockService)
if err != nil { if err != nil {
t.Fatalf("Failed to add mock service to services: %v", err) t.Fatalf("Failed to add mock service to the services list: %v", err)
} }
err = mockServices.stop() err = mockServices.stop()
......
...@@ -11,14 +11,25 @@ import ( ...@@ -11,14 +11,25 @@ import (
"fmt" "fmt"
) )
// Status holds the status of the network.
type Status int type Status int
const ( const (
// Stopped signifies that the network follower is stopped; none of its
// processes are running.
Stopped Status = 0 Stopped Status = 0
// Running signifies that the network follower and its processes are active
// and running.
Running Status = 2000 Running Status = 2000
// Stopping signifies that the network follower has been signalled to stop
// and is in the processes of stopping the processes.
Stopping Status = 3000 Stopping Status = 3000
) )
// String returns a human-readable string version of the status. This function
// adheres to the fmt.Stringer interface.
func (s Status) String() string { func (s Status) String() string {
switch s { switch s {
case Stopped: case Stopped:
...@@ -28,6 +39,6 @@ func (s Status) String() string { ...@@ -28,6 +39,6 @@ func (s Status) String() string {
case Stopping: case Stopping:
return "Stopping" return "Stopping"
default: default:
return fmt.Sprintf("Unknown state %d", s) return fmt.Sprintf("Unknown status %d", s)
} }
} }
...@@ -28,11 +28,11 @@ import ( ...@@ -28,11 +28,11 @@ import (
) )
const ( const (
// SaltSize size of user salts // SaltSize is the length of user salts, in bytes.
SaltSize = 32 SaltSize = 32
) )
// createNewUser generates an identity for cMix // createNewUser generates an identity for cMix.
func createNewUser(rng *fastRNG.StreamGenerator, e2eGroup *cyclic.Group) user.Info { func createNewUser(rng *fastRNG.StreamGenerator, e2eGroup *cyclic.Group) user.Info {
// CMIX Keygen // CMIX Keygen
var transmissionRsaKey, receptionRsaKey *rsa.PrivateKey var transmissionRsaKey, receptionRsaKey *rsa.PrivateKey
...@@ -67,9 +67,8 @@ func createNewUser(rng *fastRNG.StreamGenerator, e2eGroup *cyclic.Group) user.In ...@@ -67,9 +67,8 @@ func createNewUser(rng *fastRNG.StreamGenerator, e2eGroup *cyclic.Group) user.In
} }
} }
func createKeys(rng *fastRNG.StreamGenerator, func createKeys(rng *fastRNG.StreamGenerator, e2e *cyclic.Group) (
e2e *cyclic.Group) (e2eKeyBytes, e2eKeyBytes, transmissionSalt, receptionSalt []byte,
transmissionSalt, receptionSalt []byte,
transmissionRsaKey, receptionRsaKey *rsa.PrivateKey) { transmissionRsaKey, receptionRsaKey *rsa.PrivateKey) {
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
...@@ -81,7 +80,7 @@ func createKeys(rng *fastRNG.StreamGenerator, ...@@ -81,7 +80,7 @@ func createKeys(rng *fastRNG.StreamGenerator,
// DH Keygen // DH Keygen
// FIXME: Why 256 bits? -- this is spec but not explained, it has // FIXME: Why 256 bits? -- this is spec but not explained, it has
// to do with optimizing operations on one side and still preserves // to do with optimizing operations on one side and still preserves
// decent security -- cite this. Why valid for BOTH e2e and cmix? // decent security -- cite this. Why valid for BOTH e2e and cMix?
stream := rng.GetStream() stream := rng.GetStream()
e2eKeyBytes, err = csprng.GenerateInGroup(e2e.GetPBytes(), 256, stream) e2eKeyBytes, err = csprng.GenerateInGroup(e2e.GetPBytes(), 256, stream)
stream.Close() stream.Close()
...@@ -128,8 +127,8 @@ func createKeys(rng *fastRNG.StreamGenerator, ...@@ -128,8 +127,8 @@ func createKeys(rng *fastRNG.StreamGenerator,
} }
// createNewVanityUser generates an identity for cMix // createNewVanityUser generates an identity for cMix. The identity's
// The identity's ReceptionID is not random but starts with the supplied prefix // ReceptionID is not random but starts with the supplied prefix.
func createNewVanityUser(rng csprng.Source, func createNewVanityUser(rng csprng.Source,
e2e *cyclic.Group, prefix string) user.Info { e2e *cyclic.Group, prefix string) user.Info {
// DH Keygen // DH Keygen
...@@ -164,8 +163,8 @@ func createNewVanityUser(rng csprng.Source, ...@@ -164,8 +163,8 @@ func createNewVanityUser(rng csprng.Source,
jww.FATAL.Panicf(err.Error()) jww.FATAL.Panicf(err.Error())
} }
// just in case more than one go routine tries to access // Just in case more than one go routine tries to access receptionSalt and
// receptionSalt and receptionID // receptionID
var mu sync.Mutex var mu sync.Mutex
done := make(chan struct{}) done := make(chan struct{})
found := make(chan bool) found := make(chan bool)
...@@ -177,11 +176,13 @@ func createNewVanityUser(rng csprng.Source, ...@@ -177,11 +176,13 @@ func createNewVanityUser(rng csprng.Source,
pref := prefix pref := prefix
ignoreCase := false ignoreCase := false
// check if case-insensitivity is enabled
// Check if case-insensitivity is enabled
if strings.HasPrefix(prefix, "(?i)") { if strings.HasPrefix(prefix, "(?i)") {
pref = strings.ToLower(pref[4:]) pref = strings.ToLower(pref[4:])
ignoreCase = true ignoreCase = true
} }
// Check if prefix contains valid Base64 characters // Check if prefix contains valid Base64 characters
match, _ := regexp.MatchString("^[A-Za-z0-9+/]+$", pref) match, _ := regexp.MatchString("^[A-Za-z0-9+/]+$", pref)
if match == false { if match == false {
...@@ -233,11 +234,13 @@ func createNewVanityUser(rng csprng.Source, ...@@ -233,11 +234,13 @@ func createNewVanityUser(rng csprng.Source,
} }
}() }()
} }
// wait for a solution then close the done channel to signal
// the workers to exit // Wait for a solution then close the done channel to signal the workers to
// exit
<-found <-found
close(done) close(done)
wg.Wait() wg.Wait()
return user.Info{ return user.Info{
TransmissionID: transmissionID.DeepCopy(), TransmissionID: transmissionID.DeepCopy(),
TransmissionSalt: transmissionSalt, TransmissionSalt: transmissionSalt,
...@@ -251,7 +254,6 @@ func createNewVanityUser(rng csprng.Source, ...@@ -251,7 +254,6 @@ func createNewVanityUser(rng csprng.Source,
} }
} }
// createPrecannedUser
func createPrecannedUser(precannedID uint, rng csprng.Source, grp *cyclic.Group) user.Info { func createPrecannedUser(precannedID uint, rng csprng.Source, grp *cyclic.Group) user.Info {
// Salt, UID, etc gen // Salt, UID, etc gen
salt := make([]byte, SaltSize) salt := make([]byte, SaltSize)
......
...@@ -25,8 +25,8 @@ const ( ...@@ -25,8 +25,8 @@ const (
desiredPreviewSize = 32 * 24 desiredPreviewSize = 32 * 24
) )
// CompressJpeg takes a JPEG image in byte format // CompressJpeg takes a JPEG image in byte format and compresses it based on
// and compresses it based on desired output size // desired output size.
func CompressJpeg(imgBytes []byte) ([]byte, error) { func CompressJpeg(imgBytes []byte) ([]byte, error) {
// Convert bytes to a reader // Convert bytes to a reader
imgBuf := bytes.NewReader(imgBytes) imgBuf := bytes.NewReader(imgBytes)
...@@ -76,8 +76,8 @@ func CompressJpeg(imgBytes []byte) ([]byte, error) { ...@@ -76,8 +76,8 @@ func CompressJpeg(imgBytes []byte) ([]byte, error) {
return newImgBuf.Bytes(), nil return newImgBuf.Bytes(), nil
} }
// CompressJpeg takes a JPEG image in byte format // CompressJpegForPreview takes a JPEG image in byte format and compresses it
// and compresses it based on desired output size // based on desired output size.
func CompressJpegForPreview(imgBytes []byte) ([]byte, error) { func CompressJpegForPreview(imgBytes []byte) ([]byte, error) {
// Convert bytes to a reader // Convert bytes to a reader
imgBuf := bytes.NewReader(imgBytes) imgBuf := bytes.NewReader(imgBytes)
......
...@@ -58,8 +58,11 @@ func newTestingClient(face interface{}) (*Cmix, error) { ...@@ -58,8 +58,11 @@ func newTestingClient(face interface{}) (*Cmix, error) {
jww.FATAL.Panicf("Failed to create new test instance: %v", err) jww.FATAL.Panicf("Failed to create new test instance: %v", err)
} }
commsManager.AddHost(&id.Permissioning, "", cert, _, err = commsManager.AddHost(
connect.GetDefaultHostParams()) &id.Permissioning, "", cert, connect.GetDefaultHostParams())
if err != nil {
return nil, err
}
instanceComms := &connect.ProtoComms{ instanceComms := &connect.ProtoComms{
Manager: commsManager, Manager: commsManager,
} }
...@@ -82,7 +85,7 @@ func newTestingClient(face interface{}) (*Cmix, error) { ...@@ -82,7 +85,7 @@ func newTestingClient(face interface{}) (*Cmix, error) {
return c, nil return c, nil
} }
// Helper function which generates an ndf for testing // Helper function that generates an NDF for testing.
func getNDF(face interface{}) *ndf.NetworkDefinition { func getNDF(face interface{}) *ndf.NetworkDefinition {
switch face.(type) { switch face.(type) {
case *testing.T, *testing.M, *testing.B, *testing.PB: case *testing.T, *testing.M, *testing.B, *testing.PB:
...@@ -152,8 +155,8 @@ func getNDF(face interface{}) *ndf.NetworkDefinition { ...@@ -152,8 +155,8 @@ func getNDF(face interface{}) *ndf.NetworkDefinition {
} }
} }
// Signs a passed round info with the key tied to the test nodes cert // signRoundInfo signs a passed round info with the key tied to the test node's
// used throughout utils and other tests // cert used throughout utils and other tests.
func signRoundInfo(ri *pb.RoundInfo) error { func signRoundInfo(ri *pb.RoundInfo) error {
privKeyFromFile := testkeys.LoadFromPath(testkeys.GetNodeKeyPath()) privKeyFromFile := testkeys.LoadFromPath(testkeys.GetNodeKeyPath())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment