blob: c385d33c4a447543822a10b010c6f3c9f13cfb58 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include <tuple>
#include <vector>
#include <base/bind.h>
#include <chaps/attributes.h>
#include <chaps/chaps_proxy_mock.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include "cryptohome/cert_provision.h"
#include "cryptohome/cert_provision_cryptohome.h"
#include "cryptohome/cert_provision_keystore.h"
#include "cryptohome/cert_provision_pca.h"
#include "cryptohome/cert_provision_util.h"
#include "cryptohome/mock_cert_provision_cryptohome.h"
#include "cryptohome/mock_cert_provision_keystore.h"
#include "cryptohome/mock_cert_provision_pca.h"
#include "cert_provision.pb.h" // NOLINT(build/include)
using ::brillo::SecureBlob;
using ::testing::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
namespace {
// Some arbitrary certificate label used for testing.
const char kCertLabel[] = "test";
// Some non-zero handles for PKCS#11 session and object.
const CK_SESSION_HANDLE kSession = 17;
const CK_OBJECT_HANDLE kObject = 112;
} // namespace
namespace cert_provision {
// Test class for testing KeyStoreImpl.
class CertProvisionKeyStoreTest : public testing::Test {
public:
using Pkcs11Mock = chaps::ChapsProxyMock;
CertProvisionKeyStoreTest() = default;
void SetUp() {
ON_CALL(pkcs11_, OpenSession(_, _, _, _))
.WillByDefault(DoAll(SetArgPointee<3>(kSession), Return(0)));
ON_CALL(pkcs11_, FindObjects(_, _, _, _))
.WillByDefault(Invoke(this, &CertProvisionKeyStoreTest::FindObjects));
ON_CALL(pkcs11_, GetAttributeValue(_, _, _, _, _))
.WillByDefault(
Invoke(this, &CertProvisionKeyStoreTest::GetAttributeValue));
ON_CALL(pkcs11_, CreateObject(_, _, _, _))
.WillByDefault(Invoke(this, &CertProvisionKeyStoreTest::CreateObject));
ON_CALL(pkcs11_, Sign(_, _, _, _, _, _))
.WillByDefault(Invoke(this, &CertProvisionKeyStoreTest::Sign));
}
// Mocks looking for objects.
uint32_t FindObjects(const SecureBlob& /* isolate_credential */,
uint64_t /* session_id */,
uint64_t max_object_count,
std::vector<uint64_t>* objects) {
while (!found_objects_.empty() && objects->size() < max_object_count) {
objects->push_back(found_objects_.back());
found_objects_.pop_back();
}
return CKR_OK;
}
// Mocks getting CKA_VALUE for an object.
uint32_t GetAttributeValue(const SecureBlob& /* isolate_credential */,
uint64_t /* session_id */,
uint64_t /* object_handle */,
const std::vector<uint8_t>& attributes_in,
std::vector<uint8_t>* attributes_out) {
chaps::Attributes chaps_attr;
chaps_attr.Parse(attributes_in);
CK_ATTRIBUTE* attr = chaps_attr.attributes();
if (chaps_attr.num_attributes() != 1 || attr->type != CKA_VALUE) {
return CKR_GENERAL_ERROR;
}
if (attr->pValue) {
if (attr->ulValueLen == object_value_.size()) {
memcpy(attr->pValue, object_value_.data(), object_value_.size());
} else {
return CKR_GENERAL_ERROR;
}
}
attr->ulValueLen = object_value_.size();
chaps_attr.Serialize(attributes_out);
return CKR_OK;
}
// Mocks creating an object.
uint32_t CreateObject(const SecureBlob& /* isolate_credential */,
uint64_t /* session_id */,
const std::vector<uint8_t>& attributes,
uint64_t* new_object_handle) {
*new_object_handle = kObject;
object_value_.clear();
chaps::Attributes chaps_attr;
chaps_attr.Parse(attributes);
CK_ATTRIBUTE* attr = chaps_attr.attributes();
for (CK_ULONG i = 0; i < chaps_attr.num_attributes(); ++i, ++attr) {
if (attr->type == CKA_VALUE) {
object_value_.assign(reinterpret_cast<char*>(attr->pValue),
attr->ulValueLen);
break;
}
}
return CKR_OK;
}
uint32_t Sign(const brillo::SecureBlob& /* isolate_credential */,
uint64_t /* session_id */,
const std::vector<uint8_t>& /* data */,
uint64_t /* max_out_length */,
uint64_t* actual_out_length,
std::vector<uint8_t>* signature) {
*actual_out_length = signature_.size();
uint8_t* sig_data =
reinterpret_cast<uint8_t*>(const_cast<char*>(signature_.data()));
signature->assign(sig_data, sig_data + signature_.size());
return CKR_OK;
}
protected:
NiceMock<Pkcs11Mock> pkcs11_{false /* do not pre-initialize */};
KeyStoreImpl key_store_;
// Objects to return from C_FindObjects.
std::vector<uint64_t> found_objects_;
// CKA_VALUE attribute for getting/setting.
std::string object_value_;
// Signature returned by Sign().
std::string signature_;
private:
DISALLOW_COPY_AND_ASSIGN(CertProvisionKeyStoreTest);
};
// Checks that initialization succeeds under normal conditions
// and that the session is closed on deleting the KeyStoreImpl object.
TEST_F(CertProvisionKeyStoreTest, InitSuccess) {
EXPECT_TRUE(key_store_.Init());
EXPECT_CALL(pkcs11_, CloseSession(_, _)).WillOnce(Return(0));
}
// Checks that initialization fails, if session cannot be opened.
TEST_F(CertProvisionKeyStoreTest, InitFailureSession) {
EXPECT_CALL(pkcs11_, OpenSession(_, _, _, _))
.WillOnce(Return(CKR_GENERAL_ERROR));
EXPECT_FALSE(key_store_.Init());
EXPECT_CALL(pkcs11_, CloseSession(_, _)).Times(0);
key_store_.TearDown();
}
// Checks that reading an existing ProvisionStatus returns the right data.
TEST_F(CertProvisionKeyStoreTest, ReadProvisionStatusSuccessExists) {
EXPECT_TRUE(key_store_.Init());
ProvisionStatus provision_status_stored;
provision_status_stored.set_key_id("100"); // some random value
provision_status_stored.SerializeToString(&object_value_);
found_objects_.assign({kObject});
// Expect to be called twice - once to get the size, then the value itself.
EXPECT_CALL(pkcs11_, GetAttributeValue(_, kSession, kObject, _, _)).Times(2);
ProvisionStatus provision_status;
EXPECT_TRUE(key_store_.ReadProvisionStatus(kCertLabel, &provision_status));
EXPECT_EQ(provision_status_stored.key_id(), provision_status.key_id());
}
// Checks that if there is no ProvisionStatus object, an empty proto is
// returned.
TEST_F(CertProvisionKeyStoreTest, ReadProvisionStatusSuccessNew) {
EXPECT_TRUE(key_store_.Init());
found_objects_.clear();
EXPECT_CALL(pkcs11_, GetAttributeValue(_, _, _, _, _)).Times(0);
ProvisionStatus provision_status;
EXPECT_TRUE(key_store_.ReadProvisionStatus(kCertLabel, &provision_status));
EXPECT_FALSE(provision_status.provisioned());
}
// Checks ReadProvisionStatus behavior if not initialized.
TEST_F(CertProvisionKeyStoreTest, ReadProvisionStatusFailureNotInit) {
EXPECT_CALL(pkcs11_, FindObjects(_, _, _, _)).Times(0);
EXPECT_CALL(pkcs11_, GetAttributeValue(_, _, _, _, _)).Times(0);
ProvisionStatus provision_status;
EXPECT_FALSE(key_store_.ReadProvisionStatus(kCertLabel, &provision_status));
}
// Checks ReadProvisionStatus behavior in case of GetAttribute error.
TEST_F(CertProvisionKeyStoreTest, ReadProvisionStatusFailureGet) {
EXPECT_TRUE(key_store_.Init());
found_objects_.assign({kObject});
EXPECT_CALL(pkcs11_, GetAttributeValue(_, kSession, kObject, _, _))
.WillOnce(Return(CKR_GENERAL_ERROR));
ProvisionStatus provision_status;
EXPECT_FALSE(key_store_.ReadProvisionStatus(kCertLabel, &provision_status));
}
// Checks ReadProvisionStatus behavior if there are multiple objects.
TEST_F(CertProvisionKeyStoreTest, ReadProvisionStatusFailureMultiple) {
EXPECT_TRUE(key_store_.Init());
found_objects_.assign({kObject, kObject+1});
EXPECT_CALL(pkcs11_, GetAttributeValue(_, _, _, _, _)).Times(0);
ProvisionStatus provision_status;
EXPECT_FALSE(key_store_.ReadProvisionStatus(kCertLabel, &provision_status));
}
// Checks that KeyStoreImpl saves ProvisionStatus if there was no previous
// ProvisionStatus objects.
TEST_F(CertProvisionKeyStoreTest, WriteProvisionStatusSuccessNew) {
EXPECT_TRUE(key_store_.Init());
found_objects_.clear();
EXPECT_CALL(pkcs11_, DestroyObject(_, _, _)).Times(0);
EXPECT_CALL(pkcs11_, CreateObject(_, kSession, _, _));
ProvisionStatus provision_status;
provision_status.set_key_id("200"); // some random value
EXPECT_TRUE(key_store_.WriteProvisionStatus(kCertLabel, provision_status));
EXPECT_EQ(provision_status.SerializeAsString(), object_value_);
}
// Checks that KeyStoreImpl saves ProvisionStatus if there was already some
// ProvisionStatus object(s).
TEST_F(CertProvisionKeyStoreTest, WriteProvisionStatusSuccessExist) {
EXPECT_TRUE(key_store_.Init());
found_objects_.assign({kObject+1, kObject+2});
EXPECT_CALL(pkcs11_, DestroyObject(_, kSession, kObject+1));
EXPECT_CALL(pkcs11_, DestroyObject(_, kSession, kObject+2));
EXPECT_CALL(pkcs11_, CreateObject(_, kSession, _, _));
ProvisionStatus provision_status;
provision_status.set_key_id("200"); // some random value
EXPECT_TRUE(key_store_.WriteProvisionStatus(kCertLabel, provision_status));
EXPECT_EQ(provision_status.SerializeAsString(), object_value_);
}
// Checks WriteProvisionStatus behavior if not initialized.
TEST_F(CertProvisionKeyStoreTest, WriteProvisionStatusFailureNotInit) {
EXPECT_CALL(pkcs11_, FindObjects(_, _, _, _)).Times(0);
EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _)).Times(0);
ProvisionStatus provision_status;
provision_status.set_key_id("200"); // some random value
EXPECT_FALSE(key_store_.WriteProvisionStatus(kCertLabel, provision_status));
}
// Checks WriteProvisionStatus behavior in case of CreateObject error.
TEST_F(CertProvisionKeyStoreTest, WriteProvisionStatusFailureCreate) {
EXPECT_TRUE(key_store_.Init());
EXPECT_CALL(pkcs11_, CreateObject(_, kSession, _, _))
.WillOnce(Return(CKR_GENERAL_ERROR));
ProvisionStatus provision_status;
EXPECT_FALSE(key_store_.WriteProvisionStatus(kCertLabel, provision_status));
}
// Checks that DeleteKeys succeeds if there are no objects to delete.
TEST_F(CertProvisionKeyStoreTest, DeleteKeysSuccessEmpty) {
EXPECT_TRUE(key_store_.Init());
found_objects_.clear();
EXPECT_CALL(pkcs11_, DestroyObject(_, _, _)).Times(0);
std::string key_id = "300"; // some random value
EXPECT_TRUE(key_store_.DeleteKeys(key_id, kCertLabel));
}
// Checks that DeleteKeys delets all required objects.
TEST_F(CertProvisionKeyStoreTest, DeleteKeysSuccess) {
EXPECT_TRUE(key_store_.Init());
found_objects_.assign({kObject+1, kObject+2});
EXPECT_CALL(pkcs11_, DestroyObject(_, kSession, kObject+1));
EXPECT_CALL(pkcs11_, DestroyObject(_, kSession, kObject+2));
std::string key_id = "300"; // some random value
EXPECT_TRUE(key_store_.DeleteKeys(key_id, kCertLabel));
}
// Checks DeleteKeys behavior if not initialized.
TEST_F(CertProvisionKeyStoreTest, DeleteKeysFailureNotInit) {
EXPECT_CALL(pkcs11_, FindObjects(_, _, _, _)).Times(0);
std::string key_id = "300"; // some random value
EXPECT_FALSE(key_store_.DeleteKeys(key_id, kCertLabel));
}
// Checks that DeleteKeys ignores DestroyObject errors.
TEST_F(CertProvisionKeyStoreTest, DeleteKeysSuccessDestroyError) {
EXPECT_TRUE(key_store_.Init());
found_objects_.assign({kObject});
EXPECT_CALL(pkcs11_, DestroyObject(_, kSession, kObject))
.WillOnce(Return(CKR_GENERAL_ERROR));
std::string key_id = "300"; // some random value
EXPECT_TRUE(key_store_.DeleteKeys(key_id, kCertLabel));
}
// Checks that Sign succeeds if there is a key.
TEST_F(CertProvisionKeyStoreTest, SignSuccess) {
EXPECT_TRUE(key_store_.Init());
found_objects_.assign({kObject});
std::string key_id = "400"; // some arbitrary value
std::string data = "data"; // arbitrary data to sign
signature_ = "sig-sha1";
EXPECT_CALL(pkcs11_, SignInit(_, kSession, CKM_SHA1_RSA_PKCS, _, kObject));
EXPECT_CALL(pkcs11_, Sign(_, kSession, _, _, _, _));
std::string sig;
EXPECT_TRUE(key_store_.Sign(
key_id, kCertLabel, SignMechanism::SHA1_RSA_PKCS, data, &sig));
EXPECT_EQ("sig-sha1", sig);
found_objects_.assign({kObject});
signature_ = "sig-sha256";
EXPECT_CALL(pkcs11_, SignInit(_, kSession, CKM_SHA256_RSA_PKCS, _, kObject));
EXPECT_CALL(pkcs11_, Sign(_, kSession, _, _, _, _));
sig.clear();
EXPECT_TRUE(key_store_.Sign(
key_id, kCertLabel, SignMechanism::SHA256_RSA_PKCS, data, &sig));
EXPECT_EQ("sig-sha256", sig);
}
// Checks that Sign fails if there's no key.
TEST_F(CertProvisionKeyStoreTest, SignFailureNoKey) {
EXPECT_TRUE(key_store_.Init());
found_objects_.clear();
EXPECT_CALL(pkcs11_, SignInit(_, _, _, _, _)).Times(0);
EXPECT_CALL(pkcs11_, Sign(_, _, _, _, _, _)).Times(0);
std::string key_id = "400"; // some arbitrary value
std::string data = "data"; // arbitrary data to sign
std::string sig;
EXPECT_FALSE(key_store_.Sign(
key_id, kCertLabel, SignMechanism::SHA1_RSA_PKCS, data, &sig));
}
// Checks Sign behavior if not initialized.
TEST_F(CertProvisionKeyStoreTest, SignFailureNotInit) {
found_objects_.assign({kObject});
EXPECT_CALL(pkcs11_, SignInit(_, _, _, _, _)).Times(0);
EXPECT_CALL(pkcs11_, Sign(_, _, _, _, _, _)).Times(0);
std::string key_id = "400"; // some arbitrary value
std::string data = "data"; // arbitrary data to sign
std::string sig;
EXPECT_FALSE(key_store_.Sign(
key_id, kCertLabel, SignMechanism::SHA1_RSA_PKCS, data, &sig));
}
// Checks WriteProvisionStatus behavior in case of SignInit error.
TEST_F(CertProvisionKeyStoreTest, SignFailureSignInit) {
EXPECT_TRUE(key_store_.Init());
found_objects_.assign({kObject});
EXPECT_CALL(pkcs11_, SignInit(_, kSession, CKM_SHA1_RSA_PKCS, _, kObject))
.WillOnce(Return(CKR_GENERAL_ERROR));
EXPECT_CALL(pkcs11_, Sign(_, _, _, _, _, _)).Times(0);
std::string key_id = "400"; // some arbitrary value
std::string data = "data"; // arbitrary data to sign
std::string sig;
EXPECT_FALSE(key_store_.Sign(
key_id, kCertLabel, SignMechanism::SHA1_RSA_PKCS, data, &sig));
}
// Checks WriteProvisionStatus behavior in case of SignInit error.
TEST_F(CertProvisionKeyStoreTest, SignFailureSign) {
EXPECT_TRUE(key_store_.Init());
found_objects_.assign({kObject});
EXPECT_CALL(pkcs11_, SignInit(_, kSession, CKM_SHA1_RSA_PKCS, _, kObject));
EXPECT_CALL(pkcs11_, Sign(_, kSession, _, _, _, _))
.WillOnce(Return(CKR_GENERAL_ERROR));
std::string key_id = "400"; // some arbitrary value
std::string data = "data"; // arbitrary data to sign
std::string sig;
EXPECT_FALSE(key_store_.Sign(
key_id, kCertLabel, SignMechanism::SHA1_RSA_PKCS, data, &sig));
}
} // namespace cert_provision