1 /*
2 * steghide 0.5.1 - a steganography program
3 * Copyright (C) 1999-2003 Stefan Hetzl <shetzl@chello.at>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 */
20
21 #include "AUtils.h"
22 #include "BinaryIO.h"
23 #include "BitString.h"
24 #include "EmbData.h"
25 #include "error.h"
26 #include "MCryptPP.h"
27 #include "MHashPP.h"
28 #include "common.h"
29
EmbData(MODE m,std::string pp,std::string fn)30 EmbData::EmbData (MODE m, std::string pp, std::string fn)
31 : Mode(m), Passphrase(pp), FileName(fn)
32 {
33 if (m == EXTRACT) {
34 NumBitsNeeded = NumBitsRequested = NBitsMagic ;
35 Version = CodeVersion ;
36 State = READ_MAGIC ;
37 Reservoir = BitString() ;
38 }
39 }
40
finished()41 bool EmbData::finished ()
42 {
43 myassert (Mode == EXTRACT) ;
44 return (State == END) ;
45 }
46
getNumBitsRequested()47 unsigned long EmbData::getNumBitsRequested ()
48 {
49 myassert (Mode == EXTRACT) ;
50 return NumBitsRequested ;
51 }
52
addBits(BitString addbits)53 void EmbData::addBits (BitString addbits)
54 {
55 myassert (Mode == EXTRACT) ;
56
57 #ifdef DEBUG
58 printDebug (1, "\nEmbData::addBits called with") ;
59 printDebug (1, " addbits:") ;
60 addbits.printDebug (1, 2) ;
61 printDebug (1, " Reservoir:") ;
62 Reservoir.printDebug (1, 2) ;
63 #endif
64
65 Reservoir.append (addbits) ;
66 BitString bits ;
67 if (Reservoir.getLength() >= NumBitsNeeded) {
68 bits = Reservoir.cutBits (0, NumBitsNeeded) ; // take exactly the first NumBitsNeeded bits from Reservoir | addbits
69 }
70 else { // Reservoir.getLength() < NumBitsNeeded
71 myassert(false) ;
72 }
73
74 #ifdef DEBUG
75 printDebug (1, "bits is now:") ;
76 bits.printDebug (1, 2) ;
77 #endif
78
79 switch (State) {
80 case READ_MAGIC:
81 {
82 #ifdef DEBUG
83 printDebug (1, "in the READ_MAGIC state") ;
84 #endif
85 if (bits.getValue(0, NBitsMagic) == Magic) {
86 NumBitsNeeded = 1 ;
87 NumBitsRequested = AUtils::bminus<unsigned long> (NumBitsNeeded, Reservoir.getLength()) ;
88 State = READ_VERSION ;
89 }
90 else {
91 throw SteghideError (_("could not extract any data with that passphrase!")) ;
92 }
93 break ;
94 }
95
96 case READ_VERSION:
97 {
98 #ifdef DEBUG
99 printDebug (1, "in the READ_VERSION state") ;
100 #endif
101 if (bits[0] == true) {
102 Version++ ;
103 NumBitsNeeded = AUtils::bminus ((UWORD32) 1, Reservoir.getLength()) ;
104 }
105 else {
106 if (Version > CodeVersion) {
107 throw CorruptDataError (_("attempting to read an embedding of version %d but steghide %s only supports embeddings of version %d."), Version, VERSION, CodeVersion) ;
108 }
109 NumBitsNeeded = EncryptionAlgorithm::IRep_size + EncryptionMode::IRep_size ;
110 NumBitsRequested = AUtils::bminus<unsigned long> (NumBitsNeeded, Reservoir.getLength()) ;
111 State = READ_ENCINFO ;
112 }
113 break ;
114 }
115
116 case READ_ENCINFO: {
117 #ifdef DEBUG
118 printDebug (1, "in the READ_ENCINFO state") ;
119 #endif
120
121 unsigned int algo = (unsigned int) bits.getValue (0, EncryptionAlgorithm::IRep_size) ;
122 if (EncryptionAlgorithm::isValidIntegerRep (algo)) {
123 EncAlgo.setValue ((EncryptionAlgorithm::IRep) algo) ;
124 }
125 unsigned int mode = (unsigned int) bits.getValue (EncryptionAlgorithm::IRep_size, EncryptionMode::IRep_size) ;
126 if (EncryptionMode::isValidIntegerRep (mode)) {
127 EncMode.setValue ((EncryptionMode::IRep) mode) ;
128 }
129
130 NumBitsNeeded = NBitsNPlainBits ;
131 NumBitsRequested = AUtils::bminus<unsigned long> (NumBitsNeeded, Reservoir.getLength()) ;
132 State = READ_NPLAINBITS ;
133
134 #ifndef USE_LIBMCRYPT
135 if (EncAlgo.getIntegerRep() != EncryptionAlgorithm::NONE) {
136 throw SteghideError (_(
137 "The embedded data is encrypted but steghide has been compiled without encryption\n"
138 "support. To be able to read the embedded data, you have to install libmcrypt\n"
139 "(http://mcrypt.sourceforge.net/) and recompile steghide.")) ;
140 }
141 #endif
142 break ; }
143
144 case READ_NPLAINBITS: {
145 #ifdef DEBUG
146 printDebug (1, "in the READ_NPLAINBITS state") ;
147 #endif
148
149 NPlainBits = bits.getValue (0, NBitsNPlainBits) ;
150
151 #ifdef USE_LIBMCRYPT
152 NumBitsNeeded = (UWORD32) MCryptPP::getEncryptedSize (EncAlgo, EncMode, NPlainBits) ;
153 #else
154 NumBitsNeeded = NPlainBits ;
155 #endif
156 NumBitsRequested = AUtils::bminus<unsigned long> (NumBitsNeeded, Reservoir.getLength()) ;
157
158 State = READ_ENCRYPTED ;
159 break ; }
160
161 case READ_ENCRYPTED: {
162 #ifdef DEBUG
163 printDebug (1, "in the READ_ENCRYPTED state") ;
164 #endif
165
166 BitString plain ;
167 #ifdef USE_LIBMCRYPT
168 if (EncAlgo.getIntegerRep() == EncryptionAlgorithm::NONE) {
169 plain = bits ;
170 }
171 else {
172 MCryptPP crypto (EncAlgo, EncMode) ;
173 plain = crypto.decrypt (bits, Passphrase) ;
174 }
175 #else
176 plain = bits ;
177 #endif
178
179 plain.truncate (0, NPlainBits) ; // cut off random padding used to achieve full number of encryption blocks
180
181 unsigned long pos = 0 ;
182
183 // read Compression (and uncompress)
184 Compression = ((plain[pos++]) ? 9 : 0) ; // to make compression contain a value that makes sense
185 #ifdef DEBUG
186 printDebug (2, " compression: %d\n", plain[pos - 1]) ;
187 #endif
188 if (Compression > 0) {
189 UWORD32 NUncompressedBits = plain.getValue (pos, NBitsNUncompressedBits) ;
190 #ifdef DEBUG
191 printDebug (2, " nuncobits: %lu\n", NUncompressedBits) ;
192 #endif
193 pos += NBitsNUncompressedBits ;
194
195 plain.truncate (pos, plain.getLength()) ;
196 pos = 0 ;
197 plain.uncompress (NUncompressedBits) ;
198 }
199
200 // read Checksum
201 Checksum = plain[pos++] ;
202 #ifdef DEBUG
203 printDebug (2, " checksum: %d\n", plain[pos - 1]) ;
204 #endif
205 if (Checksum) {
206 CRC32 = plain.getValue (pos, NBitsCrc32) ;
207 #ifdef DEBUG
208 printDebug (2, " crc32: 0x%x\n", CRC32) ;
209 #endif
210 pos += NBitsCrc32 ;
211 }
212
213 // read filename
214 char curchar = '\0' ;
215 FileName = "" ;
216 do {
217 curchar = (char) plain.getValue (pos, 8) ;
218 if (curchar != '\0') {
219 FileName += curchar ;
220 }
221 pos += 8 ;
222 } while (curchar != '\0') ;
223
224 // extract data
225 if ((plain.getLength() - pos) % 8 != 0) {
226 throw CorruptDataError (_("the embedded data has an invalid length.")) ;
227 }
228 const unsigned long extdatalen = (plain.getLength() - pos) / 8 ;
229 Data.resize (extdatalen) ;
230 for (unsigned int i = 0 ; i < extdatalen ; i++) {
231 Data[i] = ((BYTE) plain.getValue (pos, 8)) ;
232 pos += 8 ;
233 }
234
235 NumBitsNeeded = 0 ;
236 NumBitsRequested = 0 ;
237 State = END ;
238 break ; }
239
240 case END:
241 default: {
242 myassert (0) ;
243 break ; }
244 }
245 }
246
checksumOK(void) const247 bool EmbData::checksumOK (void) const
248 {
249 // test if checksum is ok
250 bool ok = true ;
251 if (Checksum) {
252 MHashPP hash (MHASH_CRC32) ;
253 for (std::vector<BYTE>::const_iterator i = Data.begin() ; i != Data.end() ; i++) {
254 hash << *i ;
255 }
256 hash << MHashPP::endhash ;
257 unsigned long calccrc32 = hash.getHashBits().getValue(0, NBitsCrc32) ;
258
259 if (calccrc32 == CRC32) {
260 ok = true ;
261 }
262 else {
263 ok = false ;
264 }
265 }
266 return ok ;
267 }
268
setEncAlgo(EncryptionAlgorithm a)269 void EmbData::setEncAlgo (EncryptionAlgorithm a)
270 {
271 EncAlgo = a ;
272 }
273
getEncAlgo() const274 EncryptionAlgorithm EmbData::getEncAlgo () const
275 {
276 return EncAlgo ;
277 }
278
setEncMode(EncryptionMode m)279 void EmbData::setEncMode (EncryptionMode m)
280 {
281 EncMode = m ;
282 }
283
getEncMode() const284 EncryptionMode EmbData::getEncMode () const
285 {
286 return EncMode ;
287 }
288
setCompression(int c)289 void EmbData::setCompression (int c)
290 {
291 Compression = c ;
292 }
293
getCompression(void) const294 int EmbData::getCompression (void) const
295 {
296 return Compression ;
297 }
298
setChecksum(bool c)299 void EmbData::setChecksum (bool c)
300 {
301 Checksum = c ;
302 }
303
getChecksum(void) const304 bool EmbData::getChecksum (void) const
305 {
306 return Checksum ;
307 }
308
getBitString()309 BitString EmbData::getBitString ()
310 {
311 myassert (Mode == EMBED) ;
312
313 // assembling data that can be compressed
314 BitString compr ;
315
316 compr.append (Checksum) ;
317 if (Checksum) {
318 MHashPP hash (MHASH_CRC32) ;
319 for (std::vector<BYTE>::iterator i = Data.begin() ; i != Data.end() ; i++) {
320 hash << *i ;
321 }
322 hash << MHashPP::endhash ;
323 compr.append (hash.getHashBits()) ;
324 }
325
326 compr.append (stripDir (FileName)) ;
327 compr.append ((BYTE) 0, 8) ; // end of fileame
328
329 compr.append (Data) ;
330
331 // assembling data that can be encrypted
332 BitString plain ;
333 plain.append ((Compression > 0) ? true : false) ;
334 if (Compression > 0) {
335 plain.append (compr.getLength(), NBitsNUncompressedBits) ;
336 compr.compress (Compression) ;
337 }
338 plain.append (compr) ;
339
340 // put it all together
341 BitString main ;
342 main.append(Magic, NBitsMagic) ;
343 for (unsigned short i = 0 ; i < CodeVersion ; i++) {
344 main.append(true) ;
345 }
346 main.append(false) ; // end of version
347 main.append((UWORD16) EncAlgo.getIntegerRep(), EncryptionAlgorithm::IRep_size) ;
348 main.append((UWORD16) EncMode.getIntegerRep(), EncryptionMode::IRep_size) ;
349 main.append(plain.getLength(), NBitsNPlainBits) ;
350
351 #ifdef USE_LIBMCRYPT
352 if (EncAlgo.getIntegerRep() != EncryptionAlgorithm::NONE) {
353 MCryptPP crypto (EncAlgo, EncMode) ;
354 plain = crypto.encrypt (plain, Passphrase) ;
355 }
356 #else
357 myassert (EncAlgo.getIntegerRep() == EncryptionAlgorithm::NONE) ;
358 #endif
359
360 main.append (plain) ;
361
362 return main ;
363 }
364
stripDir(std::string s)365 std::string EmbData::stripDir (std::string s)
366 {
367 unsigned int start = 0 ;
368 if ((start = s.find_last_of ("/\\")) == std::string::npos) {
369 start = 0 ;
370 }
371 else {
372 start += 1 ;
373 }
374 return s.substr (start, std::string::npos) ;
375 }
376