OEMCrypto Tests Provisioning Method
Merge from widevine repo of http://go/wvgerrit/21682 This CL updates oemcrypto/test/oec_device_features.cpp to figure out the provisioning method and filter out tests that are not relevant to the device's method. This CL also introduces unit tests for GetOEMPublicCertificate. Unit tests for RewrapDeviceRSAKey30 will be in a future CL. Change-Id: Ib7065ce866d1171ca61b9aa08188fa2ac8d90fc2
This commit is contained in:
@@ -31,23 +31,43 @@ void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
uint32_t nonce = 0;
|
||||
uint8_t buffer[1];
|
||||
size_t size = 0;
|
||||
provisioning_method = OEMCrypto_GetProvisioningMethod();
|
||||
printf("provisioning_method = %s\n",
|
||||
ProvisioningMethodName(provisioning_method));
|
||||
uses_keybox =
|
||||
(OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_GetKeyData(buffer, &size));
|
||||
printf("uses_keybox = %s.\n", uses_keybox ? "true" : "false");
|
||||
loads_certificate = uses_keybox && (OEMCrypto_ERROR_NOT_IMPLEMENTED !=
|
||||
OEMCrypto_RewrapDeviceRSAKey(
|
||||
0, buffer, 0, buffer, 0, &nonce,
|
||||
buffer, 0, buffer, buffer, &size));
|
||||
OEMCrypto_SESSION session;
|
||||
OEMCryptoResult result = OEMCrypto_OpenSession(&session);
|
||||
if (result != OEMCrypto_SUCCESS) {
|
||||
printf("--- ERROR: Could not open session: %d ----\n", result);
|
||||
}
|
||||
// If the device uses a keybox, check to see if loading a certificate is
|
||||
// installed.
|
||||
if (provisioning_method == OEMCrypto_Keybox) {
|
||||
loads_certificate =
|
||||
(OEMCrypto_ERROR_NOT_IMPLEMENTED !=
|
||||
OEMCrypto_RewrapDeviceRSAKey(session, buffer, 0, buffer, 0, &nonce,
|
||||
buffer, 0, buffer, buffer, &size));
|
||||
} else if (provisioning_method == OEMCrypto_OEMCertificate) {
|
||||
// If the device says it uses Provisioning 3.0, then it should be able to
|
||||
// load a DRM certificate. These devices must support RewrapDeviceRSAKey30.
|
||||
loads_certificate = true;
|
||||
} else {
|
||||
// Other devices are either broken, or they have a baked in certificate.
|
||||
loads_certificate = false;
|
||||
}
|
||||
printf("loads_certificate = %s.\n", loads_certificate ? "true" : "false");
|
||||
uses_certificate = (OEMCrypto_ERROR_NOT_IMPLEMENTED !=
|
||||
OEMCrypto_GenerateRSASignature(0, buffer, 0, buffer,
|
||||
OEMCrypto_GenerateRSASignature(session, buffer, 0, buffer,
|
||||
&size, kSign_RSASSA_PSS));
|
||||
printf("uses_certificate = %s.\n", uses_certificate ? "true" : "false");
|
||||
generic_crypto =
|
||||
(OEMCrypto_ERROR_NOT_IMPLEMENTED !=
|
||||
OEMCrypto_Generic_Encrypt(0, buffer, 0, buffer,
|
||||
OEMCrypto_Generic_Encrypt(session, buffer, 0, buffer,
|
||||
OEMCrypto_AES_CBC_128_NO_PADDING, buffer));
|
||||
printf("generic_crypto = %s.\n", generic_crypto ? "true" : "false");
|
||||
OEMCrypto_CloseSession(session);
|
||||
api_version = OEMCrypto_APIVersion();
|
||||
printf("api_version = %d.\n", api_version);
|
||||
usage_table = OEMCrypto_SupportsUsageTable();
|
||||
@@ -80,6 +100,9 @@ void DeviceFeatures::Initialize(bool is_cast_receiver,
|
||||
case FORCE_TEST_KEYBOX:
|
||||
printf("FORCE_TEST_KEYBOX: User requested calling InstallKeybox.\n");
|
||||
break;
|
||||
case TEST_PROVISION_30:
|
||||
printf("TEST_PROVISION_30: Device provisioed with OEM Cert.\n");
|
||||
break;
|
||||
}
|
||||
OEMCrypto_Terminate();
|
||||
}
|
||||
@@ -95,8 +118,11 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
|
||||
if (!cast_receiver) FilterOut(&filter, "*CastReceiver*");
|
||||
if (!usage_table) FilterOut(&filter, "*UsageTable*");
|
||||
if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*");
|
||||
if (provisioning_method
|
||||
!= OEMCrypto_OEMCertificate) FilterOut(&filter, "*Prov30*");
|
||||
if (api_version < 10) FilterOut(&filter, "*API10*");
|
||||
if (api_version < 11) FilterOut(&filter, "*API11*");
|
||||
if (api_version < 12) FilterOut(&filter, "*API12*");
|
||||
// Performance tests take a long time. Filter them out if they are not
|
||||
// specifically requested.
|
||||
if (filter.find("Performance") == std::string::npos) {
|
||||
@@ -106,6 +132,27 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) {
|
||||
}
|
||||
|
||||
void DeviceFeatures::PickDerivedKey() {
|
||||
if (api_version >= 12) {
|
||||
switch (provisioning_method) {
|
||||
case OEMCrypto_OEMCertificate:
|
||||
derive_key_method = TEST_PROVISION_30;
|
||||
return;
|
||||
case OEMCrypto_DrmCertificate:
|
||||
if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) {
|
||||
derive_key_method = LOAD_TEST_RSA_KEY;
|
||||
}
|
||||
return;
|
||||
case OEMCrypto_Keybox:
|
||||
// Fall through to api_version < 12 case.
|
||||
break;
|
||||
case OEMCrypto_ProvisioningError:
|
||||
printf(
|
||||
"ERROR: OEMCrypto_GetProvisioningMethod() returns "
|
||||
"OEMCrypto_ProvisioningError\n");
|
||||
// Then fall through to api_version < 12 case.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (uses_keybox) {
|
||||
// If device uses a keybox, try to load the test keybox.
|
||||
if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestKeybox()) {
|
||||
@@ -145,4 +192,17 @@ void DeviceFeatures::FilterOut(std::string* current_filter,
|
||||
}
|
||||
}
|
||||
|
||||
const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method) {
|
||||
switch (method) {
|
||||
case OEMCrypto_ProvisioningError:
|
||||
return "OEMCrypto_ProvisioningError";
|
||||
case OEMCrypto_DrmCertificate:
|
||||
return "OEMCrypto_DrmCertificate";
|
||||
case OEMCrypto_Keybox:
|
||||
return "OEMCrypto_Keybox";
|
||||
case OEMCrypto_OEMCertificate:
|
||||
return "OEMCrypto_OEMCertificate";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
@@ -16,6 +16,7 @@ class DeviceFeatures {
|
||||
LOAD_TEST_RSA_KEY, // Call LoadTestRSAKey before deriving keys.
|
||||
EXISTING_TEST_KEYBOX, // Keybox is already the test keybox.
|
||||
FORCE_TEST_KEYBOX, // User requested calling InstallKeybox.
|
||||
TEST_PROVISION_30, // Device has OEM Certificate installed.
|
||||
};
|
||||
|
||||
enum DeriveMethod derive_key_method;
|
||||
@@ -26,6 +27,7 @@ class DeviceFeatures {
|
||||
bool cast_receiver; // Device supports alternate rsa signature padding.
|
||||
bool usage_table; // Device saves usage information.
|
||||
uint32_t api_version;
|
||||
OEMCrypto_ProvisioningMethod provisioning_method;
|
||||
|
||||
void Initialize(bool is_cast_receiver, bool force_load_test_keybox);
|
||||
std::string RestrictFilter(const std::string& initial_filter);
|
||||
@@ -37,6 +39,7 @@ class DeviceFeatures {
|
||||
};
|
||||
|
||||
extern DeviceFeatures global_features;
|
||||
const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method);
|
||||
|
||||
} // namespace wvoec
|
||||
|
||||
|
||||
@@ -531,6 +531,54 @@ void Session::TestDecryptCTR(bool select_key_first,
|
||||
}
|
||||
}
|
||||
|
||||
void Session::LoadOEMCert(bool verify_cert) {
|
||||
vector<uint8_t> public_cert;
|
||||
size_t public_cert_length = 0;
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER,
|
||||
OEMCrypto_GetOEMPublicCertificate(session_id(), NULL,
|
||||
&public_cert_length));
|
||||
ASSERT_GT(public_cert_length, 0);
|
||||
public_cert.resize(public_cert_length);
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS,
|
||||
OEMCrypto_GetOEMPublicCertificate(session_id(), &public_cert[0],
|
||||
&public_cert_length));
|
||||
// load the cert into rsa_key_.
|
||||
openssl_ptr<BIO, BIO_vfree> bio(
|
||||
BIO_new_mem_buf(&public_cert[0], public_cert_length));
|
||||
ASSERT_TRUE(bio.NotNull());
|
||||
openssl_ptr<X509, X509_free> cert(
|
||||
PEM_read_bio_X509(bio.get(), NULL, 0, NULL));
|
||||
ASSERT_TRUE(cert.NotNull());
|
||||
openssl_ptr<EVP_PKEY, EVP_PKEY_free> pubkey(X509_get_pubkey(cert.get()));
|
||||
ASSERT_TRUE(pubkey.NotNull());
|
||||
public_rsa_ = EVP_PKEY_get1_RSA(pubkey.get());
|
||||
if (!public_rsa_) {
|
||||
cout << "d2i_RSAPrivateKey failed.\n";
|
||||
dump_openssl_error();
|
||||
ASSERT_TRUE(NULL != public_rsa_);
|
||||
}
|
||||
if (verify_cert) {
|
||||
vector<char> buffer(80);
|
||||
X509_NAME* name = X509_get_subject_name(cert.get());
|
||||
printf(" OEM Certificate Name: %s\n",
|
||||
X509_NAME_oneline(name, &buffer[0], buffer.size()));
|
||||
openssl_ptr<X509_STORE, X509_STORE_free> store(X509_STORE_new());
|
||||
ASSERT_TRUE(store.NotNull());
|
||||
openssl_ptr<X509_STORE_CTX, X509_STORE_CTX_free> store_ctx(
|
||||
X509_STORE_CTX_new());
|
||||
ASSERT_TRUE(store_ctx.NotNull());
|
||||
X509_STORE_CTX_init(store_ctx.get(), store.get(), cert.get(), NULL);
|
||||
// TODO(fredgc): Verify cert is signed by Google.
|
||||
int result = X509_verify_cert(store_ctx.get());
|
||||
ASSERT_GE(0, result) << " OEM Cert not valid. "
|
||||
<< X509_verify_cert_error_string(store_ctx->error);
|
||||
if (result == 0) {
|
||||
printf("Cert not verified: %s.\n",
|
||||
X509_verify_cert_error_string(store_ctx->error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted,
|
||||
size_t message_size,
|
||||
std::vector<uint8_t>* signature,
|
||||
|
||||
@@ -203,6 +203,9 @@ class Session {
|
||||
void TestDecryptCTR(bool select_key_first = true,
|
||||
OEMCryptoResult expected_result = OEMCrypto_SUCCESS,
|
||||
int key_index = 0);
|
||||
// Calls OEMCrypto_GetOEMPublicCertificate and loads the OEM cert's public
|
||||
// rsa key into public_rsa_.
|
||||
void LoadOEMCert(bool verify_cert = false);
|
||||
// Creates RSAPrivateKeyMessage for the specified rsa_key, encrypts it with
|
||||
// the specified encryption key, and then signs it with the server's mac key.
|
||||
// If encryption_key is null, use the session's enc_key_.
|
||||
|
||||
@@ -90,7 +90,15 @@ TEST_F(OEMCryptoClientTest, VersionNumber) {
|
||||
cout << " OEMCrypto does not support usage tables." << endl;
|
||||
}
|
||||
ASSERT_GE(version, 8u);
|
||||
ASSERT_LE(version, 11u);
|
||||
ASSERT_LE(version, 12u);
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) {
|
||||
OEMCrypto_ProvisioningMethod provisioning_method =
|
||||
OEMCrypto_GetProvisioningMethod();
|
||||
cout << " Provisioning method = "
|
||||
<< ProvisioningMethodName(provisioning_method) << endl;
|
||||
ASSERT_NE(OEMCrypto_ProvisioningError, provisioning_method);
|
||||
}
|
||||
|
||||
const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) {
|
||||
@@ -473,6 +481,102 @@ TEST_F(OEMCryptoKeyboxTest, GenerateDerivedKeysFromKeyboxLargeBuffer) {
|
||||
enc_context.size()));
|
||||
}
|
||||
|
||||
class OEMCryptoProv30Test : public OEMCryptoClientTest {};
|
||||
|
||||
TEST_F(OEMCryptoProv30Test, DeviceClaimsOEMCertificate) {
|
||||
ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod());
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv30Test, OEMCertValid) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
bool kVerify = true;
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify.
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv30Test, OEMCertSignature) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert());
|
||||
OEMCryptoResult sts;
|
||||
// Sign a Message
|
||||
vector<uint8_t> data(500);
|
||||
RAND_pseudo_bytes(&data[0], data.size());
|
||||
size_t signature_length = 0;
|
||||
vector<uint8_t> signature(1);
|
||||
|
||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(),
|
||||
&signature[0], &signature_length,
|
||||
kSign_RSASSA_PSS);
|
||||
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
ASSERT_NE(static_cast<size_t>(0), signature_length);
|
||||
signature.resize(signature_length, 0);
|
||||
|
||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(),
|
||||
&signature[0], &signature_length,
|
||||
kSign_RSASSA_PSS);
|
||||
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
|
||||
data, &signature[0], signature_length, kSign_RSASSA_PSS));
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert());
|
||||
OEMCryptoResult sts;
|
||||
// Sign a Message
|
||||
vector<uint8_t> data(500);
|
||||
RAND_pseudo_bytes(&data[0], data.size());
|
||||
size_t signature_length = 0;
|
||||
vector<uint8_t> signature(1);
|
||||
|
||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(),
|
||||
&signature[0], &signature_length,
|
||||
kSign_PKCS1_Block1);
|
||||
if (OEMCrypto_ERROR_SHORT_BUFFER == sts) {
|
||||
signature.resize(signature_length, 0);
|
||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(),
|
||||
&signature[0], &signature_length,
|
||||
kSign_PKCS1_Block1);
|
||||
}
|
||||
EXPECT_NE(OEMCrypto_SUCCESS, sts)
|
||||
<< "OEM Cert Signed with forbidden kSign_PKCS1_Block1.";
|
||||
vector<uint8_t> zero(signature_length, 0);
|
||||
ASSERT_EQ(zero, signature); // signature should not be computed.
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoProv30Test, OEMCertSignatureLargeBuffer) {
|
||||
Session s;
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert());
|
||||
OEMCryptoResult sts;
|
||||
// Sign a Message
|
||||
static size_t kMaxMessageSize = 8 * 1024;
|
||||
vector<uint8_t> data(kMaxMessageSize);
|
||||
RAND_pseudo_bytes(&data[0], data.size());
|
||||
size_t signature_length = 0;
|
||||
vector<uint8_t> signature(1);
|
||||
|
||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(),
|
||||
&signature[0], &signature_length,
|
||||
kSign_RSASSA_PSS);
|
||||
|
||||
ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts);
|
||||
ASSERT_NE(static_cast<size_t>(0), signature_length);
|
||||
signature.resize(signature_length);
|
||||
|
||||
sts = OEMCrypto_GenerateRSASignature(s.session_id(), &data[0], data.size(),
|
||||
&signature[0], &signature_length,
|
||||
kSign_RSASSA_PSS);
|
||||
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, sts);
|
||||
ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(
|
||||
data, &signature[0], signature_length, kSign_RSASSA_PSS));
|
||||
}
|
||||
|
||||
//
|
||||
// AddKey Tests
|
||||
//
|
||||
@@ -968,8 +1072,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithFutureVerification) {
|
||||
ASSERT_NO_FATAL_FAILURE(s.open());
|
||||
ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s));
|
||||
ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0));
|
||||
// OEMCrypto should reject API12 until the spec has been defined.
|
||||
memcpy(s.license().keys[1].control.verification, "kc12", 4);
|
||||
// OEMCrypto should reject API13 until the spec has been defined.
|
||||
memcpy(s.license().keys[1].control.verification, "kc13", 4);
|
||||
ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign());
|
||||
OEMCryptoResult sts = OEMCrypto_LoadKeys(
|
||||
s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0],
|
||||
|
||||
@@ -21,21 +21,21 @@ namespace wvoec {
|
||||
// These tests are required for LollyPop Android devices.
|
||||
class OEMCryptoAndroidLMPTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize());
|
||||
}
|
||||
virtual void SetUp() { ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); }
|
||||
|
||||
virtual void TearDown() {
|
||||
OEMCrypto_Terminate();
|
||||
}
|
||||
virtual void TearDown() { OEMCrypto_Terminate(); }
|
||||
};
|
||||
|
||||
// Android devices must have a keybox.
|
||||
// Android devices must have a keybox, or use provisioning 3.0.
|
||||
TEST_F(OEMCryptoAndroidLMPTest, GetKeyDataImplemented) {
|
||||
uint8_t key_data[256];
|
||||
size_t key_data_len = sizeof(key_data);
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_GetKeyData(key_data, &key_data_len));
|
||||
if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_GetKeyData(key_data, &key_data_len));
|
||||
} else {
|
||||
ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoAndroidLMPTest, MinVersionNumber9) {
|
||||
@@ -48,9 +48,15 @@ TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) {
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoAndroidLMPTest, RewrapDeviceRSAKeyImplemented) {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_RewrapDeviceRSAKey(0, NULL, 0, NULL, 0, NULL,
|
||||
NULL, 0, NULL, NULL, NULL));
|
||||
if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_RewrapDeviceRSAKey(0, NULL, 0, NULL, 0, NULL, NULL, 0,
|
||||
NULL, NULL, NULL));
|
||||
} else {
|
||||
ASSERT_NE(OEMCrypto_ERROR_NOT_IMPLEMENTED,
|
||||
OEMCrypto_RewrapDeviceRSAKey30(0, NULL, NULL, 0, NULL, 0, NULL,
|
||||
NULL, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoAndroidLMPTest, RSASignatureImplemented) {
|
||||
@@ -96,7 +102,12 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) {
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox());
|
||||
if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) {
|
||||
ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox());
|
||||
} else {
|
||||
// Android should use keybox or provisioning 3.0.
|
||||
ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(OEMCryptoAndroidMNCTest, NumberOfSessionsImplemented) {
|
||||
|
||||
Reference in New Issue
Block a user