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

View File

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

View File

@ -10,7 +10,6 @@ import (
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
"io" "io"
"log" "log"
mrand "math/rand"
"os" "os"
"sync" "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 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 // Usable names for profileStore divisions
const ( const (
GroupMaster ProfileGroupID = iota 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 // it should offer no way to prove that those divisions are or are not in use
type ProfileStore interface { type ProfileStore interface {
InitializeProfileGroup(groupid ProfileGroupID, password string) ([]*model.Profile, error) 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 // 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 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 // generateFile creates a storage backing for a profileStore in a file
// format is [rand*blockSize]*numBlocks // format is [rand*blockSize]*numBlocks
// which yeilds a file of numBlocks blocks of initially random data of blockSize // 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) // (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 { func (ps *profileStore) generateFile() error {
ps.mutex.Lock() ps.mutex.Lock()
defer ps.mutex.Unlock()
file, err := os.OpenFile(ps.filename, os.O_CREATE|os.O_WRONLY, 0600) file, err := os.OpenFile(ps.filename, os.O_CREATE|os.O_WRONLY, 0600)
if err != nil { if err != nil {
log.Printf("Error opening profile file for writting: %v", err) log.Printf("Error opening profile file for writting: %v", err)
ps.mutex.Unlock()
return err return err
} }
// Generate and write NumProfileBlocks blocks // Generate and write NumProfileBlocks blocks
for i := 0; i < ps.numBlocks; i++ { for i := 0; i < ps.numBlocks; i++ {
padding := make([]byte, ps.fullBlocksize) 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.Write(padding[:])
} }
file.Close() file.Close()
ps.mutex.Unlock()
return nil return nil
} }
@ -162,6 +152,7 @@ func (ps *profileStore) InitializeProfileGroup(groupid ProfileGroupID, password
pg := &ps.profileGroups[groupid] pg := &ps.profileGroups[groupid]
if len(pg.profiles) != 0 { if len(pg.profiles) != 0 {
ps.mutex.Unlock()
return nil, errors.New("ProfileGroup already has loaded profiles, cannot reinitialize") 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) log.Printf("Error opening profile store file: %v", err)
return err return err
} }
defer file.Close()
file.Seek(int64(ps.fullBlocksize*(int(groupid)*(ps.numBlocks/ps.numGroups))), 0) file.Seek(int64(ps.fullBlocksize*(int(groupid)*(ps.numBlocks/ps.numGroups))), 0)
for i := 0; i < ps.numBlocks/ps.numGroups; i++ { for i := 0; i < ps.numBlocks/ps.numGroups; i++ {
@ -214,7 +206,6 @@ func (ps *profileStore) attemptLoad(groupid ProfileGroupID) error {
_, err := file.Read(encryptedBytes[:]) _, err := file.Read(encryptedBytes[:])
if err != nil { if err != nil {
log.Printf("Error reading from profile store file: %v", err) log.Printf("Error reading from profile store file: %v", err)
file.Close()
return err return err
} }
profile := model.AttemptLoadProfile(encryptedBytes[:], ps.profileGroups[groupid].password, ps.getSaveFn(groupid, i)) 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) ps.profileGroups[groupid].profiles = append(ps.profileGroups[groupid].profiles, profile)
} }
file.Close()
return nil return nil
} }
// createKey derives a key and salt for use in encrypting cwtchPeers // 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 salt [128]byte
var dkr [32]byte
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil { 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) dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512)
var dkr [32]byte
copy(dkr[:], dk) 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 // 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 // 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() 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))) profile.OnProfileStoreAdd(key, salt, ps.getSaveFn(groupid, len(ps.profileGroups[groupid].profiles)))
ps.profileGroups[groupid].profiles = append(ps.profileGroups[groupid].profiles, profile) ps.profileGroups[groupid].profiles = append(ps.profileGroups[groupid].profiles, profile)
ps.mutex.Unlock() ps.mutex.Unlock()
profile.Save() 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") 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 // Redundtant, but testing path
err = alice.Save() err = alice.Save()
@ -60,11 +63,17 @@ func TestProfileStore(t *testing.T) {
statFileTest(t) statFileTest(t)
carol := model.GenerateNewProfile("Carol") 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) ps.InitializeProfileGroup(GroupDeniable1, DeniablePassword)
secretBob := model.GenerateNewProfile("Secret Bob") 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 // Confirm loading works
psL := NewProfileStore(ProfilesFile, DataBlockSizeSmall, 6, 2) psL := NewProfileStore(ProfilesFile, DataBlockSizeSmall, 6, 2)