diff --git a/api/client.go b/api/client.go index 2e5934be9b4db2784476bd7a8d87a44b2132edcf..86cb1775ceb94108ff86385a0ca0e601a4588b85 100644 --- a/api/client.go +++ b/api/client.go @@ -14,10 +14,16 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/context" + "gitlab.com/elixxir/client/context/params" "gitlab.com/elixxir/client/context/stoppable" "gitlab.com/elixxir/client/context/switchboard" + "gitlab.com/elixxir/client/network" "gitlab.com/elixxir/client/storage" pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/crypto/csprng" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/crypto/large" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/crypto/tls" "gitlab.com/xx_network/primitives/id" @@ -36,32 +42,68 @@ type Client struct { // with the network. Note that this does not register a username/identity, but // merely creates a new cryptographic identity for adding such information // at a later date. -func NewClient(network, storageDir string, password []byte) (*Client, error) { +func NewClient(netJSON, storageDir string, password []byte) (*Client, error) { if clientStorageExists(storageDir) { return nil, errors.Errorf("client already exists at %s", storageDir) } + // 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 - //ndf, err := parseNDF(network) - //if err != nil { - // return nil, err - //} + ndf, err := parseNDF(netJSON) + if err != nil { + return nil, err + } + cmixGrp, e2eGrp := decodeGroups(ndf) + + user := createNewUser(rngStream, cmixGrp, e2eGrp) // Create Storage + passwordStr := string(password) + storageSess, err := storage.New(storageDir, passwordStr, + user.UID, user.Salt, user.RSAKey, user.IsPrecanned, + user.CMixKey, user.E2EKey, cmixGrp, e2eGrp, rngStreamGen) + if err != nil { + return nil, err + } - // Create network, context, switchboard + // Save NDF to be used in the future + err = storageSess.SetNDF(netJSON) + if err != nil { + return nil, err + } - // Generate Keys + // Set up a new context + ctx := &context.Context{ + Session: storageSess, + Switchboard: switchboard.New(), + Rng: rngStreamGen, + Manager: nil, + } - // Register with network + // Initialize network and link it to context + netman, err := network.NewManager(ctx, params.GetDefaultNetwork(), ndf) + if err != nil { + return nil, err + } + ctx.Manager = netman client := &Client{ - storage: nil, - ctx: nil, - switchboard: nil, - network: nil, + storage: storageSess, + ctx: ctx, + switchboard: ctx.Switchboard, + network: netman, + } + + // Now register with network, note that regCode is no longer required + err = client.RegisterWithPermissioning("") + if err != nil { + return nil, err } + return client, nil } @@ -72,16 +114,47 @@ func LoadClient(storageDir string, password []byte) (*Client, error) { storageDir) } + // Use fastRNG for RNG ops (AES fortuna based RNG using system RNG) + rngStreamGen := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG) + // Load Storage + passwordStr := string(password) + storageSess, err := storage.Load(storageDir, passwordStr, rngStreamGen) + if err != nil { + return nil, err + } + + netJSON, err := storageSess.GetNDF() + if err != nil { + return nil, err + } + ndf, err := parseNDF(string(netJSON)) + if err != nil { + return nil, err + } - // Load and create network, context, switchboard + // Set up a new context + ctx := &context.Context{ + Session: storageSess, + Switchboard: switchboard.New(), + Rng: rngStreamGen, + Manager: nil, + } + + // Initialize network and link it to context + netman, err := network.NewManager(ctx, params.GetDefaultNetwork(), ndf) + if err != nil { + return nil, err + } + ctx.Manager = netman client := &Client{ - storage: nil, - ctx: nil, - switchboard: nil, - network: nil, + storage: storageSess, + ctx: ctx, + switchboard: ctx.Switchboard, + network: netman, } + return client, nil } @@ -372,3 +445,19 @@ func parseNDF(ndfString string) (*ndf.NetworkDefinition, error) { return ndf, nil } + +// decodeGroups returns the e2e and cmix groups from the ndf +func decodeGroups(ndf *ndf.NetworkDefinition) (cmixGrp, e2eGrp *cyclic.Group) { + largeIntBits := 16 + + //Generate the cmix group + cmixGrp = cyclic.NewGroup( + large.NewIntFromString(ndf.CMIX.Prime, largeIntBits), + large.NewIntFromString(ndf.CMIX.Generator, largeIntBits)) + //Generate the e2e group + e2eGrp = cyclic.NewGroup( + large.NewIntFromString(ndf.E2E.Prime, largeIntBits), + large.NewIntFromString(ndf.E2E.Generator, largeIntBits)) + + return cmixGrp, e2eGrp +} diff --git a/api/user.go b/api/user.go new file mode 100644 index 0000000000000000000000000000000000000000..bc190327736e958984ce9ce2bf2ef383730d5bfb --- /dev/null +++ b/api/user.go @@ -0,0 +1,82 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2019 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +package api + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/csprng" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/xx" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" +) + +type user struct { + UID *id.ID + Salt []byte + RSAKey *rsa.PrivateKey + CMixKey *cyclic.Int + E2EKey *cyclic.Int + IsPrecanned bool +} + +const ( + // SaltSize size of user salts + SaltSize = 32 +) + +// createNewUser generates an identity for cMix +func createNewUser(rng csprng.Source, cmix, e2e *cyclic.Group) user { + // RSA Keygen (4096 bit defaults) + rsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen) + if err != nil { + jww.FATAL.Panicf(err.Error()) + } + + // 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()) + } + + // Salt, UID, etc gen + salt := make([]byte, SaltSize) + n, err := csprng.NewSystemRNG().Read(salt) + if err != nil { + jww.FATAL.Panicf(err.Error()) + } + if n != SaltSize { + jww.FATAL.Panicf("salt size too small: %d", n) + } + userID, err := xx.NewID(rsaKey.GetPublic(), salt, id.User) + if err != nil { + jww.FATAL.Panicf(err.Error()) + } + + return user{ + UID: userID, + Salt: salt, + RSAKey: rsaKey, + CMixKey: cmix.NewIntFromBytes(cMixKeyBytes), + E2EKey: e2e.NewIntFromBytes(e2eKeyBytes), + IsPrecanned: false, + } +} + +// TODO: Add precanned user code structures here. diff --git a/go.sum b/go.sum index 9a85c6d4f3ce3fe8b73412c9612227a9c57b1ad1..608d540ab0a7e9be922cde1dfd79a58e1ef4934b 100644 --- a/go.sum +++ b/go.sum @@ -254,6 +254,7 @@ gitlab.com/xx_network/comms v0.0.0-20200910173932-bd179f5fee4f h1:ExTCqEoro7VuS1 gitlab.com/xx_network/comms v0.0.0-20200910173932-bd179f5fee4f/go.mod h1:+jEkDQKoK51WLl2ZZuxfAZkz6YFbUQ+oZfH0dt2wIF0= gitlab.com/xx_network/comms v0.0.0-20200915154643-d533291041b7 h1:lPx1wpkjNpwLaZ0pyd7/iCcdITjT+eCMmb0HXCVoIkk= gitlab.com/xx_network/comms v0.0.0-20200915154643-d533291041b7/go.mod h1:+jEkDQKoK51WLl2ZZuxfAZkz6YFbUQ+oZfH0dt2wIF0= +gitlab.com/xx_network/crypto v0.0.0-20200805231039-4aa0e350ed0a h1:BlfWGPokU6yU69O+PGGsgc5iA/P9gERbHzYUvjoYbgM= gitlab.com/xx_network/crypto v0.0.0-20200806202113-978fa1984bbf/go.mod h1:i0df/q6dDCBiscgD51fMoS2U2TBrm6LcyN822JmB5Tw= gitlab.com/xx_network/crypto v0.0.0-20200806235322-ede3c15881ce h1:gypNBUl2guESEv4MDgH+miwYqR4jPoWM8dLt2Zs5gIs= gitlab.com/xx_network/crypto v0.0.0-20200806235322-ede3c15881ce/go.mod h1:i0df/q6dDCBiscgD51fMoS2U2TBrm6LcyN822JmB5Tw= diff --git a/storage/session.go b/storage/session.go index 3a9c8f51d77e39e5b825d1b3637b1e9869c76d41..5e093fff4052b995408417d1b78c8d22a1f741e4 100644 --- a/storage/session.go +++ b/storage/session.go @@ -19,13 +19,14 @@ import ( "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/large" "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/crypto/large" "gitlab.com/elixxir/ekv" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" "sync" "testing" + "time" ) // Number of rounds to store in the CheckedRound buffer @@ -213,6 +214,25 @@ func (s *Session) Partition() *partition.Store { return s.partition } +// SetNDF stores a network definition json file +func (s *Session) SetNDF(ndfJSON string) error { + return s.Set("NetworkDefinition", + &versioned.Object{ + Version: uint64(1), + Data: []byte(ndfJSON), + Timestamp: time.Now(), + }) +} + +// Returns the stored network definition json file +func (s *Session) GetNDF() (string, error) { + ndf, err := s.Get("NetworkDefinition") + if err != nil { + return "", err + } + return string(ndf.Data), nil +} + // Get an object from the session func (s *Session) Get(key string) (*versioned.Object, error) { return s.kv.Get(key)