From 40e72ad9222c4bc5834c875026bbf486832b82ca Mon Sep 17 00:00:00 2001 From: Enrico Candino Date: Sun, 30 Oct 2022 01:06:34 +0200 Subject: [PATCH] Fix for absolute path and added images dir flag (#41) * fix for absolute path, added images dir flag Signed-off-by: Enrico Candino * added test Signed-off-by: Enrico Candino Signed-off-by: Enrico Candino --- .gitignore | 2 +- .golangci.yml | 6 ++ go.mod | 2 +- internal/cli/encrypt.go | 11 ++- internal/encrypt/encrypt.go | 65 +++++++++++----- internal/encrypt/encrypt_test.go | 125 ++++++++++++++++++++++++------- out/.gitignore | 2 + 7 files changed, 163 insertions(+), 50 deletions(-) create mode 100644 out/.gitignore diff --git a/.gitignore b/.gitignore index 6618467..a2e13a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .DS_Store images/* -out/ +out/* dist/ diff --git a/.golangci.yml b/.golangci.yml index 9556178..bbcb07c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,12 @@ run: go: "1.19" +issues: + exclude-rules: + - path: _test.go + linters: + - funlen + linters: enable-all: true diff --git a/go.mod b/go.mod index 3a99cbc..af6d03b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/enrichman/stegosecrets -go 1.18 +go 1.19 require ( github.com/auyer/steganography v1.0.1 diff --git a/internal/cli/encrypt.go b/internal/cli/encrypt.go index 798d047..ee72114 100644 --- a/internal/cli/encrypt.go +++ b/internal/cli/encrypt.go @@ -2,6 +2,7 @@ package cli import ( "bytes" + "path/filepath" "github.com/enrichman/stegosecrets/internal/encrypt" "github.com/enrichman/stegosecrets/internal/log" @@ -15,6 +16,7 @@ var ( keyParts int keyThreshold int outputDir string + imagesDir string ) func newEncryptCmd() *cobra.Command { @@ -28,7 +30,8 @@ func newEncryptCmd() *cobra.Command { encryptCmd.Flags().StringVarP(&cleartextFile, "file", "f", "", "file") encryptCmd.Flags().IntVarP(&keyParts, "parts", "p", 0, "parts") encryptCmd.Flags().IntVarP(&keyThreshold, "threshold", "t", 0, "threshold") - encryptCmd.Flags().StringVarP(&outputDir, "output", "o", "", "output dir") + encryptCmd.Flags().StringVarP(&outputDir, "output", "o", "out", "output dir") + encryptCmd.Flags().StringVarP(&imagesDir, "images", "i", "images", "images dir") return encryptCmd } @@ -37,6 +40,8 @@ func runEncryptCmd(cmd *cobra.Command, args []string) error { encrypter, err := encrypt.NewEncrypter( encrypt.WithParts(keyParts), encrypt.WithThreshold(keyThreshold), + encrypt.WithOutputDir(outputDir), + encrypt.WithImagesDir(imagesDir), ) if err != nil { return errors.Wrap(err, "failed creating encrypter") @@ -49,11 +54,13 @@ func runEncryptCmd(cmd *cobra.Command, args []string) error { } var toEncrypt []byte + if cleartextFile != "" { toEncrypt, err = file.ReadFile(cleartextFile) + cleartextFile = filepath.Base(cleartextFile) } else { - cleartextFile = "secret.enc" toEncrypt, err = getInputFromStdin() + cleartextFile = "secret" } if err != nil { diff --git a/internal/encrypt/encrypt.go b/internal/encrypt/encrypt.go index e7f0464..3d0ba0a 100644 --- a/internal/encrypt/encrypt.go +++ b/internal/encrypt/encrypt.go @@ -16,6 +16,8 @@ import ( type Encrypter struct { Parts int Threshold int + OutputDir string + ImagesDir string Logger log.Logger } @@ -62,6 +64,36 @@ func WithThreshold(threshold int) OptFunc { } } +func WithOutputDir(outputDir string) OptFunc { + return func(e *Encrypter) error { + absDir, err := filepath.Abs(outputDir) + if err != nil { + return errors.Wrap(err, "error getting absolute path for output directory") + } + + if err := os.MkdirAll(absDir, 0o744); err != nil { + return errors.Wrap(err, "failed creating output filede") + } + + e.OutputDir = absDir + + return nil + } +} + +func WithImagesDir(imagesDir string) OptFunc { + return func(e *Encrypter) error { + absDir, err := filepath.Abs(imagesDir) + if err != nil { + return errors.Wrap(err, "error getting absolute path for images directory") + } + + e.ImagesDir = absDir + + return nil + } +} + func (e *Encrypter) Encrypt(reader io.Reader, filename string) error { e.Logger.Print(fmt.Sprintf("Encrypting '%s'", filename)) @@ -93,19 +125,13 @@ func (e *Encrypter) Encrypt(reader io.Reader, filename string) error { return nil } -const outDirName = "out" - func (e *Encrypter) generateAndSaveMasterKey(filename string) ([]byte, error) { masterKey, err := sss.GenerateMasterKey() if err != nil { return nil, errors.Wrap(err, "failed generating master key") } - if err := os.MkdirAll(outDirName, 0o744); err != nil { - return nil, errors.Wrapf(err, "failed creatind folder '%s'", outDirName) - } - - err = file.WriteKey(masterKey, fmt.Sprintf("%s/%s.enc", outDirName, filename)) + err = file.WriteKey(masterKey, filepath.Join(e.OutputDir, fmt.Sprintf("%s.enc", filename))) if err != nil { return nil, errors.Wrap(err, "failed writing key file") } @@ -119,8 +145,7 @@ func (e *Encrypter) encryptAndSaveMessage(masterKey []byte, reader io.Reader, fi return errors.Wrap(err, "failed reading message") } - // FIX? is this a copy/paste bug? - err = file.WriteChecksum(message, fmt.Sprintf("%s/%s.enc", outDirName, filename)) + err = file.WriteChecksum(message, filepath.Join(e.OutputDir, filename)) if err != nil { return errors.Wrap(err, "failed writing checksum file of original message") } @@ -130,12 +155,14 @@ func (e *Encrypter) encryptAndSaveMessage(masterKey []byte, reader io.Reader, fi return errors.Wrap(err, "failed encrypting message") } - err = file.WriteFile(encryptedMessage, fmt.Sprintf("%s/%s.enc", outDirName, filename)) + encryptedFilename := filepath.Join(e.OutputDir, fmt.Sprintf("%s.enc", filename)) + + err = file.WriteFile(encryptedMessage, encryptedFilename) if err != nil { return errors.Wrap(err, "failed writing encoded file") } - err = file.WriteChecksum(encryptedMessage, fmt.Sprintf("%s/%s.enc", outDirName, filename)) + err = file.WriteChecksum(encryptedMessage, encryptedFilename) if err != nil { return errors.Wrap(err, "failed writing checksum file") } @@ -153,7 +180,7 @@ func (e *Encrypter) splitAndSaveKey(masterKey []byte) error { images, err := e.getImages(len(parts)) if err != nil { - return errors.Wrap(err, "failed getting images") + e.Logger.Print("failed getting images") } if len(images) == 0 { @@ -169,11 +196,9 @@ func (e *Encrypter) splitAndSaveKey(masterKey []byte) error { } func (e *Encrypter) getImages(count int) ([]string, error) { - dir := "images" - - files, err := os.ReadDir(dir) + files, err := os.ReadDir(e.ImagesDir) if err != nil { - return nil, errors.Wrapf(err, "failed reading folder '%s'", dir) + return nil, errors.Wrapf(err, "failed reading images folder '%s'", e.ImagesDir) } images := make([]string, 0, count) @@ -181,7 +206,7 @@ func (e *Encrypter) getImages(count int) ([]string, error) { for _, file := range files { switch filepath.Ext(file.Name()) { case ".jpg", ".jpeg", ".png": - images = append(images, filepath.Join(dir, file.Name())) + images = append(images, filepath.Join(e.ImagesDir, file.Name())) } if len(images) >= count { @@ -192,7 +217,7 @@ func (e *Encrypter) getImages(count int) ([]string, error) { // TODO we can improve this lenImages := len(images) if lenImages == 0 { - return nil, errors.Errorf("no image files in %s dir: run 'stego images' to get some random pics", dir) + return nil, errors.Errorf("no image files in %s dir: run 'stego images' to get some random pics", e.ImagesDir) } for lenImages < count { @@ -205,7 +230,7 @@ func (e *Encrypter) getImages(count int) ([]string, error) { func (e *Encrypter) saveKeysIntoImages(parts []sss.Part, images []string) error { for i, part := range parts { - partialKeyFilename := fmt.Sprintf("%s/%d", outDirName, i+1) + partialKeyFilename := filepath.Join(e.OutputDir, fmt.Sprintf("%03d", i+1)) // write .key file err := file.WriteKey(part.Bytes(), partialKeyFilename) @@ -215,7 +240,7 @@ func (e *Encrypter) saveKeysIntoImages(parts []sss.Part, images []string) error // if the images are available hide the key inside them if len(images) > 0 { - imageOutName := fmt.Sprintf("%s%s", partialKeyFilename, filepath.Ext(images[i])) + imageOutName := partialKeyFilename + filepath.Ext(images[i]) err := image.EncodeSecretFromFile(part.Bytes(), images[i], imageOutName) if err != nil { diff --git a/internal/encrypt/encrypt_test.go b/internal/encrypt/encrypt_test.go index 8da6499..f9275c5 100644 --- a/internal/encrypt/encrypt_test.go +++ b/internal/encrypt/encrypt_test.go @@ -1,46 +1,119 @@ package encrypt_test import ( - "fmt" + "io/fs" + "os" + "path/filepath" "testing" "github.com/enrichman/stegosecrets/internal/encrypt" "github.com/stretchr/testify/assert" ) -func TestNewEncrypter(t *testing.T) { - t.Run("new encrypter with valid options", func(t *testing.T) { - threshold := 3 - parts := 5 +func TestNewEncrypter_WithPartsThreshold(t *testing.T) { + type args struct { + parts int + threshold int + } - e, err := encrypt.NewEncrypter(encrypt.WithThreshold(threshold), encrypt.WithParts(parts)) + tt := []struct { + name string + args args + wantErr bool + }{ + { + name: "valid options", + args: args{ + parts: 5, + threshold: 3, + }, + wantErr: false, + }, + { + name: "invalid threshold", + args: args{ + threshold: -1, + }, + wantErr: true, + }, + { + name: "invalid parts", + args: args{ + parts: 300, + }, + wantErr: true, + }, + { + name: "invalid threshold and parts", + args: args{ + parts: 3, + threshold: 5, + }, + wantErr: true, + }, + { + name: "invalid output", + args: args{ + parts: 3, + threshold: 5, + }, + wantErr: true, + }, + } - assert.NotNil(t, e) - assert.Nil(t, err) - }) - t.Run("new encrypter with invalid threshold", func(t *testing.T) { - threshold := -1 + for _, tc := range tt { + tc := tc + t.Run(tc.name, func(t *testing.T) { + encrypter, err := encrypt.NewEncrypter( + encrypt.WithParts(tc.args.parts), + encrypt.WithThreshold(tc.args.threshold), + ) - e, err := encrypt.NewEncrypter(encrypt.WithThreshold(threshold)) + if tc.wantErr { + assert.NotNil(t, err) + assert.Nil(t, encrypter) + } else { + assert.Nil(t, err) + assert.NotNil(t, encrypter) + } + }) + } +} - assert.Nil(t, e) - assert.EqualError(t, err, "invalid threshold") - }) - t.Run("new encrypter with invalid parts", func(t *testing.T) { - parts := 300 +func TestNewEncrypter_WithOutputDir(t *testing.T) { + // it should create the output dir + t.Run("non existing dir", func(t *testing.T) { + dir := filepath.Join(os.TempDir(), "non-existing-dir") + assert.Nil(t, os.RemoveAll(dir)) - e, err := encrypt.NewEncrypter(encrypt.WithParts(parts)) + _, err := os.Stat(dir) + assert.ErrorIs(t, err, fs.ErrNotExist) + + encrypter, err := encrypt.NewEncrypter( + encrypt.WithOutputDir(dir), + ) + + assert.Nil(t, err) + assert.NotNil(t, encrypter) - assert.Nil(t, e) - assert.EqualError(t, err, "invalid parts") + _, err = os.Stat(dir) + assert.Nil(t, err) }) - t.Run("new encrypter with invalid threshold and parts pair", func(t *testing.T) { - threshold := 5 - parts := 3 +} + +func TestNewEncrypter_WithImagesDir(t *testing.T) { + t.Run("set images dir", func(t *testing.T) { + imagesDir := "random" - e, err := encrypt.NewEncrypter(encrypt.WithParts(parts), encrypt.WithThreshold(threshold)) + encrypter, err := encrypt.NewEncrypter( + encrypt.WithImagesDir(imagesDir), + ) - assert.Nil(t, e) - assert.EqualError(t, err, fmt.Sprintf("threshold %d cannot exceed the parts %d", threshold, parts)) + assert.Nil(t, err) + assert.NotNil(t, encrypter) + + absoluteImagesDir, err := filepath.Abs(imagesDir) + assert.Nil(t, err) + assert.Equal(t, absoluteImagesDir, encrypter.ImagesDir) }) } diff --git a/out/.gitignore b/out/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/out/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore