1 /*
2 * LZFu (un)compression functions
3 *
4 * Copyright (C) 2009-2018, Joachim Metz <joachim.metz@gmail.com>
5 *
6 * Refer to AUTHORS for acknowledgements.
7 *
8 * This software is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This software is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this software. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <common.h>
23 #include <byte_stream.h>
24 #include <memory.h>
25 #include <types.h>
26
27 #include "libfmapi_checksum.h"
28 #include "libfmapi_libcerror.h"
29 #include "libfmapi_libcnotify.h"
30 #include "libfmapi_lzfu.h"
31
32 #define LIBFMAPI_LZFU_SIGNATURE_COMPRESSED 0x75465a4c
33 #define LIBFMAPI_LZFU_SIGNATURE_UNCOMPRESSED 0x414c454d
34
35 const char *libfmapi_lzfu_rtf_dictionary = \
36 "{\\rtf1\\ansi\\mac\\deff0\\deftab720"
37 "{\\fonttbl;}"
38 "{\\f0\\fnil \\froman \\fswiss \\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier"
39 "{\\colortbl\\red0\\green0\\blue0\r\n\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
40
41 /* Determines the uncompressed data size from the LZFu header in the compressed data
42 * Returns 1 on success or -1 on error
43 */
libfmapi_lzfu_get_uncompressed_data_size(uint8_t * compressed_data,size_t compressed_data_size,size_t * uncompressed_data_size,libcerror_error_t ** error)44 int libfmapi_lzfu_get_uncompressed_data_size(
45 uint8_t *compressed_data,
46 size_t compressed_data_size,
47 size_t *uncompressed_data_size,
48 libcerror_error_t **error )
49 {
50 libfmapi_lzfu_header_t lzfu_header;
51
52 uint8_t *lzfu_data = 0;
53 static char *function = "libfmapi_lzfu_get_uncompressed_data_size";
54
55 #if defined( HAVE_DEBUG_OUTPUT )
56 uint8_t lz_buffer[ 4096 ];
57
58 uint8_t *lzfu_reference_data = 0;
59 size_t compressed_data_iterator = 0;
60 size_t uncompressed_data_iterator = 0;
61 uint16_t lz_buffer_iterator = 0;
62 uint16_t reference_offset = 0;
63 uint16_t reference_size = 0;
64 uint16_t reference_iterator = 0;
65 uint8_t flag_byte_bit_mask = 0;
66 uint8_t flag_byte = 0;
67 #endif
68
69 if( compressed_data == NULL )
70 {
71 libcerror_error_set(
72 error,
73 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
74 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
75 "%s: invalid compressed data.",
76 function );
77
78 return( -1 );
79 }
80 if( compressed_data_size > (size_t) SSIZE_MAX )
81 {
82 libcerror_error_set(
83 error,
84 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
85 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
86 "%s: invalid compressed data size value exceeds maximum.",
87 function );
88
89 return( -1 );
90 }
91 if( uncompressed_data_size == NULL )
92 {
93 libcerror_error_set(
94 error,
95 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
96 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
97 "%s: invalid uncompressed data size.",
98 function );
99
100 return( -1 );
101 }
102 lzfu_data = compressed_data;
103
104 byte_stream_copy_to_uint32_little_endian(
105 lzfu_data,
106 lzfu_header.compressed_data_size );
107
108 lzfu_data += 4;
109
110 byte_stream_copy_to_uint32_little_endian(
111 lzfu_data,
112 lzfu_header.uncompressed_data_size );
113
114 lzfu_data += 4;
115
116 byte_stream_copy_to_uint32_little_endian(
117 lzfu_data,
118 lzfu_header.signature );
119
120 lzfu_data += 8;
121
122 if( ( lzfu_header.signature != LIBFMAPI_LZFU_SIGNATURE_COMPRESSED )
123 && ( lzfu_header.signature != LIBFMAPI_LZFU_SIGNATURE_UNCOMPRESSED ) )
124 {
125 libcerror_error_set(
126 error,
127 LIBCERROR_ERROR_DOMAIN_RUNTIME,
128 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
129 "%s: unsupported compression signature: 0x%08" PRIx32 ".",
130 function,
131 lzfu_header.signature );
132
133 return( -1 );
134 }
135 compressed_data_size -= sizeof( libfmapi_lzfu_header_t );
136
137 /* The compressed data size includes 12 bytes of the header
138 */
139 lzfu_header.compressed_data_size -= 12;
140
141 if( (size_t) lzfu_header.compressed_data_size != compressed_data_size )
142 {
143 libcerror_error_set(
144 error,
145 LIBCERROR_ERROR_DOMAIN_RUNTIME,
146 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
147 "%s: mismatch in compressed data size (%" PRIu32 " != %" PRIzd ").",
148 function,
149 lzfu_header.compressed_data_size,
150 compressed_data_size );
151
152 return( -1 );
153 }
154 #if defined( HAVE_DEBUG_OUTPUT )
155 if( libcnotify_verbose != 0 )
156 {
157 if( memory_copy(
158 lz_buffer,
159 libfmapi_lzfu_rtf_dictionary,
160 207 ) == NULL )
161 {
162 libcerror_error_set(
163 error,
164 LIBCERROR_ERROR_DOMAIN_MEMORY,
165 LIBCERROR_MEMORY_ERROR_COPY_FAILED,
166 "%s: unable to initialize lz buffer.",
167 function );
168
169 return( -1 );
170 }
171 lz_buffer_iterator = 207;
172
173 if( memory_set(
174 &( lz_buffer[ lz_buffer_iterator ] ),
175 0,
176 4096 - lz_buffer_iterator ) == NULL )
177 {
178 libcerror_error_set(
179 error,
180 LIBCERROR_ERROR_DOMAIN_MEMORY,
181 LIBCERROR_MEMORY_ERROR_SET_FAILED,
182 "%s: unable to clear lz buffer.",
183 function );
184
185 return( -1 );
186 }
187 while( compressed_data_iterator < (size_t) lzfu_header.compressed_data_size )
188 {
189 flag_byte = lzfu_data[ compressed_data_iterator++ ];
190
191 /* Check every bit in the chunk flag byte from LSB to MSB
192 */
193 for( flag_byte_bit_mask = 0x01;
194 flag_byte_bit_mask != 0x00;
195 flag_byte_bit_mask <<= 1 )
196 {
197 if( compressed_data_iterator == (size_t) lzfu_header.compressed_data_size )
198 {
199 break;
200 }
201 /* Check if the byte value is a literal or a reference
202 */
203 if( ( flag_byte & flag_byte_bit_mask ) == 0 )
204 {
205 lz_buffer[ lz_buffer_iterator++ ] = lzfu_data[ compressed_data_iterator ];
206
207 uncompressed_data_iterator++;
208 compressed_data_iterator++;
209
210 /* Make sure the lz buffer iterator wraps around
211 */
212 lz_buffer_iterator %= 4096;
213
214 lz_buffer[ lz_buffer_iterator ] = 0;
215 }
216 else
217 {
218 lzfu_reference_data = &( lzfu_data[ compressed_data_iterator ] );
219
220 compressed_data_iterator += 2;
221
222 byte_stream_copy_to_uint16_big_endian(
223 lzfu_reference_data,
224 reference_offset );
225
226 reference_size = ( reference_offset & 0x000f ) + 2;
227 reference_offset >>= 4;
228
229 for( reference_iterator = 0; reference_iterator < reference_size; reference_iterator++ )
230 {
231 lz_buffer[ lz_buffer_iterator++ ] = lz_buffer[ reference_offset ];
232
233 uncompressed_data_iterator++;
234 reference_offset++;
235
236 /* Make sure the lz buffer iterator and reference offset wrap around
237 */
238 lz_buffer_iterator %= 4096;
239 reference_offset %= 4096;
240
241 lz_buffer[ lz_buffer_iterator ] = 0;
242 }
243 }
244 }
245 }
246 if( (size_t) ( lzfu_header.uncompressed_data_size + 2 ) != uncompressed_data_iterator )
247 {
248 libcnotify_printf(
249 "%s: mismatch in uncompressed data size (in header: %" PRIu32 " != required: %" PRIzd ").\n",
250 function,
251 lzfu_header.uncompressed_data_size + 2,
252 uncompressed_data_iterator );
253 }
254 }
255 #endif
256 /* Compensate for the 2 trailing zero bytes
257 */
258 *uncompressed_data_size = lzfu_header.uncompressed_data_size + 2;
259
260 return( 1 );
261 }
262
263 /* Compresses data using LZFu compression
264 * Returns 1 on success or -1 on error
265 */
libfmapi_lzfu_compress(uint8_t * compressed_data,size_t * compressed_data_size,uint8_t * uncompressed_data,size_t uncompressed_data_size,libcerror_error_t ** error)266 int libfmapi_lzfu_compress(
267 uint8_t *compressed_data,
268 size_t *compressed_data_size,
269 uint8_t *uncompressed_data,
270 size_t uncompressed_data_size,
271 libcerror_error_t **error )
272 {
273 static char *function = "libfmapi_lzfu_compress";
274
275 if( compressed_data == NULL )
276 {
277 libcerror_error_set(
278 error,
279 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
280 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
281 "%s: invalid compressed data.",
282 function );
283
284 return( -1 );
285 }
286 if( compressed_data_size == NULL )
287 {
288 libcerror_error_set(
289 error,
290 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
291 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
292 "%s: invalid compressed data size.",
293 function );
294
295 return( -1 );
296 }
297 if( uncompressed_data == NULL )
298 {
299 libcerror_error_set(
300 error,
301 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
302 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
303 "%s: invalid uncompressed data.",
304 function );
305
306 return( -1 );
307 }
308 if( uncompressed_data_size > (size_t) SSIZE_MAX )
309 {
310 libcerror_error_set(
311 error,
312 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
313 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
314 "%s: invalid uncompressed data size value exceeds maximum.",
315 function );
316
317 return( -1 );
318 }
319 /* TODO implement */
320 libcerror_error_set(
321 error,
322 LIBCERROR_ERROR_DOMAIN_RUNTIME,
323 LIBCERROR_RUNTIME_ERROR_GENERIC,
324 "%s: NOT IMPLEMENTED YET",
325 function );
326
327 return( -1 );
328 }
329
330 /* Decompresses data using LZFu compression
331 * Returns 1 on success or -1 on error
332 */
libfmapi_lzfu_decompress(uint8_t * uncompressed_data,size_t * uncompressed_data_size,uint8_t * compressed_data,size_t compressed_data_size,libcerror_error_t ** error)333 int libfmapi_lzfu_decompress(
334 uint8_t *uncompressed_data,
335 size_t *uncompressed_data_size,
336 uint8_t *compressed_data,
337 size_t compressed_data_size,
338 libcerror_error_t **error )
339 {
340 libfmapi_lzfu_header_t lzfu_header;
341 uint8_t lz_buffer[ 4096 ];
342
343 uint8_t *lzfu_data = 0;
344 uint8_t *lzfu_reference_data = 0;
345 static char *function = "libfmapi_lzfu_decompress";
346 size_t compressed_data_iterator = 0;
347 size_t uncompressed_data_iterator = 0;
348 uint32_t calculated_checksum = 0;
349 uint16_t lz_buffer_iterator = 0;
350 uint16_t reference_offset = 0;
351 uint16_t reference_size = 0;
352 uint16_t reference_iterator = 0;
353 uint8_t flag_byte_bit_mask = 0;
354 uint8_t flag_byte = 0;
355
356 if( uncompressed_data == NULL )
357 {
358 libcerror_error_set(
359 error,
360 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
361 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
362 "%s: invalid uncompressed data.",
363 function );
364
365 return( -1 );
366 }
367 if( uncompressed_data_size == NULL )
368 {
369 libcerror_error_set(
370 error,
371 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
372 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
373 "%s: invalid uncompressed data size.",
374 function );
375
376 return( -1 );
377 }
378 if( compressed_data == NULL )
379 {
380 libcerror_error_set(
381 error,
382 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
383 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
384 "%s: invalid compressed data.",
385 function );
386
387 return( -1 );
388 }
389 if( compressed_data_size > (size_t) SSIZE_MAX )
390 {
391 libcerror_error_set(
392 error,
393 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
394 LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM,
395 "%s: invalid compressed data size value exceeds maximum.",
396 function );
397
398 return( -1 );
399 }
400 if( memory_copy(
401 lz_buffer,
402 libfmapi_lzfu_rtf_dictionary,
403 207 ) == NULL )
404 {
405 libcerror_error_set(
406 error,
407 LIBCERROR_ERROR_DOMAIN_MEMORY,
408 LIBCERROR_MEMORY_ERROR_COPY_FAILED,
409 "%s: unable to initialize lz buffer.",
410 function );
411
412 return( -1 );
413 }
414 lz_buffer_iterator = 207;
415
416 if( memory_set(
417 &( lz_buffer[ lz_buffer_iterator ] ),
418 0,
419 4096 - lz_buffer_iterator ) == NULL )
420 {
421 libcerror_error_set(
422 error,
423 LIBCERROR_ERROR_DOMAIN_MEMORY,
424 LIBCERROR_MEMORY_ERROR_SET_FAILED,
425 "%s: unable to clear lz buffer.",
426 function );
427
428 return( -1 );
429 }
430 lzfu_data = compressed_data;
431
432 byte_stream_copy_to_uint32_little_endian(
433 lzfu_data,
434 lzfu_header.compressed_data_size );
435
436 lzfu_data += 4;
437
438 byte_stream_copy_to_uint32_little_endian(
439 lzfu_data,
440 lzfu_header.uncompressed_data_size );
441
442 lzfu_data += 4;
443
444 byte_stream_copy_to_uint32_little_endian(
445 lzfu_data,
446 lzfu_header.signature );
447
448 lzfu_data += 4;
449
450 byte_stream_copy_to_uint32_little_endian(
451 lzfu_data,
452 lzfu_header.checksum );
453
454 lzfu_data += 4;
455
456 #if defined( HAVE_DEBUG_OUTPUT )
457 if( libcnotify_verbose != 0 )
458 {
459 libcnotify_printf(
460 "%s: lzfu header compressed data size\t: %" PRIu32 "\n",
461 function,
462 lzfu_header.compressed_data_size );
463 libcnotify_printf(
464 "%s: lzfu header uncompressed data size\t: %" PRIu32 "\n",
465 function,
466 lzfu_header.uncompressed_data_size );
467 libcnotify_printf(
468 "%s: lzfu header signature\t\t\t: 0x08%" PRIx32 "\n",
469 function,
470 lzfu_header.signature );
471 libcnotify_printf(
472 "%s: lzfu header checksum\t\t: %" PRIu32 "\n",
473 function,
474 lzfu_header.checksum );
475 }
476 #endif
477
478 if( ( lzfu_header.signature != LIBFMAPI_LZFU_SIGNATURE_COMPRESSED )
479 && ( lzfu_header.signature != LIBFMAPI_LZFU_SIGNATURE_UNCOMPRESSED ) )
480 {
481 libcerror_error_set(
482 error,
483 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
484 LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE,
485 "%s: unsupported compression signature: 0x%08" PRIx32 ".",
486 function,
487 lzfu_header.signature );
488
489 return( -1 );
490 }
491 compressed_data_size -= sizeof( libfmapi_lzfu_header_t );
492
493 /* The compressed data size includes 12 bytes of the header
494 */
495 lzfu_header.compressed_data_size -= 12;
496
497 if( lzfu_header.compressed_data_size != compressed_data_size )
498 {
499 libcerror_error_set(
500 error,
501 LIBCERROR_ERROR_DOMAIN_RUNTIME,
502 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
503 "%s: mismatch in compressed data size (%" PRIu32 " != %" PRIzd ").",
504 function,
505 lzfu_header.compressed_data_size,
506 compressed_data_size );
507
508 return( -1 );
509 }
510 /* Make sure the uncompressed buffer is large enough
511 */
512 if( *uncompressed_data_size < lzfu_header.uncompressed_data_size )
513 {
514 libcerror_error_set(
515 error,
516 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
517 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
518 "%s: uncompressed data too small.",
519 function );
520
521 *uncompressed_data_size = lzfu_header.uncompressed_data_size;
522
523 return( -1 );
524 }
525 if( libfmapi_checksum_calculate_weak_crc32(
526 &calculated_checksum,
527 lzfu_data,
528 (size_t) lzfu_header.compressed_data_size,
529 0,
530 error ) != 1 )
531 {
532 libcerror_error_set(
533 error,
534 LIBCERROR_ERROR_DOMAIN_RUNTIME,
535 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
536 "%s: unable to calculate weak CRC-32.",
537 function );
538
539 return( -1 );
540 }
541 if( lzfu_header.checksum != calculated_checksum )
542 {
543 libcerror_error_set(
544 error,
545 LIBCERROR_ERROR_DOMAIN_INPUT,
546 LIBCERROR_INPUT_ERROR_CHECKSUM_MISMATCH,
547 "%s: mismatch in checksum ( %" PRIu32 " != %" PRIu32 " ).",
548 function,
549 lzfu_header.checksum,
550 calculated_checksum );
551
552 return( -1 );
553 }
554 while( compressed_data_iterator < (size_t) lzfu_header.compressed_data_size )
555 {
556 flag_byte = lzfu_data[ compressed_data_iterator++ ];
557
558 /* Check every bit in the chunk flag byte from LSB to MSB
559 */
560 for( flag_byte_bit_mask = 0x01; flag_byte_bit_mask != 0x00; flag_byte_bit_mask <<= 1 )
561 {
562 if( compressed_data_iterator == (size_t) lzfu_header.compressed_data_size )
563 {
564 break;
565 }
566 /* Check if the byte value is a literal or a reference
567 */
568 if( ( flag_byte & flag_byte_bit_mask ) == 0 )
569 {
570 if( compressed_data_iterator >= (size_t) lzfu_header.compressed_data_size )
571 {
572 libcerror_error_set(
573 error,
574 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
575 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
576 "%s: missing compressed data.",
577 function );
578
579 *uncompressed_data_size = 0;
580
581 return( -1 );
582 }
583 if( uncompressed_data_iterator >= *uncompressed_data_size )
584 {
585 libcerror_error_set(
586 error,
587 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
588 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
589 "%s: uncompressed data too small.",
590 function );
591
592 *uncompressed_data_size = uncompressed_data_iterator;
593
594 return( -1 );
595 }
596 lz_buffer[ lz_buffer_iterator++ ] = lzfu_data[ compressed_data_iterator ];
597 uncompressed_data[ uncompressed_data_iterator++ ] = lzfu_data[ compressed_data_iterator ];
598
599 compressed_data_iterator++;
600
601 /* Make sure the lz buffer iterator wraps around
602 */
603 lz_buffer_iterator %= 4096;
604
605 lz_buffer[ lz_buffer_iterator ] = 0;
606 }
607 else
608 {
609 if( ( compressed_data_iterator + 1 ) >= (size_t) lzfu_header.compressed_data_size )
610 {
611 libcerror_error_set(
612 error,
613 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
614 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
615 "%s: missing compressed data.",
616 function );
617
618 *uncompressed_data_size = 0;
619
620 return( -1 );
621 }
622 lzfu_reference_data = &( lzfu_data[ compressed_data_iterator ] );
623
624 compressed_data_iterator += 2;
625
626 byte_stream_copy_to_uint16_big_endian(
627 lzfu_reference_data,
628 reference_offset );
629
630 reference_size = ( reference_offset & 0x000f ) + 2;
631 reference_offset >>= 4;
632
633 if( ( uncompressed_data_iterator + reference_size - 1 ) >= *uncompressed_data_size )
634 {
635 libcerror_error_set(
636 error,
637 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
638 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
639 "%s: uncompressed data too small.",
640 function );
641
642 *uncompressed_data_size = uncompressed_data_iterator + reference_size;
643
644 return( -1 );
645 }
646 for( reference_iterator = 0; reference_iterator < reference_size; reference_iterator++ )
647 {
648 lz_buffer[ lz_buffer_iterator++ ] = lz_buffer[ reference_offset ];
649 uncompressed_data[ uncompressed_data_iterator++ ] = lz_buffer[ reference_offset ];
650
651 reference_offset++;
652
653 /* Make sure the lz buffer iterator and reference offset wrap around
654 */
655 lz_buffer_iterator %= 4096;
656 reference_offset %= 4096;
657
658 lz_buffer[ lz_buffer_iterator ] = 0;
659 }
660 }
661 }
662 }
663 *uncompressed_data_size = uncompressed_data_iterator;
664
665 return( 1 );
666 }
667
668