Correctly parse v18.0 provisioning requests
The previous code fixed a backwards compatibility error for v18 provisioning requests being parsed by a v17 or older server. This bumped the minor version number to v18.1. v17 servers are still expected to fail when trying to parse v18.0 provisioning requests, and v18.1 requests will pass. However, it did not correctly account for existing v18.0 requests on v18.1+ servers. v18.0 messages were naively detected by a failure to parse, and the parse function was not run again. This left the resulting nonce and session_id values uninitialized. This CL fixes that by explicitly handling the v18.0 and v18.1+ cases, ensuring that the parse function succeeds and all relevant information is copied over. Furthermore, the unit test that was meant to catch this edge case has been improved to validate the resulting parsed message. All code changes affect the server. This does not affect the client code eg OEMCrypto PiperOrigin-RevId: 523714529 Merged from https://widevine-internal-review.googlesource.com/170110 Change-Id: I21911c4bb4304de2d93f092f356402bbd4240874
This commit is contained in:
committed by
Robert Shih
parent
c6e7c70a6b
commit
a2a27c44ef
@@ -2,11 +2,15 @@
|
||||
// source code may only be used and distributed under the Widevine
|
||||
// License Agreement.
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "OEMCryptoCENCCommon.h"
|
||||
#include "core_message_deserialize.h"
|
||||
#include "core_message_features.h"
|
||||
#include "core_message_serialize_proto.h"
|
||||
#include "core_message_types.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "odk.h"
|
||||
@@ -18,6 +22,9 @@ using oemcrypto_core_message::ODK_CommonRequest;
|
||||
using oemcrypto_core_message::ODK_ProvisioningRequest;
|
||||
using oemcrypto_core_message::deserialize::CoreCommonRequestFromMessage;
|
||||
using oemcrypto_core_message::deserialize::CoreProvisioningRequestFromMessage;
|
||||
using oemcrypto_core_message::features::CoreMessageFeatures;
|
||||
using oemcrypto_core_message::serialize::
|
||||
CreateCoreProvisioningResponseFromProto;
|
||||
|
||||
TEST(CoreMessageTest, RenwalRequest) {
|
||||
std::string oem =
|
||||
@@ -69,40 +76,121 @@ TEST(CoreMessageTest, ParseCoreCommonRequestFromMessage) {
|
||||
EXPECT_EQ(odk_common_request.session_id, 1);
|
||||
}
|
||||
|
||||
// Make sure that the first version of the V18 provisioning request (no hidden
|
||||
// 4-byte value, all 0s in message counter struct) will still parse with current
|
||||
// v18 code.
|
||||
TEST(CoreMessageTest, ProvisionRequestRoundtrip_V18_Initial) {
|
||||
std::vector<std::string> should_pass = {
|
||||
// Pulled from ODKTest provision round trip, extra 4 bytes removed
|
||||
"000000050000005e00000012deadbeefcafebabe000000000000000000000000"
|
||||
"000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000000000",
|
||||
// Same thing but v17 in nonce. Almost like testing on the v17 server (but
|
||||
// not quite since the v17 parsing code has been slightly changed anyway)
|
||||
"000000050000005e00000011deadbeefcafebabe000000000000000000000000"
|
||||
"000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000000000",
|
||||
};
|
||||
struct TestParameters_18V0 {
|
||||
std::string message;
|
||||
uint16_t expected_api_minor_version;
|
||||
uint16_t expected_api_major_version;
|
||||
uint32_t expected_nonce;
|
||||
uint32_t expected_session_id;
|
||||
uint64_t expected_master_generation_number;
|
||||
};
|
||||
|
||||
ODK_ProvisioningRequest request;
|
||||
for (auto& tc : should_pass) {
|
||||
ASSERT_TRUE(CoreProvisioningRequestFromMessage(absl::HexStringToBytes(tc),
|
||||
&request));
|
||||
}
|
||||
|
||||
// Fail cases have non-zero values after the bytes interpreted as length
|
||||
std::vector<std::string> should_fail = {
|
||||
// Change a 0 to a 1 in the message counter
|
||||
"000000050000005e00000012deadbeefcafebabe000000000000000100000000"
|
||||
"000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000000000",
|
||||
};
|
||||
|
||||
for (auto& tc : should_fail) {
|
||||
ASSERT_FALSE(CoreProvisioningRequestFromMessage(absl::HexStringToBytes(tc),
|
||||
&request));
|
||||
}
|
||||
void PrintTo(const TestParameters_18V0& p, std::ostream* os) {
|
||||
*os << "request = " << p.message << ", expected : {version = v"
|
||||
<< p.expected_api_major_version << "." << p.expected_api_minor_version
|
||||
<< ", nonce = " << p.expected_nonce
|
||||
<< ", session_id = " << p.expected_session_id
|
||||
<< ", master_generation_number = " << p.expected_master_generation_number
|
||||
<< "}";
|
||||
}
|
||||
|
||||
class ProvisioningRoundTripTest_18V0
|
||||
: public ::testing::Test,
|
||||
public ::testing::WithParamInterface<TestParameters_18V0> {};
|
||||
|
||||
// Make sure that the first version of the V18 provisioning request (no hidden
|
||||
// 4-byte value, all 0s in message counter struct) will still parse with
|
||||
// current v18 code. This test is in this file rather than odk_test.cpp
|
||||
// because of the use of absl::HexStringToBytes
|
||||
TEST_P(ProvisioningRoundTripTest_18V0, ProvisioningRoundtrip) {
|
||||
TestParameters_18V0 tc = GetParam();
|
||||
|
||||
ODK_ProvisioningRequest request;
|
||||
|
||||
// Make sure we can parse the request
|
||||
ASSERT_TRUE(CoreProvisioningRequestFromMessage(
|
||||
absl::HexStringToBytes(tc.message), &request));
|
||||
EXPECT_EQ(request.api_minor_version, tc.expected_api_minor_version);
|
||||
EXPECT_EQ(request.api_major_version, tc.expected_api_major_version);
|
||||
EXPECT_EQ(request.nonce, tc.expected_nonce);
|
||||
EXPECT_EQ(request.session_id, tc.expected_session_id);
|
||||
|
||||
if (request.api_major_version >= 18) {
|
||||
EXPECT_EQ(request.counter_info.master_generation_number,
|
||||
tc.expected_master_generation_number);
|
||||
}
|
||||
|
||||
// Make sure we can create a response from that request with the same core
|
||||
// message
|
||||
const CoreMessageFeatures features =
|
||||
CoreMessageFeatures::DefaultFeatures(ODK_MAJOR_VERSION);
|
||||
std::string serialized_provisioning_resp;
|
||||
video_widevine::ProvisioningResponse provisioning_response;
|
||||
provisioning_response.set_device_certificate("device_certificate");
|
||||
provisioning_response.set_device_rsa_key("device_rsa_key");
|
||||
provisioning_response.set_device_rsa_key_iv("device_rsa_key_iv");
|
||||
if (!provisioning_response.SerializeToString(&serialized_provisioning_resp)) {
|
||||
FAIL() << "Cannot set up prov response";
|
||||
}
|
||||
std::string oemcrypto_core_message;
|
||||
EXPECT_TRUE(CreateCoreProvisioningResponseFromProto(
|
||||
features, serialized_provisioning_resp, request,
|
||||
OEMCrypto_RSA_Private_Key, &oemcrypto_core_message));
|
||||
|
||||
// Extract core message from generated prov response and match values with
|
||||
// request
|
||||
ODK_CommonRequest odk_common_request;
|
||||
ASSERT_TRUE(CoreCommonRequestFromMessage(oemcrypto_core_message,
|
||||
&odk_common_request));
|
||||
EXPECT_EQ(odk_common_request.message_type, 6u);
|
||||
EXPECT_EQ(odk_common_request.nonce, tc.expected_nonce);
|
||||
EXPECT_EQ(odk_common_request.session_id, tc.expected_session_id);
|
||||
}
|
||||
|
||||
std::vector<TestParameters_18V0> TestCases() {
|
||||
return std::vector<TestParameters_18V0>{
|
||||
// Source: ODKTest ProvisionRequestRoundtrip running on v18.0 ODK checkout
|
||||
{"000000050000005a00000012deadbeefcafebabe12345678abcdffff0000000c0000003"
|
||||
"200000154001200000004ffffffffffffffffffffffffffffffffdddddddddddddddddd"
|
||||
"ddddddddddddddeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
0, 18, 0xdeadbeef, 0xcafebabe, 0x12345678abcdffff},
|
||||
// same as previous request, but replace counter info with all 0s
|
||||
{"000000050000005a00000012deadbeefcafebabe0000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000",
|
||||
0, 18, 0xdeadbeef, 0xcafebabe, 0x0},
|
||||
// Source: ODKTest ProvisionRequestRoundtrip running on v17.2 ODK checkout
|
||||
{"000000050000005800020011deadbeefcafebabe00000020fffffffffffffffffffffff"
|
||||
"fffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000"
|
||||
"0000000000000000000000000000000000",
|
||||
2, 17, 0xdeadbeef, 0xcafebabe, 0x0},
|
||||
// Source: ODKTest ProvisionRequestRoundtrip running on v18.2 ODK checkout
|
||||
{"000000050000005e00020012deadbeefcafebabe0000004012345678abcdffff0000000"
|
||||
"c0000003200000154001200020004ffffffffffffffffffffffffffffffffdddddddddd"
|
||||
"ddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeee",
|
||||
2, 18, 0xdeadbeef, 0xcafebabe, 0x12345678abcdffff},
|
||||
// Source: CDM unit tests on oemcrypto-v18 internal commit 5c77383 (pre
|
||||
// v18.0 -> v18.1 ODK bump)
|
||||
{"000000050000005a00000012b85dfa09000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000",
|
||||
0, 18, 0xb85dfa09, 0x0, 0x0},
|
||||
// Same as above but non-zero counter info
|
||||
{"000000050000005a00000012b85dfa09000000001000000000000001000000000000000"
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
"00000000000000000000000000000000000000",
|
||||
0, 18, 0xb85dfa09, 0x0, 0x1000000000000001},
|
||||
// Source: CDM unit tests on oemcrypto-v18 internal commit fc46827a (post
|
||||
// v18.0 -> v18.1 ODK bump)
|
||||
{"000000050000005e000100127c8ac703000000000000004000000000000000000000000"
|
||||
"00000000000000000001200010000746573740000000000000000000000007465737400"
|
||||
"0000000000000000000000000000000000000000000000",
|
||||
1, 18, 0x7c8ac703, 0x0, 0x0},
|
||||
};
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ProvisioningRoundTripTests_18V0,
|
||||
ProvisioningRoundTripTest_18V0,
|
||||
::testing::ValuesIn(TestCases()));
|
||||
|
||||
} // namespace wvodk_test
|
||||
|
||||
@@ -1105,20 +1105,21 @@ std::vector<VersionParameters> TestCases() {
|
||||
// number.
|
||||
{16, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 16, 5},
|
||||
{17, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 17, 2},
|
||||
{18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 1},
|
||||
{18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 2},
|
||||
// Here are some known good versions. Make extra sure they work.
|
||||
{ODK_MAJOR_VERSION, 16, 3, 16, 3},
|
||||
{ODK_MAJOR_VERSION, 16, 4, 16, 4},
|
||||
{ODK_MAJOR_VERSION, 16, 5, 16, 5},
|
||||
{ODK_MAJOR_VERSION, 17, 1, 17, 1},
|
||||
{ODK_MAJOR_VERSION, 17, 2, 17, 2},
|
||||
{ODK_MAJOR_VERSION, 18, 0, 18, 0},
|
||||
{ODK_MAJOR_VERSION, 18, 1, 18, 1},
|
||||
{ODK_MAJOR_VERSION, 18, 2, 18, 2},
|
||||
{0, 16, 3, 16, 3},
|
||||
{0, 16, 4, 16, 4},
|
||||
{0, 16, 5, 16, 5},
|
||||
{0, 17, 1, 17, 1},
|
||||
{0, 17, 2, 17, 2},
|
||||
{0, 18, 0, 17, 2}, // Change to 18 when the default version is updated.
|
||||
{0, 18, 2, 17, 2}, // Change to 18 when the default version is updated.
|
||||
};
|
||||
return test_cases;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user