Skip to content

Commit

Permalink
Updates authenticity report (#285)
Browse files Browse the repository at this point in the history
- Unsigned report is only present in the accumulated one
- If number of GOPs exceeds 20 all units are hashed with the
  default hash algo
- Signed stream is only reported once in latest

This change avoids producing an "unsigned" GOP in the beginning
if that one does not include a SEI/OBU Metadata.
Tests have been updated accordingly.

Co-authored-by: bjornvolcker <bjornvolcker@users.noreply.github.com>
  • Loading branch information
bjornvolcker and bjornvolcker authored Dec 18, 2024
1 parent b639729 commit fa0d50a
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 163 deletions.
39 changes: 28 additions & 11 deletions lib/src/signed_video_h26x_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ static const char *kAuthResultValidStr[SV_AUTH_NUM_SIGNED_GOP_VALID_STATES] = {"
"SIGNATURE PRESENT", "NOT OK", "OK WITH MISSING INFO", "OK", "VERSION MISMATCH"};
#endif

/* Before the first SEI/OBU Metadata arrives the hashing algorithm is unknown. While waiting, the
* complete NALU/OBU data is stored. As a result, the memory increases dramatically in particular
* if the stream is not signed. If no SEI/OBU Metadata has arrived after 20 GOPs, the default hash
* is used. This limits the size to a minimum and the operations can proceed. */
#define MAX_UNHASHED_GOPS 20

/**
* The function is called when we receive a SEI NALU holding all the GOP information such as a
* signed hash. The payload is decoded and the signature hash is verified against the gop_hash in
Expand Down Expand Up @@ -504,7 +510,6 @@ verify_hashes_with_sei(signed_video_t *self, int *num_expected_nalus, int *num_r
int num_received_hashes = -1;
char validation_status = 'P';
h26x_nalu_list_item_t *sei = h26x_nalu_list_get_next_sei_item(self->nalu_list);
svrc_t status = SV_UNKNOWN_FAILURE;

bool gop_is_ok = verify_gop_hash(self);
bool order_ok = verify_linked_hash(self);
Expand Down Expand Up @@ -552,7 +557,7 @@ verify_hashes_with_sei(signed_video_t *self, int *num_expected_nalus, int *num_r
if (num_expected_nalus) *num_expected_nalus = num_expected_hashes;
if (num_received_nalus) *num_received_nalus = num_received_hashes;

return (status == SV_OK);
return true;
}

/* Verifying hashes without the SEI means that we have nothing to verify against. Therefore, we mark
Expand Down Expand Up @@ -1093,7 +1098,16 @@ maybe_validate_gop(signed_video_t *self, h26x_nalu_t *nalu)
update_validation_status = true;
}
self->gop_info->verified_signature_hash = -1;
self->validation_flags.has_auth_result = true;
validation_flags->has_auth_result = true;
if (latest->authenticity == SV_AUTH_RESULT_NOT_SIGNED) {
// Only report "stream is unsigned" in the accumulated report.
validation_flags->has_auth_result = false;
}
if (latest->authenticity == SV_AUTH_RESULT_SIGNATURE_PRESENT) {
// Do not report "stream is signed" more than once.
validation_flags->has_auth_result =
latest->authenticity != self->accumulated_validation->authenticity;
}
public_key_has_changed |= latest->public_key_has_changed; // Pass on public key failure.
}

Expand Down Expand Up @@ -1205,10 +1219,11 @@ signed_video_add_h26x_nalu(signed_video_t *self, const uint8_t *nalu_data, size_
// Skip validation if it is done with the legacy code.
if (self->legacy_sv) return SV_OK;

validation_flags_t *validation_flags = &(self->validation_flags);
h26x_nalu_list_t *nalu_list = self->nalu_list;
h26x_nalu_t nalu = parse_nalu_info(nalu_data, nalu_data_size, self->codec, true, true);
DEBUG_LOG("Received a %s of size %zu B", nalu_type_to_str(&nalu), nalu.nalu_data_size);
self->validation_flags.has_auth_result = false;
validation_flags->has_auth_result = false;

self->accumulated_validation->number_of_received_nalus++;

Expand All @@ -1221,22 +1236,24 @@ signed_video_add_h26x_nalu(signed_video_t *self, const uint8_t *nalu_data, size_
// is set accordingly.
SV_THROW(h26x_nalu_list_append(nalu_list, &nalu));
SV_THROW_IF(nalu.is_valid < 0, SV_UNKNOWN_FAILURE);
update_validation_flags(&self->validation_flags, &nalu);
update_validation_flags(validation_flags, &nalu);
SV_THROW(register_nalu(self, nalu_list->last_item));
// As soon as the first Signed Video SEI arrives (|signing_present| is true) and the
// crypto TLV tag has been decoded it is feasible to hash the temporarily stored NAL
// Units.
if (!self->validation_flags.hash_algo_known && self->validation_flags.signing_present &&
is_recurrent_data_decoded(self)) {
if (!self->validation_flags.hash_algo_known) {
if (!validation_flags->hash_algo_known &&
((validation_flags->signing_present && is_recurrent_data_decoded(self)) ||
(nalu_list->num_gops > MAX_UNHASHED_GOPS))) {
if (!validation_flags->hash_algo_known) {
DEBUG_LOG("No cryptographic information found in SEI. Using default hash algo");
self->validation_flags.hash_algo_known = true;
validation_flags->hash_algo_known = true;
}
if (nalu.is_golden_sei) SV_THROW(prepare_golden_sei(self, nalu_list->last_item));

// Determine if legacy validation should be applied, that is, if the legacy way of
// using linked hashes and recursive GOP hash is detected.
if (!(nalu.reserved_byte & 0x30) && !nalu.is_golden_sei) {
if (validation_flags->signing_present &&
(!(nalu.reserved_byte & 0x30) && !nalu.is_golden_sei)) {
self->legacy_sv = legacy_sv_create(self);
SV_THROW_IF(!self->legacy_sv, SV_MEMORY);
accumulated_validation_init(self->accumulated_validation);
Expand All @@ -1249,7 +1266,7 @@ signed_video_add_h26x_nalu(signed_video_t *self, const uint8_t *nalu_data, size_

// Need to make a copy of the |nalu| independently of failure.
svrc_t copy_nalu_status =
h26x_nalu_list_copy_last_item(nalu_list, self->validation_flags.hash_algo_known);
h26x_nalu_list_copy_last_item(nalu_list, validation_flags->hash_algo_known);
// Make sure to return the first failure if both operations failed.
status = (status == SV_OK) ? copy_nalu_status : status;
if (status != SV_OK) {
Expand Down
1 change: 1 addition & 0 deletions lib/src/signed_video_h26x_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct _h26x_nalu_list_t {
h26x_nalu_list_item_t *last_item; // Points to the last item in the linked list, that is, the
// latest NALU added for validation.
int num_items; // The number of items linked together in the list.
int num_gops; // The number of gops linked together in the list, that is, I-frames.
};

/**
Expand Down
4 changes: 4 additions & 0 deletions lib/src/signed_video_h26x_nalu_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ h26x_nalu_list_refresh(h26x_nalu_list_t *list)

// Start from scratch, that is, reset num_items.
list->num_items = 0;
list->num_gops = -1;
// Rewind first_item to get the 'true' first list item.
while (list->first_item && (list->first_item)->prev) {
list->first_item = (list->first_item)->prev;
Expand All @@ -225,6 +226,9 @@ h26x_nalu_list_refresh(h26x_nalu_list_t *list)
h26x_nalu_list_item_t *item = list->first_item;
while (item) {
list->num_items++;
if (item->nalu && item->nalu->is_first_nalu_in_gop) {
list->num_gops++;
}

if (!item->next) break;
item = item->next;
Expand Down
Loading

0 comments on commit fa0d50a

Please sign in to comment.