diff --git a/lib/src/includes/signed_video_sign.h b/lib/src/includes/signed_video_sign.h index ba4f65f..f6e11a2 100644 --- a/lib/src/includes/signed_video_sign.h +++ b/lib/src/includes/signed_video_sign.h @@ -502,10 +502,13 @@ signed_video_set_recurrence_interval_frames(signed_video_t *self, unsigned recur * If this API is not used, SEI payload is written with EPBs, hence equivalent with setting * |sei_epb| to True. * + * NOTE: AV1 does not have emulation prevention. Therefore, this API is not supported for AV1. + * * @param self Session struct pointer * @param sei_epb SEI payload written with EPB (default True) * * @returns SV_OK SEI w/o EPB was successfully set, + * SV_NOT_SUPPORTED if codec is AV1, * SV_INVALID_PARAMETER Invalid parameter. */ SignedVideoReturnCode diff --git a/lib/src/legacy/legacy_h26x_common.c b/lib/src/legacy/legacy_h26x_common.c index c90beb3..9b57875 100644 --- a/lib/src/legacy/legacy_h26x_common.c +++ b/lib/src/legacy/legacy_h26x_common.c @@ -156,7 +156,7 @@ legacy_gop_info_reset(legacy_gop_info_t *gop_info) gop_info->verified_signature_hash = -1; // If a reset is forced, the stored hashes in |hash_list| have no meaning anymore. gop_info->list_idx = 0; - gop_info->has_reference_hash = false; + gop_info->has_reference_hash = true; gop_info->global_gop_counter_is_synced = false; } diff --git a/lib/src/signed_video_h26x_common.c b/lib/src/signed_video_h26x_common.c index 840ba7b..b9fa320 100644 --- a/lib/src/signed_video_h26x_common.c +++ b/lib/src/signed_video_h26x_common.c @@ -37,7 +37,7 @@ #include "includes/signed_video_signing_plugin.h" #include "signed_video_authenticity.h" // latest_validation_init() #include "signed_video_defines.h" // svrc_t -#include "signed_video_h26x_internal.h" // h26x_nalu_list_item_t +#include "signed_video_h26x_internal.h" // h26x_nalu_list_item_t, METADATA_TYPE_USER_PRIVATE #include "signed_video_h26x_nalu_list.h" // h26x_nalu_list_create() #include "signed_video_internal.h" // gop_info_t, gop_state_t, MAX_HASH_SIZE, DEFAULT_HASH_SIZE #include "signed_video_openssl_internal.h" @@ -46,7 +46,6 @@ #define USER_DATA_UNREGISTERED 5 #define H264_NALU_HEADER_LEN 1 // length of forbidden_zero_bit, nal_ref_idc and nal_unit_type #define H265_NALU_HEADER_LEN 2 // length of nal_unit_header as per ISO/ITU spec -#define METADATA_TYPE_USER_PRIVATE 25 #define AV1_OBU_HEADER_LEN 1 // The salt added to the recursive hash to get the final gop_hash #define GOP_HASH_SALT 1 @@ -259,7 +258,7 @@ gop_info_reset(gop_info_t *gop_info) gop_info->verified_signature_hash = -1; // If a reset is forced, the stored hashes in |hash_list| have no meaning anymore. gop_info->list_idx = 0; - gop_info->has_reference_hash = false; + gop_info->has_reference_hash = true; gop_info->global_gop_counter_is_synced = false; } @@ -1149,11 +1148,11 @@ hash_and_add(signed_video_t *self, const h26x_nalu_t *nalu) svrc_t status = SV_UNKNOWN_FAILURE; SV_TRY() if (nalu->is_first_nalu_part && !nalu->is_last_nalu_part) { - // If this is the first part of a non-complete NALU, initialize the |crypto_handle| to enable - // sequentially updating the hash with more parts. + // If this is the first part of a non-complete NALU/OBU, initialize the |crypto_handle| to + // enable sequentially updating the hash with more parts. SV_THROW(openssl_init_hash(self->crypto_handle, false)); } - // Select hash function, hash the NALU and store as 'latest hash' + // Select hash function, hash the NALU/OBU and store as 'latest hash' hash_wrapper_t hash_wrapper = get_hash_wrapper(self, nalu); SV_THROW(hash_wrapper(self, nalu, nalu_hash, hash_size)); #ifdef SIGNED_VIDEO_DEBUG @@ -1274,7 +1273,7 @@ signed_video_create(SignedVideoCodec codec) self->authenticity_level = DEFAULT_AUTHENTICITY_LEVEL; self->recurrence = RECURRENCE_ALWAYS; self->add_public_key_to_sei = true; - self->sei_epb = true; + self->sei_epb = codec != SV_CODEC_AV1; self->signing_started = false; self->sign_data = sign_or_verify_data_create(); self->sign_data->hash_size = openssl_get_hash_size(self->crypto_handle); diff --git a/lib/src/signed_video_h26x_internal.h b/lib/src/signed_video_h26x_internal.h index 823d5d3..51e92ba 100644 --- a/lib/src/signed_video_h26x_internal.h +++ b/lib/src/signed_video_h26x_internal.h @@ -26,6 +26,8 @@ #include "signed_video_defines.h" // svrc_t #include "signed_video_internal.h" // gop_info_t, gop_state_t, MAX_HASH_SIZE +#define METADATA_TYPE_USER_PRIVATE 25 + typedef struct _h26x_nalu_list_item_t h26x_nalu_list_item_t; typedef enum { diff --git a/lib/src/signed_video_h26x_sign.c b/lib/src/signed_video_h26x_sign.c index 96297f8..65a7c0d 100644 --- a/lib/src/signed_video_h26x_sign.c +++ b/lib/src/signed_video_h26x_sign.c @@ -28,7 +28,7 @@ #include "includes/signed_video_signing_plugin.h" #include "signed_video_authenticity.h" // allocate_memory_and_copy_string #include "signed_video_defines.h" // svrc_t, sv_tlv_tag_t -#include "signed_video_h26x_internal.h" // parse_nalu_info() +#include "signed_video_h26x_internal.h" // parse_nalu_info(), METADATA_TYPE_USER_PRIVATE #include "signed_video_internal.h" // gop_info_t #include "signed_video_openssl_internal.h" #include "signed_video_tlv.h" // tlv_list_encode_or_get_size() @@ -248,9 +248,29 @@ generate_sei_nalu(signed_video_t *self, uint8_t **payload, uint8_t **payload_sig sei_buffer_size += payload_size / 256 + 1; // Size field sei_buffer_size += payload_size; sei_buffer_size += 1; // Stop bit in a separate byte + if (self->codec != SV_CODEC_AV1) { + sei_buffer_size += self->codec == SV_CODEC_H264 ? 6 : 7; // NALU header + sei_buffer_size += payload_size / 256 + 1; // Size field + sei_buffer_size += payload_size; + sei_buffer_size += 1; // Stop bit in a separate byte + } else { + payload_size += 3; // 2 trailing-bit bytes, 1 metadata_type byte + int payload_size_bytes = 0; + size_t tmp_payload_size = payload_size; + while (tmp_payload_size > 0) { + payload_size_bytes++; + tmp_payload_size >>= 7; + } + sei_buffer_size += 1; // OBU header + sei_buffer_size += payload_size_bytes; // Size field + sei_buffer_size += payload_size; + } - // Secure enough memory for emulation prevention. Worst case will add 1 extra byte per 3 bytes. - sei_buffer_size = sei_buffer_size * 4 / 3; + if (self->codec != SV_CODEC_AV1) { + // Secure enough memory for emulation prevention. Worst case will add 1 extra byte per 3 + // bytes. + sei_buffer_size = sei_buffer_size * 4 / 3; + } // Allocate memory for payload + SEI header to return *payload = (uint8_t *)malloc(sei_buffer_size); @@ -263,30 +283,54 @@ generate_sei_nalu(signed_video_t *self, uint8_t **payload, uint8_t **payload_sig // Reset last_two_bytes before writing bytes self->last_two_bytes = LAST_TWO_BYTES_INIT_VALUE; uint16_t *last_two_bytes = &self->last_two_bytes; - // Start code prefix - *payload_ptr++ = 0x00; - *payload_ptr++ = 0x00; - *payload_ptr++ = 0x00; - *payload_ptr++ = 0x01; - - if (self->codec == SV_CODEC_H264) { - write_byte(last_two_bytes, &payload_ptr, 0x06, false); // SEI NAL type - } else if (self->codec == SV_CODEC_H265) { - write_byte(last_two_bytes, &payload_ptr, 0x4E, false); // SEI NAL type - // nuh_layer_id and nuh_temporal_id_plus1 - write_byte(last_two_bytes, &payload_ptr, 0x01, false); - } - // last_payload_type_byte : user_data_unregistered - write_byte(last_two_bytes, &payload_ptr, 0x05, false); - - // Payload size - size_t size_left = payload_size; - while (size_left >= 0xFF) { - write_byte(last_two_bytes, &payload_ptr, 0xFF, false); - size_left -= 0xFF; + if (self->codec != SV_CODEC_AV1) { + // Start code prefix + *payload_ptr++ = 0x00; + *payload_ptr++ = 0x00; + *payload_ptr++ = 0x00; + *payload_ptr++ = 0x01; + + if (self->codec == SV_CODEC_H264) { + write_byte(last_two_bytes, &payload_ptr, 0x06, false); // SEI NAL type + } else if (self->codec == SV_CODEC_H265) { + write_byte(last_two_bytes, &payload_ptr, 0x4E, false); // SEI NAL type + // nuh_layer_id and nuh_temporal_id_plus1 + write_byte(last_two_bytes, &payload_ptr, 0x01, false); + } + // last_payload_type_byte : user_data_unregistered + write_byte(last_two_bytes, &payload_ptr, 0x05, false); + + // Payload size + size_t size_left = payload_size; + while (size_left >= 0xFF) { + write_byte(last_two_bytes, &payload_ptr, 0xFF, false); + size_left -= 0xFF; + } + // last_payload_size_byte - u(8) + write_byte(last_two_bytes, &payload_ptr, (uint8_t)size_left, false); + } else { + write_byte(last_two_bytes, &payload_ptr, 0x2A, false); // OBU header + // Write payload size + size_t size_left = payload_size; + while (size_left > 0) { + // get first 7 bits + int byte = (0x7F & size_left); + // Check if more bytes to come + size_left >>= 7; + if (size_left > 0) { + // More bytes to come. Set highest bit + byte |= 0x80; + } else { + // No more bytes to come. Clear highest bit + byte &= 0x7F; + } + write_byte(last_two_bytes, &payload_ptr, byte, false); // obu_size + } + // Write metadata_type + write_byte(last_two_bytes, &payload_ptr, METADATA_TYPE_USER_PRIVATE, false); // metadata_type + // Intermediate trailing byte + write_byte(last_two_bytes, &payload_ptr, 0x80, false); // trailing byte } - // last_payload_size_byte - u(8) - write_byte(last_two_bytes, &payload_ptr, (uint8_t)size_left, false); // User data unregistered UUID field h26x_set_nal_uuid_type(self, &payload_ptr, UUID_TYPE_SIGNED_VIDEO); @@ -402,7 +446,7 @@ get_sign_and_complete_sei_nalu(signed_video_t *self, tlv_list_encode_or_get_size(self, gop_info_encoders, num_gop_encoders, payload_ptr); payload_ptr += written_size; - // Stop bit + // Stop bit (Trailing bit identical for both H.26x and AV1) write_byte(last_two_bytes, &payload_ptr, 0x80, false); #ifdef SIGNED_VIDEO_DEBUG @@ -502,11 +546,6 @@ signed_video_add_nalu_part_for_signing_with_timestamp(signed_video_t *self, } nalu.hashable_data_size = nalu_data_size; } - if (self->codec == SV_CODEC_AV1) { - // Not yet implemented, but return SV_OK to run through tests. - free(nalu.nalu_data_wo_epb); - return prepare_for_nalus_to_prepend(self); - } svrc_t status = SV_UNKNOWN_FAILURE; SV_TRY() @@ -884,6 +923,7 @@ SignedVideoReturnCode signed_video_set_sei_epb(signed_video_t *self, bool sei_epb) { if (!self) return SV_INVALID_PARAMETER; + if (self->codec == SV_CODEC_AV1) return SV_NOT_SUPPORTED; self->sei_epb = sei_epb; return SV_OK; } diff --git a/tests/check/check_signed_video_sign.c b/tests/check/check_signed_video_sign.c index f69358f..1149bd7 100644 --- a/tests/check/check_signed_video_sign.c +++ b/tests/check/check_signed_video_sign.c @@ -38,6 +38,10 @@ #include "test_helpers.h" #include "test_stream.h" +/* General comments to the validation tests. + * All tests loop through the settings in settings[NUM_SETTINGS]; See signed_video_helpers.h. The + * index in the loop is _i and something the check test framework provides. */ + static void setup() { @@ -167,8 +171,6 @@ get_seis(signed_video_t *sv, int num_seis_to_get, int *num_seis_gotten) */ START_TEST(api_inputs) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. SignedVideoReturnCode sv_rc; SignedVideoCodec codec = settings[_i].codec; sign_algo_t algo = SIGN_ALGO_ECDSA; @@ -236,10 +238,15 @@ START_TEST(api_inputs) // Setting emulation prevention. sv_rc = signed_video_set_sei_epb(NULL, false); ck_assert_int_eq(sv_rc, SV_INVALID_PARAMETER); - sv_rc = signed_video_set_sei_epb(sv, false); - ck_assert_int_eq(sv_rc, SV_OK); - sv_rc = signed_video_set_sei_epb(sv, true); - ck_assert_int_eq(sv_rc, SV_OK); + if (codec != SV_CODEC_AV1) { + sv_rc = signed_video_set_sei_epb(sv, false); + ck_assert_int_eq(sv_rc, SV_OK); + sv_rc = signed_video_set_sei_epb(sv, true); + ck_assert_int_eq(sv_rc, SV_OK); + } else { + ck_assert_int_eq(signed_video_set_sei_epb(sv, false), SV_NOT_SUPPORTED); + ck_assert_int_eq(signed_video_set_sei_epb(sv, true), SV_NOT_SUPPORTED); + } // Checking signed_video_set_hash_algo(). sv_rc = signed_video_set_hash_algo(NULL, "sha512"); @@ -359,9 +366,6 @@ END_TEST */ START_TEST(incorrect_operation) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. - SignedVideoCodec codec = settings[_i].codec; signed_video_t *sv = signed_video_create(codec); @@ -420,10 +424,6 @@ END_TEST * pointer inputs. */ START_TEST(vendor_axis_communications_operation) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. - if (settings[_i].codec == SV_CODEC_AV1) return; - SignedVideoReturnCode sv_rc; struct sv_setting setting = settings[_i]; signed_video_t *sv = get_initialized_signed_video(setting, false); @@ -503,12 +503,8 @@ START_TEST(correct_nalu_sequence_without_eos) // |settings|; See signed_video_helpers.h. test_stream_t *list = create_signed_nalus("IPPIPPIPPIPPIPPIPP", settings[_i]); - if (settings[_i].codec == SV_CODEC_AV1) { - test_stream_check_types(list, "IPPIPPIPPIPPIPPIPP"); - } else { - test_stream_check_types(list, "IPPISPPISPPISPPISPPISPP"); - verify_seis(list, settings[_i]); - } + test_stream_check_types(list, "IPPISPPISPPISPPISPPISPP"); + verify_seis(list, settings[_i]); test_stream_free(list); } END_TEST @@ -544,8 +540,7 @@ END_TEST START_TEST(correct_multislice_nalu_sequence_without_eos) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. + // For AV1, multi-slices are covered in one single OBU (OBU Frame). if (settings[_i].codec == SV_CODEC_AV1) return; test_stream_t *list = create_signed_nalus("IiPpPpIiPpPp", settings[_i]); @@ -569,10 +564,6 @@ END_TEST */ START_TEST(sei_increase_with_gop_length) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. - if (settings[_i].codec == SV_CODEC_AV1) return; - struct sv_setting setting = settings[_i]; // Turn off emulation prevention setting.ep_before_signing = false; @@ -600,10 +591,6 @@ END_TEST */ START_TEST(fallback_to_gop_level) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. - if (settings[_i].codec == SV_CODEC_AV1) return; - // By construction, run the test for SV_AUTHENTICITY_LEVEL_FRAME only. if (settings[_i].auth_level != SV_AUTHENTICITY_LEVEL_FRAME) return; @@ -641,10 +628,6 @@ END_TEST */ START_TEST(undefined_nalu_in_sequence) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. - if (settings[_i].codec == SV_CODEC_AV1) return; - test_stream_t *list = create_signed_nalus("IPXPIPPIP", settings[_i]); test_stream_check_types(list, "IPXPISPPISP"); verify_seis(list, settings[_i]); @@ -662,7 +645,6 @@ END_TEST */ START_TEST(two_completed_seis_pending) { - if (settings[_i].codec == SV_CODEC_AV1) return; #ifdef SIGNED_VIDEO_DEBUG // Verification the signature is not yet working for buffered signatures. return; @@ -751,8 +733,6 @@ END_TEST */ START_TEST(golden_sei_created) { - if (settings[_i].codec == SV_CODEC_AV1) return; - SignedVideoReturnCode sv_rc; signed_video_t *sv = get_initialized_signed_video(settings[_i], false); ck_assert(sv); @@ -789,7 +769,6 @@ END_TEST */ START_TEST(two_completed_seis_pending_legacy) { - if (settings[_i].codec == SV_CODEC_AV1) return; #ifdef SIGNED_VIDEO_DEBUG // Verification the signature is not yet working for buffered signatures. return; @@ -856,10 +835,6 @@ END_TEST */ START_TEST(correct_timestamp) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. - if (settings[_i].codec == SV_CODEC_AV1) return; - SignedVideoCodec codec = settings[_i].codec; SignedVideoReturnCode sv_rc; @@ -942,10 +917,6 @@ END_TEST */ START_TEST(correct_signing_nalus_in_parts) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. - if (settings[_i].codec == SV_CODEC_AV1) return; - test_stream_t *list = create_signed_splitted_nalus("IPPIPP", settings[_i]); test_stream_check_types(list, "IPPISPP"); verify_seis(list, settings[_i]); @@ -959,8 +930,7 @@ END_TEST #define NUM_EPB_CASES 2 START_TEST(w_wo_emulation_prevention_bytes) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. + // Emulation prevention does not apply to AV1. if (settings[_i].codec == SV_CODEC_AV1) return; struct sv_setting setting = settings[_i]; @@ -1034,10 +1004,6 @@ END_TEST * Verify the setter for maximum SEI payload size. */ START_TEST(limited_sei_payload_size) { - // This test runs in a loop with loop index _i, corresponding to struct sv_setting _i in - // |settings|; See signed_video_helpers.h. - if (settings[_i].codec == SV_CODEC_AV1) return; - // No need to run this with GOP level authentication, since only frame level // authentication can dynamically affect the payload size. if (settings[_i].auth_level != SV_AUTHENTICITY_LEVEL_FRAME) return; diff --git a/tests/check/test_helpers.c b/tests/check/test_helpers.c index 894df27..2bc8855 100644 --- a/tests/check/test_helpers.c +++ b/tests/check/test_helpers.c @@ -96,8 +96,8 @@ struct sv_setting settings[NUM_SETTINGS] = { {SV_CODEC_H264, SV_AUTHENTICITY_LEVEL_FRAME, true, true, false, 0, "sha512", 0, 1, false, false}, // AV1 tests - {SV_CODEC_AV1, SV_AUTHENTICITY_LEVEL_GOP, true, true, false, 0, NULL, 0, 1, false, false}, - {SV_CODEC_AV1, SV_AUTHENTICITY_LEVEL_FRAME, true, true, false, 0, NULL, 0, 1, false, false}, + {SV_CODEC_AV1, SV_AUTHENTICITY_LEVEL_GOP, true, false, false, 0, NULL, 0, 1, false, false}, + {SV_CODEC_AV1, SV_AUTHENTICITY_LEVEL_FRAME, true, false, false, 0, NULL, 0, 1, false, false}, }; static char private_key_rsa[RSA_PRIVATE_KEY_ALLOC_BYTES]; @@ -388,7 +388,9 @@ get_initialized_signed_video(struct sv_setting settings, bool new_private_key) ck_assert_int_eq(signed_video_set_authenticity_level(sv, settings.auth_level), SV_OK); ck_assert_int_eq(signed_video_set_max_sei_payload_size(sv, settings.max_sei_payload_size), SV_OK); ck_assert_int_eq(signed_video_set_hash_algo(sv, settings.hash_algo_name), SV_OK); - ck_assert_int_eq(signed_video_set_sei_epb(sv, settings.ep_before_signing), SV_OK); + if (settings.codec != SV_CODEC_AV1) { + ck_assert_int_eq(signed_video_set_sei_epb(sv, settings.ep_before_signing), SV_OK); + } ck_assert_int_eq(signed_video_set_using_golden_sei(sv, settings.with_golden_sei), SV_OK); if (settings.with_golden_sei) {