Skip to content

Commit 34f3f96

Browse files
authored
Decodes SEIs at registration (#435)
This commit change the behavior to decode the optional tags in a SEI when being registered. After that, the signature is verified. Further, to avoid the risk of reading the same Public key twice the Public key is not updated if it has changed. This is a security precaution since change of key should never be allowed.
1 parent 3afb1f4 commit 34f3f96

File tree

3 files changed

+89
-25
lines changed

3 files changed

+89
-25
lines changed

lib/src/sv_auth.c

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,54 @@ prepare_for_validation(signed_video_t *self)
10071007
return status;
10081008
}
10091009

1010+
static void
1011+
extract_optional_info_from_sei(signed_video_t *self, bu_list_item_t *item)
1012+
{
1013+
bu_info_t *bu = item->bu;
1014+
if (!bu->is_sv_sei) {
1015+
return;
1016+
}
1017+
// Even if a SEI without signature (signing multiple GOPs) could include optional
1018+
// information like the public key it is not safe to use that until the SEI can be
1019+
// verified. Therefore, a SEI is not decoded to get the cryptographic information if it
1020+
// is not signed directly.
1021+
if (!bu->is_signed) {
1022+
return;
1023+
}
1024+
1025+
const uint8_t *tlv_data = bu->tlv_data;
1026+
size_t tlv_size = bu->tlv_size;
1027+
size_t num_of_tags = 0;
1028+
const sv_tlv_tag_t *optional_tags = sv_get_optional_tags(&num_of_tags);
1029+
sv_tlv_find_and_decode_tags(self, tlv_data, tlv_size, optional_tags, num_of_tags);
1030+
}
1031+
1032+
// If this is a Signed Video generated SEI, including a signature, decode all the
1033+
// optional TLV information and verify the signature.
1034+
static svrc_t
1035+
verify_sei_signature(signed_video_t *self, bu_list_item_t *item, int *verified_result)
1036+
{
1037+
bu_info_t *bu = item->bu;
1038+
if (!bu->is_sv_sei || !bu->is_signed) {
1039+
return SV_OK;
1040+
}
1041+
const sv_tlv_tag_t signature_tag = SIGNATURE_TAG;
1042+
if (!sv_tlv_find_and_decode_tags(
1043+
self, item->bu->tlv_data, item->bu->tlv_size, &signature_tag, 1)) {
1044+
return SV_OK;
1045+
}
1046+
if (!self->has_public_key) {
1047+
// If no public key has been set, validation is not supported. This can happen if the
1048+
// Public key was not added to the SEI and the validation side has not set it
1049+
// manually.
1050+
return SV_NOT_SUPPORTED;
1051+
}
1052+
1053+
memcpy(self->verify_data->hash, item->hash, self->verify_data->hash_size);
1054+
1055+
return sv_openssl_verify_hash(self->verify_data, verified_result);
1056+
}
1057+
10101058
// If public_key is not received then try to decode all recurrent tags.
10111059
static bool
10121060
is_recurrent_data_decoded(signed_video_t *self)
@@ -1267,8 +1315,22 @@ register_bu(signed_video_t *self, bu_list_item_t *item)
12671315

12681316
if (bu->is_valid == 0) return SV_OK;
12691317

1318+
extract_optional_info_from_sei(self, item);
12701319
sv_update_hashable_data(bu);
1271-
return hash_and_add_for_auth(self, item);
1320+
1321+
svrc_t status = SV_UNKNOWN_FAILURE;
1322+
SV_TRY()
1323+
SV_THROW(hash_and_add_for_auth(self, item));
1324+
if (bu->is_signed) {
1325+
SV_THROW(verify_sei_signature(self, item, &item->verified_signature));
1326+
// TODO: Decide what to do if verification fails. Should mark public key as not
1327+
// present?
1328+
DEBUG_LOG("Verified SEI signature with result %d", item->verified_signature);
1329+
}
1330+
SV_CATCH()
1331+
SV_DONE(status)
1332+
1333+
return status;
12721334
}
12731335

12741336
/* All Bitstream Units in the |bu_list| are re-registered by hashing them. */
@@ -1399,6 +1461,7 @@ add_bitstream_unit(signed_video_t *self, const uint8_t *bu_data, size_t bu_data_
13991461
validation_flags->has_auth_result = false;
14001462

14011463
self->accumulated_validation->number_of_received_nalus++;
1464+
const bool nalus_pending_registration = !self->validation_flags.hash_algo_known;
14021465

14031466
svrc_t status = SV_UNKNOWN_FAILURE;
14041467
SV_TRY()
@@ -1414,22 +1477,19 @@ add_bitstream_unit(signed_video_t *self, const uint8_t *bu_data, size_t bu_data_
14141477
// As soon as the first Signed Video SEI arrives (|signing_present| is true) and the
14151478
// crypto TLV tag has been decoded it is feasible to hash the temporarily stored
14161479
// Bitstream Units.
1417-
if (!validation_flags->hash_algo_known &&
1418-
((validation_flags->signing_present && is_recurrent_data_decoded(self)) ||
1419-
(bu_list->num_gops > MAX_UNHASHED_GOPS))) {
1420-
if (!validation_flags->hash_algo_known) {
1421-
DEBUG_LOG("No cryptographic information found in SEI. Using default hash algo");
1422-
validation_flags->hash_algo_known = true;
1423-
}
1424-
if (bu.is_golden_sei) SV_THROW(prepare_golden_sei(self, bu_list->last_item));
1425-
1426-
// Determine if legacy validation should be applied, that is, if the legacy way of
1427-
// using linked hashes and recursive GOP hash is detected.
1428-
if (validation_flags->signing_present && (!(bu.reserved_byte & 0x30) && !bu.is_golden_sei)) {
1429-
self->legacy_sv = legacy_sv_create(self);
1430-
SV_THROW_IF(!self->legacy_sv, SV_MEMORY);
1431-
sv_accumulated_validation_init(self->accumulated_validation);
1432-
}
1480+
if (!validation_flags->signing_present && (bu_list->num_gops > MAX_UNHASHED_GOPS)) {
1481+
validation_flags->hash_algo_known = true;
1482+
}
1483+
if (bu.is_golden_sei) SV_THROW(prepare_golden_sei(self, bu_list->last_item));
1484+
1485+
// Determine if legacy validation should be applied, that is, if the legacy way of
1486+
// using linked hashes and recursive GOP hash is detected.
1487+
if (bu.is_sv_sei && (!(bu.reserved_byte & 0x30) && !bu.is_golden_sei)) {
1488+
self->legacy_sv = legacy_sv_create(self);
1489+
SV_THROW_IF(!self->legacy_sv, SV_MEMORY);
1490+
sv_accumulated_validation_init(self->accumulated_validation);
1491+
}
1492+
if (nalus_pending_registration && self->validation_flags.hash_algo_known) {
14331493
SV_THROW(reregister_bu(self));
14341494
}
14351495
SV_THROW(maybe_validate_gop(self, &bu));

lib/src/sv_tlv.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ decode_public_key(signed_video_t *self, const uint8_t *data, size_t data_size)
681681
pem_pkey_t *pem_public_key = &self->pem_public_key;
682682
uint8_t version = *data_ptr++;
683683
uint16_t pubkey_size = (uint16_t)(data_size - 1); // Only version and the key is stored
684+
bool public_key_has_changed = false;
684685

685686
// The algo was removed in version 2 since it is not needed. Simply move to next byte if
686687
// older version.
@@ -702,11 +703,14 @@ decode_public_key(signed_video_t *self, const uint8_t *data, size_t data_size)
702703
}
703704

704705
int key_diff = memcmp(data_ptr, pem_public_key->key, pubkey_size);
705-
if (self->has_public_key && key_diff && self->latest_validation) {
706-
self->latest_validation->public_key_has_changed = true;
706+
public_key_has_changed = self->has_public_key && key_diff;
707+
if (self->latest_validation) {
708+
self->latest_validation->public_key_has_changed = public_key_has_changed;
709+
}
710+
if (!public_key_has_changed) {
711+
memcpy(pem_public_key->key, data_ptr, pubkey_size);
712+
self->has_public_key = true;
707713
}
708-
memcpy(pem_public_key->key, data_ptr, pubkey_size);
709-
self->has_public_key = true;
710714
data_ptr += pubkey_size;
711715

712716
// Convert to EVP_PKEY_CTX

tests/check/check_signed_video_auth.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,12 +1155,12 @@ START_TEST(detect_change_of_public_key)
11551155
//
11561156
// IPPIS ...P. -> ( valid)
11571157
// ISPPIS ....P. -> ( valid)
1158-
// ISPIS* N.NP. -> ( invalid, key has changed, wrong link)
1159-
// IS*PIS* ...P. -> ( valid)
1158+
// ISPIS* N.NPN -> ( invalid, key has changed, wrong link)
1159+
// IS*PIS* NNNPN -> ( invalid)
11601160
signed_video_accumulated_validation_t final_validation = {
11611161
SV_AUTH_RESULT_NOT_OK, true, 16, 13, 3, SV_PUBKEY_VALIDATION_NOT_FEASIBLE, true, 0, 0};
1162-
const struct validation_stats expected = {.valid_gops = 3,
1163-
.invalid_gops = 1,
1162+
const struct validation_stats expected = {.valid_gops = 2,
1163+
.invalid_gops = 2,
11641164
.pending_bu = 4,
11651165
.public_key_has_changed = true,
11661166
.final_validation = &final_validation};

0 commit comments

Comments
 (0)