From 3776db1640a853d2ffe732b813a63489a0b81538 Mon Sep 17 00:00:00 2001 From: Jono Wenger <jono@elixxir.io> Date: Mon, 10 Oct 2022 23:30:31 +0000 Subject: [PATCH] Add internal password generation and storage --- Makefile | 2 + creds/password.go | 314 +++++++++++++++++++++++++++++++ creds/password_test.go | 366 +++++++++++++++++++++++++++++++++++++ go.mod | 22 +-- go.sum | 38 ---- main.go | 6 + utils/localStorage.go | 120 ++++++++++++ utils/localStorage_test.go | 169 +++++++++++++++++ 8 files changed, 979 insertions(+), 58 deletions(-) create mode 100644 creds/password.go create mode 100644 creds/password_test.go create mode 100644 utils/localStorage.go create mode 100644 utils/localStorage_test.go diff --git a/Makefile b/Makefile index c899add2..1a6a1426 100644 --- a/Makefile +++ b/Makefile @@ -15,12 +15,14 @@ update_release: GOFLAGS="" go get -d gitlab.com/elixxir/client@release GOFLAGS="" go get gitlab.com/elixxir/crypto@release GOFLAGS="" go get gitlab.com/elixxir/primitives@release + GOFLAGS="" go get gitlab.com/xx_network/crypto@release GOFLAGS="" go get gitlab.com/xx_network/primitives@release update_master: GOFLAGS="" go get -d gitlab.com/elixxir/client@master GOFLAGS="" go get gitlab.com/elixxir/crypto@master GOFLAGS="" go get gitlab.com/elixxir/primitives@master + GOFLAGS="" go get gitlab.com/xx_network/crypto@master GOFLAGS="" go get gitlab.com/xx_network/primitives@master binary: diff --git a/creds/password.go b/creds/password.go new file mode 100644 index 00000000..c980ef33 --- /dev/null +++ b/creds/password.go @@ -0,0 +1,314 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +//go:build js && wasm + +package creds + +import ( + "crypto/cipher" + "encoding/json" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/xxdk-wasm/utils" + "gitlab.com/xx_network/crypto/csprng" + "golang.org/x/crypto/argon2" + "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/chacha20poly1305" + "io" + "os" + "syscall/js" +) + +// Data lengths. +const ( + // Length of the internal password (256-bit) + internalPasswordLen = 32 + + // keyLen is the length of the key generated + keyLen = chacha20poly1305.KeySize + + // saltLen is the length of the salt. Recommended to be 16 bytes here: + // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-argon2-04#section-3.1 + saltLen = 16 +) + +// Storage keys. +const ( + // Key used to store the encrypted internal password salt in local storage. + saltKey = "xxInternalPasswordSalt" + + // Key used to store the encrypted internal password in local storage. + passwordKey = "xxEncryptedInternalPassword" + + // Key used to store the argon2 parameters used to encrypted/decrypt the + // password. + argonParamsKey = "xxEncryptedInternalPasswordParams" +) + +// Error messages. +const ( + // getInternalPassword + getPasswordStorageErr = "could not retrieve encrypted internal password from storage: %+v" + getSaltStorageErr = "could not retrieve salt from storage: %+v" + getParamsStorageErr = "could not retrieve encryption parameters from storage: %+v" + paramsUnmarshalErr = "failed to unmarshal encryption parameters loaded from storage: %+v" + decryptPasswordErr = "could not decrypt internal password: %+v" + + // initInternalPassword + readInternalPasswordErr = "could not generate internal password: %+v" + internalPasswordNumBytesErr = "expected %d bytes for internal password, found %d bytes" + + // decryptPassword + readNonceLenErr = "read %d bytes, too short to decrypt" + decryptWithPasswordErr = "cannot decrypt with password: %+v" + + // makeSalt + readSaltErr = "could not generate salt: %+v" + saltNumBytesErr = "expected %d bytes for salt, found %d bytes" +) + +// GetOrInitJS takes a user-provided password and returns its associated 256-bit +// internal password. +// +// If the internal password has not previously been created, then it is +// generated, saved to local storage, and returned. If the internal password has +// been previously generated, it is retrieved from local storage and returned. +// +// Any password saved to local storage is encrypted using the user-provided +// password. +// +// Parameters: +// - args[0] - The user supplied password (string). +// +// Returns: +// - Internal password (Uint8Array). +// - Throws TypeError on failure. +func GetOrInitJS(_ js.Value, args []js.Value) interface{} { + internalPassword, err := GetOrInit(args[0].String()) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return utils.CopyBytesToJS(internalPassword) +} + +// ChangeExternalPasswordJS allows a user to change their external password. +// +// Parameters: +// - args[0] - The user's old password (string). +// - args[1] - The user's new password (string). +// +// Returns: +// - Throws TypeError on failure. +func ChangeExternalPasswordJS(_ js.Value, args []js.Value) interface{} { + err := ChangeExternalPassword(args[0].String(), args[1].String()) + if err != nil { + utils.Throw(utils.TypeError, err) + return nil + } + + return nil +} + +// GetOrInit takes a user-provided password and returns its associated 256-bit +// internal password. +// +// If the internal password has not previously been created, then it is +// generated, saved to local storage, and returned. If the internal password has +// been previously generated, it is retrieved from local storage and returned. +// +// Any password saved to local storage is encrypted using the user-provided +// password. +func GetOrInit(externalPassword string) ([]byte, error) { + localStorage := utils.GetLocalStorage() + internalPassword, err := getInternalPassword(externalPassword, localStorage) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + rng := csprng.NewSystemRNG() + return initInternalPassword( + externalPassword, localStorage, rng, defaultParams()) + } + + return nil, err + } + + return internalPassword, nil +} + +// ChangeExternalPassword allows a user to change their external password. +func ChangeExternalPassword(oldExternalPassword, newExternalPassword string) error { + localStorage := utils.GetLocalStorage() + internalPassword, err := getInternalPassword(oldExternalPassword, localStorage) + if err != nil { + return err + } + + salt, err := makeSalt(csprng.NewSystemRNG()) + if err != nil { + return err + } + localStorage.SetItem(saltKey, salt) + + key := deriveKey(newExternalPassword, salt, defaultParams()) + + encryptedInternalPassword := encryptPassword( + internalPassword, key, csprng.NewSystemRNG()) + localStorage.SetItem(passwordKey, encryptedInternalPassword) + + return nil +} + +// initInternalPassword generates a new internal password, stores an encrypted +// version in local storage, and returns it. +func initInternalPassword(externalPassword string, + localStorage *utils.LocalStorage, csprng io.Reader, + params argonParams) ([]byte, error) { + internalPassword := make([]byte, internalPasswordLen) + + // Generate internal password + n, err := csprng.Read(internalPassword) + if err != nil { + return nil, errors.Errorf(readInternalPasswordErr, err) + } else if n != internalPasswordLen { + return nil, errors.Errorf( + internalPasswordNumBytesErr, internalPasswordLen, n) + } + + // Generate and store salt + salt, err := makeSalt(csprng) + if err != nil { + return nil, err + } + localStorage.SetItem(saltKey, salt) + + // Store argon2 parameters + paramsData, err := json.Marshal(params) + if err != nil { + return nil, err + } + localStorage.SetItem(argonParamsKey, paramsData) + + key := deriveKey(externalPassword, salt, params) + + encryptedInternalPassword := encryptPassword(internalPassword, key, csprng) + localStorage.SetItem(passwordKey, encryptedInternalPassword) + + return internalPassword, nil +} + +// getInternalPassword retrieves the internal password from local storage, +// decrypts it, and returns it. +func getInternalPassword( + externalPassword string, localStorage *utils.LocalStorage) ([]byte, error) { + encryptedInternalPassword, err := localStorage.GetItem(passwordKey) + if err != nil { + return nil, errors.WithMessage(err, getPasswordStorageErr) + } + + salt, err := localStorage.GetItem(saltKey) + if err != nil { + return nil, errors.WithMessage(err, getSaltStorageErr) + } + + paramsData, err := localStorage.GetItem(argonParamsKey) + if err != nil { + return nil, errors.WithMessage(err, getParamsStorageErr) + } + + var params argonParams + err = json.Unmarshal(paramsData, ¶ms) + if err != nil { + return nil, errors.Errorf(paramsUnmarshalErr, err) + } + + key := deriveKey(externalPassword, salt, params) + + decryptedInternalPassword, err := + decryptPassword(encryptedInternalPassword, key) + if err != nil { + return nil, errors.Errorf(decryptPasswordErr, err) + } + + return decryptedInternalPassword, nil +} + +// encryptPassword encrypts the data for a shared URL using XChaCha20-Poly1305. +func encryptPassword(data, password []byte, csprng io.Reader) []byte { + chaCipher := initChaCha20Poly1305(password) + nonce := make([]byte, chaCipher.NonceSize()) + if _, err := io.ReadFull(csprng, nonce); err != nil { + jww.FATAL.Panicf("Could not generate nonce %+v", err) + } + ciphertext := chaCipher.Seal(nonce, nonce, data, nil) + return ciphertext +} + +// decryptPassword decrypts the encrypted data from a shared URL using +// XChaCha20-Poly1305. +func decryptPassword(data, password []byte) ([]byte, error) { + chaCipher := initChaCha20Poly1305(password) + nonceLen := chaCipher.NonceSize() + if (len(data) - nonceLen) <= 0 { + return nil, errors.Errorf(readNonceLenErr, len(data)) + } + nonce, ciphertext := data[:nonceLen], data[nonceLen:] + plaintext, err := chaCipher.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, errors.Errorf(decryptWithPasswordErr, err) + } + return plaintext, nil +} + +// initChaCha20Poly1305 returns a XChaCha20-Poly1305 cipher.AEAD that uses the +// given password hashed into a 256-bit key. +func initChaCha20Poly1305(password []byte) cipher.AEAD { + pwHash := blake2b.Sum256(password) + chaCipher, err := chacha20poly1305.NewX(pwHash[:]) + if err != nil { + jww.FATAL.Panicf("Could not init XChaCha20Poly1305 mode: %+v", err) + } + + return chaCipher +} + +// argonParams contains the cost parameters used by Argon2. +type argonParams struct { + Time uint32 // Number of passes over the memory + Memory uint32 // Amount of memory used in KiB + Threads uint8 // Number of threads used +} + +// defaultParams returns the recommended general purposes parameters. +func defaultParams() argonParams { + return argonParams{ + Time: 1, + Memory: 64 * 1024, // ~64 MB + Threads: 4, + } +} + +// deriveKey derives a key from a user supplied password and a salt via the +// Argon2 algorithm. +func deriveKey(password string, salt []byte, params argonParams) []byte { + return argon2.IDKey([]byte(password), salt, + params.Time, params.Memory, params.Threads, keyLen) +} + +// makeSalt generates a salt of the correct length of key generation. +func makeSalt(csprng io.Reader) ([]byte, error) { + b := make([]byte, saltLen) + size, err := csprng.Read(b) + if err != nil { + return nil, errors.Errorf(readSaltErr, err) + } else if size != saltLen { + return nil, errors.Errorf(saltNumBytesErr, saltLen, size) + } + + return b, nil +} diff --git a/creds/password_test.go b/creds/password_test.go new file mode 100644 index 00000000..7bba31c2 --- /dev/null +++ b/creds/password_test.go @@ -0,0 +1,366 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +//go:build js && wasm + +package creds + +import ( + "bytes" + "crypto/rand" + "encoding/base64" + "fmt" + "gitlab.com/elixxir/xxdk-wasm/utils" + "gitlab.com/xx_network/crypto/csprng" + "strings" + "testing" +) + +// Tests that running GetOrInit twice returns the same internal password both +// times. +func TestGetOrInit(t *testing.T) { + externalPassword := "myPassword" + internalPassword, err := GetOrInit(externalPassword) + if err != nil { + t.Errorf("%+v", err) + } + + loadedInternalPassword, err := GetOrInit(externalPassword) + if err != nil { + t.Errorf("%+v", err) + } + + if !bytes.Equal(internalPassword, loadedInternalPassword) { + t.Errorf("Internal password from storage does not match original."+ + "\nexpected: %+v\nreceived: %+v", + internalPassword, loadedInternalPassword) + } +} + +func TestChangeExternalPassword(t *testing.T) { + oldExternalPassword := "myPassword" + newExternalPassword := "hunter2" + oldInternalPassword, err := GetOrInit(oldExternalPassword) + if err != nil { + t.Errorf("%+v", err) + } + + err = ChangeExternalPassword(oldExternalPassword, newExternalPassword) + if err != nil { + t.Errorf("%+v", err) + } + + newInternalPassword, err := GetOrInit(newExternalPassword) + if err != nil { + t.Errorf("%+v", err) + } + + if !bytes.Equal(oldInternalPassword, newInternalPassword) { + t.Errorf("Internal password was not changed in storage. Old and new "+ + "should be different.\nold: %+v\nnew: %+v", + oldInternalPassword, newInternalPassword) + } + + _, err = GetOrInit(oldExternalPassword) + expectedErr := strings.Split(decryptWithPasswordErr, "%")[0] + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error when trying to get internal password with "+ + "old external password.\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that the internal password returned by initInternalPassword matches +// the encrypted one saved to local storage. +func Test_initInternalPassword(t *testing.T) { + externalPassword := "myPassword" + ls := utils.GetLocalStorage() + rng := csprng.NewSystemRNG() + + internalPassword, err := initInternalPassword( + externalPassword, ls, rng, defaultParams()) + if err != nil { + t.Errorf("%+v", err) + } + + // Attempt to retrieve encrypted internal password from storage + encryptedInternalPassword, err := ls.GetItem(passwordKey) + if err != nil { + t.Errorf( + "Failed to load encrypted internal password from storage: %+v", err) + } + + // Attempt to retrieve salt from storage + salt, err := ls.GetItem(saltKey) + if err != nil { + t.Errorf("Failed to load salt from storage: %+v", err) + } + + // Attempt to decrypt + key := deriveKey(externalPassword, salt, defaultParams()) + decryptedInternalPassword, err := + decryptPassword(encryptedInternalPassword, key) + if err != nil { + t.Errorf("Failed to load decrpyt internal password: %+v", err) + } + + if !bytes.Equal(internalPassword, decryptedInternalPassword) { + t.Errorf("Decrypted internal password from storage does not match "+ + "original.\nexpected: %+v\nreceived: %+v", + internalPassword, decryptedInternalPassword) + } +} + +// Tests that initInternalPassword returns an error when the RNG returns an +// error when read. +func Test_initInternalPassword_CsprngReadError(t *testing.T) { + externalPassword := "myPassword" + ls := utils.GetLocalStorage() + b := bytes.NewBuffer([]byte{}) + + expectedErr := strings.Split(readInternalPasswordErr, "%")[0] + + _, err := initInternalPassword(externalPassword, ls, b, defaultParams()) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error when RNG returns a read error."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that initInternalPassword returns an error when the RNG does not +// return enough bytes. +func Test_initInternalPassword_CsprngReadNumBytesError(t *testing.T) { + externalPassword := "myPassword" + ls := utils.GetLocalStorage() + b := bytes.NewBuffer(make([]byte, internalPasswordLen/2)) + + expectedErr := fmt.Sprintf( + internalPasswordNumBytesErr, internalPasswordLen, internalPasswordLen/2) + + _, err := initInternalPassword(externalPassword, ls, b, defaultParams()) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error when RNG does not return enough bytes."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that getInternalPassword returns the internal password that is saved +// to local storage by initInternalPassword. +func Test_getInternalPassword(t *testing.T) { + externalPassword := "myPassword" + ls := utils.GetLocalStorage() + rng := csprng.NewSystemRNG() + + internalPassword, err := initInternalPassword( + externalPassword, ls, rng, defaultParams()) + if err != nil { + t.Errorf("%+v", err) + } + + loadedInternalPassword, err := getInternalPassword(externalPassword, ls) + if err != nil { + t.Errorf("%+v", err) + } + + if !bytes.Equal(internalPassword, loadedInternalPassword) { + t.Errorf("Internal password from storage does not match original."+ + "\nexpected: %+v\nreceived: %+v", + internalPassword, loadedInternalPassword) + } +} + +// Tests that getInternalPassword returns an error when the password cannot be +// loaded from local storage. +func Test_getInternalPassword_LocalStorageGetPasswordError(t *testing.T) { + externalPassword := "myPassword" + ls := utils.GetLocalStorage() + ls.Clear() + + expectedErr := strings.Split(getPasswordStorageErr, "%")[0] + + _, err := getInternalPassword(externalPassword, ls) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error when password cannot be loaded from storage."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that getInternalPassword returns an error when the salt cannot be +// loaded from local storage. +func Test_getInternalPassword_LocalStorageGetError(t *testing.T) { + externalPassword := "myPassword" + ls := utils.GetLocalStorage() + ls.Clear() + ls.SetItem(passwordKey, []byte("password")) + + expectedErr := strings.Split(getSaltStorageErr, "%")[0] + + _, err := getInternalPassword(externalPassword, ls) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error when salt cannot be loaded from storage."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that getInternalPassword returns an error when the password cannot be +// decrypted. +func Test_getInternalPassword_DecryptPasswordError(t *testing.T) { + externalPassword := "myPassword" + ls := utils.GetLocalStorage() + ls.Clear() + ls.SetItem(saltKey, []byte("salt")) + ls.SetItem(passwordKey, []byte("password")) + ls.SetItem(argonParamsKey, []byte(`{"Time": 1, "Memory": 65536, "Threads": 4}`)) + + expectedErr := strings.Split(decryptPasswordErr, "%")[0] + + _, err := getInternalPassword(externalPassword, ls) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error when the password is decrypted."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Smoke test of encryptPassword and decryptPassword. +func Test_encryptPassword_decryptPassword(t *testing.T) { + plaintext := []byte("Hello, World!") + password := []byte("test_password") + ciphertext := encryptPassword(plaintext, password, rand.Reader) + decrypted, err := decryptPassword(ciphertext, password) + if err != nil { + t.Errorf("%+v", err) + } + + for i := range plaintext { + if plaintext[i] != decrypted[i] { + t.Errorf("%b != %b", plaintext[i], decrypted[i]) + } + } +} + +// Tests that decryptPassword does not panic when given too little data. +func Test_decryptPassword_ShortData(t *testing.T) { + // Anything under 24 should cause an error. + ciphertext := []byte{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + _, err := decryptPassword(ciphertext, []byte("dummyPassword")) + expectedErr := fmt.Sprintf(readNonceLenErr, 24) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error on short decryption."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } + + // Empty string shouldn't panic should cause an error. + ciphertext = []byte{} + _, err = decryptPassword(ciphertext, []byte("dummyPassword")) + expectedErr = fmt.Sprintf(readNonceLenErr, 0) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error on short decryption."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that deriveKey returns a key of the correct length and that it is the +// same for the same set of password and salt. Also checks that keys with the +// same salt or passwords do not collide. +func Test_deriveKey(t *testing.T) { + p := testParams() + salts := make([][]byte, 6) + passwords := make([]string, len(salts)) + keys := make(map[string]bool, len(salts)*len(passwords)) + + for i := range salts { + prng := csprng.NewSystemRNG() + salt, _ := makeSalt(prng) + salts[i] = salt + + password := make([]byte, 16) + _, _ = prng.Read(password) + passwords[i] = base64.StdEncoding.EncodeToString(password)[:16] + } + + for _, salt := range salts { + for _, password := range passwords { + key := deriveKey(password, salt, p) + + // Check that the length of the key is correct + if len(key) != keyLen { + t.Errorf("Incorrect key length.\nexpected: %d\nreceived: %d", + keyLen, len(key)) + } + + // Check that the same key is generated when the same password and salt + // are used + key2 := deriveKey(password, salt, p) + + if !bytes.Equal(key, key2) { + t.Errorf("Keys with same password and salt do not match."+ + "\nexpected: %v\nreceived: %v", key, key2) + } + + if keys[string(key)] { + t.Errorf("Key already exists.") + } + keys[string(key)] = true + } + } +} + +// Tests that multiple calls to makeSalt results in unique salts of the +// specified length. +func Test_makeSalt(t *testing.T) { + salts := make(map[string]bool, 50) + for i := 0; i < 50; i++ { + salt, err := makeSalt(csprng.NewSystemRNG()) + if err != nil { + t.Errorf("MakeSalt returned an error: %+v", err) + } + + if len(salt) != saltLen { + t.Errorf("Incorrect salt length.\nexpected: %d\nreceived: %d", + saltLen, len(salt)) + } + + if salts[string(salt)] { + t.Errorf("Salt already exists (%d).", i) + } + salts[string(salt)] = true + } +} + +// Tests that makeSalt returns an error when the RNG returns an error when read. +func Test_makeSalt_ReadError(t *testing.T) { + b := bytes.NewBuffer([]byte{}) + + expectedErr := strings.Split(readSaltErr, "%")[0] + _, err := makeSalt(b) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error when RNG returns a read error."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that makeSalt returns an error when the RNG does not return enough +// bytes. +func Test_makeSalt_ReadNumBytesError(t *testing.T) { + b := bytes.NewBuffer(make([]byte, saltLen/2)) + + expectedErr := fmt.Sprintf(saltNumBytesErr, saltLen, saltLen/2) + _, err := makeSalt(b) + if err == nil || !strings.Contains(err.Error(), expectedErr) { + t.Errorf("Unexpected error when RNG does not return enough bytes."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// testParams returns params used in testing that are quick. +func testParams() argonParams { + return argonParams{ + Time: 1, + Memory: 1, + Threads: 1, + } +} diff --git a/go.mod b/go.mod index 2c3a02b5..6f3b57ee 100644 --- a/go.mod +++ b/go.mod @@ -10,45 +10,32 @@ require ( gitlab.com/elixxir/client v1.5.1-0.20221006000407-60c78c92fd15 gitlab.com/elixxir/crypto v0.0.7-0.20221003185354-b091598d2322 gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6 + gitlab.com/xx_network/crypto v0.0.5-0.20220913213008-98764f5b3287 gitlab.com/xx_network/primitives v0.0.4-0.20220809193445-9fc0a5209548 + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa ) require ( git.xx.network/elixxir/grpc-web-go-client v0.0.0-20220908170150-ef04339ffe65 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/badoux/checkmail v1.2.1 // indirect - github.com/beevik/ntp v0.3.0 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cloudflare/circl v1.2.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/elliotchance/orderedmap v1.4.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.11.7 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.2 // indirect - github.com/pkg/profile v1.6.0 // indirect github.com/rs/cors v1.8.2 // indirect github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect github.com/soheilhy/cmux v0.1.5 // indirect - github.com/spf13/afero v1.9.2 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.5.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.12.0 // indirect - github.com/subosito/gotenv v1.4.0 // indirect github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect github.com/ttacon/libphonenumber v1.2.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect @@ -57,19 +44,14 @@ require ( gitlab.com/elixxir/comms v0.0.4-0.20221005205938-10f2defa5b33 // indirect gitlab.com/elixxir/ekv v0.2.1 // indirect gitlab.com/xx_network/comms v0.0.4-0.20221005205845-b34d538ffd85 // indirect - gitlab.com/xx_network/crypto v0.0.5-0.20220913213008-98764f5b3287 // indirect gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c // indirect golang.org/x/sys v0.0.0-20220731174439-a90be440212d // indirect golang.org/x/text v0.3.7 // indirect google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect google.golang.org/grpc v1.49.0 // indirect google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.66.6 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.7 // indirect ) diff --git a/go.sum b/go.sum index f9f4acba..c164cd8e 100644 --- a/go.sum +++ b/go.sum @@ -87,8 +87,6 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/badoux/checkmail v1.2.1 h1:TzwYx5pnsV6anJweMx2auXdekBwGr/yt1GgalIx9nBQ= github.com/badoux/checkmail v1.2.1/go.mod h1:XroCOBU5zzZJcLvgwU15I+2xXyCdTWXyR9MGfRhBYy0= -github.com/beevik/ntp v0.3.0 h1:xzVrPrE4ziasFXgBVBZJDP0Wg/KpMwk2KHJ4Ba8GrDw= -github.com/beevik/ntp v0.3.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -167,7 +165,6 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2 github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= @@ -333,7 +330,6 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -351,7 +347,6 @@ github.com/improbable-eng/grpc-web v0.12.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6 github.com/improbable-eng/grpc-web v0.14.1/go.mod h1:zEjGHa8DAlkoOXmswrNvhUGEYQA9UI7DhrGeHR1DMGU= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -407,7 +402,6 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea h1:uyJ13zfy6l79CM3HnVhDalIyZ4RJAyVfDrbnfFeJoC4= github.com/liyue201/goqr v0.0.0-20200803022322-df443203d4ea/go.mod h1:w4pGU9PkiX2hAWyF0yuHEHmYTQFAd6WHzp6+IY7JVjE= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -440,7 +434,6 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -486,10 +479,8 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw= github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= @@ -499,7 +490,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM= github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -564,19 +554,14 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -595,7 +580,6 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= -github.com/subosito/gotenv v1.4.0 h1:yAzM1+SmVcz5R4tXGsNMu1jUl2aOJXoiWUCEwwnGrvs= github.com/subosito/gotenv v1.4.0/go.mod h1:mZd6rFysKEcUhUHXJk0C/08wAgyDBFuwEYL7vWWGaGo= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0XS0qTf5FznsMOzTjGqavBGuCbo0= @@ -630,26 +614,8 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f h1:yXGvNBqzZwAhDYlSnxPRbgor6JWoOt1Z7s3z1O9JR40= gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= -gitlab.com/elixxir/client v1.5.1-0.20221004163122-5a4635dce0fa h1:sjZ+73Jesh/wU036YbZ5UAGjLIeKCVscf7sQDHMC4DM= -gitlab.com/elixxir/client v1.5.1-0.20221004163122-5a4635dce0fa/go.mod h1:wuTIcLuMnvIGSo8i/Gg/SbYF57bE+CbKPpA1Xbk2AKk= -gitlab.com/elixxir/client v1.5.1-0.20221005170957-7bd7001c3f39 h1:KDgJOCkKwKQBBDM6q9MF3OBNkr0Y7h/YEcgX8OH/Chg= -gitlab.com/elixxir/client v1.5.1-0.20221005170957-7bd7001c3f39/go.mod h1:wuTIcLuMnvIGSo8i/Gg/SbYF57bE+CbKPpA1Xbk2AKk= -gitlab.com/elixxir/client v1.5.1-0.20221005204036-f587d711ed7f h1:NLftJa6tnVt2qsV9UrnAsMP4tPWypkTVypQ7kr8C2RM= -gitlab.com/elixxir/client v1.5.1-0.20221005204036-f587d711ed7f/go.mod h1:wuTIcLuMnvIGSo8i/Gg/SbYF57bE+CbKPpA1Xbk2AKk= -gitlab.com/elixxir/client v1.5.1-0.20221005210039-a909ab7496c4 h1:YBFNjqoZYwk8qwBeDReehS6Woau1R8XBDWLQkvH4Iok= -gitlab.com/elixxir/client v1.5.1-0.20221005210039-a909ab7496c4/go.mod h1:PtvF9F/m8eDK8WfFfXkSa+RN6yVYUq3oy/SQ5+DsWIM= -gitlab.com/elixxir/client v1.5.1-0.20221005215834-e857bc44efec h1:Al03sdDXqYkfbH0sbtNPm4qEtvSXvSPQQJfdLsf/oOo= -gitlab.com/elixxir/client v1.5.1-0.20221005215834-e857bc44efec/go.mod h1:2lMkL4403zPYS3ndydbe4iqxI1BXpwjKNlJd4G/jjf8= -gitlab.com/elixxir/client v1.5.1-0.20221005220305-f198ad1b1c55 h1:rh2yIrASX/sItHDtA6sxhkdTMUbP20NS1ov+xUZYX2A= -gitlab.com/elixxir/client v1.5.1-0.20221005220305-f198ad1b1c55/go.mod h1:2lMkL4403zPYS3ndydbe4iqxI1BXpwjKNlJd4G/jjf8= -gitlab.com/elixxir/client v1.5.1-0.20221005221039-2a972160e7b9 h1:3eu0FHuwR2DzsluF57571idWyqEtC8TmB52hoflShIg= -gitlab.com/elixxir/client v1.5.1-0.20221005221039-2a972160e7b9/go.mod h1:2lMkL4403zPYS3ndydbe4iqxI1BXpwjKNlJd4G/jjf8= -gitlab.com/elixxir/client v1.5.1-0.20221005222316-6aad5ef9fb23 h1:wRXqIMw7EzJMWxmi3XbX9m9jDR/gtNF+pTirCd4Sdsk= -gitlab.com/elixxir/client v1.5.1-0.20221005222316-6aad5ef9fb23/go.mod h1:2lMkL4403zPYS3ndydbe4iqxI1BXpwjKNlJd4G/jjf8= gitlab.com/elixxir/client v1.5.1-0.20221006000407-60c78c92fd15 h1:jo3AWj63GcLN1ANkzTSZZb58U1Oayyy+d7sjP+bK1VE= gitlab.com/elixxir/client v1.5.1-0.20221006000407-60c78c92fd15/go.mod h1:PtvF9F/m8eDK8WfFfXkSa+RN6yVYUq3oy/SQ5+DsWIM= -gitlab.com/elixxir/comms v0.0.4-0.20220916185715-f1e9a5eda939 h1:+VRx2ULHKs040bBhDAOKNCZnbcXxUk3jD9JoKQzQpLk= -gitlab.com/elixxir/comms v0.0.4-0.20220916185715-f1e9a5eda939/go.mod h1:AO6XkMhaHJW8eXlgL5m3UUcJqsSP8F5Wm1GX+wyq/rw= gitlab.com/elixxir/comms v0.0.4-0.20221005205938-10f2defa5b33 h1:mtn/b+/+cMoZNSEo6293U48uqz+aE0si90mPwlhh08w= gitlab.com/elixxir/comms v0.0.4-0.20221005205938-10f2defa5b33/go.mod h1:oRteMH+R5t1j/FZ+KJJnZUcqJO2sLXnWksN5HPkZUIo= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= @@ -668,9 +634,6 @@ gitlab.com/elixxir/primitives v0.0.3-0.20220810173935-592f34a88326/go.mod h1:9Bb gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6 h1:/cxxZBP5jTPDpC3zgOx9vV1ojmJyG8pYtkl3IbcewNQ= gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6/go.mod h1:9Bb2+u+CDSwsEU5Droo6saDAXuBDvLRjexpBhPAYxhA= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= -gitlab.com/xx_network/comms v0.0.4-0.20220913215811-c4bf83b27de3/go.mod h1:E2QKOKyPKLRjLUwMxgZpTKueEsHDEqshfqOHJ54ttxU= -gitlab.com/xx_network/comms v0.0.4-0.20220916185248-8a984b8594de h1:44VKuVgT6X1l+MX8/oNmYORA+pa4nkOWV8hYxi4SCzc= -gitlab.com/xx_network/comms v0.0.4-0.20220916185248-8a984b8594de/go.mod h1:E2QKOKyPKLRjLUwMxgZpTKueEsHDEqshfqOHJ54ttxU= gitlab.com/xx_network/comms v0.0.4-0.20221005205845-b34d538ffd85 h1:bX2IYFnEbWTNGhZHfzHME19pkfD4Q7oTxFGI70PM2PM= gitlab.com/xx_network/comms v0.0.4-0.20221005205845-b34d538ffd85/go.mod h1:E2QKOKyPKLRjLUwMxgZpTKueEsHDEqshfqOHJ54ttxU= gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE= @@ -1246,7 +1209,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/main.go b/main.go index a7042228..cc39e0fb 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "fmt" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/bindings" + "gitlab.com/elixxir/xxdk-wasm/creds" "gitlab.com/elixxir/xxdk-wasm/utils" "gitlab.com/elixxir/xxdk-wasm/wasm" "os" @@ -36,6 +37,11 @@ func main() { js.Global().Set("Base64ToUint8Array", js.FuncOf(utils.Base64ToUint8Array)) js.Global().Set("Uint8ArrayEquals", js.FuncOf(utils.Uint8ArrayEquals)) + // creds/password.go + js.Global().Set("GetOrInit", js.FuncOf(creds.GetOrInitJS)) + js.Global().Set("ChangeExternalPassword", + js.FuncOf(creds.ChangeExternalPasswordJS)) + // wasm/backup.go js.Global().Set("NewCmixFromBackup", js.FuncOf(wasm.NewCmixFromBackup)) js.Global().Set("InitializeBackup", js.FuncOf(wasm.InitializeBackup)) diff --git a/utils/localStorage.go b/utils/localStorage.go new file mode 100644 index 00000000..11b49c82 --- /dev/null +++ b/utils/localStorage.go @@ -0,0 +1,120 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +//go:build js && wasm + +package utils + +import ( + "encoding/base64" + "os" + "syscall/js" +) + +// LocalStorage contains the js.Value representation of localStorage. +type LocalStorage struct { + v js.Value +} + +// jsStorage is the global that stores Javascript as window.localStorage. +// +// - Specification: +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage-dev +// - Documentation: +// https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage +var jsStorage = LocalStorage{js.Global().Get("localStorage")} + +// GetLocalStorage returns Javascript's local storage. +func GetLocalStorage() *LocalStorage { + return &jsStorage +} + +// GetItem returns a key's value from the local storage given its name. Returns +// os.ErrNotExist if the key does not exist. Underneath, it calls +// localStorage.GetItem(). +// +// - Specification: +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-getitem-dev +// - Documentation: +// https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem +func (s *LocalStorage) GetItem(keyName string) ([]byte, error) { + keyValue := s.v.Call("getItem", keyName) + if keyValue.IsNull() { + return nil, os.ErrNotExist + } + + decodedKeyValue, err := base64.StdEncoding.DecodeString(keyValue.String()) + if err != nil { + return nil, err + } + + return decodedKeyValue, nil +} + +// SetItem adds a key's value to local storage given its name. Underneath, it +// calls localStorage.SetItem(). +// +// - Specification: +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-setitem-dev +// - Documentation: +// https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem +func (s *LocalStorage) SetItem(keyName string, keyValue []byte) { + encodedKeyValue := base64.StdEncoding.EncodeToString(keyValue) + s.v.Call("setItem", keyName, encodedKeyValue) +} + +// RemoveItem removes a key's value from local storage given its name. If there +// is no item with the given key, this function does nothing. Underneath, it +// calls localStorage.RemoveItem(). +// +// - Specification: +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-removeitem-dev +// - Documentation: +// https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem +func (s *LocalStorage) RemoveItem(keyName string) { + s.v.Call("removeItem", keyName) +} + +// Clear clears all the keys in storage. Underneath, it calls +// localStorage.clear(). +// +// - Specification: +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-clear-dev +// - Documentation: +// https://developer.mozilla.org/en-US/docs/Web/API/Storage/clear +func (s *LocalStorage) Clear() { + s.v.Call("clear") +} + +// Key returns the name of the nth key in localStorage. Return os.ErrNotExist if +// the key does not exist. The order of keys is not defined. If there is no item +// with the given key, this function does nothing. Underneath, it calls +// localStorage.key(). +// +// - Specification: +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-key-dev +// - Documentation: +// https://developer.mozilla.org/en-US/docs/Web/API/Storage/key +func (s *LocalStorage) Key(n int) (string, error) { + keyName := s.v.Call("key", n) + if keyName.IsNull() { + return "", os.ErrNotExist + } + + return keyName.String(), nil +} + +// Length returns the number of keys in localStorage. Underneath, it accesses +// the property localStorage.length. +// +// - Specification: +// https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-key-dev +// - Documentation: +// https://developer.mozilla.org/en-US/docs/Web/API/Storage/length +func (s *LocalStorage) Length() int { + return s.v.Get("length").Int() +} diff --git a/utils/localStorage_test.go b/utils/localStorage_test.go new file mode 100644 index 00000000..953872cd --- /dev/null +++ b/utils/localStorage_test.go @@ -0,0 +1,169 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +//go:build js && wasm + +package utils + +import ( + "bytes" + "github.com/pkg/errors" + "os" + "strconv" + "testing" +) + +// Tests that a value set with LocalStorage.SetItem and retrieved with +// LocalStorage.GetItem matches the original. +func TestLocalStorage_GetItem_SetItem(t *testing.T) { + values := map[string][]byte{ + "key1": []byte("key value"), + "key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + "key3": {0, 49, 0, 0, 0, 38, 249, 93, 242, 189, 222, 32, 138, 248, 121, + 151, 42, 108, 82, 199, 163, 61, 4, 200, 140, 231, 225, 20, 35, 243, + 253, 161, 61, 2, 227, 208, 173, 183, 33, 66, 236, 107, 105, 119, 26, + 42, 44, 60, 109, 172, 38, 47, 220, 17, 129, 4, 234, 241, 141, 81, + 84, 185, 32, 120, 115, 151, 128, 196, 143, 117, 222, 78, 44, 115, + 109, 20, 249, 46, 158, 139, 231, 157, 54, 219, 141, 252}, + } + + for keyName, keyValue := range values { + jsStorage.SetItem(keyName, keyValue) + + loadedValue, err := jsStorage.GetItem(keyName) + if err != nil { + t.Errorf("Failed to load %q: %+v", keyName, err) + } + + if !bytes.Equal(keyValue, loadedValue) { + t.Errorf("Loaded value does not match original for %q"+ + "\nexpected: %q\nreceived: %q", keyName, keyValue, loadedValue) + } + } +} + +// Tests that LocalStorage.GetItem returns the error os.ErrNotExist when the key +// does not exist in storage. +func TestLocalStorage_GetItem_NotExistError(t *testing.T) { + _, err := jsStorage.GetItem("someKey") + if err == nil || !errors.Is(err, os.ErrNotExist) { + t.Errorf("Incorrect error for non existant key."+ + "\nexpected: %v\nreceived: %v", os.ErrNotExist, err) + } +} + +// Tests that LocalStorage.RemoveItem deletes a key from store and that it +// cannot be retrieved. +func TestLocalStorage_RemoveItem(t *testing.T) { + keyName := "key" + jsStorage.SetItem(keyName, []byte("value")) + jsStorage.RemoveItem(keyName) + + _, err := jsStorage.GetItem(keyName) + if err == nil || !errors.Is(err, os.ErrNotExist) { + t.Errorf("Failed to remove %q: %+v", keyName, err) + } +} + +// Tests that LocalStorage.Clear deletes all keys from storage. +func TestLocalStorage_Clear(t *testing.T) { + for i := 0; i < 10; i++ { + jsStorage.SetItem(strconv.Itoa(i), []byte(strconv.Itoa(i))) + } + + jsStorage.Clear() + + l := jsStorage.Length() + + if l > 0 { + t.Errorf("Clear did not delete all keys. Found %d keys.", l) + } +} + +// Tests that LocalStorage.Key return all added keys when looping through all +// indexes. +func TestLocalStorage_Key(t *testing.T) { + jsStorage.v.Call("clear") + values := map[string][]byte{ + "key1": []byte("key value"), + "key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + "key3": {0, 49, 0, 0, 0, 38, 249, 93}, + } + + for keyName, keyValue := range values { + jsStorage.SetItem(keyName, keyValue) + } + + numKeys := len(values) + for i := 0; i < numKeys; i++ { + keyName, err := jsStorage.Key(i) + if err != nil { + t.Errorf("No key found for index %d: %+v", i, err) + } + + if _, exists := values[keyName]; !exists { + t.Errorf("No key with name %q added to storage.", keyName) + } + delete(values, keyName) + } + + if len(values) != 0 { + t.Errorf("%d keys not read from storage: %q", len(values), values) + } +} + +// Tests that LocalStorage.Key returns the error os.ErrNotExist when the index +// is greater than or equal to the number of keys. +func TestLocalStorage_Key_NotExistError(t *testing.T) { + jsStorage.v.Call("clear") + jsStorage.SetItem("key", []byte("value")) + + _, err := jsStorage.Key(1) + if err == nil || !errors.Is(err, os.ErrNotExist) { + t.Errorf("Incorrect error for non existant key index."+ + "\nexpected: %v\nreceived: %v", os.ErrNotExist, err) + } + + _, err = jsStorage.Key(2) + if err == nil || !errors.Is(err, os.ErrNotExist) { + t.Errorf("Incorrect error for non existant key index."+ + "\nexpected: %v\nreceived: %v", os.ErrNotExist, err) + } +} + +// Tests that LocalStorage.Length returns the correct Length when adding and +// removing various keys. +func TestLocalStorage_Length(t *testing.T) { + jsStorage.v.Call("clear") + values := map[string][]byte{ + "key1": []byte("key value"), + "key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + "key3": {0, 49, 0, 0, 0, 38, 249, 93}, + } + + i := 0 + for keyName, keyValue := range values { + jsStorage.SetItem(keyName, keyValue) + i++ + + if jsStorage.Length() != i { + t.Errorf("Incorrect length.\nexpected: %d\nreceived: %d", + i, jsStorage.Length()) + } + } + + i = len(values) + for keyName := range values { + jsStorage.RemoveItem(keyName) + i-- + + if jsStorage.Length() != i { + t.Errorf("Incorrect length.\nexpected: %d\nreceived: %d", + i, jsStorage.Length()) + } + } +} -- GitLab