Skip to content
Snippets Groups Projects
Commit a8a3a81c authored by Jake Taylor's avatar Jake Taylor :lips:
Browse files

Merge branch 'lesure/offlineNodes' into 'XX-3484/StaleNodes'

# Conflicts:
#   go.mod
parents d365bcf8 60cde144
No related branches found
No related tags found
3 merge requests!53Release,!24Lesure/offline nodes,!15prevent hostpool from adding stale nodes
///////////////////////////////////////////////////////////////////////////////
// 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"
"gitlab.com/xx_network/primitives/utils"
"golang.org/x/crypto/chacha20poly1305"
"path/filepath"
"strings"
)
const mnemonicFile = ".recovery"
// StoreSecretWithMnemonic creates a mnemonic and uses it to encrypt the secret.
// This encrypted data saved in storage.
func StoreSecretWithMnemonic(secret []byte, path string) (string, error) {
// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
rng := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG).GetStream()
// Ensure path is appended by filepath separator "/"
if !strings.HasSuffix(path, string(filepath.Separator)) {
path = path + string(filepath.Separator)
}
// Create a mnemonic
mnemonic, err := xxMnemonic.GenerateMnemonic(rng, 32)
if err != nil {
return "", errors.Errorf("Failed to generate mnemonic: %v", err)
}
// Decode mnemonic
decodedMnemonic, err := xxMnemonic.DecodeMnemonic(mnemonic)
if err != nil {
return "", errors.Errorf("Failed to decode mnemonic: %v", err)
}
// Encrypt secret with mnemonic as key
ciphertext, err := encryptWithMnemonic(secret, decodedMnemonic, rng)
if err != nil {
return "", errors.Errorf("Failed to encrypt secret with mnemonic: %v", err)
}
// Save encrypted secret to file
recoveryFile := path + mnemonicFile
err = utils.WriteFileDef(recoveryFile, ciphertext)
if err != nil {
return "", errors.Errorf("Failed to save mnemonic information to file")
}
return mnemonic, nil
}
// LoadSecretWithMnemonic loads the encrypted secret from storage and decrypts
// the secret using the given mnemonic.
func LoadSecretWithMnemonic(mnemonic, path string) (secret []byte, err error) {
// Ensure path is appended by filepath separator "/"
if !strings.HasSuffix(path, string(filepath.Separator)) {
path = path + string(filepath.Separator)
}
// Ensure that the recovery file exists
recoveryFile := path + mnemonicFile
if !utils.Exists(recoveryFile) {
return nil, errors.Errorf("Recovery file does not exist. " +
"Did you properly set up recovery or provide an incorrect filepath?")
}
// Read file from storage
data, err := utils.ReadFile(recoveryFile)
if err != nil {
return nil, errors.Errorf("Failed to load mnemonic information: %v", err)
}
// Decode mnemonic
decodedMnemonic, err := xxMnemonic.DecodeMnemonic(mnemonic)
if err != nil {
return nil, errors.Errorf("Failed to decode mnemonic: %v", err)
}
// Decrypt the stored secret
secret, err = decryptWithMnemonic(data, decodedMnemonic)
if err != nil {
return nil, errors.Errorf("Failed to decrypt secret: %v", err)
}
return secret, nil
}
// encryptWithMnemonic is a helper function which encrypts the given secret
// using the mnemonic as the key.
func encryptWithMnemonic(data, decodedMnemonic []byte,
rng csprng.Source) (ciphertext []byte, error error) {
chaCipher, err := chacha20poly1305.NewX(decodedMnemonic[:])
if err != nil {
return nil, errors.Errorf("Failed to initalize encryption algorithm: %v", err)
}
// Generate the nonce
nonce := make([]byte, chaCipher.NonceSize())
nonce, err = csprng.Generate(chaCipher.NonceSize(), rng)
if err != nil {
return nil, errors.Errorf("Failed to generate nonce: %v", err)
}
ciphertext = chaCipher.Seal(nonce, nonce, data, nil)
return ciphertext, nil
}
// decryptWithMnemonic is a helper function which decrypts the secret
// from storage, using the mnemonic as the key.
func decryptWithMnemonic(data, decodedMnemonic []byte) ([]byte, error) {
chaCipher, err := chacha20poly1305.NewX(decodedMnemonic[:])
if err != nil {
return nil, errors.Errorf("Failed to initalize encryption algorithm: %v", err)
}
nonceLen := chaCipher.NonceSize()
nonce, ciphertext := data[:nonceLen], data[nonceLen:]
plaintext, err := chaCipher.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, errors.Wrap(err, "Cannot decrypt with password!")
}
return plaintext, nil
}
///////////////////////////////////////////////////////////////////////////////
// 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 (
"bytes"
"gitlab.com/xx_network/crypto/csprng"
xxMnemonic "gitlab.com/xx_network/crypto/mnemonic"
"gitlab.com/xx_network/primitives/utils"
"io"
"math/rand"
"testing"
)
func TestStoreSecretWithMnemonic(t *testing.T) {
secret := []byte("test123")
storageDir := "ignore.1/"
mnemonic, err := StoreSecretWithMnemonic(secret, storageDir)
if err != nil {
t.Errorf("StoreSecretWithMnemonic error; %v", err)
}
// Tests the mnemonic returned is valid
_, err = xxMnemonic.DecodeMnemonic(mnemonic)
if err != nil {
t.Errorf("StoreSecretWithMnemonic did not return a decodable mnemonic: %v", err)
}
// Test that the file was written to
if !utils.Exists(storageDir + mnemonicFile) {
t.Errorf("Mnemonic file does not exist in storage: %v", err)
}
}
func TestEncryptDecryptMnemonic(t *testing.T) {
prng := NewPrng(32)
// Generate a test mnemonic
testMnemonic, err := xxMnemonic.GenerateMnemonic(prng, 32)
if err != nil {
t.Fatalf("GenerateMnemonic error: %v", err)
}
decodedMnemonic, err := xxMnemonic.DecodeMnemonic(testMnemonic)
if err != nil {
t.Fatalf("DecodeMnemonic error: %v", err)
}
secret := []byte("test123")
// Encrypt the secret
ciphertext, err := encryptWithMnemonic(secret, decodedMnemonic, prng)
if err != nil {
t.Fatalf("encryptWithMnemonic error: %v", err)
}
// Decrypt the secret
received, err := decryptWithMnemonic(ciphertext, decodedMnemonic)
if err != nil {
t.Fatalf("decryptWithMnemonic error: %v", err)
}
// Test if secret matches decrypted data
if !bytes.Equal(received, secret) {
t.Fatalf("Decrypted data does not match original plaintext."+
"\n\tExpected: %v\n\tReceived: %v", secret, received)
}
}
func TestLoadSecretWithMnemonic(t *testing.T) {
secret := []byte("test123")
storageDir := "ignore.1"
mnemonic, err := StoreSecretWithMnemonic(secret, storageDir)
if err != nil {
t.Errorf("StoreSecretWithMnemonic error; %v", err)
}
received, err := LoadSecretWithMnemonic(mnemonic, storageDir)
if err != nil {
t.Errorf("LoadSecretWithMnemonic error: %v", err)
}
if !bytes.Equal(received, secret) {
t.Fatalf("Loaded secret does not match original data."+
"\n\tExpected: %v\n\tReceived: %v", secret, received)
}
_, err = LoadSecretWithMnemonic(mnemonic, "badDirectory")
if err == nil {
t.Fatalf("LoadSecretWithMnemonic should error when provided a path " +
"where a recovery file does not exist.")
}
}
// Prng is a PRNG that satisfies the csprng.Source interface.
type Prng struct{ prng io.Reader }
func NewPrng(seed int64) csprng.Source { return &Prng{rand.New(rand.NewSource(seed))} }
func (s *Prng) Read(b []byte) (int, error) { return s.prng.Read(b) }
func (s *Prng) SetSeed([]byte) error { return nil }
///////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
///////////////////////////////////////////////////////////////////////////////
package bindings
import "gitlab.com/elixxir/client/api"
// StoreSecretWithMnemonic stores the secret tied with the mnemonic to storage.
// Unlike other storage operations, this does not use EKV, as that is
// intrinsically tied to client operations, which the user will not have while
// trying to recover their account. As such, we store the encrypted data
// directly, with a specified path. Path will be a valid filepath in which the
// recover file will be stored as ".recovery".
//
// As an example, given "home/user/xxmessenger/storagePath",
// the recovery file will be stored at
// "home/user/xxmessenger/storagePath/.recovery"
func StoreSecretWithMnemonic(secret []byte, path string) (string, error) {
return api.StoreSecretWithMnemonic(secret, path)
}
// LoadSecretWithMnemonic loads the secret stored from the call to
// StoreSecretWithMnemonic. The path given should be the same filepath
// as the path given in StoreSecretWithMnemonic. There should be a file
// in this path called ".recovery". This operation is not tied
// to client operations, as the user will not have a client when trying to
// recover their account.
func LoadSecretWithMnemonic(mnemonic, path string) (secret []byte, err error) {
return api.LoadSecretWithMnemonic(mnemonic, path)
}
......@@ -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=
......
......@@ -141,15 +141,13 @@ func generateIdentities(protoIds []ephemeral.ProtoIdentity, ourId *id.ID,
// Add identities for every ephemeral ID
for i, eid := range protoIds {
// Expand the grace period for both start and end
eid.End.Add(validityGracePeriod)
eid.Start.Add(-validityGracePeriod)
identities[i] = reception.Identity{
EphId: eid.Id,
Source: ourId,
AddressSize: addressSize,
End: eid.End,
StartValid: eid.Start,
EndValid: eid.End,
StartValid: eid.Start.Add(-validityGracePeriod),
EndValid: eid.End.Add(validityGracePeriod),
Ephemeral: false,
}
......@@ -211,6 +209,6 @@ func calculateTickerTime(baseIDs []ephemeral.ProtoIdentity, now time.Time) time.
// Factor out the grace period previously expanded upon
// Calculate and return that duration
gracePeriod := lastIdentity.End.Add(-2 * validityGracePeriod)
gracePeriod := lastIdentity.End.Add(-1 * validityGracePeriod)
return gracePeriod.Sub(now)
}
......@@ -17,6 +17,7 @@ import (
"gitlab.com/xx_network/comms/signature"
"gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id"
"gitlab.com/xx_network/primitives/id/ephemeral"
"gitlab.com/xx_network/primitives/netTime"
"gitlab.com/xx_network/primitives/utils"
"testing"
......@@ -122,3 +123,18 @@ func setupInstance(instance interfaces.NetworkManager) error {
return nil
}
func TestGenerateIdentities(t *testing.T) {
eid, s, e, err := ephemeral.GetId(id.NewIdFromString("zezima", id.Node, t), 16, time.Now().UnixNano())
if err != nil {
t.Errorf("Failed to get eid: %+v", err)
}
protoIds := []ephemeral.ProtoIdentity{{eid, s, e}}
generated := generateIdentities(protoIds, id.NewIdFromString("escaline", id.Node, t), 16)
if generated[0].EndValid != protoIds[0].End.Add(5*time.Minute) {
t.Errorf("End was not modified. Orig %+v, Generated %+v", protoIds[0].End, generated[0].End)
}
if generated[0].StartValid != protoIds[0].Start.Add(-5*time.Minute) {
t.Errorf("End was not modified. Orig %+v, Generated %+v", protoIds[0].End, generated[0].End)
}
}
......@@ -86,6 +86,7 @@ func NewManager(session *storage.Session, switchboard *switchboard.Switchboard,
param: params,
tracker: &tracker,
addrSpace: ephemeral.NewAddressSpace(),
events: events,
}
m.Internal = internal.Internal{
......
......@@ -47,6 +47,7 @@ const currentSessionVersion = 0
// Session object, backed by encrypted filestore
type Session struct {
kv *versioned.KV
mux sync.RWMutex
//memoized data
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment