From 13b5d17214cf615de0205b084a9a6c1bf80a56d0 Mon Sep 17 00:00:00 2001 From: Angus Champion de Crespigny Date: Sat, 23 Jun 2018 19:39:15 -0400 Subject: [PATCH] On branch profile-password Changes to be committed: modified: app/app.go Added password parameters to app.go functions modified: app/cli/main.go Added password prompts to collect the password from user input Modified Load and New conditions modified: peer/cwtch_peer.go Created CreateKey, DecryptProfile, and EncryptProfile functions Implemented these functions in the functions CwtchNewProfile CwtchLoadProfile Save --- app/app.go | 9 +++-- app/cli/main.go | 89 +++++++++++++++++++++++++++--------------- peer/cwtch_peer.go | 97 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 146 insertions(+), 49 deletions(-) diff --git a/app/app.go b/app/app.go index b4ab679..709e7f7 100644 --- a/app/app.go +++ b/app/app.go @@ -12,8 +12,8 @@ type Application struct { } // NewProfile creates a new cwtchPeer with a given name. -func (app *Application) NewProfile(name string, filename string) error { - profile := peer.NewCwtchPeer(name) +func (app *Application) NewProfile(name string, filename string, password string) error { + profile := peer.NewCwtchPeer(name, password) app.Peer = profile err := profile.Save(filename) if err == nil { @@ -35,7 +35,10 @@ func (app *Application) NewProfile(name string, filename string) error { // SetProfile loads an existing profile from the given filename. func (app *Application) SetProfile(filename string) error { - profile, err := peer.LoadCwtchPeer(filename) + profile, err := peer.LoadCwtchPeer(filename, password) + if err != nil { + return err + } app.Peer = profile if err == nil { diff --git a/app/cli/main.go b/app/cli/main.go index c600dec..ec9e7ac 100644 --- a/app/cli/main.go +++ b/app/cli/main.go @@ -121,28 +121,28 @@ func main() { cwtch := ` - #, #' - @@@@@@: - @@@@@@. - @'@@+#' @@@@+ - ''''''@ #+@ : - @''''+;+' . ' - @''@' :+' , ; ##, +' - ,@@ ;' #'#@''. #''@''# - # ''''''#:,,#'''''@ - : @''''@ :+'''@ - ' @;+'@ @'# - .:# '#..# '# @ - @@@@@@ - @@@@@@ - '@@@@ - @# . . - +++, #'@+'@ - ''', ''''''# - .#+# ''', @'''+, - @''# ''', .#@ - :; '@''# .;. ''', ' : ;. , - @+'''@ '+'+ @++ @+'@+''''+@ #+'''#: ''';#''+@ @@@@ @@@@@@@@@ :@@@@# + #, #' + @@@@@@: + @@@@@@. + @'@@+#' @@@@+ + ''''''@ #+@ : + @''''+;+' . ' + @''@' :+' , ; ##, +' + ,@@ ;' #'#@''. #''@''# + # ''''''#:,,#'''''@ + : @''''@ :+'''@ + ' @;+'@ @'# + .:# '#..# '# @ + @@@@@@ + @@@@@@ + '@@@@ + @# . . + +++, #'@+'@ + ''', ''''''# + .#+# ''', @'''+, + @''# ''', .#@ + :; '@''# .;. ''', ' : ;. , + @+'''@ '+'+ @++ @+'@+''''+@ #+'''#: ''';#''+@ @@@@ @@@@@@@@@ :@@@@# #''''''# +''. +'': +'''''''''+ @'''''''# '''+'''''@ @@@@ @@@@@@@@@@@@@@@@: @'''@@'''@ @''# ,'''@ ''+ @@''+#+ :'''@@+''' ''''@@'''' @@@@ @@@@@@@@@@@@@@@@@ '''# @''# +''@ @'''# ;''@ +''+ @''@ ,+'', '''@ #'''. @@@@ @@@@ '@@@# @@@@ @@ -180,22 +180,49 @@ func main() { quit = true case "new-profile": if len(commands) == 3 { - err := app.NewProfile(commands[1], commands[2]) - profilefile = commands[2] - if err == nil { - fmt.Printf("New profile created for %v\n", commands[1]) - } else { - fmt.Printf("Error creating profile for %v: %v\n", commands[1], err) + fmt.Print("** WARNING: PASSWORDS CANNOT BE RECOVERED! **\n") + + password := "" + failcount := 0; + for ; failcount < 3; failcount++ { + fmt.Print("Enter a password to encrypt the profile: ") + bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin)) + if string(bytePassword) == "" { + fmt.Print("\nBlank password not allowed.") + continue + } + fmt.Print("\nRe-enter password: ") + bytePassword2, _ := terminal.ReadPassword(int(syscall.Stdin)) + if(bytes.Equal(bytePassword, bytePassword2)){ + password = string(bytePassword) + break + }else{ + fmt.Print("\nPASSWORDS DIDN'T MATCH! Try again.\n") + } } + + if(failcount >= 3){ + fmt.Printf("Error creating profile for %v: Your password entries must match!\n", commands[1]) + }else{ + err := app.NewProfile(commands[1], commands[2], password) + profilefile = commands[2] + if err == nil { + fmt.Printf("\nNew profile created for %v\n", commands[1]) + } else { + fmt.Printf("\nError creating profile for %v: %v\n", commands[1], err) + } + } } else { fmt.Printf("Error creating NewProfile, usage: %s\n", usages["new-profile"]) } case "load-profile": if len(commands) == 2 { - err := app.SetProfile(commands[1]) - profilefile = commands[1] + fmt.Print("Enter a password to decrypt the profile: ") + bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) + err = app.SetProfile(commands[1], string(bytePassword)) if err == nil { - fmt.Printf("Loaded profile for %v\n", commands[1]) + fmt.Printf("\nLoaded profile for %v\n", commands[1]) + profilefile = commands[1] } else { fmt.Printf("Error loading profile for %v: %v\n", commands[1], err) } diff --git a/peer/cwtch_peer.go b/peer/cwtch_peer.go index f945442..56e9aa4 100644 --- a/peer/cwtch_peer.go +++ b/peer/cwtch_peer.go @@ -65,6 +65,57 @@ type CwtchPeerInterface interface { Shutdown() } +// CreateHash hashes the user's password to a length suitable for file encryption +func CreateKey(key string) ([32]byte, [128]byte) { +var salt [128]byte +if _, err := io.ReadFull(rand.Reader, salt[:]); +err != nil { + panic(err) +} +dk := pbkdf2.Key([]byte(key), salt[:], 4096, 32, sha3.New512) + +var dkr [32]byte +copy(dkr[:], dk) +return dkr, salt +} + +//EncryptMessage takes a message and encrypts the message under the group key. +func EncryptProfile(p *CwtchPeer, password [32]byte) []byte { + var nonce [24]byte + if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil { + panic(err) + } + //copy Peer struct, then remove password and save the copy + cpc := &CwtchPeer{} + deepcopier.Copy(p).To(cpc) + var blankpass [32]byte + var blanksalt [128]byte + cpc.password = blankpass + cpc.salt = blanksalt + bytes, _ := json.Marshal(cpc) + + encrypted := secretbox.Seal(nonce[:], []byte(bytes), &nonce, &password) + return encrypted +} + +//EncryptMessage takes a message and encrypts the message under the group key. +func DecryptProfile(ciphertext []byte, password [32]byte) (error, *CwtchPeer){ + + var decryptNonce [24]byte + copy(decryptNonce[:], ciphertext[:24]) + decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &password) + if ok { + cp := &CwtchPeer{} + err := json.Unmarshal(decrypted, &cp) + if err == nil { + return nil, cp + } else { + return err, nil + } + } + return fmt.Errorf("Failed to decrypt"), nil +} + func (cp *cwtchPeer) setup() { cp.Log = make(chan string) cp.connectionsManager = connections.NewConnectionsManager() @@ -85,32 +136,48 @@ func (cp *cwtchPeer) setup() { } } -// NewCwtchPeer creates and returns a new cwtchPeer with the given name. -func NewCwtchPeer(name string) CwtchPeerInterface { - cp := new(cwtchPeer) +// NewCwtchPeer creates and returns a new CwtchPeer with the given name. +func NewCwtchPeer(name string, password string) *CwtchPeer { + cp := new(CwtchPeer) cp.Profile = model.GenerateNewProfile(name) cp.setup() + pass, salt := CreateKey(password) + cp.password = pass + cp.salt = salt return cp } -// Save saves the cwtchPeer profile state to a file. -func (cp *cwtchPeer) Save(profilefile string) error { +// Save saves the CwtchPeer profile state to a file. +func (cp *CwtchPeer) Save(profilefile string) error { cp.mutex.Lock() - bytes, _ := json.MarshalIndent(cp, "", "\t") - err := ioutil.WriteFile(profilefile, bytes, 0600) + encryptedbytes := EncryptProfile(cp, cp.password) + encryptedbytes = append(cp.salt[:],encryptedbytes...) + err := ioutil.WriteFile(profilefile, encryptedbytes, 0600) cp.profilefile = profilefile cp.mutex.Unlock() return err } -// LoadCwtchPeer loads an existing cwtchPeer from a file. -func LoadCwtchPeer(profilefile string) (CwtchPeerInterface, error) { - bytes, _ := ioutil.ReadFile(profilefile) - cp := new(cwtchPeer) - err := json.Unmarshal(bytes, &cp) - cp.setup() - cp.profilefile = profilefile - return cp, err +// LoadCwtchPeer loads an existing CwtchPeer from a file. CHECK METHOD RETURN +func LoadCwtchPeer(profilefile string, password string) (*CwtchPeer, error) { + encryptedbytes, _ := ioutil.ReadFile(profilefile) + + //get the salt + salt, encryptedbytes := encryptedbytes[0:128], encryptedbytes[128:] + + dk := pbkdf2.Key([]byte(password), salt, 4096, 32, sha3.New512) + var dkr [32]byte + copy(dkr[:], dk) + + err, cp := DecryptProfile(encryptedbytes, dkr) + if err == nil { + cp.setup() + cp.profilefile = profilefile + cp.password = dkr + return cp, nil + } else { + return nil, err + } } // ImportGroup intializes a group from an imported source rather than a peer invite