Skip to content
Snippets Groups Projects
Commit 18f59545 authored by Josh Brooks's avatar Josh Brooks
Browse files

Implement mnemonic in client

parent dc16e1e9
Branches
Tags
2 merge requests!23Release,!19Implement mnemonic in client
///////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
///////////////////////////////////////////////////////////////////////////////
package api
import (
"github.com/pkg/errors"
"gitlab.com/elixxir/crypto/fastRNG"
"gitlab.com/xx_network/crypto/csprng"
xxMnemonic "gitlab.com/xx_network/crypto/mnemonic"
"golang.org/x/crypto/salsa20"
)
const (
nonceSize = 8
)
// StoreSecretWithMnemonic creates a mnemonic and uses it to encrypt the secret.
// This encrypted data saved in storage.
func (c *Client) StoreSecretWithMnemonic(secret []byte) (string, error) {
rng := c.rng.GetStream()
// Create a mnemonic
mnemonic, err := xxMnemonic.GenerateMnemonic(rng, 32)
if err != nil {
return "", errors.Errorf("Failed to generate mnemonic: %v", err)
}
// Encrypt secret with mnemonic as key
ciphertext, nonce, err := encryptWithMnemonic(mnemonic, secret, rng)
if err != nil {
return "", errors.Errorf("Failed to encrypt secret with mnemonic: %v", err)
}
// Concatenate ciphertext with nonce for storage
data := marshalMnemonicInformation(nonce, ciphertext)
// Save data to storage
err = c.storage.SaveMnemonicInformation(data)
if err != nil {
return "", errors.Errorf("Failed to store mnemonic information: %v", err)
}
return mnemonic, nil
}
// LoadSecretWithMnemonic loads the encrypted secret from storage and decrypts
// the secret using the given mnemonic.
func (c *Client) LoadSecretWithMnemonic(mnemonic string) (secret []byte, err error) {
data, err := c.storage.LoadMnemonicInformation()
if err != nil {
return nil, errors.Errorf("Failed to load mnemonic information: %v", err)
}
nonce, ciphertext := unmarshalMnemonicInformation(data)
secret = decryptWithMnemonic(nonce, ciphertext, mnemonic)
return secret, nil
}
// encryptWithMnemonic is a helper function which encrypts the given secret
// using the mnemonic as the key.
func encryptWithMnemonic(mnemonic string, secret []byte,
rng *fastRNG.Stream) (ciphertext, nonce []byte, err error) {
// Place the key into a 32 byte array for salsa 20
var keyArray [32]byte
copy(keyArray[:], mnemonic)
// Generate the nonce
nonce, err = csprng.Generate(nonceSize, rng)
if err != nil {
return nil, nil, errors.Errorf("Failed to generate nonce for encryption: %v", err)
}
// Encrypt the secret
ciphertext = make([]byte, len(secret))
salsa20.XORKeyStream(ciphertext, secret, nonce, &keyArray)
return ciphertext, nonce, nil
}
// decryptWithMnemonic is a helper function which decrypts the secret
// from storage, using the mnemonic as the key.
func decryptWithMnemonic(nonce, ciphertext []byte, mnemonic string) (secret []byte) {
// Place the key into a 32 byte array for salsa 20
var keyArray [32]byte
copy(keyArray[:], mnemonic)
// Decrypt the secret
secret = make([]byte, len(ciphertext))
salsa20.XORKeyStream(secret, ciphertext, nonce, &keyArray)
return secret
}
// marshalMnemonicInformation is a helper function which concatenates the nonce
// and ciphertext.
func marshalMnemonicInformation(nonce, ciphertext []byte) []byte {
return append(nonce, ciphertext...)
}
// unmarshalMnemonicInformation is a helper function which separates the
// concatenated data containing the nonce and ciphertext of the mnemonic
// handling. This is the inverse of marshalMnemonicInformation.
func unmarshalMnemonicInformation(data []byte) (nonce, ciphertext []byte) {
return data[:nonceSize], data[nonceSize:]
}
......@@ -22,7 +22,7 @@ require (
gitlab.com/elixxir/ekv v0.1.5
gitlab.com/elixxir/primitives v0.0.3-0.20210803231939-7b924f78eaac
gitlab.com/xx_network/comms v0.0.4-0.20210813170223-ab758f0bbec5
gitlab.com/xx_network/crypto v0.0.5-0.20210803231814-b18476a2257c
gitlab.com/xx_network/crypto v0.0.5-0.20210909170042-07755821e8c5
gitlab.com/xx_network/primitives v0.0.4-0.20210803222745-e898d5e546e9
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/net v0.0.0-20210525063256-abc453219eb5
......
......@@ -239,6 +239,8 @@ github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w=
github.com/ttacon/libphonenumber v1.2.1 h1:fzOfY5zUADkCkbIafAed11gL1sW+bJ26p6zWLBMElR4=
github.com/ttacon/libphonenumber v1.2.1/go.mod h1:E0TpmdVMq5dyVlQ7oenAkhsLu86OkUl+yR4OAxyEg/M=
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/zeebo/assert v0.0.0-20181109011804-10f827ce2ed6/go.mod h1:yssERNPivllc1yU3BvpjYI5BUW+zglcz6QWqeVRL5t0=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
......@@ -272,6 +274,8 @@ gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt
gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk=
gitlab.com/xx_network/crypto v0.0.5-0.20210803231814-b18476a2257c h1:pwP50UFdh68KPcP7VopsH34/d6rIuVDwXymgFXnCOjA=
gitlab.com/xx_network/crypto v0.0.5-0.20210803231814-b18476a2257c/go.mod h1:e/5MGrKDQbCNJnskBeDscrolr2EK5iIatEgTQnEWmOg=
gitlab.com/xx_network/crypto v0.0.5-0.20210909170042-07755821e8c5 h1:1K+FuxnlOTHI5H2OmoBokh/K9Pbmel8VBaCdP5RW9FU=
gitlab.com/xx_network/crypto v0.0.5-0.20210909170042-07755821e8c5/go.mod h1:hLNL8YCSiEMof0wgZHuRmN5C98TYZiLaz/rLqUYTdII=
gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA=
gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug=
gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc=
......
package storage
import (
"gitlab.com/elixxir/client/storage/versioned"
"gitlab.com/xx_network/primitives/netTime"
)
const (
mnemonicKvKey = "mnemonic"
mnemonicKvVersion = 0
mnemonicPath = "/.recovery"
)
func (s *Session) SaveMnemonicInformation(data []byte) error {
s.mnemonicMux.Lock()
defer s.mnemonicMux.Unlock()
vo := &versioned.Object{
Version: mnemonicKvVersion,
Timestamp: netTime.Now(),
Data: data,
}
return s.mnemonicKV.Set(mnemonicKvKey, mnemonicKvVersion, vo)
}
func (s *Session) LoadMnemonicInformation() ([]byte, error) {
s.mux.RLock()
defer s.mux.RUnlock()
vo, err := s.mnemonicKv.Get(mnemonicKvKey, mnemonicKvVersion)
if err != nil {
return nil, err
}
return vo.Data, err
}
......@@ -47,7 +47,11 @@ const currentSessionVersion = 0
// Session object, backed by encrypted filestore
type Session struct {
kv *versioned.KV
mnemonicKV *versioned.KV
mnemonicKv *versioned.KV
mux sync.RWMutex
mnemonicMux sync.RWMutex
//memoized data
regStatus RegistrationStatus
......@@ -78,8 +82,16 @@ func initStore(baseDir, password string) (*Session, error) {
"Failed to create storage session")
}
// Create a separate file store system for account recovery
mnemonicFS, err := ekv.NewFilestore(baseDir+mnemonicPath, password)
if err != nil {
return nil, errors.WithMessage(err,
"Failed to create mnemonic store")
}
s = &Session{
kv: versioned.NewKV(fs),
mnemonicKV: versioned.NewKV(mnemonicFS),
}
return s, nil
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment