This commit is contained in:
Dan Ballard 2018-08-28 10:48:57 -05:00
parent a1d37c3d14
commit f94c235b23
4 changed files with 65 additions and 50 deletions

View File

@ -29,9 +29,9 @@ type application struct {
// Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
type Application interface {
InitProfiles(password string) error
InitDeniableProfiles(password string) error
CreatePeer(name string) peer.CwtchPeer
InitProfiles(password string) (int, error)
InitDeniableProfiles(password string) (int, error)
CreatePeer(name string) (peer.CwtchPeer, error)
GetPeer(onion string) peer.CwtchPeer
ListPeers() map[string]string
@ -77,50 +77,57 @@ func (app *application) startTor(torPath string) error {
return nil
}
// InitProfiles will attempt to load the normal portion of profile storage with the supplied password
func (app *application) InitProfiles(password string) error {
// InitProfiles will attempt to load the normal portion of profile storage with the supplied password, returning number of profiles loaded
func (app *application) InitProfiles(password string) (int, error) {
profiles, err := app.profileStore.InitializeProfileGroup(storage.GroupMaster, password)
if err != nil {
return err
return 0, nil
}
app.loadProfiles(profiles)
return nil
n := app.loadProfiles(profiles)
return n, nil
}
// InitDeniableProfiles will attempt to load the deniable portion of profile storage with the supplied password
func (app *application) InitDeniableProfiles(password string) error {
// InitDeniableProfiles will attempt to load the deniable portion of profile storage with the supplied password, returning number of profiles loaded
func (app *application) InitDeniableProfiles(password string) (int, error) {
profiles, err := app.profileStore.InitializeProfileGroup(storage.GroupDeniable1, password)
if err != nil {
return err
return 0, err
}
app.loadProfiles(profiles)
return nil
n := app.loadProfiles(profiles)
return n, nil
}
func (app *application) loadProfiles(profiles []*model.Profile) {
func (app *application) loadProfiles(profiles []*model.Profile) int {
app.mutex.Lock()
count := 0
for _, profile := range profiles {
fmt.Printf("init from profile: %v\n", profile.Name)
peer := peer.InitFromProfile(profile)
app.peers[peer.GetProfile().Onion] = peer
app.startPeer(peer)
count++
}
app.mutex.Unlock()
return count
}
// NewProfile creates a new cwtchPeer with a given name.
func (app *application) CreatePeer(name string) peer.CwtchPeer {
func (app *application) CreatePeer(name string) (peer.CwtchPeer, error) {
app.mutex.Lock()
p := peer.NewCwtchPeer(name)
app.profileStore.AddProfile(storage.GroupMaster, p.GetProfile())
err := app.profileStore.AddProfile(storage.GroupMaster, p.GetProfile())
if err != nil {
log.Printf("Could not add profile to profile store: %v", err)
return nil, err
}
app.peers[p.GetProfile().Onion] = p
app.startPeer(p)
return p
return p, nil
}
func (app *application) startPeer(peer peer.CwtchPeer) {

View File

@ -278,18 +278,21 @@ func main() {
quit = true
case "new-profile":
if len(commands) == 2 {
p := app.CreatePeer(commands[1])
peer = p
p, err := app.CreatePeer(commands[1])
if err != nil {
fmt.Printf("Error creating profile: %v\n", err)
} else {
peer = p
}
} else {
fmt.Printf("Error creating NewProfile, usage: %s\n", usages["new-profile"])
}
case "load-profiles":
fmt.Print("Enter a password to decrypt the profiles: ")
bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
err = app.InitProfiles(string(bytePassword))
n, err := app.InitProfiles(string(bytePassword))
if err == nil {
profiles := app.ListPeers()
fmt.Printf("\n%v profiles active now\n", len(profiles))
fmt.Printf("\n%v profiles active now\n", n)
fmt.Printf("You should run `select-profile` to use a profile\n")
} else {
fmt.Printf("\nError loading profiles: %v\n", err)

View File

@ -10,7 +10,6 @@ import (
"golang.org/x/crypto/sha3"
"io"
"log"
mrand "math/rand"
"os"
"sync"
)
@ -57,10 +56,6 @@ const (
NumProfileBlocks = 32 // means each small store is about 2.2 MB, medium is 10 MB, and large is 40 MB
)
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
)
// Usable names for profileStore divisions
const (
GroupMaster ProfileGroupID = iota
@ -93,7 +88,7 @@ type profileStore struct {
// it should offer no way to prove that those divisions are or are not in use
type ProfileStore interface {
InitializeProfileGroup(groupid ProfileGroupID, password string) ([]*model.Profile, error)
AddProfile(groupid ProfileGroupID, profile *model.Profile)
AddProfile(groupid ProfileGroupID, profile *model.Profile) error
}
// NewProfileStore creates a new storage class and file for storing profile data
@ -119,14 +114,6 @@ func NewProfileStore(filepath string, blocksize int, numBlocks int, numGroups in
return ps
}
func randStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[mrand.Intn(len(letterBytes))]
}
return string(b)
}
// generateFile creates a storage backing for a profileStore in a file
// format is [rand*blockSize]*numBlocks
// which yeilds a file of numBlocks blocks of initially random data of blockSize
@ -134,23 +121,26 @@ func randStringBytes(n int) string {
// (the fact the file exists indicates at least 1 profile, but no way beyond that to determine exactly how many)
func (ps *profileStore) generateFile() error {
ps.mutex.Lock()
defer ps.mutex.Unlock()
file, err := os.OpenFile(ps.filename, os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
log.Printf("Error opening profile file for writting: %v", err)
ps.mutex.Unlock()
return err
}
// Generate and write NumProfileBlocks blocks
for i := 0; i < ps.numBlocks; i++ {
padding := make([]byte, ps.fullBlocksize)
rand.Read(padding)
_, err := rand.Read(padding)
if err != nil {
log.Printf("Error: could not read from Rand to fill profile padding: %v", err)
return err
}
file.Write(padding[:])
}
file.Close()
ps.mutex.Unlock()
return nil
}
@ -162,6 +152,7 @@ func (ps *profileStore) InitializeProfileGroup(groupid ProfileGroupID, password
pg := &ps.profileGroups[groupid]
if len(pg.profiles) != 0 {
ps.mutex.Unlock()
return nil, errors.New("ProfileGroup already has loaded profiles, cannot reinitialize")
}
@ -207,6 +198,7 @@ func (ps *profileStore) attemptLoad(groupid ProfileGroupID) error {
log.Printf("Error opening profile store file: %v", err)
return err
}
defer file.Close()
file.Seek(int64(ps.fullBlocksize*(int(groupid)*(ps.numBlocks/ps.numGroups))), 0)
for i := 0; i < ps.numBlocks/ps.numGroups; i++ {
@ -214,7 +206,6 @@ func (ps *profileStore) attemptLoad(groupid ProfileGroupID) error {
_, err := file.Read(encryptedBytes[:])
if err != nil {
log.Printf("Error reading from profile store file: %v", err)
file.Close()
return err
}
profile := model.AttemptLoadProfile(encryptedBytes[:], ps.profileGroups[groupid].password, ps.getSaveFn(groupid, i))
@ -225,30 +216,35 @@ func (ps *profileStore) attemptLoad(groupid ProfileGroupID) error {
ps.profileGroups[groupid].profiles = append(ps.profileGroups[groupid].profiles, profile)
}
file.Close()
return nil
}
// createKey derives a key and salt for use in encrypting cwtchPeers
func createKey(password string) ([32]byte, [128]byte) {
func createKey(password string) ([32]byte, [128]byte, error) {
var salt [128]byte
var dkr [32]byte
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
panic(err)
fmt.Printf("Error: Could not read Rand for salt: %v", err)
return dkr, salt, err
}
dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512)
var dkr [32]byte
copy(dkr[:], dk)
return dkr, salt
return dkr, salt, nil
}
// AddProfile adds a profile to a group, and wires it in to be Save()able: gives it a key, salt and save function
// then it is saved
func (ps *profileStore) AddProfile(groupid ProfileGroupID, profile *model.Profile) {
func (ps *profileStore) AddProfile(groupid ProfileGroupID, profile *model.Profile) error {
ps.mutex.Lock()
key, salt := createKey(ps.profileGroups[groupid].password)
key, salt, err := createKey(ps.profileGroups[groupid].password)
if err != nil {
return err
}
profile.OnProfileStoreAdd(key, salt, ps.getSaveFn(groupid, len(ps.profileGroups[groupid].profiles)))
ps.profileGroups[groupid].profiles = append(ps.profileGroups[groupid].profiles, profile)
ps.mutex.Unlock()
profile.Save()
return nil
}

View File

@ -49,7 +49,10 @@ func TestProfileStore(t *testing.T) {
t.Error("Expected error on trying to save profile before added to ProfileStore")
}
ps.AddProfile(GroupMaster, alice)
err = ps.AddProfile(GroupMaster, alice)
if err != nil {
t.Errorf("Error adding profile to profile store: %v", err)
}
// Redundtant, but testing path
err = alice.Save()
@ -60,11 +63,17 @@ func TestProfileStore(t *testing.T) {
statFileTest(t)
carol := model.GenerateNewProfile("Carol")
ps.AddProfile(GroupMaster, carol)
err = ps.AddProfile(GroupMaster, carol)
if err != nil {
t.Errorf("Error adding profile to profile store: %v", err)
}
ps.InitializeProfileGroup(GroupDeniable1, DeniablePassword)
secretBob := model.GenerateNewProfile("Secret Bob")
ps.AddProfile(GroupDeniable1, secretBob)
err = ps.AddProfile(GroupDeniable1, secretBob)
if err != nil {
t.Errorf("Error adding profile to profile store: %v", err)
}
// Confirm loading works
psL := NewProfileStore(ProfilesFile, DataBlockSizeSmall, 6, 2)