Skip to content
Snippets Groups Projects
Commit 7f9ae590 authored by Richard T. Carback III's avatar Richard T. Carback III
Browse files

remove deprecated user code

parent 374d3ae5
No related branches found
No related tags found
No related merge requests found
package user
const (
NotStarted uint32 = iota // Set on session creation
KeyGenComplete = 1000 // Set upon generation of session information
PermissioningComplete = 2000 // Set upon completion of RegisterWithPermissioning
UDBComplete = 3000 // Set upon completion of RegisterWithUdb
)
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 Privategrity Corporation /
// /
// All rights reserved. /
////////////////////////////////////////////////////////////////////////////////
package user
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/gob"
"fmt"
"github.com/pkg/errors"
"gitlab.com/elixxir/client/globals"
"gitlab.com/elixxir/client/keyStore"
"gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/primitives/switchboard"
"gitlab.com/xx_network/primitives/id"
"io"
"sync"
"time"
)
// Errors
var ErrQuery = errors.New("element not in map")
// Interface for User Session operations
type Session interface {
StoreSession() error
Immolate() error
GetKeyStore() *keyStore.KeyStore
GetRekeyManager() *keyStore.RekeyManager
LockStorage()
UnlockStorage()
GetSessionData() ([]byte, error)
StorageIsEmpty() bool
GetSessionLocation() uint8
LoadEncryptedSession(store globals.Storage) ([]byte, error)
SetE2EGrp(g *cyclic.Group)
SetUser(u *id.ID)
}
type NodeKeys struct {
TransmissionKey *cyclic.Int
ReceptionKey *cyclic.Int
}
// Creates a new Session interface for registration
func NewSession(store globals.Storage,
password string) Session {
// With an underlying Session data structure
return Session(&SessionObj{
KeyMaps: keyStore.NewStore(),
RekeyManager: keyStore.NewRekeyManager(),
store: store,
password: password,
storageLocation: globals.LocationA,
})
}
//LoadSession loads the encrypted session from the storage location and processes it
// Returns a session object on success
func LoadSession(store globals.Storage, password string) (Session, error) {
if store == nil {
err := errors.New("LoadSession: Local Storage not available")
return nil, err
}
wrappedSession, loadLocation, err := processSession(store, password)
if err != nil {
return nil, err
}
for wrappedSession.Version != SessionVersion {
switch wrappedSession.Version {
case 1:
globals.Log.INFO.Println("Converting session file from V1 to V2")
wrappedSession, err = ConvertSessionV1toV2(wrappedSession)
default:
}
if err != nil {
return nil, err
}
}
//extract the session from the wrapper
var sessionBytes bytes.Buffer
sessionBytes.Write(wrappedSession.Session)
dec := gob.NewDecoder(&sessionBytes)
session := SessionObj{}
err = dec.Decode(&session)
if err != nil {
return nil, errors.Wrap(err, "Unable to decode session")
}
session.storageLocation = loadLocation
// Reconstruct Key maps
session.KeyMaps.ReconstructKeys(session.E2EGrp,
session.CurrentUser)
// Create switchboard
//session.listeners = switchboard.New()
// Create quit channel for reception runner
//session.quitReceptionRunner = make(chan struct{})
// Set storage pointer
session.store = store
session.password = password
return &session, nil
}
//processSession: gets the loadLocation and decrypted wrappedSession
func processSession(store globals.Storage, password string) (*SessionStorageWrapper, uint8, error) {
var wrappedSession *SessionStorageWrapper
loadLocation := globals.NoSave
//load sessions
wrappedSessionA, errA := processSessionWrapper(store.LoadA(), password)
wrappedSessionB, errB := processSessionWrapper(store.LoadB(), password)
//figure out which session to use of the two locations
if errA != nil && errB != nil {
return nil, globals.NoSave, errors.Errorf("Loading both sessions errored: \n "+
"SESSION A ERR: %s \n SESSION B ERR: %s", errA, errB)
} else if errA == nil && errB != nil {
loadLocation = globals.LocationA
wrappedSession = wrappedSessionA
} else if errA != nil && errB == nil {
loadLocation = globals.LocationB
wrappedSession = wrappedSessionB
} else {
if wrappedSessionA.Timestamp.After(wrappedSessionB.Timestamp) {
loadLocation = globals.LocationA
wrappedSession = wrappedSessionA
} else {
loadLocation = globals.LocationB
wrappedSession = wrappedSessionB
}
}
return wrappedSession, loadLocation, nil
}
//processSessionWrapper acts as a helper function for processSession
func processSessionWrapper(sessionGob []byte, password string) (*SessionStorageWrapper, error) {
if sessionGob == nil || len(sessionGob) < 12 {
return nil, errors.New("No session file passed")
}
decryptedSessionGob, err := decrypt(sessionGob, password)
if err != nil {
return nil, errors.Wrap(err, "Could not decode the "+
"session wrapper")
}
var sessionBytes bytes.Buffer
sessionBytes.Write(decryptedSessionGob)
dec := gob.NewDecoder(&sessionBytes)
wrappedSession := SessionStorageWrapper{}
err = dec.Decode(&wrappedSession)
if err != nil {
return nil, errors.Wrap(err, "Unable to decode session wrapper")
}
return &wrappedSession, nil
}
// Struct holding relevant session data
// When adding to this structure, ALWAYS ALWAYS
// consider if you want the data to be in the session file
type SessionObj struct {
// E2E KeyStore
KeyMaps *keyStore.KeyStore
// do not touch until removing session, neeeded for keystores
E2EGrp *cyclic.Group
CurrentUser *id.ID
// Rekey Manager
RekeyManager *keyStore.RekeyManager
// Non exported fields (not GOB encoded/decoded)
// Local pointer to storage of this session
store globals.Storage
lock sync.Mutex
// The password used to encrypt this session when saved
password string
storageLocation uint8
}
//WriteToSession: Writes to the location where session is being stored the arbitrary replacement string
// The replacement string is meant to be the output of a loadEncryptedSession
func WriteToSession(replacement []byte, store globals.Storage) error {
//Write to both
err := store.SaveA(replacement)
if err != nil {
return errors.Errorf("Failed to save to session A: %v", err)
}
err = store.SaveB(replacement)
if err != nil {
return errors.Errorf("Failed to save to session B: %v", err)
}
return nil
}
func (s *SessionObj) SetE2EGrp(g *cyclic.Group) {
s.E2EGrp = g
}
func (s *SessionObj) SetUser(u *id.ID) {
s.CurrentUser = u
}
//LoadEncryptedSession: gets the encrypted session file from storage
// Returns it as a base64 encoded string
func (s *SessionObj) LoadEncryptedSession(store globals.Storage) ([]byte, error) {
sessionData, _, err := processSession(store, s.password)
if err != nil {
return make([]byte, 0), err
}
encryptedSession := encrypt(sessionData.Session, s.password)
return encryptedSession, nil
}
type SearchedUserRecord struct {
Id id.ID
Pk []byte
}
func (s *SessionObj) StorageIsEmpty() bool {
s.LockStorage()
defer s.UnlockStorage()
return s.store.IsEmpty()
}
type SessionStorageWrapper struct {
Version uint32
Timestamp time.Time
Session []byte
}
func (s *SessionObj) storeSession() error {
if s.store == nil {
err := errors.New("StoreSession: Local Storage not available")
return err
}
sessionData, err := s.getSessionData()
encryptedSession := encrypt(sessionData, s.password)
if s.storageLocation == globals.LocationA {
err = s.store.SaveB(encryptedSession)
if err != nil {
err = errors.New(fmt.Sprintf("StoreSession: Could not save the encoded user"+
" session in location B: %s", err.Error()))
} else {
s.storageLocation = globals.LocationB
}
} else if s.storageLocation == globals.LocationB {
err = s.store.SaveA(encryptedSession)
if err != nil {
err = errors.New(fmt.Sprintf("StoreSession: Could not save the encoded user"+
" session in location A: %s", err.Error()))
} else {
s.storageLocation = globals.LocationA
}
} else {
err = errors.New("Could not store because no location is " +
"selected")
}
return err
}
func (s *SessionObj) StoreSession() error {
s.LockStorage()
err := s.storeSession()
s.UnlockStorage()
return err
}
// Immolate scrubs all cryptographic data from ram and logs out
// the ram overwriting can be improved
func (s *SessionObj) Immolate() error {
s.LockStorage()
if s == nil {
err := errors.New("immolate: Cannot immolate that which has no life")
return err
}
globals.Log.WARN.Println("Immolate not implemented, did nothing")
s.UnlockStorage()
return nil
}
func (s *SessionObj) GetSessionData() ([]byte, error) {
s.LockStorage()
defer s.UnlockStorage()
return s.getSessionData()
}
func (s *SessionObj) GetKeyStore() *keyStore.KeyStore {
return s.KeyMaps
}
func (s *SessionObj) GetRekeyManager() *keyStore.RekeyManager {
return s.RekeyManager
}
func (s *SessionObj) GetSwitchboard() *switchboard.Switchboard {
return nil //s.listeners
}
func (s *SessionObj) GetQuitChan() chan struct{} {
return nil //s.quitReceptionRunner
}
func (s *SessionObj) getSessionData() ([]byte, error) {
var sessionBuffer bytes.Buffer
enc := gob.NewEncoder(&sessionBuffer)
err := enc.Encode(s)
if err != nil {
err = errors.New(fmt.Sprintf("StoreSession: Could not encode user"+
" session: %s", err.Error()))
return nil, err
}
sw := SessionStorageWrapper{
Version: SessionVersion,
Session: sessionBuffer.Bytes(),
Timestamp: time.Now(),
}
var wrapperBuffer bytes.Buffer
enc = gob.NewEncoder(&wrapperBuffer)
err = enc.Encode(&sw)
if err != nil {
err = errors.New(fmt.Sprintf("StoreSession: Could not encode user"+
" session wrapper: %s", err.Error()))
return nil, err
}
return wrapperBuffer.Bytes(), nil
}
// Locking a mutex that belongs to the session object makes the locking
// independent of the implementation of the storage, which is probably good.
func (s *SessionObj) LockStorage() {
s.lock.Lock()
}
func (s *SessionObj) UnlockStorage() {
s.lock.Unlock()
}
func clearCyclicInt(c *cyclic.Int) {
c.Reset()
//c.Set(cyclic.NewMaxInt())
//c.SetInt64(0)
}
// FIXME Shouldn't we just be putting pseudorandom bytes in to obscure the mem?
func burntString(length int) string {
b := make([]byte, length)
rand.Read(b)
return string(b)
}
// Internal crypto helper functions below
func hashPassword(password string) []byte {
hasher := sha256.New()
hasher.Write([]byte(password))
return hasher.Sum(nil)
}
func initAESGCM(password string) cipher.AEAD {
aesCipher, _ := aes.NewCipher(hashPassword(password))
// NOTE: We use gcm as it's authenticated and simplest to set up
aesGCM, err := cipher.NewGCM(aesCipher)
if err != nil {
globals.Log.FATAL.Panicf("Could not init AES GCM mode: %s",
err.Error())
}
return aesGCM
}
func encrypt(data []byte, password string) []byte {
aesGCM := initAESGCM(password)
nonce := make([]byte, aesGCM.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
globals.Log.FATAL.Panicf("Could not generate nonce: %s",
err.Error())
}
ciphertext := aesGCM.Seal(nonce, nonce, data, nil)
return ciphertext
}
func decrypt(data []byte, password string) ([]byte, error) {
aesGCM := initAESGCM(password)
nonceLen := aesGCM.NonceSize()
nonce, ciphertext := data[:nonceLen], data[nonceLen:]
plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, errors.Wrap(err, "Cannot decrypt with password!")
}
return plaintext, nil
}
func (s *SessionObj) GetSessionLocation() uint8 {
if s.storageLocation == globals.LocationA {
return globals.LocationA
} else if s.storageLocation == globals.LocationB {
return globals.LocationB
}
return globals.NoSave
}
package user
const SessionVersion = 2
////////////////////////////////////////////////////////////////////////////////
// Copyright © 2019 Privategrity Corporation /
// /
// All rights reserved. /
////////////////////////////////////////////////////////////////////////////////
package user
import (
"crypto/sha256"
"gitlab.com/elixxir/client/globals"
"gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/large"
"math/rand"
"testing"
)
// TestUserRegistry tests the constructors/getters/setters
// surrounding the User struct and the Registry interface
func TestUserSession(t *testing.T) {
pass := 0
// Storage
storage := &globals.RamStorage{}
rng := rand.New(rand.NewSource(42))
ses := NewSession(storage, "password")
regSignature := make([]byte, 768)
rng.Read(regSignature)
err := ses.StoreSession()
if err != nil {
t.Errorf("Session not stored correctly: %s", err.Error())
}
ses.Immolate()
//TODO: write test which validates the immolation
ses, err = LoadSession(storage, "password")
if err != nil {
t.Errorf("Unable to login with valid user: %v",
err.Error())
} else {
pass++
}
//Logout
ses.Immolate()
// Error tests
// Test nil LocalStorage
_, err = LoadSession(nil, "password")
if err == nil {
t.Errorf("Error did not catch a nil LocalStorage")
}
// Test invalid / corrupted LocalStorage
h := sha256.New()
h.Write([]byte(string(20000)))
randBytes := h.Sum(nil)
storage.SaveA(randBytes)
storage.SaveB(randBytes)
defer func() {
recover()
}()
_, err = LoadSession(storage, "password")
if err == nil {
t.Errorf("LoadSession should error on bad decrypt!")
}
}
//Tests the isEmpty function before and after StoreSession
func TestSessionObj_StorageIsEmpty(t *testing.T) {
// Storage
storage := &globals.RamStorage{}
//Keys
rng := rand.New(rand.NewSource(42))
ses := NewSession(storage, "password")
regSignature := make([]byte, 768)
rng.Read(regSignature)
//Test that the session is empty before the StoreSession call
if !ses.StorageIsEmpty() {
t.Errorf("session should be empty before the StoreSession call")
}
err := ses.StoreSession()
if err != nil {
t.Errorf("Failed to store session: %v", err)
}
//Test that the session is not empty after the StoreSession call
if ses.StorageIsEmpty() {
t.Errorf("session should not be empty after a StoreSession call")
}
}
func getGroups() (*cyclic.Group, *cyclic.Group) {
cmixGrp := cyclic.NewGroup(
large.NewIntFromString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"+
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD"+
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"+
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"+
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"+
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"+
"83655D23DCA3AD961C62F356208552BB9ED529077096966D"+
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"+
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"+
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510"+
"15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16),
large.NewIntFromString("2", 16))
e2eGrp := cyclic.NewGroup(
large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B"+
"7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE"+
"DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F"+
"8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041"+
"023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45"+
"3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209"+
"6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29"+
"A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E"+
"37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2"+
"78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696"+
"015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E"+
"6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873"+
"847AEF49F66E43873", 16),
large.NewIntFromString("2", 16))
return cmixGrp, e2eGrp
}
package user
import (
"bytes"
"encoding/gob"
"fmt"
"github.com/pkg/errors"
"gitlab.com/elixxir/client/globals"
"gitlab.com/elixxir/client/keyStore"
"gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/primitives/format"
"gitlab.com/elixxir/primitives/switchboard"
"gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id"
"sync"
)
// Struct holding relevant session data
type SessionObjV1 struct {
// Currently authenticated user
CurrentUser *UserV1
Keys map[id.ID]NodeKeys
RSAPrivateKey *rsa.PrivateKey
RSAPublicKey *rsa.PublicKey
CMIXDHPrivateKey *cyclic.Int
CMIXDHPublicKey *cyclic.Int
E2EDHPrivateKey *cyclic.Int
E2EDHPublicKey *cyclic.Int
CmixGrp *cyclic.Group
E2EGrp *cyclic.Group
Salt []byte
// Last received message ID. Check messages after this on the gateway.
LastMessageID string
//Interface map for random data storage
InterfaceMap map[string]interface{}
// E2E KeyStore
KeyMaps *keyStore.KeyStore
// Rekey Manager
RekeyManager *keyStore.RekeyManager
// Non exported fields (not GOB encoded/decoded)
// Local pointer to storage of this session
store globals.Storage
// Switchboard
listeners *switchboard.Switchboard
// Quit channel for message reception runner
quitReceptionRunner chan struct{}
lock sync.Mutex
// The password used to encrypt this session when saved
password string
//The validation signature provided by permissioning
regValidationSignature []byte
// Buffer of messages that cannot be decrypted
garbledMessages []*format.Message
RegState *uint32
storageLocation uint8
ContactsByValue map[string]SearchedUserRecord
}
// Struct representing a User in the system
type UserV1 struct {
User *id.ID
Nick string
Email string
}
// ConvertSessionV1toV2 converts the session object from version 1 to version 2.
// This conversion includes:
// 1. Changing the RegState values to the new integer values (1 to 2000, and 2
// to 3000).
func ConvertSessionV1toV2(inputWrappedSession *SessionStorageWrapper) (*SessionStorageWrapper, error) {
//extract teh session from the wrapper
var sessionBytes bytes.Buffer
//get the old session object
sessionBytes.Write(inputWrappedSession.Session)
dec := gob.NewDecoder(&sessionBytes)
sessionV1 := SessionObjV1{}
err := dec.Decode(&sessionV1)
if err != nil {
return nil, errors.Wrap(err, "Unable to decode session")
}
sessionV2 := SessionObj{}
// Convert RegState to new values
if *sessionV1.RegState == 1 {
*sessionV1.RegState = 2000
} else if *sessionV1.RegState == 2 {
*sessionV1.RegState = 3000
}
sessionV2.KeyMaps = sessionV1.KeyMaps
sessionV2.RekeyManager = sessionV1.RekeyManager
//re encode the session
var sessionBuffer bytes.Buffer
enc := gob.NewEncoder(&sessionBuffer)
err = enc.Encode(sessionV2)
if err != nil {
err = errors.New(fmt.Sprintf("ConvertSessionV1toV2: Could not "+
" store session v2: %s", err.Error()))
return nil, err
}
//build the session wrapper
ssw := SessionStorageWrapper{
Version: 2,
Timestamp: inputWrappedSession.Timestamp,
Session: sessionBuffer.Bytes(),
}
return &ssw, nil
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment