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:
Matt Feddersen
2023-04-12 09:00:41 -07:00
committed by Robert Shih
parent c6e7c70a6b
commit a2a27c44ef
7 changed files with 147 additions and 68 deletions

View File

@@ -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

View File

@@ -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;
}