aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cast/streaming/offer_messages.cc8
-rw-r--r--cast/streaming/offer_messages_unittest.cc105
-rw-r--r--platform/base/error.cc2
-rw-r--r--platform/base/error.h3
4 files changed, 108 insertions, 10 deletions
diff --git a/cast/streaming/offer_messages.cc b/cast/streaming/offer_messages.cc
index dfc5a15e..cea500cd 100644
--- a/cast/streaming/offer_messages.cc
+++ b/cast/streaming/offer_messages.cc
@@ -120,12 +120,10 @@ ErrorOr<Stream> ParseStream(const Json::Value& value, Stream::Type type) {
return ssrc.error();
}
auto aes_key = ParseAesHexBytes(value, "aesKey");
- if (!aes_key) {
- return aes_key.error();
- }
auto aes_iv_mask = ParseAesHexBytes(value, "aesIvMask");
- if (!aes_iv_mask) {
- return aes_iv_mask.error();
+ if (!aes_key || !aes_iv_mask) {
+ return Error(Error::Code::kUnencryptedOffer,
+ "Offer stream must have both a valid aesKey and aesIvMask");
}
auto rtp_timebase = ParseRtpTimebase(value, "timeBase");
if (!rtp_timebase) {
diff --git a/cast/streaming/offer_messages_unittest.cc b/cast/streaming/offer_messages_unittest.cc
index 3976d46b..a2117f67 100644
--- a/cast/streaming/offer_messages_unittest.cc
+++ b/cast/streaming/offer_messages_unittest.cc
@@ -86,10 +86,16 @@ constexpr char kValidOffer[] = R"({
]
})";
-void ExpectFailureOnParse(absl::string_view body) {
+void ExpectFailureOnParse(
+ absl::string_view body,
+ absl::optional<Error::Code> expected = absl::nullopt) {
ErrorOr<Json::Value> root = json::Parse(body);
ASSERT_TRUE(root.is_value()) << root.error();
- EXPECT_TRUE(Offer::Parse(std::move(root.value())).is_error());
+ ErrorOr<Offer> error_or_offer = Offer::Parse(std::move(root.value()));
+ EXPECT_TRUE(error_or_offer.is_error());
+ if (expected) {
+ EXPECT_EQ(expected, error_or_offer.error().code());
+ }
}
void ExpectEqualsValidOffer(const Offer& offer) {
@@ -183,8 +189,8 @@ TEST(OfferTest, ErrorOnEmptyOffer) {
}
TEST(OfferTest, ErrorOnMissingMandatoryFields) {
- // It's okay if castMode is omitted, but if supportedStreams isanne //
- // omitted we should fail here.
+ // It's okay if castMode is omitted, but if supportedStreams is omitted we
+ // should fail here.
ExpectFailureOnParse(R"({
"castMode": "mirroring"
})");
@@ -493,5 +499,96 @@ TEST(OfferTest, ToJsonFailsWithInvalidStreams) {
EXPECT_TRUE(video_stream_invalid.ToJson().is_error());
}
+TEST(OfferTest, FailsIfUnencrypted) {
+ // Video stream missing AES fields.
+ ExpectFailureOnParse(R"({
+ "castMode": "mirroring",
+ "supportedStreams": [{
+ "index": 2,
+ "type": "video_source",
+ "codecName": "vp8",
+ "rtpProfile": "cast",
+ "rtpPayloadType": 100,
+ "ssrc": 19088743,
+ "timeBase": "1/48000",
+ "resolutions": [],
+ "maxBitRate": 10000,
+ "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
+ }]
+ })",
+ Error::Code::kUnencryptedOffer);
+
+ ExpectFailureOnParse(R"({
+ "castMode": "mirroring",
+ "supportedStreams": [{
+ "index": 2,
+ "type": "video_source",
+ "codecName": "vp8",
+ "rtpProfile": "cast",
+ "rtpPayloadType": 100,
+ "ssrc": 19088743,
+ "timeBase": "1/48000",
+ "resolutions": [],
+ "maxBitRate": 10000,
+ "aesKey": "51027e4e2347cbcb49d57ef10177aebc"
+ }]
+ })",
+ Error::Code::kUnencryptedOffer);
+
+ // Audio stream missing AES fields.
+ ExpectFailureOnParse(R"({
+ "castMode": "mirroring",
+ "supportedStreams": [{
+ "index": 2,
+ "type": "audio_source",
+ "codecName": "opus",
+ "rtpProfile": "cast",
+ "rtpPayloadType": 96,
+ "ssrc": 19088743,
+ "bitRate": 124000,
+ "timeBase": "1/48000",
+ "channels": 2,
+ "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
+ }]
+ })",
+ Error::Code::kUnencryptedOffer);
+
+ ExpectFailureOnParse(R"({
+ "castMode": "mirroring",
+ "supportedStreams": [{
+ "index": 2,
+ "type": "audio_source",
+ "codecName": "opus",
+ "rtpProfile": "cast",
+ "rtpPayloadType": 96,
+ "ssrc": 19088743,
+ "bitRate": 124000,
+ "timeBase": "1/48000",
+ "channels": 2,
+ "aesKey": "51027e4e2347cbcb49d57ef10177aebc"
+ }]
+ })",
+ Error::Code::kUnencryptedOffer);
+
+ // And finally, fields provided but not properly formatted.
+ ExpectFailureOnParse(R"({
+ "castMode": "mirroring",
+ "supportedStreams": [{
+ "index": 2,
+ "type": "audio_source",
+ "codecName": "opus",
+ "rtpProfile": "cast",
+ "rtpPayloadType": 96,
+ "ssrc": 19088743,
+ "bitRate": 124000,
+ "timeBase": "1/48000",
+ "channels": 2,
+ "aesKey": "51027e4e2347$bcb49d57ef10177aebc",
+ "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1"
+ }]
+ })",
+ Error::Code::kUnencryptedOffer);
+}
+
} // namespace cast
} // namespace openscreen
diff --git a/platform/base/error.cc b/platform/base/error.cc
index 58d81e76..bad8a84f 100644
--- a/platform/base/error.cc
+++ b/platform/base/error.cc
@@ -256,6 +256,8 @@ std::ostream& operator<<(std::ostream& os, const Error::Code& code) {
return os << "UnknownCodec";
case Error::Code::kSocketFailure:
return os << "SocketFailure";
+ case Error::Code::kUnencryptedOffer:
+ return os << "UnencryptedOffer";
case Error::Code::kNone:
break;
}
diff --git a/platform/base/error.h b/platform/base/error.h
index dc4c3a7f..9deacd2f 100644
--- a/platform/base/error.h
+++ b/platform/base/error.h
@@ -186,7 +186,8 @@ class Error {
// Cast streaming errors
kTypeError,
kUnknownCodec,
- kSocketFailure
+ kSocketFailure,
+ kUnencryptedOffer
};
Error();