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);