1package steg 2 3import ( 4 "encoding/binary" 5 "fmt" 6 "github.com/DimitarPetrov/stegify/bits" 7 "image" 8 "io" 9 "os" 10) 11 12//Decode performs steganography decoding of Reader with previously encoded data by the Encode function and writes to result Writer. 13func Decode(carrier io.Reader, result io.Writer) error { 14 RGBAImage, _, err := getImageAsRGBA(carrier) 15 if err != nil { 16 return fmt.Errorf("error parsing carrier image: %v", err) 17 } 18 19 dx := RGBAImage.Bounds().Dx() 20 dy := RGBAImage.Bounds().Dy() 21 22 dataBytes := make([]byte, 0, 2048) 23 resultBytes := make([]byte, 0, 2048) 24 25 dataCount := extractDataCount(RGBAImage) 26 27 var count int 28 29 for x := 0; x < dx && dataCount > 0; x++ { 30 for y := 0; y < dy && dataCount > 0; y++ { 31 if count >= dataSizeHeaderReservedBytes { 32 c := RGBAImage.RGBAAt(x, y) 33 dataBytes = append(dataBytes, bits.GetLastTwoBits(c.R), bits.GetLastTwoBits(c.G), bits.GetLastTwoBits(c.B)) 34 dataCount -= 3 35 } else { 36 count += 4 37 } 38 } 39 } 40 41 if dataCount < 0 { 42 dataBytes = dataBytes[:len(dataBytes)+dataCount] //remove bytes that are not part of data and mistakenly added 43 } 44 45 dataBytes = align(dataBytes) // len(dataBytes) must be aliquot of 4 46 47 for i := 0; i < len(dataBytes); i += 4 { 48 resultBytes = append(resultBytes, bits.ConstructByteOfQuartersAsSlice(dataBytes[i:i+4])) 49 } 50 51 if _, err = result.Write(resultBytes); err != nil { 52 return err 53 } 54 55 return nil 56} 57 58//MultiCarrierDecode performs steganography decoding of Readers with previously encoded data chunks by the MultiCarrierEncode function and writes to result Writer. 59//NOTE: The order of the carriers MUST be the same as the one when encoding. 60func MultiCarrierDecode(carriers []io.Reader, result io.Writer) error { 61 for i := 0; i < len(carriers); i++ { 62 if err := Decode(carriers[i], result); err != nil { 63 return fmt.Errorf("error decoding chunk with index %d: %v", i, err) 64 } 65 } 66 return nil 67} 68 69//DecodeByFileNames performs steganography decoding of data previously encoded by the Encode function. 70//The data is decoded from file carrier and it is saved in separate new file 71func DecodeByFileNames(carrierFileName string, resultName string) (err error) { 72 return MultiCarrierDecodeByFileNames([]string{carrierFileName}, resultName) 73} 74 75//MultiCarrierDecodeByFileNames performs steganography decoding of data previously encoded by the MultiCarrierEncode function. 76//The data is decoded from carrier files and it is saved in separate new file 77//NOTE: The order of the carriers MUST be the same as the one when encoding. 78func MultiCarrierDecodeByFileNames(carrierFileNames []string, resultName string) (err error) { 79 if len(carrierFileNames) == 0 { 80 return fmt.Errorf("missing carriers names") 81 } 82 83 carriers := make([]io.Reader, 0, len(carrierFileNames)) 84 for _, name := range carrierFileNames { 85 carrier, err := os.Open(name) 86 if err != nil { 87 return fmt.Errorf("error opening carrier file %s: %v", name, err) 88 } 89 defer func() { 90 closeErr := carrier.Close() 91 if err == nil { 92 err = closeErr 93 } 94 }() 95 carriers = append(carriers, carrier) 96 } 97 98 result, err := os.Create(resultName) 99 if err != nil { 100 return fmt.Errorf("error creating result file: %v", err) 101 } 102 defer func() { 103 closeErr := result.Close() 104 if err == nil { 105 err = closeErr 106 } 107 }() 108 109 err = MultiCarrierDecode(carriers, result) 110 if err != nil { 111 _ = os.Remove(resultName) 112 } 113 return err 114} 115 116func align(dataBytes []byte) []byte { 117 switch len(dataBytes) % 4 { 118 case 1: 119 dataBytes = append(dataBytes, byte(0), byte(0), byte(0)) 120 case 2: 121 dataBytes = append(dataBytes, byte(0), byte(0)) 122 case 3: 123 dataBytes = append(dataBytes, byte(0)) 124 } 125 return dataBytes 126} 127 128func extractDataCount(RGBAImage *image.RGBA) int { 129 dataCountBytes := make([]byte, 0, 16) 130 131 dx := RGBAImage.Bounds().Dx() 132 dy := RGBAImage.Bounds().Dy() 133 134 count := 0 135 136 for x := 0; x < dx && count < dataSizeHeaderReservedBytes; x++ { 137 for y := 0; y < dy && count < dataSizeHeaderReservedBytes; y++ { 138 c := RGBAImage.RGBAAt(x, y) 139 dataCountBytes = append(dataCountBytes, bits.GetLastTwoBits(c.R), bits.GetLastTwoBits(c.G), bits.GetLastTwoBits(c.B)) 140 count += 4 141 } 142 } 143 144 dataCountBytes = append(dataCountBytes, byte(0)) 145 146 var bs = []byte{bits.ConstructByteOfQuartersAsSlice(dataCountBytes[:4]), 147 bits.ConstructByteOfQuartersAsSlice(dataCountBytes[4:8]), 148 bits.ConstructByteOfQuartersAsSlice(dataCountBytes[8:12]), 149 bits.ConstructByteOfQuartersAsSlice(dataCountBytes[12:])} 150 151 return int(binary.LittleEndian.Uint32(bs)) 152} 153