Skip to content
Snippets Groups Projects
Commit 62226d2e authored by Jonah Husson's avatar Jonah Husson
Browse files

Merge branch 'release' into dnf/notificationsbot

parents 5abbf10e 7d5be535
No related branches found
No related tags found
No related merge requests found
......@@ -20,16 +20,17 @@ import (
// RequestAuthenticatedChannel sends a request to another party to establish an
// authenticated channel
// It will not run if the network status is not healthy
// An error will be returned if a channel already exists, if a request was
// already received, or if a request was already sent
// An error will be returned if a channel already exists or if a request was
// already received
// When a confirmation occurs, the channel will be created and the callback
// will be called
// Can be retried.
func (c *Client) RequestAuthenticatedChannel(recipient, me contact.Contact,
message string) error {
message string) (id.Round, error) {
jww.INFO.Printf("RequestAuthenticatedChannel(%s)", recipient.ID)
if !c.network.GetHealthTracker().IsHealthy() {
return errors.New("Cannot request authenticated channel " +
return 0, errors.New("Cannot request authenticated channel " +
"creation when the network is not healthy")
}
......@@ -60,11 +61,12 @@ func (c *Client) GetAuthenticatedChannelRequest(partner *id.ID) (contact.Contact
// An error will be returned if a channel already exists, if a request doest
// exist, or if the passed in contact does not exactly match the received
// request
func (c *Client) ConfirmAuthenticatedChannel(recipient contact.Contact) error {
// Can be retried.
func (c *Client) ConfirmAuthenticatedChannel(recipient contact.Contact) (id.Round, error) {
jww.INFO.Printf("ConfirmAuthenticatedChannel(%s)", recipient.ID)
if !c.network.GetHealthTracker().IsHealthy() {
return errors.New("Cannot request authenticated channel " +
return 0, errors.New("Cannot request authenticated channel " +
"creation when the network is not healthy")
}
......
......@@ -84,33 +84,12 @@ func NewClient(ndfJSON, storageDir string, password []byte, registrationCode str
protoUser := createNewUser(rngStream, cmixGrp, e2eGrp)
// Get current client version
currentVersion, err := version.ParseVersion(SEMVER)
if err != nil {
return errors.WithMessage(err, "Could not parse version string.")
}
// Create Storage
passwordStr := string(password)
storageSess, err := storage.New(storageDir, passwordStr, protoUser,
currentVersion, cmixGrp, e2eGrp, rngStreamGen)
err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
cmixGrp, e2eGrp, rngStreamGen, false, registrationCode)
if err != nil {
return err
}
// Save NDF to be used in the future
storageSess.SetBaseNDF(def)
//store the registration code for later use
storageSess.SetRegCode(registrationCode)
//move the registration state to keys generated
err = storageSess.ForwardRegistrationStatus(storage.KeyGenComplete)
if err != nil {
return errors.WithMessage(err, "Failed to denote state "+
"change in session")
}
//TODO: close the session
return nil
}
......@@ -135,29 +114,39 @@ func NewPrecannedClient(precannedID uint, defJSON, storageDir string, password [
protoUser := createPrecannedUser(precannedID, rngStream, cmixGrp, e2eGrp)
// Get current client version
currentVersion, err := version.ParseVersion(SEMVER)
err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
cmixGrp, e2eGrp, rngStreamGen, true, "")
if err != nil {
return errors.WithMessage(err, "Could not parse version string.")
return err
}
//TODO: close the session
return nil
}
// Create Storage
passwordStr := string(password)
storageSess, err := storage.New(storageDir, passwordStr, protoUser,
currentVersion, cmixGrp, e2eGrp, rngStreamGen)
// NewVanityClient creates a user with a receptionID that starts with the supplied prefix
// It creates client storage, generates keys, connects, and registers
// with the network. Note that this does not register a username/identity, but
// merely creates a new cryptographic identity for adding such information
// at a later date.
func NewVanityClient(ndfJSON, storageDir string, password []byte, registrationCode string, userIdPrefix string) error {
jww.INFO.Printf("NewVanityClient()")
// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
rngStreamGen := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG)
rngStream := rngStreamGen.GetStream()
// Parse the NDF
def, err := parseNDF(ndfJSON)
if err != nil {
return err
}
cmixGrp, e2eGrp := decodeGroups(def)
// Save NDF to be used in the future
storageSess.SetBaseNDF(def)
protoUser := createNewVanityUser(rngStream, cmixGrp, e2eGrp, userIdPrefix)
//move the registration state to indicate registered with permissioning
err = storageSess.ForwardRegistrationStatus(
storage.PermissioningComplete)
err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
cmixGrp, e2eGrp, rngStreamGen, false, registrationCode)
if err != nil {
return errors.WithMessage(err, "Failed to denote state "+
"change in session")
return err
}
//TODO: close the session
......@@ -583,3 +572,43 @@ func decodeGroups(ndf *ndf.NetworkDefinition) (cmixGrp, e2eGrp *cyclic.Group) {
return cmixGrp, e2eGrp
}
// checkVersionAndSetupStorage is common code shared by NewClient, NewPrecannedClient and NewVanityClient
// it checks client version and creates a new storage for user data
func checkVersionAndSetupStorage(def *ndf.NetworkDefinition, storageDir string, password []byte,
protoUser user.User, cmixGrp, e2eGrp *cyclic.Group, rngStreamGen *fastRNG.StreamGenerator,
isPrecanned bool, registrationCode string) error {
// Get current client version
currentVersion, err := version.ParseVersion(SEMVER)
if err != nil {
return errors.WithMessage(err, "Could not parse version string.")
}
// Create Storage
passwordStr := string(password)
storageSess, err := storage.New(storageDir, passwordStr, protoUser,
currentVersion, cmixGrp, e2eGrp, rngStreamGen)
if err != nil {
return err
}
// Save NDF to be used in the future
storageSess.SetBaseNDF(def)
if !isPrecanned {
//store the registration code for later use
storageSess.SetRegCode(registrationCode)
//move the registration state to keys generated
err = storageSess.ForwardRegistrationStatus(storage.KeyGenComplete)
} else {
//move the registration state to indicate registered with permissioning
err = storageSess.ForwardRegistrationStatus(storage.PermissioningComplete)
}
if err != nil {
return errors.WithMessage(err, "Failed to denote state "+
"change in session")
}
return nil
}
......@@ -17,6 +17,10 @@ import (
"gitlab.com/xx_network/crypto/xx"
"gitlab.com/xx_network/primitives/id"
"math/rand"
"regexp"
"runtime"
"strings"
"sync"
)
const (
......@@ -134,3 +138,126 @@ func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic.
ReceptionRSA: rsaKey,
}
}
// createNewVanityUser generates an identity for cMix
// The identity's ReceptionID is not random but starts with the supplied prefix
func createNewVanityUser(rng csprng.Source, cmix, e2e *cyclic.Group, prefix string) user.User {
// CMIX Keygen
// FIXME: Why 256 bits? -- this is spec but not explained, it has
// to do with optimizing operations on one side and still preserves
// decent security -- cite this.
cMixKeyBytes, err := csprng.GenerateInGroup(cmix.GetPBytes(), 256, rng)
if err != nil {
jww.FATAL.Panicf(err.Error())
}
// 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
// decent security -- cite this. Why valid for BOTH e2e and cmix?
e2eKeyBytes, err := csprng.GenerateInGroup(e2e.GetPBytes(), 256, rng)
if err != nil {
jww.FATAL.Panicf(err.Error())
}
// RSA Keygen (4096 bit defaults)
transmissionRsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen)
if err != nil {
jww.FATAL.Panicf(err.Error())
}
// Salt, UID, etc gen
transmissionSalt := make([]byte, SaltSize)
n, err := csprng.NewSystemRNG().Read(transmissionSalt)
if err != nil {
jww.FATAL.Panicf(err.Error())
}
if n != SaltSize {
jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
}
transmissionID, err := xx.NewID(transmissionRsaKey.GetPublic(), transmissionSalt, id.User)
if err != nil {
jww.FATAL.Panicf(err.Error())
}
receptionRsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen)
if err != nil {
jww.FATAL.Panicf(err.Error())
}
var mu sync.Mutex // just in case more than one go routine tries to access receptionSalt and receptionID
done := make(chan struct{})
found:= make(chan bool)
wg:= &sync.WaitGroup{}
cores := runtime.NumCPU()
var receptionSalt []byte
var receptionID *id.ID
pref := prefix
ignoreCase := false
// check if case-insensitivity is enabled
if strings.HasPrefix(prefix, "(?i)") {
pref = strings.ToLower(pref[4:])
ignoreCase = true
}
// Check if prefix contains valid Base64 characters
match, _ := regexp.MatchString("^[A-Za-z0-9+/]+$", pref)
if match == false {
jww.FATAL.Panicf("Prefix contains non-Base64 characters")
}
jww.INFO.Printf("Vanity userID generation started. Prefix: %s Ignore-Case: %v NumCPU: %d", pref, ignoreCase, cores)
for w := 0; w < cores; w++{
wg.Add(1)
go func() {
rSalt := make([]byte, SaltSize)
for {
select {
case <- done:
defer wg.Done()
return
default:
n, err = csprng.NewSystemRNG().Read(rSalt)
if err != nil {
jww.FATAL.Panicf(err.Error())
}
if n != SaltSize {
jww.FATAL.Panicf("receptionSalt size too small: %d", n)
}
rID, err := xx.NewID(receptionRsaKey.GetPublic(), rSalt, id.User)
if err != nil {
jww.FATAL.Panicf(err.Error())
}
id := rID.String()
if ignoreCase {
id = strings.ToLower(id)
}
if strings.HasPrefix(id, pref) {
mu.Lock()
receptionID = rID
receptionSalt = rSalt
mu.Unlock()
found <- true
defer wg.Done()
return
}
}
}
}()
}
// wait for a solution then close the done channel to signal the workers to exit
<- found
close(done)
wg.Wait()
return user.User{
TransmissionID: transmissionID.DeepCopy(),
TransmissionSalt: transmissionSalt,
TransmissionRSA: transmissionRsaKey,
ReceptionID: receptionID.DeepCopy(),
ReceptionSalt: receptionSalt,
ReceptionRSA: receptionRsaKey,
Precanned: false,
CmixDhPrivateKey: cmix.NewIntFromBytes(cMixKeyBytes),
E2eDhPrivateKey: e2e.NewIntFromBytes(e2eKeyBytes),
}
}
......@@ -12,26 +12,23 @@ import (
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/params"
"gitlab.com/elixxir/client/interfaces/utility"
"gitlab.com/elixxir/client/storage"
ds "gitlab.com/elixxir/comms/network/dataStructures"
"gitlab.com/xx_network/primitives/id"
"gitlab.com/elixxir/crypto/contact"
"gitlab.com/elixxir/crypto/diffieHellman"
cAuth "gitlab.com/elixxir/crypto/e2e/auth"
"gitlab.com/elixxir/primitives/format"
"gitlab.com/elixxir/primitives/states"
"io"
"time"
)
func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
storage *storage.Session, net interfaces.NetworkManager) error {
storage *storage.Session, net interfaces.NetworkManager) (id.Round, error) {
/*edge checking*/
// check that messages can be sent over the network
if !net.GetHealthTracker().IsHealthy() {
return errors.New("Cannot confirm authenticated message " +
return 0, errors.New("Cannot confirm authenticated message " +
"when the network is not healthy")
}
......@@ -40,14 +37,14 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
// the lock
storedContact, err := storage.Auth().GetReceivedRequest(partner.ID)
if err != nil {
return errors.Errorf("failed to find a pending Auth Request: %s",
return 0, errors.Errorf("failed to find a pending Auth Request: %s",
err)
}
// verify the passed contact matches what is stored
if storedContact.DhPubKey.Cmp(partner.DhPubKey) != 0 {
storage.Auth().Fail(partner.ID)
return errors.WithMessage(err, "Pending Auth Request has different "+
return 0, errors.WithMessage(err, "Pending Auth Request has different "+
"pubkey than stored")
}
......@@ -68,7 +65,7 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
_, err = rng.Read(salt)
if err != nil {
storage.Auth().Fail(partner.ID)
return errors.Wrap(err, "Failed to generate salt for "+
return 0, errors.Wrap(err, "Failed to generate salt for "+
"confirmation")
}
......@@ -108,21 +105,19 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
if err := storage.E2e().AddPartner(partner.ID, partner.DhPubKey, newPrivKey,
p, p); err != nil {
storage.Auth().Fail(partner.ID)
return errors.Errorf("Failed to create channel with partner (%s) "+
return 0, errors.Errorf("Failed to create channel with partner (%s) "+
"on confirmation: %+v",
partner.ID, err)
}
// delete the in progress negotiation
// this unlocks the request lock
if err := storage.Auth().Delete(partner.ID); err != nil {
return errors.Errorf("UNRECOVERABLE! Failed to delete in "+
//fixme - do these deletes at a later date
/*if err := storage.Auth().Delete(partner.ID); err != nil {
return 0, errors.Errorf("UNRECOVERABLE! Failed to delete in "+
"progress negotiation with partner (%s) after creating confirmation: %+v",
partner.ID, err)
}
//store the message as a critical message so it will always be sent
storage.GetCriticalRawMessages().AddProcessing(cmixMsg, partner.ID)
}*/
jww.INFO.Printf("Confirming Auth with %s, msgDigest: %s",
partner.ID, cmixMsg.Digest())
......@@ -134,39 +129,11 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
// retried
jww.INFO.Printf("Auth Confirm with %s (msgDigest: %s) failed "+
"to transmit: %+v", partner.ID, cmixMsg.Digest(), err)
storage.GetCriticalRawMessages().Failed(cmixMsg, partner.ID)
return errors.WithMessage(err, "Auth Confirm Failed to transmit")
return 0, errors.WithMessage(err, "Auth Confirm Failed to transmit")
}
jww.INFO.Printf("Confirm Request with %s (msgDigest: %s) sent on round %d",
partner.ID, cmixMsg.Digest(), round)
/*check message delivery*/
sendResults := make(chan ds.EventReturn, 1)
roundEvents := net.GetInstance().GetRoundEvents()
roundEvents.AddRoundEventChan(round, sendResults, 1*time.Minute,
states.COMPLETED, states.FAILED)
success, numFailed, _ := utility.TrackResults(sendResults, 1)
if !success {
if numFailed > 0 {
jww.INFO.Printf("Auth Confirm with %s (msgDigest: %s) failed "+
"delivery due to round failure, will retry on reconnect",
partner.ID, cmixMsg.Digest())
} else {
jww.INFO.Printf("Auth Confirm with %s (msgDigest: %s) failed "+
"delivery due to timeout, will retry on reconnect",
partner.ID, cmixMsg.Digest())
}
jww.ERROR.Printf("auth confirm failed to transmit, will be " +
"handled on reconnect")
storage.GetCriticalRawMessages().Failed(cmixMsg, partner.ID)
} else {
jww.INFO.Printf("Auth Confirm with %s (msgDigest: %s) delivered "+
"sucesfully", partner.ID, cmixMsg.Digest())
storage.GetCriticalRawMessages().Succeeded(cmixMsg, partner.ID)
}
return nil
return round, nil
}
......@@ -12,59 +12,59 @@ import (
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/params"
"gitlab.com/elixxir/client/interfaces/utility"
"gitlab.com/elixxir/client/storage"
"gitlab.com/elixxir/client/storage/auth"
"gitlab.com/elixxir/client/storage/e2e"
ds "gitlab.com/elixxir/comms/network/dataStructures"
"gitlab.com/elixxir/crypto/contact"
"gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/diffieHellman"
cAuth "gitlab.com/elixxir/crypto/e2e/auth"
"gitlab.com/elixxir/primitives/format"
"gitlab.com/elixxir/primitives/states"
"gitlab.com/xx_network/primitives/id"
"io"
"strings"
"time"
)
const terminator = ";"
func RequestAuth(partner, me contact.Contact, message string, rng io.Reader,
storage *storage.Session, net interfaces.NetworkManager) error {
storage *storage.Session, net interfaces.NetworkManager) (id.Round, error) {
/*edge checks generation*/
// check that an authenticated channel does not already exists
if _, err := storage.E2e().GetPartner(partner.ID); err == nil ||
!strings.Contains(err.Error(), e2e.NoPartnerErrorStr) {
return errors.Errorf("Authenticated channel already " +
return 0, errors.Errorf("Authenticated channel already " +
"established with partner")
}
// check that the request is being sent from the proper ID
if !me.ID.Cmp(storage.GetUser().ReceptionID) {
return errors.Errorf("Authenticated channel request " +
return 0, errors.Errorf("Authenticated channel request " +
"can only be sent from user's identity")
}
// check that the message is properly formed
if strings.Contains(message, terminator) {
return errors.Errorf("Message cannot contain '%s'", terminator)
return 0, errors.Errorf("Message cannot contain '%s'", terminator)
}
//denote if this is a resend of an old request
resend := false
//lookup if an ongoing request is occurring
rqType, _, _, err := storage.Auth().GetRequest(partner.ID)
if err != nil && strings.Contains(err.Error(), auth.NoRequest) {
err = nil
}
if err != nil {
rqType, sr, _, err := storage.Auth().GetRequest(partner.ID)
if err != nil && !strings.Contains(err.Error(), auth.NoRequest){
if rqType == auth.Receive {
return errors.WithMessage(err,
"Cannot send a request after "+
"receiving a request")
return 0, errors.WithMessage(err,
"Cannot send a request after receiving a request")
} else if rqType == auth.Sent {
return errors.WithMessage(err,
"Cannot send a request after "+
"already sending one")
resend = true
}else{
return 0, errors.WithMessage(err,
"Cannot send a request after receiving unknown error " +
"on requesting contact status")
}
}
......@@ -76,7 +76,7 @@ func RequestAuth(partner, me contact.Contact, message string, rng io.Reader,
ecrFmt := newEcrFormat(baseFmt.GetEcrPayloadLen())
requestFmt, err := newRequestFormat(ecrFmt)
if err != nil {
return errors.Errorf("failed to make request format: %+v", err)
return 0, errors.Errorf("failed to make request format: %+v", err)
}
//check the payload fits
......@@ -85,7 +85,7 @@ func RequestAuth(partner, me contact.Contact, message string, rng io.Reader,
msgPayloadBytes := []byte(msgPayload)
if len(msgPayloadBytes) > requestFmt.MsgPayloadLen() {
return errors.Errorf("Combined message longer than space "+
return 0, errors.Errorf("Combined message longer than space "+
"available in payload; available: %v, length: %v",
requestFmt.MsgPayloadLen(), len(msgPayloadBytes))
}
......@@ -95,17 +95,27 @@ func RequestAuth(partner, me contact.Contact, message string, rng io.Reader,
salt := make([]byte, saltSize)
_, err = rng.Read(salt)
if err != nil {
return errors.Wrap(err, "Failed to generate salt")
return 0, errors.Wrap(err, "Failed to generate salt")
}
var newPrivKey, newPubKey *cyclic.Int
// in this case we have an ongoing request so we can resend the extant
// request
if resend{
newPrivKey = sr.GetMyPrivKey()
newPubKey = sr.GetMyPubKey()
//in this case it is a new request and we must generate new keys
}else{
//generate new keypair
newPrivKey = diffieHellman.GeneratePrivateKey(256, grp, rng)
newPubKey = diffieHellman.GeneratePublicKey(newPrivKey, grp)
}
//generate ownership proof
ownership := cAuth.MakeOwnershipProof(storage.E2e().GetDHPrivateKey(),
partner.DhPubKey, storage.E2e().GetGroup())
//generate new keypair
newPrivKey := diffieHellman.GeneratePrivateKey(256, grp, rng)
newPubKey := diffieHellman.GeneratePublicKey(newPrivKey, grp)
jww.TRACE.Printf("RequestAuth MYPUBKEY: %v", newPubKey.Bytes())
jww.TRACE.Printf("RequestAuth THEIRPUBKEY: %v", partner.DhPubKey.Bytes())
......@@ -130,67 +140,30 @@ func RequestAuth(partner, me contact.Contact, message string, rng io.Reader,
/*store state*/
//fixme: channel is bricked if the first store succedes but the second fails
//store the in progress auth
err = storage.Auth().AddSent(partner.ID, partner.DhPubKey, newPrivKey,
newPrivKey, confirmFp)
if err != nil {
return errors.Errorf("Failed to store auth request: %s", err)
if !resend{
err = storage.Auth().AddSent(partner.ID, partner.DhPubKey, newPrivKey,
newPrivKey, confirmFp)
if err != nil {
return 0, errors.Errorf("Failed to store auth request: %s", err)
}
}
//store the message as a critical message so it will always be sent
storage.GetCriticalRawMessages().AddProcessing(cmixMsg, partner.ID)
jww.INFO.Printf("Requesting Auth with %s, msgDigest: %s",
partner.ID, cmixMsg.Digest())
go func() {
jww.INFO.Printf("Requesting Auth with %s, msgDigest: %s",
partner.ID, cmixMsg.Digest())
/*send message*/
round, _, err := net.SendCMIX(cmixMsg, partner.ID,
params.GetDefaultCMIX())
if err != nil {
// if the send fails just set it to failed, it will
// but automatically retried
jww.WARN.Printf("Auth Request with %s (msgDigest: %s)"+
" failed to transmit: %+v", partner.ID,
cmixMsg.Digest(), err)
storage.GetCriticalRawMessages().Failed(cmixMsg,
partner.ID)
}
/*send message*/
round, _, err := net.SendCMIX(cmixMsg, partner.ID,
params.GetDefaultCMIX())
if err != nil {
// if the send fails just set it to failed, it will
// but automatically retried
return 0, errors.WithMessagef(err, "Auth Request with %s " +
"(msgDigest: %s) failed to transmit: %+v", partner.ID,
cmixMsg.Digest(), err)
}
jww.INFO.Printf("Auth Request with %s (msgDigest: %s) sent"+
" on round %d", partner.ID, cmixMsg.Digest(), round)
/*check message delivery*/
sendResults := make(chan ds.EventReturn, 1)
roundEvents := net.GetInstance().GetRoundEvents()
roundEvents.AddRoundEventChan(round, sendResults, 1*time.Minute,
states.COMPLETED, states.FAILED)
success, numFailed, _ := utility.TrackResults(sendResults, 1)
if !success {
if numFailed > 0 {
jww.WARN.Printf("Auth Request with %s "+
"(msgDigest: %s) failed "+
"delivery due to round failure, "+
"will retry on reconnect",
partner.ID, cmixMsg.Digest())
} else {
jww.WARN.Printf("Auth Request with %s "+
"(msgDigest: %s) failed "+
"delivery due to timeout, "+
"will retry on reconnect",
partner.ID, cmixMsg.Digest())
}
storage.GetCriticalRawMessages().Failed(cmixMsg,
partner.ID)
} else {
jww.INFO.Printf("Auth Request with %s (msgDigest: %s) "+
"delivered sucessfully", partner.ID,
cmixMsg.Digest())
storage.GetCriticalRawMessages().Succeeded(cmixMsg,
partner.ID)
}
}()
jww.INFO.Printf("Auth Request with %s (msgDigest: %s) sent"+
" on round %d", partner.ID, cmixMsg.Digest(), round)
return nil
return round, nil
}
......@@ -27,19 +27,20 @@ func (c *Client) MakePrecannedAuthenticatedChannel(precannedID int) (*Contact, e
// authenticated channel
// It will not run if the network status is not healthy
// An error will be returned if a channel already exists, if a request was
// already received, or if a request was already sent
// already received.
// When a confirmation occurs, the channel will be created and the callback
// will be called
// This can be called many times and retried.
//
// This function takes the marshaled send report to ensure a memory leak does
// not occur as a result of both sides of the bindings holding a refrence to
// the same pointer.
func (c *Client) RequestAuthenticatedChannel(recipientMarshaled,
meMarshaled []byte, message string) error {
meMarshaled []byte, message string) (int, error) {
recipent, err := contact.Unmarshal(recipientMarshaled)
if err != nil {
return errors.New(fmt.Sprintf("Failed to "+
return 0, errors.New(fmt.Sprintf("Failed to "+
"RequestAuthenticatedChannel: Failed to Unmarshal Recipent: "+
"%+v", err))
}
......@@ -47,11 +48,13 @@ func (c *Client) RequestAuthenticatedChannel(recipientMarshaled,
me, err := contact.Unmarshal(meMarshaled)
if err != nil {
return errors.New(fmt.Sprintf("Failed to "+
return 0, errors.New(fmt.Sprintf("Failed to "+
"RequestAuthenticatedChannel: Failed to Unmarshal Me: %+v", err))
}
return c.api.RequestAuthenticatedChannel(recipent, me, message)
rid, err := c.api.RequestAuthenticatedChannel(recipent, me, message)
return int(rid), err
}
// RegisterAuthCallbacks registers both callbacks for authenticated channels.
......@@ -79,19 +82,26 @@ func (c *Client) RegisterAuthCallbacks(request AuthRequestCallback,
// received request and sends a message to the requestor that the request has
// been confirmed
// It will not run if the network status is not healthy
// An error will be returned if a channel already exists, if a request doest
// An error will be returned if a request doest
// exist, or if the passed in contact does not exactly match the received
// request
func (c *Client) ConfirmAuthenticatedChannel(recipientMarshaled []byte) error {
// request.
// This can be called many times and retried.
//
// This function takes the marshaled send report to ensure a memory leak does
// not occur as a result of both sides of the bindings holding a refrence to
// the same pointer.
func (c *Client) ConfirmAuthenticatedChannel(recipientMarshaled []byte) (int, error) {
recipent, err := contact.Unmarshal(recipientMarshaled)
if err != nil {
return errors.New(fmt.Sprintf("Failed to "+
return 0, errors.New(fmt.Sprintf("Failed to "+
"ConfirmAuthenticatedChannel: Failed to Unmarshal Recipient: "+
"%+v", err))
}
return c.api.ConfirmAuthenticatedChannel(recipent)
rid, err := c.api.ConfirmAuthenticatedChannel(recipent)
return int(rid), err
}
// VerifyOwnership checks if the ownership proof on a passed contact matches the
......
......@@ -31,12 +31,19 @@ type NetworkHealthCallback interface {
Callback(bool)
}
// RoundEventHandler handles round events happening on the cMix network.
// RoundEventCallback handles waiting on the exact state of a round on
// the cMix network.
type RoundEventCallback interface {
EventCallback(rid, state int, timedOut bool)
}
// RoundEventHandler handles round events happening on the cMix network.
// RoundCompletionCallback is returned when the completion of a round is known.
type RoundCompletionCallback interface {
EventCallback(rid int, success, timedOut bool)
}
// MessageDeliveryCallback gets called on the determination if all events
// related to a message send were successful.
type MessageDeliveryCallback interface {
EventCallback(msgID []byte, delivered, timedOut bool, roundResults []byte)
}
......
......@@ -333,18 +333,35 @@ func (c *Client) RegisterRoundEventsHandler(rid int, cb RoundEventCallback,
return newRoundUnregister(roundID, ec, c.api.GetRoundEvents())
}
// RegisterMessageDeliveryCB allows the caller to get notified if the rounds a
// message was sent in successfully completed. Under the hood, this uses the same
// interface as RegisterRoundEventsHandler, but provides a convenient way to use
// the interface in its most common form, looking up the result of message
// retrieval
// WaitForRoundCompletion allows the caller to get notified if a round
// has completed (or failed). Under the hood, this uses an API which uses the internal
// round data, network historical round lookup, and waiting on network events
// to determine what has (or will) occur.
//
// The callbacks will return at timeoutMS if no state update occurs
func (c *Client) WaitForRoundCompletion(roundID int,
rec RoundCompletionCallback, timeoutMS int) error {
f := func(allRoundsSucceeded, timedOut bool, rounds map[id.Round]api.RoundResult) {
rec.EventCallback(roundID, allRoundsSucceeded, timedOut)
}
timeout := time.Duration(timeoutMS) * time.Millisecond
return c.api.GetRoundResults([]id.Round{id.Round(roundID)}, timeout, f)
}
// WaitForMessageDelivery allows the caller to get notified if the rounds a
// message was sent in successfully completed. Under the hood, this uses an API
// which uses the internal round data, network historical round lookup, and
// waiting on network events to determine what has (or will) occur.
//
// The callbacks will return at timeoutMS if no state update occurs
//
// This function takes the marshaled send report to ensure a memory leak does
// not occur as a result of both sides of the bindings holding a reference to
// the same pointer.
func (c *Client) WaitForRoundCompletion(marshaledSendReport []byte,
func (c *Client) WaitForMessageDelivery(marshaledSendReport []byte,
mdc MessageDeliveryCallback, timeoutMS int) error {
sr, err := UnmarshalSendReport(marshaledSendReport)
......
......@@ -18,11 +18,13 @@ import (
// "gitlab.com/elixxir/client/switchboard"
// "gitlab.com/elixxir/client/ud"
// "gitlab.com/elixxir/primitives/fact"
"gitlab.com/elixxir/client/api"
"gitlab.com/elixxir/comms/client"
"gitlab.com/xx_network/comms/connect"
//"time"
pb "gitlab.com/elixxir/comms/mixmessages"
"gitlab.com/xx_network/primitives/id"
"gitlab.com/xx_network/primitives/id/ephemeral"
"gitlab.com/xx_network/primitives/utils"
)
......@@ -63,12 +65,14 @@ var getNDFCmd = &cobra.Command{
if gwHost != "" {
host, _ := connect.NewHost(&id.TempGateway, gwHost,
cert, params)
dummyID := ephemeral.ReservedIDs[0]
pollMsg := &pb.GatewayPoll{
Partial: &pb.NDFHash{
Hash: nil,
},
LastUpdate: uint64(0),
ReceptionID: id.DummyUser.Marshal(),
ReceptionID: dummyID[:],
ClientVersion: []byte(api.SEMVER),
}
resp, err := comms.SendPoll(host, pollMsg)
if err != nil {
......
......@@ -11,6 +11,7 @@ package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
jww "github.com/spf13/jwalterweatherman"
)
......@@ -29,5 +30,9 @@ var initCmd = &cobra.Command{
}
func init() {
initCmd.Flags().StringP("userid-prefix", "", "",
"Desired prefix of userID to brute force when running init command. Prepend (?i) for case-insensitive. Only Base64 characters are valid.")
_ = viper.BindPFlag("userid-prefix", initCmd.Flags().Lookup("userid-prefix"))
rootCmd.AddCommand(initCmd)
}
......@@ -95,7 +95,7 @@ var rootCmd = &cobra.Command{
requestor contact.Contact, message string) {
jww.INFO.Printf("Channel Request: %s",
requestor.ID)
err := client.ConfirmAuthenticatedChannel(
_, err := client.ConfirmAuthenticatedChannel(
requestor)
if err != nil {
jww.FATAL.Panicf("%+v", err)
......@@ -306,7 +306,7 @@ func createClient() *api.Client {
storeDir := viper.GetString("session")
regCode := viper.GetString("regcode")
precannedID := viper.GetUint("sendid")
userIDprefix := viper.GetString("userid-prefix")
//create a new client if none exist
if _, err := os.Stat(storeDir); os.IsNotExist(err) {
// Load NDF
......@@ -320,8 +320,14 @@ func createClient() *api.Client {
err = api.NewPrecannedClient(precannedID,
string(ndfJSON), storeDir, []byte(pass))
} else {
err = api.NewClient(string(ndfJSON), storeDir,
if userIDprefix != "" {
err = api.NewVanityClient(string(ndfJSON), storeDir,
[]byte(pass), regCode, userIDprefix)
} else {
err = api.NewClient(string(ndfJSON), storeDir,
[]byte(pass), regCode)
}
}
if err != nil {
......@@ -399,7 +405,7 @@ func acceptChannel(client *api.Client, recipientID *id.ID) {
if err != nil {
jww.FATAL.Panicf("%+v", err)
}
err = client.ConfirmAuthenticatedChannel(
_, err = client.ConfirmAuthenticatedChannel(
recipientContact)
if err != nil {
jww.FATAL.Panicf("%+v", err)
......@@ -461,7 +467,7 @@ func addAuthenticatedChannel(client *api.Client, recipientID *id.ID,
me := client.GetUser().GetContact()
jww.INFO.Printf("Requesting auth channel from: %s",
recipientID)
err := client.RequestAuthenticatedChannel(recipientContact,
_, err := client.RequestAuthenticatedChannel(recipientContact,
me, msg)
if err != nil {
jww.FATAL.Panicf("%+v", err)
......
......@@ -55,7 +55,7 @@ var singleCmd = &cobra.Command{
authMgr.AddGeneralRequestCallback(func(
requester contact.Contact, message string) {
jww.INFO.Printf("Got request: %s", requester.ID)
err := client.ConfirmAuthenticatedChannel(requester)
_, err := client.ConfirmAuthenticatedChannel(requester)
if err != nil {
jww.FATAL.Panicf("%+v", err)
}
......
......@@ -55,7 +55,7 @@ var udCmd = &cobra.Command{
authMgr.AddGeneralRequestCallback(func(
requester contact.Contact, message string) {
jww.INFO.Printf("Got Request: %s", requester.ID)
err := client.ConfirmAuthenticatedChannel(requester)
_, err := client.ConfirmAuthenticatedChannel(requester)
if err != nil {
jww.FATAL.Panicf("%+v", err)
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment