From 6c733eed8ebb0274cdd4cc93c0999bd6e1ddae09 Mon Sep 17 00:00:00 2001 From: Evan Shrubsole Date: Wed, 12 Oct 2022 15:11:14 +0000 Subject: [PATCH] Add exposure criteria to WebRTC stat members. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent WebRTC stats spec changes have added restrictions on what stats are available to JavaScript. This is done to reduce that fingerprinting surface of WebRTC getStats. For example, stats exposing hardware capabilities have requirements that must be met by the browser. See [1] for more details. This CL adds the types and the enumerations. Stats with these restrictions should not be added until Chromium has implemented filtering based on the stat type. [1] https://w3c.github.io/webrtc-stats/#limiting-exposure-of-hardware-capabilities Bug: webrtc:14546 Change-Id: I6dae5d4921c7a2bc828a4fc8f7d68e0c59f3be82 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/279043 Commit-Queue: Evan Shrubsole Reviewed-by: Henrik Boström Cr-Commit-Position: refs/heads/main@{#38381} --- api/stats/rtc_stats.h | 111 ++++++++++++++++++++++++++++++++++-- stats/rtc_stats.cc | 48 ++++++++++++++++ stats/rtc_stats_unittest.cc | 14 +++++ 3 files changed, 167 insertions(+), 6 deletions(-) diff --git a/api/stats/rtc_stats.h b/api/stats/rtc_stats.h index 535e6fb345..e22043df5c 100644 --- a/api/stats/rtc_stats.h +++ b/api/stats/rtc_stats.h @@ -215,6 +215,17 @@ enum class NonStandardGroupId { kRtcStatsRelativePacketArrivalDelay, }; +// Certain stat members should only be exposed to the JavaScript API in +// certain circumstances as to avoid passive fingerprinting. +enum class StatExposureCriteria : uint8_t { + // The stat should always be exposed. This is the default. + kAlways, + // The stat exposes hardware capabilities and thus should has limited exposure + // to JavaScript. The requirements for exposure are written in the spec at + // https://w3c.github.io/webrtc-stats/#limiting-exposure-of-hardware-capabilities. + kHardwareCapability, +}; + // Interface for `RTCStats` members, which have a name and a value of a type // defined in a subclass. Only the types listed in `Type` are supported, these // are implemented by `RTCStatsMember`. The value of a member may be @@ -256,6 +267,12 @@ class RTCStatsMemberInterface { // Non-standard stats members can have group IDs in order to be exposed in // JavaScript through experiments. Standardized stats have no group IDs. virtual std::vector group_ids() const { return {}; } + // The conditions for exposing the statistic to JavaScript. Stats with + // criteria that is not kAlways has some restriction and should be filtered + // in accordance to the spec. + virtual StatExposureCriteria exposure_criteria() const { + return StatExposureCriteria::kAlways; + } // Type and value comparator. The names are not compared. These operators are // exposed for testing. bool operator==(const RTCStatsMemberInterface& other) const { @@ -295,16 +312,21 @@ template class RTCStatsMember : public RTCStatsMemberInterface { public: explicit RTCStatsMember(const char* name) - : RTCStatsMemberInterface(name, /*is_defined=*/false), value_() {} + : RTCStatsMemberInterface(name, + /*is_defined=*/false), + value_() {} RTCStatsMember(const char* name, const T& value) - : RTCStatsMemberInterface(name, /*is_defined=*/true), value_(value) {} + : RTCStatsMemberInterface(name, + /*is_defined=*/true), + value_(value) {} RTCStatsMember(const char* name, T&& value) - : RTCStatsMemberInterface(name, /*is_defined=*/true), + : RTCStatsMemberInterface(name, + /*is_defined=*/true), value_(std::move(value)) {} - explicit RTCStatsMember(const RTCStatsMember& other) + RTCStatsMember(const RTCStatsMember& other) : RTCStatsMemberInterface(other.name_, other.is_defined_), value_(other.value_) {} - explicit RTCStatsMember(RTCStatsMember&& other) + RTCStatsMember(RTCStatsMember&& other) : RTCStatsMemberInterface(other.name_, other.is_defined_), value_(std::move(other.value_)) {} @@ -358,7 +380,9 @@ class RTCStatsMember : public RTCStatsMemberInterface { protected: bool IsEqual(const RTCStatsMemberInterface& other) const override { - if (type() != other.type() || is_standardized() != other.is_standardized()) + if (type() != other.type() || + is_standardized() != other.is_standardized() || + exposure_criteria() != other.exposure_criteria()) return false; const RTCStatsMember& other_t = static_cast&>(other); @@ -411,6 +435,81 @@ WEBRTC_DECLARE_RTCSTATSMEMBER(std::vector); WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringUint64); WEBRTC_DECLARE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble); +// For stats with restricted exposure. +template +class RTCRestrictedStatsMember : public RTCStatsMember { + public: + explicit RTCRestrictedStatsMember(const char* name) + : RTCStatsMember(name) {} + RTCRestrictedStatsMember(const char* name, const T& value) + : RTCStatsMember(name, value) {} + RTCRestrictedStatsMember(const char* name, T&& value) + : RTCStatsMember(name, std::move(value)) {} + RTCRestrictedStatsMember(const RTCRestrictedStatsMember& other) + : RTCStatsMember(other) {} + RTCRestrictedStatsMember(RTCRestrictedStatsMember&& other) + : RTCStatsMember(std::move(other)) {} + + StatExposureCriteria exposure_criteria() const override { return E; } + + T& operator=(const T& value) { return RTCStatsMember::operator=(value); } + T& operator=(const T&& value) { + return RTCStatsMember::operator=(std::move(value)); + } + + private: + static_assert(E != StatExposureCriteria::kAlways, + "kAlways is the default exposure criteria. Use " + "RTCStatMember instead."); +}; + +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +extern template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; + // Using inheritance just so that it's obvious from the member's declaration // whether it's standardized or not. template diff --git a/stats/rtc_stats.cc b/stats/rtc_stats.cc index e6eb51e55c..375e1f75c0 100644 --- a/stats/rtc_stats.cc +++ b/stats/rtc_stats.cc @@ -289,6 +289,54 @@ WEBRTC_DEFINE_RTCSTATSMEMBER(rtc_stats_internal::MapStringDouble, MapToString(value_), MapToStringAsDouble(value_)); +// Restricted members that expose hardware capabilites. +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; +template class RTC_EXPORT_TEMPLATE_DECLARE(RTC_EXPORT) + RTCRestrictedStatsMember, + StatExposureCriteria::kHardwareCapability>; + template class RTC_EXPORT_TEMPLATE_DEFINE(RTC_EXPORT) RTCNonStandardStatsMember; template class RTC_EXPORT_TEMPLATE_DEFINE(RTC_EXPORT) diff --git a/stats/rtc_stats_unittest.cc b/stats/rtc_stats_unittest.cc index 36d0a4fa0e..5d3857f327 100644 --- a/stats/rtc_stats_unittest.cc +++ b/stats/rtc_stats_unittest.cc @@ -503,6 +503,20 @@ TEST(RTCStatsTest, ValueToString) { EXPECT_EQ("{bar:0.25,foo:0.5}", stats.m_map_string_double.ValueToString()); } +TEST(RTCStatsTest, RestrictedStatsTest) { + RTCStatsMember unrestricted("unrestricted"); + EXPECT_EQ(unrestricted.exposure_criteria(), StatExposureCriteria::kAlways); + RTCRestrictedStatsMember + restricted("restricted"); + EXPECT_EQ(restricted.exposure_criteria(), + StatExposureCriteria::kHardwareCapability); + + unrestricted = true; + restricted = true; + EXPECT_NE(unrestricted, restricted) + << "These can not be equal as they have different exposure criteria."; +} + TEST(RTCStatsTest, NonStandardGroupId) { auto group_id = NonStandardGroupId::kGroupIdForTesting; RTCNonStandardStatsMember with_group_id("stat", {group_id});