Skip to content

Commit

Permalink
Api refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
DimitarPetrov committed Jan 30, 2019
1 parent a28ecd5 commit a45bc92
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 55 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@


## Overview
`stegify` is a simple command line tool that is capable of fully transparent hiding files within an image.
The technique is known as [steganography](https://en.wikipedia.org/wiki/steganography). This tool uses
the technique known as LSB (Least Significant Bit) Steganography.
`stegify` is a simple command line tool capable of fully transparent hiding any file within an image.
This technique is known as LSB (Least Significant Bit) [steganography](https://en.wikipedia.org/wiki/steganography)

## Demonstration

Expand All @@ -20,11 +19,12 @@ The `Result` file contains the `Data` file hidden in it. And as you can see it i

## Install
```
$ go get github.com/DimitarPetrov/stegify
$ go get -u github.com/DimitarPetrov/stegify
```

## Usage

### As a command line tool
```
$ stegify -op encode -carrier <file-name> -data <file-name> -result <file-name>
$ stegify -op decode -carrier <file-name> -result <file-name>
Expand All @@ -40,6 +40,12 @@ The result file won't have any file extension and therefore it should be specifi

In both cases the flag `-result` could be omitted and it will be used the default file name: `result`

### Programmatically in your code

`stegify` can be used programmatically too and it provides easy to use functions working with file names
or raw Readers and Writers. You can visit [godoc](https://godoc.org/github.com/DimitarPetrov/stegify) under
`steg` package for details.

## Disclaimer

If carrier file is in jpeg or jpg format, after encoding the result file image will be png encoded (therefore it may be bigger in size)
Expand Down
37 changes: 25 additions & 12 deletions steg/steg_decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,12 @@ import (
"fmt"
"github.com/DimitarPetrov/stegify/bits"
"image"
"io"
"os"
)

//Decode performs steganography decoding of data previously encoded by the Encode function.
//The data is decoded from file carrier and it is saved in separate new file
func Decode(carrierFileName string, newFileName string) error {
carrier, err := os.Open(carrierFileName)
defer carrier.Close()
if err != nil {
return fmt.Errorf("error opening carrier file: %v", err)
}
//Decode performs steganography decoding of Reader with previously encoded data by the Encode function and writes to result Writer.
func Decode(carrier io.Reader, result io.Writer) error {

RGBAImage, _, err := getImageAsRGBA(carrier)
if err != nil {
Expand Down Expand Up @@ -55,15 +50,33 @@ func Decode(carrierFileName string, newFileName string) error {
resultBytes = append(resultBytes, bits.ConstructByteOfQuartersAsSlice(dataBytes[i:i+4]))
}

resultFile, err := os.Create(newFileName)
defer resultFile.Close()
result.Write(resultBytes)

return nil
}

//DecodeByFileNames performs steganography decoding of data previously encoded by the Encode function.
//The data is decoded from file carrier and it is saved in separate new file
func DecodeByFileNames(carrierFileName string, newFileName string) error {
carrier, err := os.Open(carrierFileName)
defer carrier.Close()
if err != nil {
return fmt.Errorf("error opening carrier file: %v", err)
}

result, err := os.Create(newFileName)
defer result.Close()
if err != nil {
return fmt.Errorf("error creating result file: %v", err)
}

resultFile.Write(resultBytes)
err = Decode(carrier, result)
if err != nil {
os.Remove(newFileName)
}

return err

return nil
}

func align(dataBytes []byte) []byte {
Expand Down
70 changes: 65 additions & 5 deletions steg/steg_decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,27 @@ import (
)

func BenchmarkDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
carrier, err := os.Open("../examples/test_decode.jpeg")
if err != nil {
b.Fatalf("Error opening carrier file: %v", err)
}

var result bytes.Buffer

err = Decode(carrier, &result)
if err != nil {
b.Fatalf("Error decoding file: %v", err)
}

carrier.Close()
}
}

func BenchmarkDecodeByFileNames(b *testing.B) {

for i := 0; i < b.N; i++ {
err := Decode("../examples/test_decode.jpeg", "benchmark_result")
err := DecodeByFileNames("../examples/test_decode.jpeg", "benchmark_result")
if err != nil {
b.Fatalf("Error decoding file: %v", err)
}
Expand All @@ -24,7 +42,43 @@ func BenchmarkDecode(b *testing.B) {

func TestDecode(t *testing.T) {

err := Decode("../examples/test_decode.jpeg", "result")
carrier, err := os.Open("../examples/test_decode.jpeg")
if err != nil {
t.Fatalf("Error opening carrier file: %v", err)
}
defer carrier.Close()

var result bytes.Buffer
err = Decode(carrier, &result)
if err != nil {
t.Fatalf("Error decoding file: %v", err)
}

wanted, err := os.Open("../examples/lake.jpeg")
if err != nil {
t.Fatalf("Error opening file examples/lake.jpg: %v", err)
}
defer wanted.Close()

wantedBytes, err := ioutil.ReadAll(wanted)
if err != nil {
t.Fatalf("Error reading file examples/lake.jpg: %v", err)
}

resultBytes, err := ioutil.ReadAll(&result)
if err != nil {
t.Fatalf("Error reading result Writer: %v", err)
}

if !bytes.Equal(wantedBytes, resultBytes) {
t.Error("Assertion failed!")
}

}

func TestDecodeByFileNames(t *testing.T) {

err := DecodeByFileNames("../examples/test_decode.jpeg", "result")
if err != nil {
t.Fatalf("Error decoding file: %v", err)
}
Expand Down Expand Up @@ -64,9 +118,9 @@ func TestDecode(t *testing.T) {

}

func TestDecodeShouldReturnErrorWhenCarrierFileMissing(t *testing.T) {
func TestDecodeByFileNamesShouldReturnErrorWhenCarrierFileMissing(t *testing.T) {

err := Decode("not_existing_file", "result")
err := DecodeByFileNames("not_existing_file", "result")
if err == nil {
os.Remove("result")
t.FailNow()
Expand All @@ -76,8 +130,14 @@ func TestDecodeShouldReturnErrorWhenCarrierFileMissing(t *testing.T) {
}

func TestDecodeShouldReturnErrorWhenCarrierFileIsNotImage(t *testing.T) {
carrier, err := os.Open("../README.md")
if err != nil {
t.Fatalf("Error opening carrier file: %v", err)
}
defer carrier.Close()

err := Decode("../README.md", "result")
var result bytes.Buffer
err = Decode(carrier, &result)
if err == nil {
t.FailNow()
}
Expand Down
57 changes: 35 additions & 22 deletions steg/steg_encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,19 @@ import (

const dataSizeHeaderReservedBytes = 20 // 20 bytes results in 30 usable bits

//Encode performs steganography encoding of data file in carrier file
//and saves the steganography encoded product in new file.
func Encode(carrierFileName string, dataFileName string, newFileName string) error {
carrier, err := os.Open(carrierFileName)
defer carrier.Close()
if err != nil {
return fmt.Errorf("error opening carrier file: %v", err)
}
//Encode performs steganography encoding of data Reader in carrier
//and writes it to the result Writer encoded as PNG image.
func Encode(carrier io.Reader, data io.Reader, result io.Writer) error {

RGBAImage, format, err := getImageAsRGBA(carrier)
if err != nil {
return fmt.Errorf("error parsing carrier image: %v", err)
}

dataFile, err := os.Open(dataFileName)
defer dataFile.Close()
if err != nil {
return fmt.Errorf("error opening data file: %v", err)
}

dataBytes := make(chan byte, 128)
errChan := make(chan error)

go readData(dataFile, dataBytes, errChan)
go readData(data, dataBytes, errChan)

dx := RGBAImage.Bounds().Dx()
dy := RGBAImage.Bounds().Dy()
Expand Down Expand Up @@ -94,22 +83,46 @@ func Encode(carrierFileName string, dataFileName string, newFileName string) err

setDataSizeHeader(RGBAImage, quartersOfBytesOf(dataCount))

resultFile, err := os.Create(newFileName + carrierFileName[strings.LastIndex(carrierFileName, "."):])
defer resultFile.Close()
if err != nil {
return fmt.Errorf("error creating result file: %v", err)
}

switch format {
case "png", "jpeg":
png.Encode(resultFile, RGBAImage)
png.Encode(result, RGBAImage)
default:
return fmt.Errorf("unsupported carrier format")
}

return nil
}

//EncodeByFileNames performs steganography encoding of data file in carrier file
//and saves the steganography encoded product in new file.
func EncodeByFileNames(carrierFileName, dataFileName, resultFileName string) error {

carrier, err := os.Open(carrierFileName)
defer carrier.Close()
if err != nil {
return fmt.Errorf("error opening carrier file: %v", err)
}

data, err := os.Open(dataFileName)
defer data.Close()
if err != nil {
return fmt.Errorf("error opening data file: %v", err)
}

resultFileWithExtension := resultFileName + carrierFileName[strings.LastIndex(carrierFileName, "."):]
result, err := os.Create(resultFileWithExtension)
defer result.Close()
if err != nil {
return fmt.Errorf("error creating result file: %v", err)
}

err = Encode(carrier, data, result)
if err != nil {
os.Remove(resultFileWithExtension)
}
return err
}

func quartersOfBytesOf(counter uint32) []byte {
bs := make([]byte, 4)
binary.LittleEndian.PutUint32(bs, counter)
Expand Down
Loading

0 comments on commit a45bc92

Please sign in to comment.