diff --git a/include/FLAC/file_decoder.h b/include/FLAC/file_decoder.h index 1c3d7ee2..d0b43a3b 100644 --- a/include/FLAC/file_decoder.h +++ b/include/FLAC/file_decoder.h @@ -30,12 +30,16 @@ typedef enum { FLAC__FILE_DECODER_MEMORY_ALLOCATION_ERROR, FLAC__FILE_DECODER_SEEK_ERROR, FLAC__FILE_DECODER_STREAM_ERROR, + FLAC__FILE_DECODER_MD5_ERROR, FLAC__FILE_DECODER_UNINITIALIZED } FLAC__FileDecoderState; extern const char *FLAC__FileDecoderStateString[]; struct FLAC__FileDecoderPrivate; typedef struct { + /* this field may not change once FLAC__file_decoder_init() is called */ + bool check_md5; /* if true, generate MD5 signature of decoded data and compare against signature in the Encoding metadata block */ + FLAC__FileDecoderState state; /* must be FLAC__FILE_DECODER_UNINITIALIZED when passed to FLAC__file_decoder_init() */ struct FLAC__FileDecoderPrivate *guts; /* must be 0 when passed to FLAC__file_decoder_init() */ } FLAC__FileDecoder; @@ -50,7 +54,8 @@ FLAC__FileDecoderState FLAC__file_decoder_init( void (*error_callback)(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data), void *client_data ); -void FLAC__file_decoder_finish(FLAC__FileDecoder *decoder); +/* only returns false if check_md5 is set AND the stored MD5 sum is non-zero AND the stored MD5 sum and computed MD5 sum do not match */ +bool FLAC__file_decoder_finish(FLAC__FileDecoder *decoder); bool FLAC__file_decoder_process_whole_file(FLAC__FileDecoder *decoder); bool FLAC__file_decoder_process_metadata(FLAC__FileDecoder *decoder); bool FLAC__file_decoder_process_one_frame(FLAC__FileDecoder *decoder); diff --git a/include/FLAC/format.h b/include/FLAC/format.h index 78acc03c..1a49893c 100644 --- a/include/FLAC/format.h +++ b/include/FLAC/format.h @@ -66,8 +66,9 @@ typedef enum { * 3: (number of channels)-1 * 5: (bits per sample)-1 * 36: total samples, 0 => unknown + *128: MD5 digest of the original unencoded audio data *---- ----------------- - * 18 bytes total + * 34 bytes total */ typedef struct { unsigned min_blocksize, max_blocksize; @@ -76,6 +77,7 @@ typedef struct { unsigned channels; unsigned bits_per_sample; uint64 total_samples; + byte md5sum[16]; } FLAC__StreamMetaData_Encoding; extern const unsigned FLAC__STREAM_METADATA_ENCODING_MIN_BLOCK_SIZE_LEN; /* = 16 bits */ @@ -86,7 +88,8 @@ extern const unsigned FLAC__STREAM_METADATA_ENCODING_SAMPLE_RATE_LEN; /* = 20 bi extern const unsigned FLAC__STREAM_METADATA_ENCODING_CHANNELS_LEN; /* = 3 bits */ extern const unsigned FLAC__STREAM_METADATA_ENCODING_BITS_PER_SAMPLE_LEN; /* = 5 bits */ extern const unsigned FLAC__STREAM_METADATA_ENCODING_TOTAL_SAMPLES_LEN; /* = 36 bits */ -extern const unsigned FLAC__STREAM_METADATA_ENCODING_LENGTH; /* = 18 bytes */ +extern const unsigned FLAC__STREAM_METADATA_ENCODING_MD5SUM_LEN; /* = 128 bits */ +extern const unsigned FLAC__STREAM_METADATA_ENCODING_LENGTH; /* = 34 bytes */ /***************************************************************************** * diff --git a/src/flac/encode.c b/src/flac/encode.c index d67c4f86..c7df4f5e 100644 --- a/src/flac/encode.c +++ b/src/flac/encode.c @@ -603,6 +603,10 @@ void metadata_callback(const FLAC__Encoder *encoder, const FLAC__StreamMetaData * would also break all streams encoded in the previous format. */ + if(-1 == fseek(f, 26, SEEK_SET)) goto samples_; + fwrite(metadata->data.encoding.md5sum, 1, 16, f); + +samples_: if(-1 == fseek(f, 21, SEEK_SET)) goto framesize_; if(fread(&b, 1, 1, f) != 1) goto framesize_; if(-1 == fseek(f, 21, SEEK_SET)) goto framesize_; diff --git a/src/libFLAC/encoder.c b/src/libFLAC/encoder.c index 08442a48..dccf796d 100644 --- a/src/libFLAC/encoder.c +++ b/src/libFLAC/encoder.c @@ -26,6 +26,7 @@ #include "private/encoder_framing.h" #include "private/fixed.h" #include "private/lpc.h" +#include "private/md5.h" #ifdef min #undef min @@ -54,6 +55,7 @@ typedef struct FLAC__EncoderPrivate { FLAC__StreamMetaData metadata; unsigned current_sample_number; unsigned current_frame_number; + struct MD5Context md5context; FLAC__EncoderWriteStatus (*write_callback)(const FLAC__Encoder *encoder, const byte buffer[], unsigned bytes, unsigned samples, unsigned current_frame, void *client_data); void (*metadata_callback)(const FLAC__Encoder *encoder, const FLAC__StreamMetaData *metadata, void *client_data); void *client_data; @@ -329,6 +331,8 @@ FLAC__EncoderState FLAC__encoder_init(FLAC__Encoder *encoder, FLAC__EncoderWrite encoder->guts->metadata.data.encoding.channels = encoder->channels; encoder->guts->metadata.data.encoding.bits_per_sample = encoder->bits_per_sample; encoder->guts->metadata.data.encoding.total_samples = 0; /* we don't know this yet; have to fill it in later */ + memset(encoder->guts->metadata.data.encoding.md5sum, 0, 16); /* we don't know this yet; have to fill it in later */ + MD5Init(&encoder->guts->md5context); if(!FLAC__add_metadata_block(&encoder->guts->metadata, &encoder->guts->frame)) return encoder->state = FLAC__ENCODER_FRAMING_ERROR; @@ -354,6 +358,7 @@ void FLAC__encoder_finish(FLAC__Encoder *encoder) encoder->blocksize = encoder->guts->current_sample_number; encoder_process_frame_(encoder, true); /* true => is last frame */ } + MD5Final(encoder->guts->metadata.data.encoding.md5sum, &encoder->guts->md5context); encoder->guts->metadata_callback(encoder, &encoder->guts->metadata, encoder->guts->client_data); if(encoder->guts != 0) { for(i = 0; i < encoder->channels; i++) { @@ -488,6 +493,14 @@ bool encoder_process_frame_(FLAC__Encoder *encoder, bool is_last_frame) assert(encoder->state == FLAC__ENCODER_OK); + /* + * Accumulate raw signal to the MD5 signature + */ + if(!FLAC__MD5Accumulate(&encoder->guts->md5context, encoder->guts->integer_signal, encoder->channels, encoder->blocksize, (encoder->bits_per_sample+7) / 8)) { + encoder->state = FLAC__ENCODER_MEMORY_ALLOCATION_ERROR; + return false; + } + /* * First do a normal encoding pass */ diff --git a/src/libFLAC/encoder_framing.c b/src/libFLAC/encoder_framing.c index 69201d75..de50081b 100644 --- a/src/libFLAC/encoder_framing.c +++ b/src/libFLAC/encoder_framing.c @@ -32,6 +32,8 @@ static bool subframe_add_residual_partitioned_rice_(FLAC__BitBuffer *bb, const i bool FLAC__add_metadata_block(const FLAC__StreamMetaData *metadata, FLAC__BitBuffer *bb) { + unsigned i; + if(!FLAC__bitbuffer_write_raw_uint32(bb, metadata->is_last, FLAC__STREAM_METADATA_IS_LAST_LEN)) return false; @@ -70,6 +72,10 @@ bool FLAC__add_metadata_block(const FLAC__StreamMetaData *metadata, FLAC__BitBuf return false; if(!FLAC__bitbuffer_write_raw_uint64(bb, metadata->data.encoding.total_samples, FLAC__STREAM_METADATA_ENCODING_TOTAL_SAMPLES_LEN)) return false; + for(i = 0; i < 16; i++) { + if(!FLAC__bitbuffer_write_raw_uint32(bb, metadata->data.encoding.md5sum[i], 8)) + return false; + } break; default: assert(0); diff --git a/src/libFLAC/file_decoder.c b/src/libFLAC/file_decoder.c index 8f5be04d..206040b1 100644 --- a/src/libFLAC/file_decoder.c +++ b/src/libFLAC/file_decoder.c @@ -23,6 +23,7 @@ #include /* for strcmp() */ #include "FLAC/file_decoder.h" #include "protected/stream_decoder.h" +#include "private/md5.h" typedef struct FLAC__FileDecoderPrivate { FLAC__StreamDecoderWriteStatus (*write_callback)(const FLAC__FileDecoder *decoder, const FLAC__FrameHeader *header, const int32 *buffer[], void *client_data); @@ -31,6 +32,9 @@ typedef struct FLAC__FileDecoderPrivate { void *client_data; FILE *file; FLAC__StreamDecoder *stream; + struct MD5Context md5context; + byte stored_md5sum[16]; /* this is what is stored in the metadata */ + byte computed_md5sum[16]; /* this is the sum we computed from the decoded data */ /* the rest of these are only used for seeking: */ FLAC__StreamMetaData_Encoding metadata; /* we keep this around so we can figure out how to seek quickly */ FLAC__FrameHeader last_frame_header; /* holds the info of the last frame we seeked to */ @@ -105,6 +109,14 @@ FLAC__FileDecoderState FLAC__file_decoder_init( if(decoder->guts->file == 0) return decoder->state = FLAC__FILE_DECODER_ERROR_OPENING_FILE; + /* We initialize the MD5Context even though we may never use it. This is + * because check_md5 may be turned on to start and then turned off if a + * seek occurs. So we always init the context here and finalize it in + * FLAC__file_decoder_finish() to make sure things are always cleaned up + *properly. + */ + MD5Init(&decoder->guts->md5context); + decoder->guts->stream = FLAC__stream_decoder_get_new_instance(); if(FLAC__stream_decoder_init(decoder->guts->stream, read_callback_, write_callback_, metadata_callback_, error_callback_, decoder) != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) return decoder->state = FLAC__FILE_DECODER_MEMORY_ALLOCATION_ERROR; /* this is based on internal knowledge of FLAC__stream_decoder_init() */ @@ -112,22 +124,33 @@ FLAC__FileDecoderState FLAC__file_decoder_init( return decoder->state; } -void FLAC__file_decoder_finish(FLAC__FileDecoder *decoder) +bool FLAC__file_decoder_finish(FLAC__FileDecoder *decoder) { + bool md5_failed = false; + assert(decoder != 0); if(decoder->state == FLAC__FILE_DECODER_UNINITIALIZED) - return; + return true; if(decoder->guts != 0) { if(decoder->guts->file != 0 && decoder->guts->file != stdin) fclose(decoder->guts->file); + /* see the comment in FLAC__file_decoder_init() as to why we always + * call MD5Final() + */ + MD5Final(decoder->guts->computed_md5sum, &decoder->guts->md5context); if(decoder->guts->stream != 0) { FLAC__stream_decoder_finish(decoder->guts->stream); FLAC__stream_decoder_free_instance(decoder->guts->stream); } + if(decoder->check_md5) { + if(memcmp(decoder->guts->stored_md5sum, decoder->guts->computed_md5sum, 16)) + md5_failed = true; + } free(decoder->guts); decoder->guts = 0; } decoder->state = FLAC__FILE_DECODER_UNINITIALIZED; + return !md5_failed; } bool FLAC__file_decoder_process_whole_file(FLAC__FileDecoder *decoder) @@ -207,6 +230,9 @@ bool FLAC__file_decoder_seek_absolute(FLAC__FileDecoder *decoder, uint64 sample) decoder->state = FLAC__FILE_DECODER_SEEKING; + /* turn off md5 checking if a seek is attempted */ + decoder->check_md5 = false; + if(!FLAC__stream_decoder_reset(decoder->guts->stream)) { decoder->state = FLAC__FILE_DECODER_STREAM_ERROR; return false; @@ -301,6 +327,10 @@ FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decode } } else { + if(file_decoder->check_md5) { + if(!FLAC__MD5Accumulate(&file_decoder->guts->md5context, buffer, header->channels, header->blocksize, (header->bits_per_sample+7) / 8)) + return FLAC__STREAM_DECODER_WRITE_ABORT; + } return file_decoder->guts->write_callback(file_decoder, header, buffer, file_decoder->guts->client_data); } } @@ -310,8 +340,13 @@ void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMe FLAC__FileDecoder *file_decoder = (FLAC__FileDecoder *)client_data; (void)decoder; - if(metadata->type == FLAC__METADATA_TYPE_ENCODING) + if(metadata->type == FLAC__METADATA_TYPE_ENCODING) { file_decoder->guts->metadata = metadata->data.encoding; + /* save the MD5 signature for comparison later */ + memcpy(file_decoder->guts->stored_md5sum, metadata->data.encoding.md5sum, 16); + if(0 == memcmp(file_decoder->guts->stored_md5sum, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) + file_decoder->check_md5 = false; + } if(file_decoder->state != FLAC__FILE_DECODER_SEEKING) file_decoder->guts->metadata_callback(file_decoder, metadata, file_decoder->guts->client_data); } diff --git a/src/libFLAC/format.c b/src/libFLAC/format.c index c23cc0d1..7f12368d 100644 --- a/src/libFLAC/format.c +++ b/src/libFLAC/format.c @@ -36,7 +36,8 @@ const unsigned FLAC__STREAM_METADATA_ENCODING_SAMPLE_RATE_LEN = 20; /* bits */ const unsigned FLAC__STREAM_METADATA_ENCODING_CHANNELS_LEN = 3; /* bits */ const unsigned FLAC__STREAM_METADATA_ENCODING_BITS_PER_SAMPLE_LEN = 5; /* bits */ const unsigned FLAC__STREAM_METADATA_ENCODING_TOTAL_SAMPLES_LEN = 36; /* bits */ -const unsigned FLAC__STREAM_METADATA_ENCODING_LENGTH = 18; /* bytes */ +const unsigned FLAC__STREAM_METADATA_ENCODING_MD5SUM_LEN = 128; /* bits */ +const unsigned FLAC__STREAM_METADATA_ENCODING_LENGTH = 34; /* bytes */ const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */ const unsigned FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */ diff --git a/src/libFLAC/stream_decoder.c b/src/libFLAC/stream_decoder.c index a3563a00..181a1e08 100644 --- a/src/libFLAC/stream_decoder.c +++ b/src/libFLAC/stream_decoder.c @@ -481,6 +481,13 @@ bool stream_decoder_read_metadata_(FLAC__StreamDecoder *decoder) return false; /* the read_callback_ sets the state for us */ used_bits += FLAC__STREAM_METADATA_ENCODING_TOTAL_SAMPLES_LEN; + for(i = 0; i < 16; i++) { + if(!FLAC__bitbuffer_read_raw_uint32(&decoder->guts->input, &x, 8, read_callback_, decoder)) + return false; /* the read_callback_ sets the state for us */ + decoder->guts->stream_header.data.encoding.md5sum[i] = (byte)x; + } + used_bits += 128; + /* skip the rest of the block */ assert(used_bits % 8 == 0); length -= (used_bits / 8);