///////////////////////////////////////////////////////////////////////////////
//
/// \file test_stream_flags.c
/// \brief Tests Stream Header and Stream Footer coders
//
// Authors: Jia Tan
// Lasse Collin
//
///////////////////////////////////////////////////////////////////////////////
#include "tests.h"
// Size of the Stream Flags field
// (taken from src/liblzma/common/stream_flags_common.h)
#define XZ_STREAM_FLAGS_SIZE 2
#ifdef HAVE_ENCODERS
// Header and footer magic bytes for .xz file format
// (taken from src/liblzma/common/stream_flags_common.c)
static const uint8_t xz_header_magic[6]
= { 0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00 };
static const uint8_t xz_footer_magic[2] = { 0x59, 0x5A };
#endif
#ifdef HAVE_ENCODERS
static void
stream_header_encode_helper(lzma_check check)
{
lzma_stream_flags flags = {
.version = 0,
.check = check,
};
uint8_t header[LZMA_STREAM_HEADER_SIZE];
// Encode Stream Header
assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK);
// Stream Header must start with Header Magic Bytes
const uint32_t magic_size = sizeof(xz_header_magic);
assert_array_eq(header, xz_header_magic, magic_size);
// Next must come Stream Flags
const uint8_t *encoded_stream_flags = header + magic_size;
// First byte is always null-byte.
// Second byte must have the Check ID in the lowest four bits
// and the highest four bits zero.
const uint8_t expected_stream_flags[] = { 0, check };
assert_array_eq(encoded_stream_flags, expected_stream_flags,
XZ_STREAM_FLAGS_SIZE);
// Last part is the CRC32 of the Stream Flags
const uint8_t *crc_ptr = encoded_stream_flags + XZ_STREAM_FLAGS_SIZE;
const uint32_t expected_crc = lzma_crc32(expected_stream_flags,
XZ_STREAM_FLAGS_SIZE, 0);
assert_uint_eq(read32le(crc_ptr), expected_crc);
}
#endif
static void
test_lzma_stream_header_encode(void)
{
#ifndef HAVE_ENCODERS
assert_skip("Encoder support disabled");
#else
for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++)
stream_header_encode_helper(i);
lzma_stream_flags flags = {
.version = 0,
.check = LZMA_CHECK_CRC32
};
uint8_t header[LZMA_STREAM_HEADER_SIZE];
// Should fail if version > 0
flags.version = 1;
assert_lzma_ret(lzma_stream_header_encode(&flags, header),
LZMA_OPTIONS_ERROR);
flags.version = 0;
// Should fail if Check ID is invalid
flags.check = INVALID_LZMA_CHECK_ID;
assert_lzma_ret(lzma_stream_header_encode(&flags, header),
LZMA_PROG_ERROR);
flags.check = LZMA_CHECK_CRC32;
// Should pass even if Backward Size is invalid
// because Stream Header doesn't have that field.
flags.backward_size = LZMA_VLI_MAX + 1;
assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK);
#endif
}
#if defined(HAVE_ENCODERS)
static void
stream_footer_encode_helper(lzma_check check)
{
lzma_stream_flags flags = {
.version = 0,
.check = check,
.backward_size = LZMA_BACKWARD_SIZE_MIN,
};
uint8_t footer[LZMA_STREAM_HEADER_SIZE];
// Encode Stream Footer
assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK);
// Stream Footer must start with CRC32
const uint32_t crc = read32le(footer);
const uint32_t expected_crc = lzma_crc32(footer + sizeof(uint32_t),
LZMA_STREAM_HEADER_SIZE - (sizeof(uint32_t) +
sizeof(xz_footer_magic)), 0);
assert_uint_eq(crc, expected_crc);
// Next the Backward Size
const uint32_t backwards_size = read32le(footer + sizeof(uint32_t));
const uint32_t expected_backwards_size = flags.backward_size / 4 - 1;
assert_uint_eq(backwards_size, expected_backwards_size);
// Next the Stream Flags
const uint8_t *stream_flags = footer + sizeof(uint32_t) * 2;
// First byte must be null
assert_uint_eq(stream_flags[0], 0);
// Second byte must have the Check ID in the lowest four bits
// and the highest four bits zero.
assert_uint_eq(stream_flags[1], check);
// And ends with Footer Magic Bytes
const uint8_t *expected_footer_magic = stream_flags +
XZ_STREAM_FLAGS_SIZE;
assert_array_eq(expected_footer_magic, xz_footer_magic,
sizeof(xz_footer_magic));
}
#endif
static void
test_lzma_stream_footer_encode(void)
{
#ifndef HAVE_ENCODERS
assert_skip("Encoder support disabled");
#else
for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++)
stream_footer_encode_helper(i);
lzma_stream_flags flags = {
.version = 0,
.backward_size = LZMA_BACKWARD_SIZE_MIN,
.check = LZMA_CHECK_CRC32
};
uint8_t footer[LZMA_STREAM_HEADER_SIZE];
// Should fail if version > 0
flags.version = 1;
assert_lzma_ret(lzma_stream_footer_encode(&flags, footer),
LZMA_OPTIONS_ERROR);
flags.version = 0;
// Should fail if Check ID is invalid
flags.check = INVALID_LZMA_CHECK_ID;
assert_lzma_ret(lzma_stream_footer_encode(&flags, footer),
LZMA_PROG_ERROR);
// Should fail if Backward Size is invalid
flags.backward_size -= 1;
assert_lzma_ret(lzma_stream_footer_encode(&flags, footer),
LZMA_PROG_ERROR);
flags.backward_size += 2;
assert_lzma_ret(lzma_stream_footer_encode(&flags, footer),
LZMA_PROG_ERROR);
flags.backward_size = LZMA_BACKWARD_SIZE_MAX + 4;
assert_lzma_ret(lzma_stream_footer_encode(&flags, footer),
LZMA_PROG_ERROR);
#endif
}
#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS)
static void
stream_header_decode_helper(lzma_check check)
{
lzma_stream_flags flags = {
.version = 0,
.check = check
};
uint8_t header[LZMA_STREAM_HEADER_SIZE];
assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK);
lzma_stream_flags dest_flags;
assert_lzma_ret(lzma_stream_header_decode(&dest_flags, header),
LZMA_OK);
// Version should be 0
assert_uint_eq(dest_flags.version, 0);
// Backward Size should be LZMA_VLI_UNKNOWN
assert_uint_eq(dest_flags.backward_size, LZMA_VLI_UNKNOWN);
// Check ID must equal the argument given to this function.
assert_uint_eq(dest_flags.check, check);
}
#endif
static void
test_lzma_stream_header_decode(void)
{
#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS)
assert_skip("Encoder or decoder support disabled");
#else
for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++)
stream_header_decode_helper(i);
lzma_stream_flags flags = {
.version = 0,
.check = LZMA_CHECK_CRC32
};
uint8_t header[LZMA_STREAM_HEADER_SIZE];
lzma_stream_flags dest;
// First encode known flags to header buffer
assert_lzma_ret(lzma_stream_header_encode(&flags, header), LZMA_OK);
// Should fail if magic bytes do not match
header[0] ^= 1;
assert_lzma_ret(lzma_stream_header_decode(&dest, header),
LZMA_FORMAT_ERROR);
header[0] ^= 1;
// Should fail if a reserved bit is set
uint8_t *stream_flags = header + sizeof(xz_header_magic);
stream_flags[0] = 1;
// Need to adjust CRC32 after making a change since the CRC32
// is verified before decoding the Stream Flags field.
uint8_t *crc32_ptr = header + sizeof(xz_header_magic)
+ XZ_STREAM_FLAGS_SIZE;
const uint32_t crc_orig = read32le(crc32_ptr);
uint32_t new_crc32 = lzma_crc32(
stream_flags, XZ_STREAM_FLAGS_SIZE, 0);
write32le(crc32_ptr, new_crc32);
assert_lzma_ret(lzma_stream_header_decode(&dest, header),
LZMA_OPTIONS_ERROR);
stream_flags[0] = 0;
write32le(crc32_ptr, crc_orig);
// Should fail if upper bits of check ID are set
stream_flags[1] |= 0xF0;
new_crc32 = lzma_crc32(stream_flags, XZ_STREAM_FLAGS_SIZE, 0);
write32le(crc32_ptr, new_crc32);
assert_lzma_ret(lzma_stream_header_decode(&dest, header),
LZMA_OPTIONS_ERROR);
stream_flags[1] = flags.check;
write32le(crc32_ptr, crc_orig);
// Should fail if CRC32 does not match.
// First, alter a byte in the Stream Flags.
stream_flags[0] = 1;
assert_lzma_ret(lzma_stream_header_decode(&dest, header),
LZMA_DATA_ERROR);
stream_flags[0] = 0;
// Next, change the CRC32.
*crc32_ptr ^= 1;
assert_lzma_ret(lzma_stream_header_decode(&dest, header),
LZMA_DATA_ERROR);
#endif
}
#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS)
static void
stream_footer_decode_helper(lzma_check check)
{
lzma_stream_flags flags = {
.version = 0,
.backward_size = LZMA_BACKWARD_SIZE_MIN,
.check = check,
};
uint8_t footer[LZMA_STREAM_HEADER_SIZE];
assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK);
lzma_stream_flags dest_flags;
assert_lzma_ret(lzma_stream_footer_decode(&dest_flags, footer),
LZMA_OK);
// Version should be 0.
assert_uint_eq(dest_flags.version, 0);
// Backward Size should equal the value from the flags.
assert_uint_eq(dest_flags.backward_size, flags.backward_size);
// Check ID must equal argument given to this function.
assert_uint_eq(dest_flags.check, check);
}
#endif
static void
test_lzma_stream_footer_decode(void)
{
#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS)
assert_skip("Encoder or decoder support disabled");
#else
for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++)
stream_footer_decode_helper(i);
lzma_stream_flags flags = {
.version = 0,
.check = LZMA_CHECK_CRC32,
.backward_size = LZMA_BACKWARD_SIZE_MIN
};
uint8_t footer[LZMA_STREAM_HEADER_SIZE];
lzma_stream_flags dest;
// First encode known flags to the footer buffer
assert_lzma_ret(lzma_stream_footer_encode(&flags, footer), LZMA_OK);
// Should fail if magic bytes do not match
footer[LZMA_STREAM_HEADER_SIZE - 1] ^= 1;
assert_lzma_ret(lzma_stream_footer_decode(&dest, footer),
LZMA_FORMAT_ERROR);
footer[LZMA_STREAM_HEADER_SIZE - 1] ^= 1;
// Should fail if a reserved bit is set.
// In the Stream Footer, the Stream Flags follow the CRC32 (4 bytes)
// and the Backward Size (4 bytes)
uint8_t *stream_flags = footer + sizeof(uint32_t) * 2;
stream_flags[0] = 1;
// Need to adjust the CRC32 so it will not fail that check instead
uint8_t *crc32_ptr = footer;
const uint32_t crc_orig = read32le(crc32_ptr);
uint8_t *backward_size = footer + sizeof(uint32_t);
uint32_t new_crc32 = lzma_crc32(backward_size, sizeof(uint32_t) +
XZ_STREAM_FLAGS_SIZE, 0);
write32le(crc32_ptr, new_crc32);
assert_lzma_ret(lzma_stream_footer_decode(&dest, footer),
LZMA_OPTIONS_ERROR);
stream_flags[0] = 0;
write32le(crc32_ptr, crc_orig);
// Should fail if upper bits of check ID are set
stream_flags[1] |= 0xF0;
new_crc32 = lzma_crc32(backward_size, sizeof(uint32_t) +
XZ_STREAM_FLAGS_SIZE, 0);
write32le(crc32_ptr, new_crc32);
assert_lzma_ret(lzma_stream_footer_decode(&dest, footer),
LZMA_OPTIONS_ERROR);
stream_flags[1] = flags.check;
write32le(crc32_ptr, crc_orig);
// Should fail if CRC32 does not match.
// First, alter a byte in the Stream Flags.
stream_flags[0] = 1;
assert_lzma_ret(lzma_stream_footer_decode(&dest, footer),
LZMA_DATA_ERROR);
stream_flags[0] = 0;
// Next, change the CRC32
*crc32_ptr ^= 1;
assert_lzma_ret(lzma_stream_footer_decode(&dest, footer),
LZMA_DATA_ERROR);
#endif
}
static void
test_lzma_stream_flags_compare(void)
{
lzma_stream_flags first = {
.version = 0,
.backward_size = LZMA_BACKWARD_SIZE_MIN,
.check = LZMA_CHECK_CRC32,
};
lzma_stream_flags second = first;
// First test should pass
assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK);
// Altering either version should cause an error
first.version = 1;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_OPTIONS_ERROR);
second.version = 1;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_OPTIONS_ERROR);
first.version = 0;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_OPTIONS_ERROR);
second.version = 0;
// Check types must be under the maximum
first.check = INVALID_LZMA_CHECK_ID;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_PROG_ERROR);
second.check = INVALID_LZMA_CHECK_ID;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_PROG_ERROR);
first.check = LZMA_CHECK_CRC32;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_PROG_ERROR);
second.check = LZMA_CHECK_CRC32;
// Check types must be equal
for (lzma_check i = 0; i < LZMA_CHECK_ID_MAX; i++) {
first.check = i;
if (i == second.check)
assert_lzma_ret(lzma_stream_flags_compare(&first,
&second), LZMA_OK);
else
assert_lzma_ret(lzma_stream_flags_compare(&first,
&second), LZMA_DATA_ERROR);
}
first.check = LZMA_CHECK_CRC32;
// Backward Size comparison is skipped if either are LZMA_VLI_UNKNOWN
first.backward_size = LZMA_VLI_UNKNOWN;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK);
second.backward_size = LZMA_VLI_MAX + 1;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second), LZMA_OK);
second.backward_size = LZMA_BACKWARD_SIZE_MIN;
// Backward Sizes need to be valid
first.backward_size = LZMA_VLI_MAX + 4;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_PROG_ERROR);
second.backward_size = LZMA_VLI_MAX + 4;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_PROG_ERROR);
first.backward_size = LZMA_BACKWARD_SIZE_MIN;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_PROG_ERROR);
second.backward_size = LZMA_BACKWARD_SIZE_MIN;
// Backward Sizes must be equal
second.backward_size = first.backward_size + 4;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_DATA_ERROR);
// Should fail if Backward Sizes are > LZMA_BACKWARD_SIZE_MAX
// even though they are equal
first.backward_size = LZMA_BACKWARD_SIZE_MAX + 1;
second.backward_size = LZMA_BACKWARD_SIZE_MAX + 1;
assert_lzma_ret(lzma_stream_flags_compare(&first, &second),
LZMA_PROG_ERROR);
}
extern int
main(int argc, char **argv)
{
tuktest_start(argc, argv);
tuktest_run(test_lzma_stream_header_encode);
tuktest_run(test_lzma_stream_footer_encode);
tuktest_run(test_lzma_stream_header_decode);
tuktest_run(test_lzma_stream_footer_decode);
tuktest_run(test_lzma_stream_flags_compare);
return tuktest_end();
}