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