Skip to content

Commit

Permalink
Fix for absolute path and added images dir flag (#41)
Browse files Browse the repository at this point in the history
* fix for absolute path, added images dir flag

Signed-off-by: Enrico Candino <enrico.candino@gmail.com>

* added test

Signed-off-by: Enrico Candino <enrico.candino@gmail.com>

Signed-off-by: Enrico Candino <enrico.candino@gmail.com>
  • Loading branch information
enrichman authored Oct 29, 2022
1 parent 4d75bcb commit 40e72ad
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.DS_Store
images/*
out/
out/*
dist/
6 changes: 6 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
run:
go: "1.19"

issues:
exclude-rules:
- path: _test.go
linters:
- funlen

linters:
enable-all: true

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/enrichman/stegosecrets

go 1.18
go 1.19

require (
github.com/auyer/steganography v1.0.1
Expand Down
11 changes: 9 additions & 2 deletions internal/cli/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cli

import (
"bytes"
"path/filepath"

"github.com/enrichman/stegosecrets/internal/encrypt"
"github.com/enrichman/stegosecrets/internal/log"
Expand All @@ -15,6 +16,7 @@ var (
keyParts int
keyThreshold int
outputDir string
imagesDir string
)

func newEncryptCmd() *cobra.Command {
Expand All @@ -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
}
Expand All @@ -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")
Expand All @@ -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 {
Expand Down
65 changes: 45 additions & 20 deletions internal/encrypt/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
type Encrypter struct {
Parts int
Threshold int
OutputDir string
ImagesDir string

Logger log.Logger
}
Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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")
}
Expand All @@ -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")
}
Expand All @@ -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")
}
Expand All @@ -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 {
Expand All @@ -169,19 +196,17 @@ 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)

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 {
Expand All @@ -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 {
Expand All @@ -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)
Expand All @@ -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 {
Expand Down
125 changes: 99 additions & 26 deletions internal/encrypt/encrypt_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
2 changes: 2 additions & 0 deletions out/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore

0 comments on commit 40e72ad

Please sign in to comment.