diff --git a/cmd/storj/main.go b/cmd/storj/main.go deleted file mode 100644 index 8028d9d68..000000000 --- a/cmd/storj/main.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package main - -import ( - "log" - "os" - - "storj.io/storj/internal/app/cli" -) - -func main() { - err := cli.New().Run(os.Args) - if err != nil { - log.Fatal(err) - } -} diff --git a/internal/app/cli/app.go b/internal/app/cli/app.go deleted file mode 100644 index 370570295..000000000 --- a/internal/app/cli/app.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package cli - -import ( - "fmt" - "os" - - "github.com/urfave/cli" - "storj.io/storj/pkg/client" -) - -// New creates a new storj cli application with the respective commands and metainfo. -func New() *cli.App { - app := cli.NewApp() - app.Name = "storj" - app.Version = "0.0.2" - app.Usage = "command line interface to the Storj network" - app.Commands = []cli.Command{ - { - Name: "get-info", - Usage: "prints bridge api information", - ArgsUsage: " ", // no args - Category: "bridge api information", - Action: func(c *cli.Context) error { - getInfo() - return nil - }, - }, - { - Name: "list-buckets", - Usage: "lists the available buckets", - ArgsUsage: " ", // no args - Category: "working with buckets and files", - Action: func(c *cli.Context) error { - listBuckets() - return nil - }, - }, - } - - cli.AppHelpTemplate = fmt.Sprintf(`%s -ENVIRONMENT VARIABLES: - STORJ_BRIDGE the bridge host (e.g. https://api.storj.io) - STORJ_BRIDGE_USER bridge username - STORJ_BRIDGE_PASS bridge password - STORJ_ENCRYPTION_KEY file encryption key - -`, cli.AppHelpTemplate) - - return app -} - -func getInfo() { - env := client.NewEnv() - info, err := client.GetInfo(env) - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(1) - } - fmt.Printf("Storj bridge: %s\n\n"+ - "Title: %s\n"+ - "Description: %s\n"+ - "Version: %s\n"+ - "Host: %s\n", - env.URL, info.Title, info.Description, info.Version, info.Host) -} - -func listBuckets() { - buckets, err := client.GetBuckets(client.NewEnv()) - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(1) - } - for _, b := range buckets { - fmt.Printf("ID: %s\tDecrypted: %t\t\tCreated: %s\tName: %s\n", - b.ID, b.Decrypted, b.Created, b.Name) - } -} diff --git a/pkg/client/buckets.go b/pkg/client/buckets.go deleted file mode 100644 index 8da27dcac..000000000 --- a/pkg/client/buckets.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "encoding/json" - "io/ioutil" - "log" - "net/http" - "strconv" -) - -// Bucket metdata -type Bucket struct { - ID string - Name string - Created string - Decrypted bool -} - -// GetBuckets returns the list of buckets -func GetBuckets(env Env) ([]Bucket, error) { - client := &http.Client{} - req, err := http.NewRequest("GET", env.URL+"/buckets", nil) - if err != nil { - return []Bucket{}, err - } - req.SetBasicAuth(env.User, env.Password) - resp, err := client.Do(req) - if err != nil { - return []Bucket{}, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return []Bucket{}, UnexpectedStatusCode.New(strconv.Itoa(resp.StatusCode)) - } - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return []Bucket{}, err - } - var buckets []Bucket - err = json.Unmarshal(b, &buckets) - if err != nil { - return buckets, err - } - for i, b := range buckets { - decryptedName, err := decryptBucketName(b.Name, env.Mnemonic) - if err != nil { - log.Printf("Could not decrypt bucket name %s: %s\n", b.Name, err) - continue - } - buckets[i].Name = decryptedName - buckets[i].Decrypted = true - } - return buckets, err -} diff --git a/pkg/client/buckets_test.go b/pkg/client/buckets_test.go deleted file mode 100644 index bc3a60030..000000000 --- a/pkg/client/buckets_test.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/julienschmidt/httprouter" - "github.com/stretchr/testify/assert" -) - -const ( - testEncryptedBucketName = "cqiNhd3Y16uXRBpRKbcGdrhVvouLRFlBM5O1jMUOr6OKJUVGpvv0LLaBv+6kqzyVvp5jFw==" - testEncryptedBucketNameDiffMnemonic = "Yq3Ky6jJ7dwWiC9MEcb5nhAl5P0xfYe6jCwwwlzd1a1kZxKYLcft/WkOC8dhcwLb3Ka9xA==" - testDecryptedBucketName = "test" -) - -func TestGetBuckets(t *testing.T) { - var tsResponse string - router := httprouter.New() - router.GET("/buckets", basicAuth(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - fmt.Fprintf(w, tsResponse) - }, testBridgeUser, testBridgePass)) - ts := httptest.NewServer(router) - defer ts.Close() - - for i, tt := range []struct { - env Env - response string - buckets []Bucket - errString string - }{ - {NewNoAuthTestEnv(ts), "", nil, "unexpected status code: 401"}, - {NewBadPassTestEnv(ts), "", nil, "unexpected status code: 401"}, - {NewTestEnv(ts), "", []Bucket{}, "unexpected end of JSON input"}, - {NewTestEnv(ts), "{", []Bucket{}, "unexpected end of JSON input"}, - {NewTestEnv(ts), "{}", []Bucket{}, "json: cannot unmarshal object into Go value of type []client.Bucket"}, - {NewTestEnv(ts), "[]", []Bucket{}, ""}, - {NewTestEnv(ts), - `[ - { - "id": "e3eca45f4d294132c07b49f4", - "name": "cqiNhd3Y16uXRBpRKbcGdrhVvouLRFlBM5O1jMUOr6OKJUVGpvv0LLaBv+6kqzyVvp5jFw==", - "created": "2016-10-12T14:40:21.259Z" - } - ]`, []Bucket{ - Bucket{ - ID: "e3eca45f4d294132c07b49f4", - Name: testDecryptedBucketName, - Created: "2016-10-12T14:40:21.259Z", - Decrypted: true, - }, - }, ""}, - {NewNoMnemonicTestEnv(ts), - `[ - { - "id": "e3eca45f4d294132c07b49f4", - "name": "cqiNhd3Y16uXRBpRKbcGdrhVvouLRFlBM5O1jMUOr6OKJUVGpvv0LLaBv+6kqzyVvp5jFw==", - "created": "2016-10-12T14:40:21.259Z" - } - ]`, []Bucket{ - Bucket{ - ID: "e3eca45f4d294132c07b49f4", - Name: testEncryptedBucketName, - Created: "2016-10-12T14:40:21.259Z", - Decrypted: false, - }, - }, ""}, - {NewTestEnv(ts), - `[ - { - "id": "e3eca45f4d294132c07b49f4", - "name": "Yq3Ky6jJ7dwWiC9MEcb5nhAl5P0xfYe6jCwwwlzd1a1kZxKYLcft/WkOC8dhcwLb3Ka9xA==", - "created": "2016-10-12T14:40:21.259Z" - } - ]`, []Bucket{ - Bucket{ - ID: "e3eca45f4d294132c07b49f4", - Name: testEncryptedBucketNameDiffMnemonic, - Created: "2016-10-12T14:40:21.259Z", - Decrypted: false, - }, - }, ""}, - {NewTestEnv(ts), - `[ - { - "id": "e3eca45f4d294132c07b49f4", - "name": "test", - "created": "2016-10-12T14:40:21.259Z" - } - ]`, []Bucket{ - Bucket{ - ID: "e3eca45f4d294132c07b49f4", - Name: testDecryptedBucketName, - Created: "2016-10-12T14:40:21.259Z", - Decrypted: false, - }, - }, ""}, - {NewNoMnemonicTestEnv(ts), - `[ - { - "id": "e3eca45f4d294132c07b49f4", - "name": "test", - "created": "2016-10-12T14:40:21.259Z" - } - ]`, []Bucket{ - Bucket{ - ID: "e3eca45f4d294132c07b49f4", - Name: testDecryptedBucketName, - Created: "2016-10-12T14:40:21.259Z", - Decrypted: false, - }, - }, ""}, - } { - tsResponse = tt.response - buckets, err := GetBuckets(tt.env) - errTag := fmt.Sprintf("Test case #%d", i) - if tt.errString != "" { - assert.EqualError(t, err, tt.errString, errTag) - continue - } - if assert.NoError(t, err, errTag) { - assert.Equal(t, tt.buckets, buckets, errTag) - } - } -} diff --git a/pkg/client/crypto.go b/pkg/client/crypto.go deleted file mode 100644 index eb9218c2c..000000000 --- a/pkg/client/crypto.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/hmac" - "crypto/sha256" - "crypto/sha512" - "encoding/base64" - "encoding/hex" - - bip39 "github.com/tyler-smith/go-bip39" -) - -const ( - bucketNameMagic = "398734aab3c4c30c9f22590e83a95f7e43556a45fc2b3060e0c39fde31f50272" - gcmDigestSize = 16 - ivSize = 32 -) - -var bucketMetaMagic = []byte{ - 66, 150, 71, 16, 50, 114, 88, 160, 163, 35, 154, 65, 162, 213, 226, 215, - 70, 138, 57, 61, 52, 19, 210, 170, 38, 164, 162, 200, 86, 201, 2, 81} - -func sha256Sum(str string) string { - checksum := sha256.Sum256([]byte(str)) - return hex.EncodeToString(checksum[:]) -} - -func mnemonicToSeed(mnemonic string) (string, error) { - seed, err := bip39.NewSeedWithErrorChecking(mnemonic, "") - if err != nil { - return "", err - } - return hex.EncodeToString(seed), nil -} - -func getDeterministicKey(key, id string) (string, error) { - input, err := hex.DecodeString(key + id) - if err != nil { - return "", err - } - checksum := sha512.Sum512(input) - str := hex.EncodeToString(checksum[:]) - return str[0 : len(str)/2], nil -} - -func generateBucketKey(mnemonic, bucketID string) (string, error) { - seed, err := mnemonicToSeed(mnemonic) - if err != nil { - return "", err - } - return getDeterministicKey(seed, bucketNameMagic) -} - -func decryptMeta(encryptedName string, key []byte) ([]byte, error) { - nameBase64, err := base64.StdEncoding.DecodeString(encryptedName) - if err != nil { - return []byte{}, err - } - if len(nameBase64) <= gcmDigestSize+ivSize { - return []byte{}, CryptoError.New("Invalid encrypted name") - } - block, err := aes.NewCipher(key) - if err != nil { - return []byte{}, err - } - aesgcm, err := cipher.NewGCMWithNonceSize(block, ivSize) - if err != nil { - return []byte{}, err - } - digest := nameBase64[:gcmDigestSize] - iv := nameBase64[gcmDigestSize : gcmDigestSize+ivSize] - cipherText := nameBase64[gcmDigestSize+ivSize:] - return aesgcm.Open(nil, iv, append(cipherText, digest...), nil) -} - -func decryptBucketName(name, mnemonic string) (string, error) { - bucketKey, err := generateBucketKey(mnemonic, bucketNameMagic) - if err != nil { - return "", err - } - bucketKeyHex, err := hex.DecodeString(bucketKey) - if err != nil { - return "", err - } - sig := hmac.New(sha512.New, bucketKeyHex) - _, err = sig.Write(bucketMetaMagic) - if err != nil { - return "", err - } - hmacSHA512 := sig.Sum(nil) - key := hmacSHA512[0 : len(hmacSHA512)/2] - decryptedName, err := decryptMeta(name, key) - if err != nil { - return "", err - } - return string(decryptedName), nil -} diff --git a/pkg/client/crypto_test.go b/pkg/client/crypto_test.go deleted file mode 100644 index 4b8192c45..000000000 --- a/pkg/client/crypto_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -const testMnemonic = "uncle obtain april oxygen cover patient layer abuse off text royal normal" - -func TestDecryptBucketName(t *testing.T) { - for i, tt := range []struct { - encryptedName string - mnemonic string - decryptedName string - errString string - }{ - {"", "", "", "Invalid mnemonic"}, - {"", testMnemonic, "", "Invalid encrypted name"}, - {testEncryptedBucketName, "", "", "Invalid mnemonic"}, - {testEncryptedBucketName, testMnemonic, testDecryptedBucketName, ""}, - {testEncryptedBucketNameDiffMnemonic, testMnemonic, "", "cipher: message authentication failed"}, - } { - decryptedName, err := decryptBucketName(tt.encryptedName, tt.mnemonic) - if tt.errString != "" { - assert.Containsf(t, err.Error(), tt.errString, "Test case #%d", i) - continue - } - if assert.NoErrorf(t, err, "Test case #%d", i) { - assert.Equalf(t, tt.decryptedName, decryptedName, "Test case #%d", i) - } - } -} diff --git a/pkg/client/env.go b/pkg/client/env.go deleted file mode 100644 index 34a4befe8..000000000 --- a/pkg/client/env.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "strings" - - "github.com/spf13/viper" -) - -// DefaultURL of the Storj Bridge API endpoint -const DefaultURL = "https://api.storj.io" - -func init() { - viper.SetEnvPrefix("storj") - viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - viper.AutomaticEnv() - viper.SetDefault("bridge", DefaultURL) -} - -// Env contains parameters for accessing the Storj network -type Env struct { - URL string - User string - Password string - Mnemonic string -} - -// NewEnv creates new Env struct with default values -func NewEnv() Env { - return Env{ - URL: viper.GetString("bridge"), - User: viper.GetString("bridge-user"), - Password: sha256Sum(viper.GetString("bridge-pass")), - Mnemonic: viper.GetString("encryption-key"), - } -} diff --git a/pkg/client/env_test.go b/pkg/client/env_test.go deleted file mode 100644 index f5ee2e7a3..000000000 --- a/pkg/client/env_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/julienschmidt/httprouter" - "github.com/stretchr/testify/assert" -) - -const ( - testBridgeURL = "http://example.com" - testBridgeUser = "testuser@storj.io" - testBridgePass = "secret" -) - -func NewTestEnv(ts *httptest.Server) Env { - return Env{ - URL: ts.URL, - User: testBridgeUser, - Password: testBridgePass, - Mnemonic: testMnemonic, - } -} - -func NewNoAuthTestEnv(ts *httptest.Server) Env { - return Env{ - URL: ts.URL, - } -} - -func NewBadPassTestEnv(ts *httptest.Server) Env { - return Env{ - URL: ts.URL, - User: testBridgeUser, - Password: "bad password", - Mnemonic: testMnemonic, - } -} - -func NewNoMnemonicTestEnv(ts *httptest.Server) Env { - return Env{ - URL: ts.URL, - User: testBridgeUser, - Password: testBridgePass, - } -} - -func basicAuth(h httprouter.Handle, requiredUser, requiredPassword string) httprouter.Handle { - return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - // Get the Basic Authentication credentials - user, password, hasAuth := r.BasicAuth() - - if hasAuth && user == requiredUser && password == requiredPassword { - // Delegate request to the given handle - h(w, r, ps) - } else { - // Request Basic Authentication otherwise - w.Header().Set("WWW-Authenticate", "Basic realm=Restricted") - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - } - } -} - -func TestNewEnv(t *testing.T) { - for _, tt := range []struct { - env Env - url string - }{ - {Env{}, ""}, - {NewEnv(), DefaultURL}, - {Env{URL: testBridgeURL}, testBridgeURL}, - } { - assert.Equal(t, tt.url, tt.env.URL) - } -} - -func TestNewEnvVars(t *testing.T) { - os.Setenv("STORJ_BRIDGE", testBridgeURL) - defer os.Unsetenv("STORJ_BRIDGE") - os.Setenv("STORJ_BRIDGE_USER", testBridgeUser) - defer os.Unsetenv("STORJ_BRIDGE_USER") - os.Setenv("STORJ_BRIDGE_PASS", testBridgePass) - defer os.Unsetenv("STORJ_BRIDGE_PASS") - os.Setenv("STORJ_ENCRYPTION_KEY", testMnemonic) - defer os.Unsetenv("STORJ_ENCRYPTION_KEY") - - env := NewEnv() - - assert.Equal(t, testBridgeURL, env.URL) - assert.Equal(t, testBridgeUser, env.User) - assert.Equal(t, sha256Sum(testBridgePass), env.Password) - assert.Equal(t, testMnemonic, env.Mnemonic) -} diff --git a/pkg/client/errors.go b/pkg/client/errors.go deleted file mode 100644 index 309a07250..000000000 --- a/pkg/client/errors.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "github.com/zeebo/errs" -) - -// UnexpectedStatusCode is an error class for unexpected HTTP response -var UnexpectedStatusCode = errs.Class("unexpected status code") - -// CryptoError is an error class for encryption errors -var CryptoError = errs.Class("encryption error") diff --git a/pkg/client/info.go b/pkg/client/info.go deleted file mode 100644 index c5c0aa3ca..000000000 --- a/pkg/client/info.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "encoding/json" - "io/ioutil" - "net/http" - "strconv" -) - -// Info struct of the GetInfo() response -type Info struct { - Title string - Description string - Version string - Host string -} - -type swagger struct { - Info struct { - Title string - Description string - Version string - } - Host string -} - -// UnmarshalJSON overrides the unmarshalling for Info to correctly extract the data from the Swagger JSON response -func (info *Info) UnmarshalJSON(b []byte) error { - var s swagger - json.Unmarshal(b, &s) - info.Title = s.Info.Title - info.Description = s.Info.Description - info.Version = s.Info.Version - info.Host = s.Host - return nil -} - -// GetInfo returns info about the Storj Bridge server -func GetInfo(env Env) (Info, error) { - info := Info{} - resp, err := http.Get(env.URL) - if err != nil { - return info, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return info, UnexpectedStatusCode.New(strconv.Itoa(resp.StatusCode)) - } - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return info, err - } - err = json.Unmarshal(b, &info) - return info, err -} diff --git a/pkg/client/info_test.go b/pkg/client/info_test.go deleted file mode 100644 index e26df5ca9..000000000 --- a/pkg/client/info_test.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 2018 Storj Labs, Inc. -// See LICENSE for copying information. - -package client - -import ( - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - - "github.com/julienschmidt/httprouter" - "github.com/stretchr/testify/assert" -) - -const ( - testTitle = "Storj Bridge" - testDescription = "Some description" - testVersion = "1.2.3" - testHost = "1.2.3.4" -) - -func TestUnmarshalJSON(t *testing.T) { - for i, tt := range []struct { - json string - info Info - errString string - }{ - {"", Info{}, "unexpected end of JSON input"}, - {"{", Info{}, "unexpected end of JSON input"}, - {"{}", Info{}, ""}, - {`{"info":{}}`, Info{}, ""}, - {`{"info":10}`, Info{}, ""}, - {`{"info":{"title":10,"description":10,"version":10},"host":10}`, Info{}, ""}, - {fmt.Sprintf(`{"info":{"description":"%s","version":"%s"},"host":"%s"}`, - testDescription, testVersion, testHost), - Info{ - Description: testDescription, - Version: testVersion, - Host: testHost, - }, - ""}, - {fmt.Sprintf(`{"info":{"title":"%s","version":"%s"},"host":"%s"}`, - testTitle, testVersion, testHost), - Info{ - Title: testTitle, - Version: testVersion, - Host: testHost, - }, - ""}, - {fmt.Sprintf(`{"info":{"title":"%s","description":"%s"},"host":"%s"}`, - testTitle, testDescription, testHost), - Info{ - Title: testTitle, - Description: testDescription, - Host: testHost, - }, - ""}, - {fmt.Sprintf(`{"info":{"title":"%s","description":"%s","version":"%s"}}`, - testTitle, testDescription, testVersion), - Info{ - Title: testTitle, - Description: testDescription, - Version: testVersion, - }, - ""}, - {fmt.Sprintf(`{"info":{"title":"%s","description":"%s","version":"%s"},"host":"%s"}`, - testTitle, testDescription, testVersion, testHost), - Info{ - Title: testTitle, - Description: testDescription, - Version: testVersion, - Host: testHost, - }, - ""}, - } { - var info Info - err := json.Unmarshal([]byte(tt.json), &info) - errTag := fmt.Sprintf("Test case #%d", i) - if tt.errString != "" { - assert.EqualError(t, err, tt.errString, errTag) - continue - } - if assert.NoError(t, err, errTag) { - assert.Equal(t, tt.info, info, errTag) - } - } -} - -func TestGetInfo(t *testing.T) { - router := httprouter.New() - router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - fmt.Fprintf(w, `{"info":{"title":"%s","description":"%s","version":"%s"},"host":"%s"}`, - testTitle, testDescription, testVersion, testHost) - }) - ts := httptest.NewServer(router) - defer ts.Close() - - for i, tt := range []struct { - env Env - errString string - }{ - {NewTestEnv(ts), ""}, - {Env{URL: ts.URL + "/info"}, "unexpected status code: 404"}, - } { - info, err := GetInfo(tt.env) - errTag := fmt.Sprintf("Test case #%d", i) - if tt.errString != "" { - assert.EqualError(t, err, tt.errString, errTag) - continue - } - if assert.NoError(t, err, errTag) { - assert.Equal(t, testTitle, info.Title, errTag) - assert.Equal(t, testDescription, info.Description, errTag) - assert.Equal(t, testVersion, info.Version, errTag) - assert.Equal(t, testHost, info.Host, errTag) - } - } -}