diff options
Diffstat (limited to 'libtransport/src/protocols/rtc/rtc_verifier.cc')
-rw-r--r-- | libtransport/src/protocols/rtc/rtc_verifier.cc | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/libtransport/src/protocols/rtc/rtc_verifier.cc b/libtransport/src/protocols/rtc/rtc_verifier.cc new file mode 100644 index 000000000..29968dd02 --- /dev/null +++ b/libtransport/src/protocols/rtc/rtc_verifier.cc @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2017-2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <core/facade.h> +#include <protocols/rtc/rtc_packet.h> +#include <protocols/rtc/rtc_verifier.h> + +namespace transport { +namespace protocol { +namespace rtc { + +RTCVerifier::RTCVerifier(std::shared_ptr<auth::Verifier> verifier, + uint32_t max_unverified_delay) + : verifier_(verifier), max_unverified_delay_(max_unverified_delay) {} + +void RTCVerifier::setState(std::shared_ptr<RTCState> rtc_state) { + rtc_state_ = rtc_state; +} + +void RTCVerifier::setVerifier(std::shared_ptr<auth::Verifier> verifier) { + verifier_ = verifier; +} + +void RTCVerifier::setMaxUnverifiedDelay(uint32_t max_unverified_delay) { + max_unverified_delay_ = max_unverified_delay; +} + +auth::VerificationPolicy RTCVerifier::verify( + core::ContentObject &content_object, bool is_fec) { + uint32_t suffix = content_object.getName().getSuffix(); + core::PayloadType payload_type = content_object.getPayloadType(); + + bool is_probe = ProbeHandler::getProbeType(suffix) != ProbeType::NOT_PROBE; + bool is_nack = !is_probe && content_object.payloadSize() == NACK_HEADER_SIZE; + bool is_manifest = !is_probe && !is_nack && !is_fec && + payload_type == core::PayloadType::MANIFEST; + bool is_data = !is_probe && !is_nack && !is_fec && + payload_type == core::PayloadType::DATA; + + if (is_probe) return verifyProbe(content_object); + if (is_nack) return verifyNack(content_object); + if (is_fec) return verifyFec(content_object); + if (is_data) return verifyData(content_object); + if (is_manifest) return verifyManifest(content_object); + + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; +} + +auth::VerificationPolicy RTCVerifier::verifyProbe( + core::ContentObject &content_object) { + switch (ProbeHandler::getProbeType(content_object.getName().getSuffix())) { + case ProbeType::INIT: { + auth::VerificationPolicy policy = verifyManifest(content_object); + if (policy != auth::VerificationPolicy::ACCEPT) { + return policy; + } + return processManifest(content_object); + } + case ProbeType::RTT: + return verifyNack(content_object); + default: + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback( + content_object.getName().getSuffix(), policy); + return policy; + } +} + +auth::VerificationPolicy RTCVerifier::verifyNack( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::verifyFec( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::verifyData( + core::ContentObject &content_object) { + uint32_t suffix = content_object.getName().getSuffix(); + + if (_is_ah(content_object.getFormat())) { + return verifier_->verifyPackets(&content_object); + } + + unverified_bytes_[suffix] = + content_object.headerSize() + content_object.payloadSize(); + unverified_packets_[suffix] = + content_object.computeDigest(manifest_hash_algo_); + + // An alert is raised when too much packets remain unverified + if (getTotalUnverified() > max_unverified_bytes_) { + unverified_bytes_.clear(); + unverified_packets_.clear(); + + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; + } + + return auth::VerificationPolicy::ACCEPT; +} + +auth::VerificationPolicy RTCVerifier::verifyManifest( + core::ContentObject &content_object) { + return verifier_->verifyPackets(&content_object); +} + +auth::VerificationPolicy RTCVerifier::processManifest( + core::ContentObject &content_object) { + uint32_t suffix = content_object.getName().getSuffix(); + + core::ContentObjectManifest manifest(content_object); + manifest.decode(); + + // Update last manifest + if (suffix > last_manifest_) { + last_manifest_ = suffix; + } + + // Extract parameters + manifest_hash_algo_ = manifest.getHashAlgorithm(); + core::ParamsRTC params = manifest.getParamsRTC(); + + if (params.prod_rate > 0) { + max_unverified_bytes_ = static_cast<uint64_t>( + (max_unverified_delay_ / 1000.0) * params.prod_rate); + } + + if (max_unverified_bytes_ == 0 || !rtc_state_) { + auth::VerificationPolicy policy = auth::VerificationPolicy::ABORT; + verifier_->callVerificationFailedCallback(suffix, policy); + return policy; + } + + // Extract hashes + auth::Verifier::SuffixMap suffix_map = + core::ContentObjectManifest::getSuffixMap(&manifest); + + // Return early if the manifest is empty + if (suffix_map.empty()) { + return auth::VerificationPolicy::ACCEPT; + } + + // Remove lost packets from digest map + manifest_digests_.insert(suffix_map.begin(), suffix_map.end()); + for (auto it = manifest_digests_.begin(); it != manifest_digests_.end();) { + if (rtc_state_->getPacketState(it->first) == PacketState::DEFINITELY_LOST) { + unverified_packets_.erase(it->first); + unverified_bytes_.erase(it->first); + it = manifest_digests_.erase(it); + } else { + ++it; + } + } + + // Verify packets + auth::Verifier::PolicyMap policies = + verifier_->verifyHashes(unverified_packets_, manifest_digests_); + + for (const auto &policy : policies) { + switch (policy.second) { + case auth::VerificationPolicy::ACCEPT: { + manifest_digests_.erase(policy.first); + unverified_packets_.erase(policy.first); + unverified_bytes_.erase(policy.first); + break; + } + case auth::VerificationPolicy::UNKNOWN: + break; + case auth::VerificationPolicy::DROP: + case auth::VerificationPolicy::ABORT: + auth::VerificationPolicy p = policy.second; + verifier_->callVerificationFailedCallback(policy.first, p); + return p; + } + } + + return auth::VerificationPolicy::ACCEPT; +} + +void RTCVerifier::onDataRecoveredFec(uint32_t suffix) { + manifest_digests_.erase(suffix); +} + +void RTCVerifier::onJumpForward(uint32_t next_suffix) { + if (next_suffix <= last_manifest_ + 1) { + return; + } + + // When we jump forward in the suffix sequence, we remove packets that + // probably won't be verified. Those packets have a suffix in the range + // [last_manifest_ + 1, next_suffix[. + for (auto it = unverified_packets_.begin(); + it != unverified_packets_.end();) { + if (it->first > last_manifest_) { + unverified_bytes_.erase(it->first); + it = unverified_packets_.erase(it); + } else { + ++it; + } + } +} + +uint32_t RTCVerifier::getTotalUnverified() const { + uint32_t total = 0; + + for (auto bytes : unverified_bytes_) { + if (bytes.second > UINT32_MAX - total) { + total = UINT32_MAX; + break; + } + total += bytes.second; + } + + return total; +} + +uint32_t RTCVerifier::getMaxUnverified() const { return max_unverified_bytes_; } + +} // end namespace rtc +} // end namespace protocol +} // end namespace transport |