diff options
-rw-r--r-- | cast/streaming/offer_messages.cc | 8 | ||||
-rw-r--r-- | cast/streaming/offer_messages_unittest.cc | 105 | ||||
-rw-r--r-- | platform/base/error.cc | 2 | ||||
-rw-r--r-- | platform/base/error.h | 3 |
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(); |