/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "media/base/rtp_utils.h" #include #include // PacketTimeUpdateParams is defined in asyncpacketsocket.h. // TODO(sergeyu): Find more appropriate place for PacketTimeUpdateParams. #include "media/base/turn_utils.h" #include "modules/rtp_rtcp/source/rtp_util.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/byte_order.h" #include "rtc_base/checks.h" #include "rtc_base/message_digest.h" namespace cricket { static const size_t kRtcpPayloadTypeOffset = 1; static const size_t kRtpExtensionHeaderLen = 4; static const size_t kAbsSendTimeExtensionLen = 3; static const size_t kOneByteExtensionHeaderLen = 1; static const size_t kTwoByteExtensionHeaderLen = 2; namespace { // Fake auth tag written by the sender when external authentication is enabled. // HMAC in packet will be compared against this value before updating packet // with actual HMAC value. static const uint8_t kFakeAuthTag[10] = {0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd, 0xba, 0xdd}; void UpdateAbsSendTimeExtensionValue(uint8_t* extension_data, size_t length, uint64_t time_us) { // Absolute send time in RTP streams. // // The absolute send time is signaled to the receiver in-band using the // general mechanism for RTP header extensions [RFC5285]. The payload // of this extension (the transmitted value) is a 24-bit unsigned integer // containing the sender's current time in seconds as a fixed point number // with 18 bits fractional part. // // The form of the absolute send time extension block: // // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | len=2 | absolute send time | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ if (length != kAbsSendTimeExtensionLen) { RTC_DCHECK_NOTREACHED(); return; } // Convert microseconds to a 6.18 fixed point value in seconds. uint32_t send_time = ((time_us << 18) / 1000000) & 0x00FFFFFF; extension_data[0] = static_cast(send_time >> 16); extension_data[1] = static_cast(send_time >> 8); extension_data[2] = static_cast(send_time); } // Assumes `length` is actual packet length + tag length. Updates HMAC at end of // the RTP packet. void UpdateRtpAuthTag(uint8_t* rtp, size_t length, const rtc::PacketTimeUpdateParams& packet_time_params) { // If there is no key, return. if (packet_time_params.srtp_auth_key.empty()) { return; } size_t tag_length = packet_time_params.srtp_auth_tag_len; // ROC (rollover counter) is at the beginning of the auth tag. const size_t kRocLength = 4; if (tag_length < kRocLength || tag_length > length) { RTC_DCHECK_NOTREACHED(); return; } uint8_t* auth_tag = rtp + (length - tag_length); // We should have a fake HMAC value @ auth_tag. RTC_DCHECK_EQ(0, memcmp(auth_tag, kFakeAuthTag, tag_length)); // Copy ROC after end of rtp packet. memcpy(auth_tag, &packet_time_params.srtp_packet_index, kRocLength); // Authentication of a RTP packet will have RTP packet + ROC size. size_t auth_required_length = length - tag_length + kRocLength; uint8_t output[64]; size_t result = rtc::ComputeHmac(rtc::DIGEST_SHA_1, &packet_time_params.srtp_auth_key[0], packet_time_params.srtp_auth_key.size(), rtp, auth_required_length, output, sizeof(output)); if (result < tag_length) { RTC_DCHECK_NOTREACHED(); return; } // Copy HMAC from output to packet. This is required as auth tag length // may not be equal to the actual HMAC length. memcpy(auth_tag, output, tag_length); } bool GetUint8(const void* data, size_t offset, int* value) { if (!data || !value) { return false; } *value = *(static_cast(data) + offset); return true; } } // namespace bool GetRtcpType(const void* data, size_t len, int* value) { if (len < kMinRtcpPacketLen) { return false; } return GetUint8(data, kRtcpPayloadTypeOffset, value); } // This method returns SSRC first of RTCP packet, except if packet is SDES. // TODO(mallinath) - Fully implement RFC 5506. This standard doesn't restrict // to send non-compound packets only to feedback messages. bool GetRtcpSsrc(const void* data, size_t len, uint32_t* value) { // Packet should be at least of 8 bytes, to get SSRC from a RTCP packet. if (!data || len < kMinRtcpPacketLen + 4 || !value) return false; int pl_type; if (!GetRtcpType(data, len, &pl_type)) return false; // SDES packet parsing is not supported. if (pl_type == kRtcpTypeSDES) return false; *value = rtc::GetBE32(static_cast(data) + 4); return true; } bool IsValidRtpPayloadType(int payload_type) { return payload_type >= 0 && payload_type <= 127; } bool IsValidRtpPacketSize(RtpPacketType packet_type, size_t size) { RTC_DCHECK_NE(RtpPacketType::kUnknown, packet_type); size_t min_packet_length = packet_type == RtpPacketType::kRtcp ? kMinRtcpPacketLen : kMinRtpPacketLen; return size >= min_packet_length && size <= kMaxRtpPacketLen; } absl::string_view RtpPacketTypeToString(RtpPacketType packet_type) { switch (packet_type) { case RtpPacketType::kRtp: return "RTP"; case RtpPacketType::kRtcp: return "RTCP"; case RtpPacketType::kUnknown: return "Unknown"; } RTC_CHECK_NOTREACHED(); } RtpPacketType InferRtpPacketType(rtc::ArrayView packet) { if (webrtc::IsRtcpPacket( rtc::reinterpret_array_view(packet))) { return RtpPacketType::kRtcp; } if (webrtc::IsRtpPacket(rtc::reinterpret_array_view(packet))) { return RtpPacketType::kRtp; } return RtpPacketType::kUnknown; } bool ValidateRtpHeader(const uint8_t* rtp, size_t length, size_t* header_length) { if (header_length) { *header_length = 0; } if (length < kMinRtpPacketLen) { return false; } size_t cc_count = rtp[0] & 0x0F; size_t header_length_without_extension = kMinRtpPacketLen + 4 * cc_count; if (header_length_without_extension > length) { return false; } // If extension bit is not set, we are done with header processing, as input // length is verified above. if (!(rtp[0] & 0x10)) { if (header_length) *header_length = header_length_without_extension; return true; } rtp += header_length_without_extension; if (header_length_without_extension + kRtpExtensionHeaderLen > length) { return false; } // Getting extension profile length. // Length is in 32 bit words. uint16_t extension_length_in_32bits = rtc::GetBE16(rtp + 2); size_t extension_length = extension_length_in_32bits * 4; size_t rtp_header_length = extension_length + header_length_without_extension + kRtpExtensionHeaderLen; // Verify input length against total header size. if (rtp_header_length > length) { return false; } if (header_length) { *header_length = rtp_header_length; } return true; } // ValidateRtpHeader() must be called before this method to make sure, we have // a sane rtp packet. bool UpdateRtpAbsSendTimeExtension(uint8_t* rtp, size_t length, int extension_id, uint64_t time_us) { // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |V=2|P|X| CC |M| PT | sequence number | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | timestamp | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | synchronization source (SSRC) identifier | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // | contributing source (CSRC) identifiers | // | .... | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Return if extension bit is not set. if (!(rtp[0] & 0x10)) { return true; } size_t cc_count = rtp[0] & 0x0F; size_t header_length_without_extension = kMinRtpPacketLen + 4 * cc_count; rtp += header_length_without_extension; // Getting extension profile ID and length. uint16_t profile_id = rtc::GetBE16(rtp); // Length is in 32 bit words. uint16_t extension_length_in_32bits = rtc::GetBE16(rtp + 2); size_t extension_length = extension_length_in_32bits * 4; rtp += kRtpExtensionHeaderLen; // Moving past extension header. constexpr uint16_t kOneByteExtensionProfileId = 0xBEDE; constexpr uint16_t kTwoByteExtensionProfileId = 0x1000; bool found = false; if (profile_id == kOneByteExtensionProfileId || profile_id == kTwoByteExtensionProfileId) { // OneByte extension header // 0 // 0 1 2 3 4 5 6 7 // +-+-+-+-+-+-+-+-+ // | ID |length | // +-+-+-+-+-+-+-+-+ // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | 0xBE | 0xDE | length=3 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | L=0 | data | ID | L=1 | data... // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // ...data | 0 (pad) | 0 (pad) | ID | L=3 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | data | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // TwoByte extension header // 0 // 0 1 2 3 4 5 6 7 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | 0x10 | 0x00 | length=3 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | L=1 | data | ID | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | L=2 | data | 0 (pad) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | ID | L=2 | data | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ size_t extension_header_length = profile_id == kOneByteExtensionProfileId ? kOneByteExtensionHeaderLen : kTwoByteExtensionHeaderLen; const uint8_t* extension_start = rtp; const uint8_t* extension_end = extension_start + extension_length; // rtp + 1 since the minimum size per header extension is two bytes for both // one- and two-byte header extensions. while (rtp + 1 < extension_end) { // See RFC8285 Section 4.2-4.3 for more information about one- and // two-byte header extensions. const int id = profile_id == kOneByteExtensionProfileId ? (*rtp & 0xF0) >> 4 : *rtp; const size_t length = profile_id == kOneByteExtensionProfileId ? (*rtp & 0x0F) + 1 : *(rtp + 1); if (rtp + extension_header_length + length > extension_end) { return false; } if (id == extension_id) { UpdateAbsSendTimeExtensionValue(rtp + extension_header_length, length, time_us); found = true; break; } rtp += extension_header_length + length; // Counting padding bytes. while ((rtp < extension_end) && (*rtp == 0)) { ++rtp; } } } return found; } bool ApplyPacketOptions(uint8_t* data, size_t length, const rtc::PacketTimeUpdateParams& packet_time_params, uint64_t time_us) { RTC_DCHECK(data); RTC_DCHECK(length); // if there is no valid `rtp_sendtime_extension_id` and `srtp_auth_key` in // PacketOptions, nothing to be updated in this packet. if (packet_time_params.rtp_sendtime_extension_id == -1 && packet_time_params.srtp_auth_key.empty()) { return true; } // If there is a srtp auth key present then the packet must be an RTP packet. // RTP packet may have been wrapped in a TURN Channel Data or TURN send // indication. size_t rtp_start_pos; size_t rtp_length; if (!UnwrapTurnPacket(data, length, &rtp_start_pos, &rtp_length)) { RTC_DCHECK_NOTREACHED(); return false; } // Making sure we have a valid RTP packet at the end. auto packet = rtc::MakeArrayView(data + rtp_start_pos, rtp_length); if (!webrtc::IsRtpPacket(packet) || !ValidateRtpHeader(data + rtp_start_pos, rtp_length, nullptr)) { RTC_DCHECK_NOTREACHED(); return false; } uint8_t* start = data + rtp_start_pos; // If packet option has non default value (-1) for sendtime extension id, // then we should parse the rtp packet to update the timestamp. Otherwise // just calculate HMAC and update packet with it. if (packet_time_params.rtp_sendtime_extension_id != -1) { UpdateRtpAbsSendTimeExtension(start, rtp_length, packet_time_params.rtp_sendtime_extension_id, time_us); } UpdateRtpAuthTag(start, rtp_length, packet_time_params); return true; } } // namespace cricket