Skip to content

Commit

Permalink
Share: Add encrypt/decrypt parameters.
Browse files Browse the repository at this point in the history
  • Loading branch information
Theotime Leveque committed Mar 20, 2017
1 parent 820116d commit 931f295
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 73 deletions.
47 changes: 47 additions & 0 deletions backend/share/encryption.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package share

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"errors"
"io"
)

// EncryptFile encrypt and return a file
func EncryptFile(text, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
b := base64.StdEncoding.EncodeToString(text)
ciphertext := make([]byte, aes.BlockSize+len(b))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
cfb := cipher.NewCFBEncrypter(block, iv)
cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
return ciphertext, nil
}

// DecryptFile encrypt and return a file
func DecryptFile(text []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
if len(text) < aes.BlockSize {
return nil, errors.New("ciphertext too short")
}
iv := text[:aes.BlockSize]
text = text[aes.BlockSize:]
cfb := cipher.NewCFBDecrypter(block, iv)
cfb.XORKeyStream(text, text)
data, err := base64.StdEncoding.DecodeString(string(text))
if err != nil {
return nil, err
}
return data, nil
}
77 changes: 45 additions & 32 deletions backend/share/share.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ package share

import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"strings"

"github.com/fatih/color"
"github.com/thylong/ian/backend/config"
)

// Upload to transfer.sh
Expand All @@ -22,19 +22,23 @@ func Upload(filename string, targetURL string, key string) (string, error) {

fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename)
if err != nil {
fmt.Fprintf(os.Stderr, "%v Failed to write to buffer.", color.RedString("Error:"))
return "", err
return "", fmt.Errorf("%v Failed to write to buffer", color.RedString("Error:"))
}

var fh io.Reader
if key == "" {
fh, err = os.Open(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "%v Failed to open file.", color.RedString("Error:"))
return "", err
return "", fmt.Errorf("%v Failed to open file", color.RedString("Error:"))
}
} else {
fh = EncryptFile(filename, []byte(key))
text, _ := ioutil.ReadFile(filename)
var encrypted []byte
encrypted, err = EncryptFile(text, []byte(key))
if err != nil {
return "", fmt.Errorf("%v Failed to encrypt file", color.RedString("Error:"))
}
fh = bytes.NewReader(encrypted)
}

// iocopy
Expand All @@ -59,37 +63,46 @@ func Upload(filename string, targetURL string, key string) (string, error) {
return string(respBody), nil
}

// ZipConfigFiles produces a Zip archive
func ZipConfigFiles(filename string) {

}

// EncryptFile encrypt and return a file
func EncryptFile(filename string, key []byte) io.Reader {
fileContent, err := ioutil.ReadFile(filename)

block, err := aes.NewCipher(key)
// Download retrieves a distant configuration file
func Download(URL string, configFileName string, key string) error {
_, err := url.ParseRequestURI(URL)
if err != nil {
fmt.Fprintf(os.Stderr, "%v %s", color.RedString("Error:"), err)
os.Exit(1)
return fmt.Errorf("%v Sorry, The link you provided is invalid", color.RedString("Error:"))
}

// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(fileContent))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
fmt.Fprintf(os.Stderr, "%v %s", color.RedString("Error:"), err)
os.Exit(1)
resp, err := http.Get(URL)
if err != nil {
return fmt.Errorf("%v Sorry, The link you provided is unreachable", color.RedString("Error:"))
}
defer resp.Body.Close()

stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], fileContent)
return bytes.NewReader(ciphertext)
confFileName := strings.TrimSuffix(configFileName, ".yml")
if confFilePath, ok := config.ConfigFilesPathes[confFileName]; ok {
f, err := os.OpenFile(confFilePath, os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
return err
}
defer f.Close()

content, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("%v Cannot read from HTTP response", color.RedString("Error:"))
}
if key != "" {
if content, err = DecryptFile(content, []byte(key)); err != nil {
return fmt.Errorf("%v Cannot decrypt downloaded file", color.RedString("Error:"))
}
}

if _, err := io.Copy(f, bytes.NewReader(content)); err != nil {
return fmt.Errorf("%v %s", color.RedString("Error:"), err)
}
}
return nil
}

// GetshareSetFromLinkCmdUsageTemplate returns shareSetFromLinkCmd usage template
func GetshareSetFromLinkCmdUsageTemplate() string {
// GetshareRetrieveFromLinkCmdUsageTemplate returns shareRetrieveFromLinkCmd usage template
func GetshareRetrieveFromLinkCmdUsageTemplate() string {
return `Usage:{{if .Runnable}}
{{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "<url> <conf_file> [flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
{{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
Expand Down
56 changes: 15 additions & 41 deletions cmd/share.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ package cmd

import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"

"github.com/fatih/color"
"github.com/spf13/cobra"
Expand All @@ -31,22 +27,23 @@ import (
var key = ""

var encryptShareCmdParam bool
var decryptShareCmdParam bool

// var shortLinkShareCmdParam bool

func init() {
RootCmd.AddCommand(shareCmd)

shareCmd.PersistentFlags().BoolVarP(&encryptShareCmdParam, "encrypt", "s", false, "Encrypt with private key before uploading")
shareCmd.PersistentFlags().BoolVarP(&encryptShareCmdParam, "encrypt", "e", false, "Encrypt with private key before uploading")
shareCmd.PersistentFlags().BoolVarP(&decryptShareCmdParam, "decrypt", "d", false, "Decrypt config file")
// shareCmd.PersistentFlags().BoolVarP(&shortLinkShareCmdParam, "bitlink", "b", false, "Get a Bit.ly shorten URL")

shareSetFromLinkCmd.SetUsageTemplate(share.GetshareSetFromLinkCmdUsageTemplate())
shareRetrieveFromLinkCmd.SetUsageTemplate(share.GetshareRetrieveFromLinkCmdUsageTemplate())

shareCmd.AddCommand(
shareConfigCmd,
shareProjectsCmd,
shareEnvCmd,
shareSetFromLinkCmd,
shareRetrieveFromLinkCmd,
// shareAllCmd,
)
}
Expand Down Expand Up @@ -98,7 +95,7 @@ var shareEnvCmd = &cobra.Command{
Long: `Share a public link to ian env.yml file.`,
Run: func(cmd *cobra.Command, args []string) {
if encryptShareCmdParam {
key = config.GetUserInput("Enter a secret key: ")
key = config.GetUserInput("Enter a secret key (32 characters minimum)")
}
link, err := share.Upload(config.ConfigFilesPathes[cmd.Use], "https://transfer.sh/", key)
if err != nil {
Expand All @@ -109,49 +106,26 @@ var shareEnvCmd = &cobra.Command{
},
}

var shareSetFromLinkCmd = &cobra.Command{
Use: "set",
Short: "Set config from config file link",
Long: `Set config from config file link.`,
var shareRetrieveFromLinkCmd = &cobra.Command{
Use: "retrieve",
Short: "Retrieve config from config file link",
Long: `Retrieve config from config file link.`,
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
fmt.Fprintf(os.Stderr, "%v Not enough argument.\n\n", color.RedString("Error:"))
cmd.Usage()
os.Exit(1)
}

_, err := url.ParseRequestURI(args[0])
if err != nil {
fmt.Fprintf(os.Stderr, "%v Sorry, The link you provided is invalid.", color.RedString("Error:"))
key := ""
if decryptShareCmdParam {
key = config.GetUserInput("Enter the secret key")
}

resp, err := http.Get(args[0])
err := share.Download(args[0], args[1], key)
if err != nil {
fmt.Fprintf(os.Stderr, "%v Sorry, The link you provided is unreachable.", color.RedString("Error:"))
}
defer resp.Body.Close()

if strings.HasSuffix(string(strings.TrimSuffix(args[0], "/")), "_e") {
// DecryptFile
}

confFileName := strings.TrimSuffix(args[1], ".yml")
fmt.Print(confFileName)
if confFilePath, ok := config.ConfigFilesPathes[confFileName]; ok {
f, err := os.OpenFile(confFilePath, os.O_TRUNC|os.O_WRONLY, 0600)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer f.Close()

if _, err := io.Copy(f, resp.Body); err != nil {
fmt.Fprintf(os.Stderr, "%v %s", color.RedString("Error:"), err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "%v %s.", color.RedString("Error:"), err)
return
}
fmt.Fprintf(os.Stderr, "%v Sorry, something went wrong.", color.RedString("Error:"))
},
}

Expand Down

0 comments on commit 931f295

Please sign in to comment.