Implement MoQT AUTHORIZATION_TAG format but do not allow caching of tags. Remove AUTHORIZATION_INFO parameter. PiperOrigin-RevId: 754859021
diff --git a/quiche/quic/moqt/moqt_framer.cc b/quiche/quic/moqt/moqt_framer.cc index 430616e..c1f84bd 100644 --- a/quiche/quic/moqt/moqt_framer.cc +++ b/quiche/quic/moqt/moqt_framer.cc
@@ -222,17 +222,23 @@ const VersionSpecificParameters& parameters, KeyValuePairList& out) { out.clear(); for (const auto& it : parameters.authorization_token) { - out.insert(VersionSpecificParameter::kAuthorizationToken, it); + if (it.type > AuthTokenType::kMaxAuthTokenType) { + QUICHE_BUG(moqt_invalid_auth_token_type) + << "Invalid Auth Token Type: " << static_cast<uint64_t>(it.type); + continue; + } + // Just support USE_VALUE for now. + quiche::QuicheBuffer parameter_value = + Serialize(WireVarInt62(AuthTokenAliasType::kUseValue), + WireVarInt62(it.type), WireBytes(it.token)); + out.insert(VersionSpecificParameter::kAuthorizationToken, + std::string(parameter_value.AsStringView())); } if (!parameters.delivery_timeout.IsInfinite()) { out.insert( VersionSpecificParameter::kDeliveryTimeout, static_cast<uint64_t>(parameters.delivery_timeout.ToMilliseconds())); } - if (parameters.authorization_info.has_value()) { - out.insert(VersionSpecificParameter::kAuthorizationInfo, - *parameters.authorization_info); - } if (!parameters.max_cache_duration.IsInfinite()) { out.insert( VersionSpecificParameter::kMaxCacheDuration,
diff --git a/quiche/quic/moqt/moqt_framer_test.cc b/quiche/quic/moqt/moqt_framer_test.cc index 690418b..96780f6 100644 --- a/quiche/quic/moqt/moqt_framer_test.cc +++ b/quiche/quic/moqt/moqt_framer_test.cc
@@ -415,7 +415,7 @@ /*group_order=*/std::nullopt, start, end_group, - VersionSpecificParameters("bar"), + VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"), }; quiche::QuicheBuffer buffer; MoqtFilterType expected_filter_type = GetFilterType(subscribe); @@ -443,7 +443,7 @@ /*group_order=*/std::nullopt, /*start=*/Location(4, 3), /*end_group=*/3, - VersionSpecificParameters("bar"), + VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"), }; quiche::QuicheBuffer buffer; EXPECT_QUIC_BUG(buffer = framer_.SerializeSubscribe(subscribe), @@ -462,7 +462,7 @@ /*end_group=*/1, /*end_object=*/1, /*parameters=*/ - VersionSpecificParameters("baz"), + VersionSpecificParameters(AuthTokenType::kOutOfBand, "baz"), }; quiche::QuicheBuffer buffer; EXPECT_QUIC_BUG(buffer = framer_.SerializeFetch(fetch),
diff --git a/quiche/quic/moqt/moqt_messages.cc b/quiche/quic/moqt/moqt_messages.cc index 516c62a..cc57440 100644 --- a/quiche/quic/moqt/moqt_messages.cc +++ b/quiche/quic/moqt/moqt_messages.cc
@@ -168,16 +168,13 @@ parameters.count(VersionSpecificParameter::kAuthorizationToken); size_t delivery_timeout = parameters.count(VersionSpecificParameter::kDeliveryTimeout); - size_t authorization_info = - parameters.count(VersionSpecificParameter::kAuthorizationInfo); size_t max_cache_duration = parameters.count(VersionSpecificParameter::kMaxCacheDuration); - if (delivery_timeout > 1 || authorization_info > 1 || - max_cache_duration > 1) { + if (delivery_timeout > 1 || max_cache_duration > 1) { // Disallowed duplicate. return false; } - if ((authorization_token > 0 || authorization_info > 0) && + if (authorization_token > 0 && !absl::c_linear_search(kAllowsAuthorization, message_type)) { return false; }
diff --git a/quiche/quic/moqt/moqt_messages.h b/quiche/quic/moqt/moqt_messages.h index 77d916e..76c74cf 100644 --- a/quiche/quic/moqt/moqt_messages.h +++ b/quiche/quic/moqt/moqt_messages.h
@@ -144,12 +144,18 @@ kInternalError = 0x1, kUnauthorized = 0x2, kProtocolViolation = 0x3, - kDuplicateTrackAlias = 0x4, - kParameterLengthMismatch = 0x5, - kTooManySubscribes = 0x6, + kInvalidRequestId = 0x4, + kDuplicateTrackAlias = 0x5, + kKeyValueFormattingError = 0x6, + kTooManySubscribes = 0x7, + kInvalidPath = 0x8, + kMalformedPath = 0x9, kGoawayTimeout = 0x10, kControlMessageTimeout = 0x11, kDataStreamTimeout = 0x12, + kAuthTokenCacheOverflow = 0x13, + kDuplicateAuthTokenAlias = 0x14, + kVersionNegotiationFailed = 0x15, }; // Error codes used by MoQT to reset streams. @@ -173,13 +179,37 @@ enum class QUICHE_EXPORT VersionSpecificParameter : uint64_t { kAuthorizationToken = 0x1, kDeliveryTimeout = 0x2, - kAuthorizationInfo = 0x3, kMaxCacheDuration = 0x4, // QUICHE-specific extensions. kOackWindowSize = 0xbbf1438, }; +enum AuthTokenType : uint64_t { + kOutOfBand = 0x0, + + kMaxAuthTokenType = 0x0, +}; + +enum AuthTokenAliasType : uint64_t { + kDelete = 0x0, + kRegister = 0x1, + kUseAlias = 0x2, + kUseValue = 0x3, + + kMaxValue = 0x3, +}; + +struct AuthToken { + AuthToken(AuthTokenType token_type, absl::string_view token) + : type(token_type), token(token) {} + bool operator==(const AuthToken& other) const { + return type == other.type && token == other.token; + } + AuthTokenType type; + std::string token; +}; + struct VersionSpecificParameters { VersionSpecificParameters() = default; // Likely parameter combinations. @@ -187,24 +217,24 @@ quic::QuicTimeDelta max_cache_duration) : delivery_timeout(delivery_timeout), max_cache_duration(max_cache_duration) {} - explicit VersionSpecificParameters(std::string authorization_info) - : authorization_info(authorization_info) {} + VersionSpecificParameters(AuthTokenType token_type, absl::string_view token) { + authorization_token.emplace_back(token_type, token); + }; VersionSpecificParameters(quic::QuicTimeDelta delivery_timeout, - std::string authorization_info) - : delivery_timeout(delivery_timeout), - authorization_info(authorization_info) {} + AuthTokenType token_type, absl::string_view token) + : delivery_timeout(delivery_timeout) { + authorization_token.emplace_back(token_type, token); + } // TODO(martinduke): Turn auth_token into structured data. - std::vector<std::string> authorization_token; + std::vector<AuthToken> authorization_token; quic::QuicTimeDelta delivery_timeout = quic::QuicTimeDelta::Infinite(); - std::optional<std::string> authorization_info; quic::QuicTimeDelta max_cache_duration = quic::QuicTimeDelta::Infinite(); std::optional<quic::QuicTimeDelta> oack_window_size; bool operator==(const VersionSpecificParameters& other) const { return authorization_token == other.authorization_token && delivery_timeout == other.delivery_timeout && - authorization_info == other.authorization_info && max_cache_duration == other.max_cache_duration && oack_window_size == other.oack_window_size; } @@ -222,6 +252,9 @@ kDoesNotExist = 0x4, // Can also mean "not interested" or "unknown". kInvalidRange = 0x5, // SUBSCRIBE_ERROR and FETCH_ERROR only. kRetryTrackAlias = 0x6, // SUBSCRIBE_ERROR only. + kMalformedAuthToken = 0x10, + kUnknownAuthTokenAlias = 0x11, + kExpiredAuthToken = 0x12, }; struct MoqtSubscribeErrorReason { @@ -710,7 +743,8 @@ bool ValidateSetupParameters(const KeyValuePairList& parameters, bool webtrans, quic::Perspective perspective); // Returns false if the parameters contain a protocol violation, or a -// parameter cannot be in |message type|. +// parameter cannot be in |message type|. Does not validate the internal +// structure of Authorization Token values. bool ValidateVersionSpecificParameters(const KeyValuePairList& parameters, MoqtMessageType message_type);
diff --git a/quiche/quic/moqt/moqt_parser.cc b/quiche/quic/moqt/moqt_parser.cc index 390ceec..27d9c81 100644 --- a/quiche/quic/moqt/moqt_parser.cc +++ b/quiche/quic/moqt/moqt_parser.cc
@@ -165,45 +165,6 @@ }); } -void KeyValuePairListToVersionSpecificParameters( - const KeyValuePairList& parameters, VersionSpecificParameters& out) { - parameters.ForEach( - [&](uint64_t key, uint64_t value) { - VersionSpecificParameter parameter = - static_cast<VersionSpecificParameter>(key); - switch (parameter) { - case VersionSpecificParameter::kDeliveryTimeout: - out.delivery_timeout = quic::QuicTimeDelta::FromMilliseconds(value); - break; - case VersionSpecificParameter::kMaxCacheDuration: - out.max_cache_duration = - quic::QuicTimeDelta::FromMilliseconds(value); - break; - case VersionSpecificParameter::kOackWindowSize: - out.oack_window_size = quic::QuicTimeDelta::FromMicroseconds(value); - break; - default: - break; - } - return true; - }, - [&](uint64_t key, absl::string_view value) { - VersionSpecificParameter parameter = - static_cast<VersionSpecificParameter>(key); - switch (parameter) { - case VersionSpecificParameter::kAuthorizationToken: - out.authorization_token.push_back(std::string(value)); - break; - case VersionSpecificParameter::kAuthorizationInfo: - out.authorization_info = value; - break; - default: - break; - } - return true; - }); -} - } // namespace void MoqtControlParser::ReadAndDispatchMessages() { @@ -477,13 +438,15 @@ if (!ParseKeyValuePairList(reader, parameters)) { return 0; } - // TODO(martinduke): Parse kAuthorizationToken. if (!ValidateVersionSpecificParameters(parameters, MoqtMessageType::kSubscribe)) { ParseError("SUBSCRIBE contains invalid parameters"); return 0; } - KeyValuePairListToVersionSpecificParameters(parameters, subscribe.parameters); + if (!KeyValuePairListToVersionSpecificParameters(parameters, + subscribe.parameters)) { + return 0; + } visitor_.OnSubscribeMessage(subscribe); return reader.PreviouslyReadPayload().length(); } @@ -524,8 +487,10 @@ ParseError("SUBSCRIBE_OK contains invalid parameters"); return 0; } - KeyValuePairListToVersionSpecificParameters(parameters, - subscribe_ok.parameters); + if (!KeyValuePairListToVersionSpecificParameters(parameters, + subscribe_ok.parameters)) { + return 0; + } visitor_.OnSubscribeOkMessage(subscribe_ok); return reader.PreviouslyReadPayload().length(); } @@ -585,8 +550,10 @@ ParseError("SUBSCRIBE_UPDATE contains invalid parameters"); return 0; } - KeyValuePairListToVersionSpecificParameters(parameters, - subscribe_update.parameters); + if (!KeyValuePairListToVersionSpecificParameters( + parameters, subscribe_update.parameters)) { + return 0; + } subscribe_update.start = Location(start_group, start_object); if (end_group > 0) { subscribe_update.end_group = end_group - 1; @@ -613,7 +580,10 @@ ParseError("ANNOUNCE contains invalid parameters"); return 0; } - KeyValuePairListToVersionSpecificParameters(parameters, announce.parameters); + if (!KeyValuePairListToVersionSpecificParameters(parameters, + announce.parameters)) { + return 0; + } visitor_.OnAnnounceMessage(announce); return reader.PreviouslyReadPayload().length(); } @@ -677,8 +647,10 @@ ParseError("TRACK_STATUS_REQUEST message contains invalid parameters"); return 0; } - KeyValuePairListToVersionSpecificParameters(parameters, - track_status_request.parameters); + if (!KeyValuePairListToVersionSpecificParameters( + parameters, track_status_request.parameters)) { + return 0; + } visitor_.OnTrackStatusRequestMessage(track_status_request); return reader.PreviouslyReadPayload().length(); } @@ -718,8 +690,10 @@ ParseError("TRACK_STATUS message contains invalid parameters"); return 0; } - KeyValuePairListToVersionSpecificParameters(parameters, - track_status.parameters); + if (!KeyValuePairListToVersionSpecificParameters(parameters, + track_status.parameters)) { + return 0; + } visitor_.OnTrackStatusMessage(track_status); return reader.PreviouslyReadPayload().length(); } @@ -748,8 +722,10 @@ ParseError("SUBSCRIBE_ANNOUNCES message contains invalid parameters"); return 0; } - KeyValuePairListToVersionSpecificParameters(parameters, - subscribe_announces.parameters); + if (!KeyValuePairListToVersionSpecificParameters( + parameters, subscribe_announces.parameters)) { + return 0; + } visitor_.OnSubscribeAnnouncesMessage(subscribe_announces); return reader.PreviouslyReadPayload().length(); } @@ -860,7 +836,10 @@ ParseError("FETCH message contains invalid parameters"); return 0; } - KeyValuePairListToVersionSpecificParameters(parameters, fetch.parameters); + if (!KeyValuePairListToVersionSpecificParameters(parameters, + fetch.parameters)) { + return 0; + }; visitor_.OnFetchMessage(fetch); return reader.PreviouslyReadPayload().length(); } @@ -895,7 +874,10 @@ return 0; } fetch_ok.group_order = static_cast<MoqtDeliveryOrder>(group_order); - KeyValuePairListToVersionSpecificParameters(parameters, fetch_ok.parameters); + if (!KeyValuePairListToVersionSpecificParameters(parameters, + fetch_ok.parameters)) { + return 0; + } visitor_.OnFetchOkMessage(fetch_ok); return reader.PreviouslyReadPayload().length(); } @@ -974,6 +956,122 @@ return true; } +// Returns false if there is a protocol violation. +bool MoqtControlParser::KeyValuePairListToVersionSpecificParameters( + const KeyValuePairList& parameters, VersionSpecificParameters& out) { + return parameters.ForEach( + [&](uint64_t key, uint64_t value) { + VersionSpecificParameter parameter = + static_cast<VersionSpecificParameter>(key); + switch (parameter) { + case VersionSpecificParameter::kDeliveryTimeout: + out.delivery_timeout = quic::QuicTimeDelta::FromMilliseconds(value); + break; + case VersionSpecificParameter::kMaxCacheDuration: + out.max_cache_duration = + quic::QuicTimeDelta::FromMilliseconds(value); + break; + case VersionSpecificParameter::kOackWindowSize: + out.oack_window_size = quic::QuicTimeDelta::FromMicroseconds(value); + break; + default: + break; + } + return true; + }, + [&](uint64_t key, absl::string_view value) { + VersionSpecificParameter parameter = + static_cast<VersionSpecificParameter>(key); + switch (parameter) { + case VersionSpecificParameter::kAuthorizationToken: + if (!ParseAuthTokenParameter(value, out)) { + return false; + } + break; + default: + break; + } + return true; + }); +} + +bool MoqtControlParser::ParseAuthTokenParameter( + absl::string_view field, VersionSpecificParameters& out) { + quic::QuicDataReader reader(field); + AuthTokenType token_type; + absl::string_view token; + uint64_t value; + if (!reader.ReadVarInt62(&value) || value > AuthTokenAliasType::kMaxValue) { + ParseError(MoqtError::kKeyValueFormattingError, + "Invalid Authorization Token Alias type"); + return false; + } + AuthTokenAliasType alias_type = static_cast<AuthTokenAliasType>(value); + switch (alias_type) { + case AuthTokenAliasType::kUseValue: + if (!reader.ReadVarInt62(&value)) { + ParseError(MoqtError::kKeyValueFormattingError, + "Malformed Authorization Token Parameter"); + return false; + } + if (value > AuthTokenType::kMaxAuthTokenType) { + ParseError(MoqtError::kKeyValueFormattingError, + "Invalid Authorization Token Type"); + return false; + } + token_type = static_cast<AuthTokenType>(value); + token = reader.PeekRemainingPayload(); + break; + case AuthTokenAliasType::kUseAlias: + if (!reader.ReadVarInt62(&value)) { + ParseError(MoqtError::kKeyValueFormattingError, + "Malformed Authorization Token Parameter"); + return false; + } + // TODO: Implement support for cache_size > 0 + ParseError(MoqtError::kKeyValueFormattingError, + "Unknown Auth Token Alias"); + return false; + case AuthTokenAliasType::kRegister: + if (!reader.ReadVarInt62(&value)) { + ParseError(MoqtError::kKeyValueFormattingError, + "Malformed Authorization Token Parameter"); + return false; + } + if (!reader.ReadVarInt62(&value)) { + ParseError(MoqtError::kKeyValueFormattingError, + "Malformed Authorization Token Parameter"); + return false; + } + token_type = static_cast<AuthTokenType>(value); + token = reader.PeekRemainingPayload(); + if (auth_token_cache_size_ + sizeof(uint64_t) + token.length() > + max_auth_token_cache_size_) { + ParseError(MoqtError::kAuthTokenCacheOverflow, + "Too many authorization token tags"); + return false; + } + break; + // TODO: Add to the cache. + // TODO: Check if the alias is already in use. + QUICHE_NOTREACHED(); + break; + case AuthTokenAliasType::kDelete: + if (!reader.ReadVarInt62(&value)) { + ParseError(MoqtError::kKeyValueFormattingError, + "Malformed Authorization Token Parameter"); + return false; + } + // TODO: Implement support for cache_size > 0 + ParseError(MoqtError::kKeyValueFormattingError, + "Unknown Auth Token Alias"); + return false; + } + // Validate cache operations. + out.authorization_token.push_back(AuthToken(token_type, token)); + return true; +} + void MoqtDataParser::ParseError(absl::string_view reason) { if (parsing_error_) { return; // Don't send multiple parse errors.
diff --git a/quiche/quic/moqt/moqt_parser.h b/quiche/quic/moqt/moqt_parser.h index 137dd87..36c8f31 100644 --- a/quiche/quic/moqt/moqt_parser.h +++ b/quiche/quic/moqt/moqt_parser.h
@@ -11,7 +11,6 @@ #include <cstddef> #include <cstdint> #include <optional> -#include <string> #include "absl/strings/string_view.h" #include "quiche/quic/core/quic_data_reader.h" @@ -143,6 +142,16 @@ // could not parse the full namespace field. bool ReadTrackNamespace(quic::QuicDataReader& reader, FullTrackName& full_track_name); + // Translates raw key/value pairs into semantically meaningful formats. + // The spec defines many encoding errors in AUTHORIZATION TOKEN as + // request level. This treats them as session-level, unless they are a result + // of expiration, incorrect internal structure, or anything else not defined + // in the MoQT spec. It is allowed to promote request errors to session errors + // in MoQT. See also https://github.com/moq-wg/moq-transport/issues/964. + bool KeyValuePairListToVersionSpecificParameters( + const KeyValuePairList& parameters, VersionSpecificParameters& out); + bool ParseAuthTokenParameter(absl::string_view field, + VersionSpecificParameters& out); MoqtControlParserVisitor& visitor_; quiche::ReadStream& stream_; @@ -153,6 +162,8 @@ std::optional<uint64_t> message_type_; std::optional<uint64_t> message_size_; + uint64_t max_auth_token_cache_size_ = 0; + uint64_t auth_token_cache_size_ = 0; bool processing_ = false; // True if currently in ProcessData(), to prevent // re-entrancy. };
diff --git a/quiche/quic/moqt/moqt_parser_test.cc b/quiche/quic/moqt/moqt_parser_test.cc index 6ba8a22..bfe3c99 100644 --- a/quiche/quic/moqt/moqt_parser_test.cc +++ b/quiche/quic/moqt/moqt_parser_test.cc
@@ -660,7 +660,7 @@ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation); } -TEST_F(MoqtMessageSpecificTest, SubscribeAuthorizationInfoTwice) { +TEST_F(MoqtMessageSpecificTest, UnknownParameterTwiceIsOk) { webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kWebTrans, &stream, visitor_); char subscribe[] = { @@ -670,14 +670,12 @@ 0x20, 0x02, // priority = 0x20 descending 0x02, // filter_type = kLatestObject 0x02, // two params - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x1f, 0x03, 0x62, 0x61, 0x72, // 0x1f = "bar" + 0x1f, 0x03, 0x62, 0x61, 0x72, // 0x1f = "bar" }; stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false); parser.ReadAndDispatchMessages(); - EXPECT_EQ(visitor_.messages_received_, 0); - EXPECT_EQ(visitor_.parsing_error_, "SUBSCRIBE contains invalid parameters"); - EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation); + EXPECT_EQ(visitor_.messages_received_, 1); } TEST_F(MoqtMessageSpecificTest, SubscribeDeliveryTimeoutTwice) { @@ -720,16 +718,113 @@ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation); } -TEST_F(MoqtMessageSpecificTest, SubscribeOkHasAuthorizationInfo) { +TEST_F(MoqtMessageSpecificTest, SubscribeAuthorizationTokenTagDelete) { + webtransport::test::InMemoryStream stream(/*stream_id=*/0); + MoqtControlParser parser(kRawQuic, &stream, visitor_); + char subscribe[] = { + 0x03, 0x14, 0x01, 0x02, 0x01, + 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + 0x04, 0x61, 0x62, 0x63, 0x64, // track_name = "abcd" + 0x20, 0x02, // priority = 0x20 descending + 0x02, // filter_type = kLatestObject + 0x01, // one param + 0x01, 0x02, 0x00, 0x00, // authorization_token = DELETE 0; + }; + stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false); + parser.ReadAndDispatchMessages(); + EXPECT_EQ(visitor_.messages_received_, 0); + EXPECT_EQ(visitor_.parsing_error_, "Unknown Auth Token Alias"); + EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kKeyValueFormattingError); +} + +TEST_F(MoqtMessageSpecificTest, SubscribeAuthorizationTokenTagRegister) { + webtransport::test::InMemoryStream stream(/*stream_id=*/0); + MoqtControlParser parser(kRawQuic, &stream, visitor_); + char subscribe[] = { + 0x03, 0x18, 0x01, 0x02, 0x01, 0x03, 0x66, 0x6f, + 0x6f, // track_namespace = "foo" + 0x04, 0x61, 0x62, 0x63, 0x64, // track_name = "abcd" + 0x20, 0x02, // priority = 0x20 descending + 0x02, // filter_type = kLatestObject + 0x01, // one param + 0x01, 0x06, 0x01, 0x10, 0x00, 0x62, 0x61, 0x72, // REGISTER 0x01 + }; + stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false); + parser.ReadAndDispatchMessages(); + EXPECT_EQ(visitor_.messages_received_, 0); + EXPECT_EQ(visitor_.parsing_error_, "Too many authorization token tags"); + EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kAuthTokenCacheOverflow); +} + +TEST_F(MoqtMessageSpecificTest, SubscribeAuthorizationTokenTagUseAlias) { + webtransport::test::InMemoryStream stream(/*stream_id=*/0); + MoqtControlParser parser(kRawQuic, &stream, visitor_); + char subscribe[] = { + 0x03, 0x14, 0x01, 0x02, 0x01, + 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + 0x04, 0x61, 0x62, 0x63, 0x64, // track_name = "abcd" + 0x20, 0x02, // priority = 0x20 descending + 0x02, // filter_type = kLatestObject + 0x01, // one param + 0x01, 0x02, 0x02, 0x07, // authorization_token = USE 7; + }; + stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false); + parser.ReadAndDispatchMessages(); + EXPECT_EQ(visitor_.messages_received_, 0); + EXPECT_EQ(visitor_.parsing_error_, "Unknown Auth Token Alias"); + EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kKeyValueFormattingError); +} + +TEST_F(MoqtMessageSpecificTest, + SubscribeAuthorizationTokenTagUnknownAliasType) { + webtransport::test::InMemoryStream stream(/*stream_id=*/0); + MoqtControlParser parser(kRawQuic, &stream, visitor_); + char subscribe[] = { + 0x03, 0x14, 0x01, 0x02, 0x01, + 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + 0x04, 0x61, 0x62, 0x63, 0x64, // track_name = "abcd" + 0x20, 0x02, // priority = 0x20 descending + 0x02, // filter_type = kLatestObject + 0x01, // one param + 0x01, 0x02, 0x04, 0x07, // authorization_token type 4 + }; + stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false); + parser.ReadAndDispatchMessages(); + EXPECT_EQ(visitor_.messages_received_, 0); + EXPECT_EQ(visitor_.parsing_error_, "Invalid Authorization Token Alias type"); + EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kKeyValueFormattingError); +} + +TEST_F(MoqtMessageSpecificTest, + SubscribeAuthorizationTokenTagUnknownTokenType) { + webtransport::test::InMemoryStream stream(/*stream_id=*/0); + MoqtControlParser parser(kRawQuic, &stream, visitor_); + char subscribe[] = { + 0x03, 0x16, 0x01, 0x02, 0x01, 0x03, + 0x66, 0x6f, 0x6f, // track_namespace = "foo" + 0x04, 0x61, 0x62, 0x63, 0x64, // track_name = "abcd" + 0x20, 0x02, // priority = 0x20 descending + 0x02, // filter_type = kLatestObject + 0x01, // one param + 0x01, 0x04, 0x03, 0x01, 0x00, 0x00 // authorization_token type 1 + }; + stream.Receive(absl::string_view(subscribe, sizeof(subscribe)), false); + parser.ReadAndDispatchMessages(); + EXPECT_EQ(visitor_.messages_received_, 0); + EXPECT_EQ(visitor_.parsing_error_, "Invalid Authorization Token Type"); + EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kKeyValueFormattingError); +} + +TEST_F(MoqtMessageSpecificTest, SubscribeOkHasAuthorizationToken) { webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kWebTrans, &stream, visitor_); char subscribe_ok[] = { - 0x04, 0x0f, 0x01, 0x03, // subscribe_id = 1, expires = 3 + 0x04, 0x11, 0x01, 0x03, // subscribe_id = 1, expires = 3 0x02, 0x01, // group_order = 2, content exists 0x0c, 0x14, // largest_group_id = 12, largest_object_id = 20, 0x02, // 2 parameters 0x02, 0x67, 0x10, // delivery_timeout = 10000 - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_token = "bar" }; stream.Receive(absl::string_view(subscribe_ok, sizeof(subscribe_ok)), false); parser.ReadAndDispatchMessages(); @@ -739,14 +834,14 @@ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation); } -TEST_F(MoqtMessageSpecificTest, SubscribeUpdateHasAuthorizationInfo) { +TEST_F(MoqtMessageSpecificTest, SubscribeUpdateHasAuthorizationToken) { webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kWebTrans, &stream, visitor_); char subscribe_update[] = { - 0x02, 0x0b, 0x02, 0x03, 0x01, 0x05, // start and end sequences - 0xaa, // priority = 0xaa - 0x01, // 1 parameter - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x02, 0x0d, 0x02, 0x03, 0x01, 0x05, // start and end sequences + 0xaa, // priority = 0xaa + 0x01, // 1 parameter + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_token = "bar" }; stream.Receive(absl::string_view(subscribe_update, sizeof(subscribe_update)), false); @@ -757,29 +852,27 @@ EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation); } -TEST_F(MoqtMessageSpecificTest, AnnounceAuthorizationInfoTwice) { +TEST_F(MoqtMessageSpecificTest, AnnounceAuthorizationTokenTwice) { webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kWebTrans, &stream, visitor_); char announce[] = { - 0x06, 0x10, 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + 0x06, 0x14, 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" 0x02, // 2 params - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization = "bar" + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization = "bar" }; stream.Receive(absl::string_view(announce, sizeof(announce)), false); parser.ReadAndDispatchMessages(); - EXPECT_EQ(visitor_.messages_received_, 0); - EXPECT_EQ(visitor_.parsing_error_, "ANNOUNCE contains invalid parameters"); - EXPECT_EQ(visitor_.parsing_error_code_, MoqtError::kProtocolViolation); + EXPECT_EQ(visitor_.messages_received_, 1); } TEST_F(MoqtMessageSpecificTest, AnnounceHasDeliveryTimeout) { webtransport::test::InMemoryStream stream(/*stream_id=*/0); MoqtControlParser parser(kWebTrans, &stream, visitor_); char announce[] = { - 0x06, 0x0e, 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + 0x06, 0x10, 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" 0x02, // 2 params - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_info = "bar" 0x02, 0x67, 0x10, // delivery_timeout = 10000 }; stream.Receive(absl::string_view(announce, sizeof(announce)), false);
diff --git a/quiche/quic/moqt/moqt_session_test.cc b/quiche/quic/moqt/moqt_session_test.cc index 2513404..010926d 100644 --- a/quiche/quic/moqt/moqt_session_test.cc +++ b/quiche/quic/moqt/moqt_session_test.cc
@@ -323,14 +323,13 @@ bool reported_error = false; EXPECT_CALL( mock_session_, - CloseSession(static_cast<uint64_t>(MoqtError::kParameterLengthMismatch), - "foo")) + CloseSession(static_cast<uint64_t>(MoqtError::kProtocolViolation), "foo")) .Times(1); EXPECT_CALL(session_callbacks_.session_terminated_callback, Call(_)) .WillOnce([&](absl::string_view error_message) { reported_error = (error_message == "foo"); }); - session_.Error(MoqtError::kParameterLengthMismatch, "foo"); + session_.Error(MoqtError::kProtocolViolation, "foo"); EXPECT_TRUE(reported_error); }
diff --git a/quiche/quic/moqt/test_tools/moqt_test_message.h b/quiche/quic/moqt/test_tools/moqt_test_message.h index 63b60ec..8478fcd 100644 --- a/quiche/quic/moqt/test_tools/moqt_test_message.h +++ b/quiche/quic/moqt/test_tools/moqt_test_message.h
@@ -512,7 +512,7 @@ } void ExpandVarints() override { - ExpandVarintsImpl("vvvvvv---v------vvvvvv---v--"); + ExpandVarintsImpl("vvvvvv---v------vvvvv--vv-----"); } MessageStructuredData structured_data() const override { @@ -520,8 +520,8 @@ } private: - uint8_t raw_packet_[28] = { - 0x03, 0x1a, 0x01, 0x02, // id and alias + uint8_t raw_packet_[30] = { + 0x03, 0x1c, 0x01, 0x02, // id and alias 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" 0x04, 0x61, 0x62, 0x63, 0x64, // track_name = "abcd" 0x20, // subscriber priority = 0x20 @@ -530,9 +530,9 @@ 0x04, // start_group = 4 (relative previous) 0x01, // start_object = 1 (absolute) // No EndGroup or EndObject - 0x02, // 2 parameters - 0x02, 0x67, 0x10, // delivery_timeout = 10000 ms - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x02, // 2 parameters + 0x02, 0x67, 0x10, // delivery_timeout = 10000 ms + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_tag = "bar" }; MoqtSubscribe subscribe_ = { @@ -544,7 +544,7 @@ /*start=*/Location(4, 1), /*end_group=*/std::nullopt, VersionSpecificParameters(quic::QuicTimeDelta::FromMilliseconds(10000), - "bar"), + AuthTokenType::kOutOfBand, "bar"), }; }; @@ -819,22 +819,22 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvvv---vvv---"); } + void ExpandVarints() override { ExpandVarintsImpl("vvvv---vvv-----"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(announce_); } private: - uint8_t raw_packet_[13] = { - 0x06, 0x0b, 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + uint8_t raw_packet_[15] = { + 0x06, 0x0d, 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" 0x01, // 1 parameter - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_tag = "bar" }; MoqtAnnounce announce_ = { /*track_namespace=*/FullTrackName{"foo"}, - VersionSpecificParameters("bar"), + VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"), }; }; @@ -974,23 +974,23 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvvv---v----vvv---"); } + void ExpandVarints() override { ExpandVarintsImpl("vvvv---v----vvv-----"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(track_status_request_); } private: - uint8_t raw_packet_[18] = { - 0x0d, 0x10, 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + uint8_t raw_packet_[20] = { + 0x0d, 0x12, 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" 0x04, 0x61, 0x62, 0x63, 0x64, // track_name = "abcd" 0x01, // 1 parameter - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_tag = "bar" }; MoqtTrackStatusRequest track_status_request_ = { /*full_track_name=*/FullTrackName({"foo", "abcd"}), - VersionSpecificParameters("bar"), + VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"), }; }; @@ -1132,22 +1132,22 @@ return true; } - void ExpandVarints() override { ExpandVarintsImpl("vvvv---vvv---"); } + void ExpandVarints() override { ExpandVarintsImpl("vvvv---vvv-----"); } MessageStructuredData structured_data() const override { return TestMessageBase::MessageStructuredData(subscribe_namespace_); } private: - uint8_t raw_packet_[13] = { - 0x11, 0x0b, 0x01, 0x03, 0x66, 0x6f, 0x6f, // namespace = "foo" + uint8_t raw_packet_[15] = { + 0x11, 0x0d, 0x01, 0x03, 0x66, 0x6f, 0x6f, // namespace = "foo" 0x01, // 1 parameter - 0x03, 0x03, 0x62, 0x61, 0x72, // authorization_info = "bar" + 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x72, // authorization_tag = "bar" }; MoqtSubscribeAnnounces subscribe_namespace_ = { /*track_namespace=*/FullTrackName{"foo"}, - VersionSpecificParameters("bar"), + VersionSpecificParameters(AuthTokenType::kOutOfBand, "bar"), }; }; @@ -1349,7 +1349,7 @@ } void ExpandVarints() override { - ExpandVarintsImpl("vvv--vvv---v---vvvvvv---"); + ExpandVarintsImpl("vvv--vvv---v---vvvvvv-----"); } MessageStructuredData structured_data() const override { @@ -1373,17 +1373,17 @@ } private: - uint8_t raw_packet_[25] = { - 0x16, 0x17, - 0x01, // fetch_id = 1 - 0x02, // priority = kHigh - 0x01, // group_order = kAscending - 0x01, // type = kStandalone - 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" - 0x03, 0x62, 0x61, 0x72, // track_name = "bar" - 0x01, 0x02, // start_object = 1, 2 - 0x05, 0x07, // end_object = 5, 6 - 0x01, 0x03, 0x03, 0x62, 0x61, 0x7a, // parameters = "baz" + uint8_t raw_packet_[27] = { + 0x16, 0x19, + 0x01, // fetch_id = 1 + 0x02, // priority = kHigh + 0x01, // group_order = kAscending + 0x01, // type = kStandalone + 0x01, 0x03, 0x66, 0x6f, 0x6f, // track_namespace = "foo" + 0x03, 0x62, 0x61, 0x72, // track_name = "bar" + 0x01, 0x02, // start_object = 1, 2 + 0x05, 0x07, // end_object = 5, 6 + 0x01, 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x7a, // parameters = "baz" }; MoqtFetch fetch_ = { @@ -1395,7 +1395,7 @@ /*start_object=*/Location{1, 2}, /*end_group=*/5, /*end_object=*/6, - VersionSpecificParameters("baz"), + VersionSpecificParameters(AuthTokenType::kOutOfBand, "baz"), }; }; @@ -1461,7 +1461,7 @@ } void ExpandVarints() override { - ExpandVarintsImpl("vvv--vvv---v---vvvvvv---"); + ExpandVarintsImpl("vvv--vvv---v---vvvvvv-----"); } MessageStructuredData structured_data() const override { @@ -1474,14 +1474,14 @@ } private: - uint8_t raw_packet_[14] = { - 0x16, 0x0c, - 0x01, // fetch_id = 1 - 0x02, // priority = kHigh - 0x01, // group_order = kAscending - 0x02, // type = kJoining - 0x02, 0x02, // joining_subscribe_id = 2, 2 groups - 0x01, 0x03, 0x03, 0x62, 0x61, 0x7a, // parameters = "baz" + uint8_t raw_packet_[16] = { + 0x16, 0x0e, + 0x01, // fetch_id = 1 + 0x02, // priority = kHigh + 0x01, // group_order = kAscending + 0x02, // type = kJoining + 0x02, 0x02, // joining_subscribe_id = 2, 2 groups + 0x01, 0x01, 0x05, 0x03, 0x00, 0x62, 0x61, 0x7a, // parameters = "baz" }; MoqtFetch fetch_ = { @@ -1494,7 +1494,7 @@ /*start_object=*/Location{1, 2}, /*end_group=*/5, /*end_object=*/6, - VersionSpecificParameters("baz"), + VersionSpecificParameters(AuthTokenType::kOutOfBand, "baz"), }; };
diff --git a/quiche/quic/moqt/tools/chat_client.cc b/quiche/quic/moqt/tools/chat_client.cc index e10ae33..cbcfc3a 100644 --- a/quiche/quic/moqt/tools/chat_client.cc +++ b/quiche/quic/moqt/tools/chat_client.cc
@@ -70,7 +70,7 @@ return std::nullopt; } VersionSpecificParameters parameters( - std::string(GetUsername(my_track_name_))); + AuthTokenType::kOutOfBand, std::string(GetUsername(my_track_name_))); if (session_->SubscribeCurrentObject(*track_name, &remote_track_visitor_, parameters)) { ++subscribes_to_make_; @@ -249,7 +249,7 @@ return; }; VersionSpecificParameters parameters( - std::string(GetUsername(my_track_name_))); + AuthTokenType::kOutOfBand, std::string(GetUsername(my_track_name_))); session_->SubscribeAnnounces(GetChatNamespace(my_track_name_), std::move(subscribe_announces_callback), parameters);