Skip to content

Commit

Permalink
Extend Parts with more info (breaking change) (#48)
Browse files Browse the repository at this point in the history
* fixes #24

improved parts with more info, small refactor, fix tests

* fix test
  • Loading branch information
enrichman authored Nov 6, 2022
1 parent 1b4507f commit 68a1e45
Show file tree
Hide file tree
Showing 26 changed files with 97 additions and 89 deletions.
34 changes: 22 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,22 @@ The partial keys will also be hidden inside images, adding an additional layer o

## Quickstart

TLDR. Go to the [usage](#usage) section for more details
### Installation

You can install the `stego` CLI with brew
```bash
brew install enrichman/tap/stegosecrets
```
or getting the [latest release](https://github.com/enrichman/stegosecrets/releases/latest), or building it from source.

### Encrypt / Decrypt

TLDR: go to the [usage](#usage) section for more details
```
stego encrypt --file mysecret.txt --parts 5 --threshold 3
```
```
stego decrypt --file mysecret.txt.enc --img 1.jpg --key 2.key --img 3.jpg
stego decrypt --file mysecret.txt.enc --img 001.jpg --key 002.key --img 003.jpg
```

## How does it work?
Expand Down Expand Up @@ -79,16 +89,16 @@ mysecret.txt.enc
mysecret.txt.enc.checksum
mysecret.txt.key
1.jpg
1.jpg.checksum
1.key
2.jpg
2.jpg.checksum
2.key
001.jpg
001.jpg.checksum
001.key
002.jpg
002.jpg.checksum
002.key
...
5.jpg
5.jpg.checksum
5.key
005.jpg
005.jpg.checksum
005.key
```

Main files:
Expand Down Expand Up @@ -117,7 +127,7 @@ mysecret.txt.enc: OK
To decrypt a file just use enough keys and/or images:

```
stego decrypt --file mysecret.txt.enc --key 1.key --key 2.key --img 3.jpg
stego decrypt --file mysecret.txt.enc --key 001.key --key 002.key --img 003.jpg
```

also the master key alone can be used to decrypt the file:
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/decrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestDecryptCmd(t *testing.T) {
"--key", testAssetsDir + "001.key",
"--key", testAssetsDir + "002.key",
},
wantCheckErr: true,
wantExecuteErr: true,
},
{
name: "decode with images",
Expand Down
11 changes: 5 additions & 6 deletions internal/cli/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (

var (
cleartextFile string
keyParts int
keyThreshold int
keyParts uint8
keyThreshold uint8
outputDir string
imagesDir string
)
Expand All @@ -28,10 +28,10 @@ func newEncryptCmd() *cobra.Command {

encryptCmd.Flags().StringVarP(&cleartextFile, "file", "f", "",
`The file to encrypt. If not specified a message from STDIN will be read.`)
encryptCmd.Flags().IntVarP(&keyParts, "parts", "p", 0,
encryptCmd.Flags().Uint8VarP(&keyParts, "parts", "p", 0,
`The number of parts (partial keys) in which the secret will be splitted.
If empty only the master-key will be generated.`)
encryptCmd.Flags().IntVarP(&keyThreshold, "threshold", "t", 0,
encryptCmd.Flags().Uint8VarP(&keyThreshold, "threshold", "t", 0,
`The minimum number of parts (partial keys) needed to decrypt the secret`)
encryptCmd.Flags().StringVarP(&outputDir, "output", "o", "out",
`The output directory where the encoded secret and keys/images will be saved.`)
Expand Down Expand Up @@ -67,8 +67,7 @@ func runEncryptCmd(cmd *cobra.Command, args []string) error {
logger := log.NewSimpleLogger(cmd.OutOrStdout(), log.NewLevel(silent, verbose))

encrypter, err := encrypt.NewEncrypter(
encrypt.WithParts(keyParts),
encrypt.WithThreshold(keyThreshold),
encrypt.WithPartsAndThreshold(keyParts, keyThreshold),
encrypt.WithOutputDir(outputDir),
encrypt.WithImagesDir(imagesDir),
encrypt.WithLogger(logger),
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ func newVersionCmd() *cobra.Command {
}

func runVersionCmd(_ *cobra.Command, _ []string) {
fmt.Printf("%s version %s", AppName, Version)
fmt.Printf("%s version %s\n", AppName, Version)
}
8 changes: 6 additions & 2 deletions internal/decrypt/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func WithPartialKeyFile(filename string) OptFunc {
return errors.Wrap(err, "failed reading partial key file")
}

part, err := sss.NewPart(partialKey)
part, err := sss.NewPartFromContent(partialKey)
if err != nil {
return errors.Wrap(err, "failed creating part")
}
Expand All @@ -95,7 +95,7 @@ func WithPartialKeyImageFile(filename string) OptFunc {
return errors.Wrap(err, "failed reading partial key image file")
}

part, err := sss.NewPart(partialKey)
part, err := sss.NewPartFromContent(partialKey)
if err != nil {
return errors.Wrap(err, "failed creating part")
}
Expand All @@ -109,6 +109,10 @@ func WithPartialKeyImageFile(filename string) OptFunc {
func (d *Decrypter) Decrypt(filename string) error {
d.Logger.Print(fmt.Sprintf("Decrypting '%s'", filepath.Base(filename)))

if len(d.MasterKey) == 0 && len(d.Parts) < 2 {
return errors.New("at least a master-key or more than one part needs to be specified")
}

encryptedFile, err := os.Open(filename)
if err != nil {
if os.IsNotExist(err) {
Expand Down
25 changes: 5 additions & 20 deletions internal/encrypt/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import (
)

type Encrypter struct {
Parts int
Threshold int
Parts uint8
Threshold uint8
OutputDir string
ImagesDir string

Expand Down Expand Up @@ -46,31 +46,16 @@ func NewEncrypter(opts ...OptFunc) (*Encrypter, error) {
}
}

if enc.Threshold > enc.Parts {
return nil, errors.Errorf("threshold %d cannot exceed the parts %d", enc.Threshold, enc.Parts)
}

return enc, nil
}

func WithParts(parts int) OptFunc {
func WithPartsAndThreshold(parts, threshold uint8) OptFunc {
return func(e *Encrypter) error {
if parts < 0 || parts > 256 {
return errors.New("invalid parts")
if threshold > parts {
return errors.Errorf("threshold %d cannot exceed parts %d", threshold, parts)
}

e.Parts = parts

return nil
}
}

func WithThreshold(threshold int) OptFunc {
return func(e *Encrypter) error {
if threshold < 0 || threshold > 256 {
return errors.New("invalid threshold")
}

e.Threshold = threshold

return nil
Expand Down
24 changes: 4 additions & 20 deletions internal/encrypt/encrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import (

func TestNewEncrypter_WithPartsThreshold(t *testing.T) {
type args struct {
parts int
threshold int
parts uint8
threshold uint8
}

tt := []struct {
Expand All @@ -33,20 +33,6 @@ func TestNewEncrypter_WithPartsThreshold(t *testing.T) {
},
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{
Expand All @@ -69,8 +55,7 @@ func TestNewEncrypter_WithPartsThreshold(t *testing.T) {
tc := tc
t.Run(tc.name, func(t *testing.T) {
encrypter, err := encrypt.NewEncrypter(
encrypt.WithParts(tc.args.parts),
encrypt.WithThreshold(tc.args.threshold),
encrypt.WithPartsAndThreshold(tc.args.parts, tc.args.threshold),
)

if tc.wantErr {
Expand Down Expand Up @@ -128,8 +113,7 @@ func TestEncrypt(t *testing.T) {
assert.DirExists(t, tmpDir)

encrypter, err := encrypt.NewEncrypter(
encrypt.WithParts(5),
encrypt.WithThreshold(2),
encrypt.WithPartsAndThreshold(5, 2),
encrypt.WithOutputDir(tmpDir),
encrypt.WithImagesDir("../../test/assets/p5t3"),
encrypt.WithLogger(log.NewSimpleLogger(io.Discard, log.None)),
Expand Down
56 changes: 41 additions & 15 deletions pkg/stego/shamir.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,42 @@ import (
)

type Part struct {
Version byte
Tag byte
Content []byte
Version byte
Parts byte
Threshold byte
Tag byte
Content []byte
}

func NewPart(content []byte) (Part, error) {
if len(content) < 3 {
func NewPartFromContent(content []byte) (Part, error) {
if len(content) < 5 {
return Part{}, errors.New("invalid part: ot enough content bytes")
}

return NewPart(
content[0],
content[1],
content[2],
content[3],
content[4:],
), nil
}

func NewPart(version, parts, threshold, tag byte, content []byte) Part {
return Part{
Version: content[0],
Tag: content[1],
Content: content[2:],
}, nil
Version: version,
Parts: parts,
Threshold: threshold,
Tag: tag,
Content: content,
}
}

func (p Part) Bytes() []byte {
return append([]byte{
p.Version,
p.Parts,
p.Threshold,
p.Tag,
}, p.Content...)
}
Expand All @@ -36,18 +52,21 @@ func (p Part) Base64() string {
return base64.StdEncoding.EncodeToString(p.Bytes())
}

func Split(secret []byte, parts, threshold int) ([]Part, error) {
partsMap, err := shamir.Split(secret, parts, threshold)
func Split(secret []byte, parts, threshold uint8) ([]Part, error) {
partsMap, err := shamir.Split(secret, int(parts), int(threshold))
if err != nil {
return nil, errors.Wrap(err, "failed splitting secret")
}

keys := []Part{}
for k, v := range partsMap {
keys = append(keys, Part{
Tag: k,
Content: v,
})
keys = append(keys, NewPart(
'1',
parts,
threshold,
k,
v,
))
}

return keys, nil
Expand All @@ -59,6 +78,13 @@ func Combine(parts []Part) ([]byte, error) {
combinedMap[p.Tag] = p.Content
}

if len(combinedMap) < int(parts[0].Threshold) {
return nil, errors.Errorf(
"not enough parts provided: parts %d, threshold %d",
len(combinedMap), parts[0].Threshold,
)
}

res, err := shamir.Combine(combinedMap)
if err != nil {
return nil, errors.Wrap(err, "failed combining secret")
Expand Down
Binary file modified test/assets/p5t3/001.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/assets/p5t3/001.jpg.checksum
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c78ff5c691b7cdd8f640860119a35b36e8b17d966a7d1600d5f5e9824a3c114d 001.jpg
70467f908715369ef358a730a43c1b20303d88f38b4c26d480a3f466dd2eff5b 001.jpg
2 changes: 1 addition & 1 deletion test/assets/p5t3/001.key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ALQIePbMA+WcTtFE6eF9x+PwIGgi4FzbZ+9D92tv+AA9NA==
MQUDKDBthCdJ1jFE654LUaQtoZUB18JoqE5Z293buoMIXKts
Binary file modified test/assets/p5t3/002.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/assets/p5t3/002.jpg.checksum
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c2a447ca753c211b124bd1e42ddbf831eadaf6dbaa7365fa8d100388b3fe223f 002.jpg
8630f1083b1d2631b05924f4a4883062bc6d9050683a3afdf15d3e065339f355 002.jpg
2 changes: 1 addition & 1 deletion test/assets/p5t3/002.key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
AB7dLa6IXdsb8pRz1suc5RurbriwYH5vFJXN8KMj31DOHg==
MQUDL+kGQiSLNRb6fbGyQANcw9/zxkagYcWe5LYt+lrJRf9W
Binary file modified test/assets/p5t3/003.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/assets/p5t3/003.jpg.checksum
Original file line number Diff line number Diff line change
@@ -1 +1 @@
dd0c81808c217346d7af17ad4755e0952c2d527052113615f2382562f647600f 003.jpg
72a9151ae40f97c9fdaf2ac325c5c4dcf4df6ee75aa4ebee73e1b1da4edfe104 003.jpg
2 changes: 1 addition & 1 deletion test/assets/p5t3/003.key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
AL+zB2r+shCUDJGyv2YvOPEkgL848MY6PKVbtH5JHEUrgg==
MQUDl2D6KnyLqV7ZcjpMASY93pEHTF0Uv4MS0I8jjhGPcMOG
Binary file modified test/assets/p5t3/004.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/assets/p5t3/004.jpg.checksum
Original file line number Diff line number Diff line change
@@ -1 +1 @@
755e60eff3cb753e07f65ae65a784c97dbfc1ac0b1cbf8973bc34d3dfa9174ad 004.jpg
3f884d49442045323dae5d335abd438eea16ff1fcc08106b20381a558852dc8f 004.jpg
2 changes: 1 addition & 1 deletion test/assets/p5t3/004.key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
AFaYZdcI/xEXsIwAtUFuM6m0yT2eAvP8hkQ2H3jZaM4grA==
MQUDg19G7nlwiQWnbcug52rLMyMwt/3n8ChsxWa9Ml/5C27M
Binary file modified test/assets/p5t3/005.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/assets/p5t3/005.jpg.checksum
Original file line number Diff line number Diff line change
@@ -1 +1 @@
798057676dd203ac9c384ece1706afe742aafecc0e3455201d9908c2da7f2523 005.jpg
aeb4135686c13a8dd788fd055e701ccdbc90cd7f3a3d561dffaf41841af3afb1 005.jpg
2 changes: 1 addition & 1 deletion test/assets/p5t3/005.key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
AK2IbuLNig4+ZBvi2yCFQCGXbMb8e3KbfSWQvB5BZTQvDw==
MQUDr0d+cStc9v8eOOH+27PyWX0uxkFMcCO7iMPOiLKrJVk2
Binary file modified test/assets/p5t3/secret.enc
Binary file not shown.
2 changes: 1 addition & 1 deletion test/assets/p5t3/secret.enc.checksum
Original file line number Diff line number Diff line change
@@ -1 +1 @@
d28b1463eb5b76dcf3b8954b67933044873235f24b482c576e1d440e6ce869e4 secret.enc
305c3b8517fa79765d2c2bca495d942eb980c12f9e4b64d6a00fa50333df1deb secret.enc
2 changes: 1 addition & 1 deletion test/assets/p5t3/secret.enc.key
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ViGCaNgwfyzb3yAnwMzS6j7aJkS75IEdsZrsx6mmSwc=
qaVBFPnIMEZN2lO/BJpGAQVmZRjix3U9RmE9T/iv7Lg=

0 comments on commit 68a1e45

Please sign in to comment.