| // Copyright 2012 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "chaps/session.h" | |
| #include "chaps/chaps.h" | |
| #include "chaps/object.h" | |
| #include "chaps/proto_conversion.h" | |
| #include "chaps/session_impl.h" | |
| #include <iterator> | |
| #include <memory> | |
| #include <string> | |
| #include <vector> | |
| #include <base/check.h> | |
| #include <base/check_op.h> | |
| #include <base/functional/bind.h> | |
| #include <base/logging.h> | |
| #include <brillo/secure_blob.h> | |
| #include <chaps/proto_bindings/ck_structs.pb.h> | |
| #include <crypto/libcrypto-compat.h> | |
| #include <crypto/scoped_openssl_types.h> | |
| #include <dbus/chaps/dbus-constants.h> | |
| #include <gmock/gmock.h> | |
| #include <gtest/gtest.h> | |
| #include <libhwsec/frontend/chaps/mock_frontend.h> | |
| #include <libhwsec-foundation/crypto/big_num_util.h> | |
| #include <libhwsec-foundation/crypto/elliptic_curve.h> | |
| #include <libhwsec-foundation/error/testing_helper.h> | |
| #include <metrics/metrics_library_mock.h> | |
| #include <openssl/bn.h> | |
| #include <openssl/err.h> | |
| #include <openssl/evp.h> | |
| #include <openssl/rsa.h> | |
| #include "chaps/chaps_factory_impl.h" | |
| #include "chaps/chaps_factory_mock.h" | |
| #include "chaps/chaps_utility.h" | |
| #include "chaps/handle_generator_mock.h" | |
| #include "chaps/object_impl.h" | |
| #include "chaps/object_mock.h" | |
| #include "chaps/object_pool_mock.h" | |
| #include "libhwsec/factory/tpm2_simulator_factory_for_test.h" | |
| #include "libhwsec/frontend/chaps/frontend.h" | |
| using ::hwsec::TPMError; | |
| using ::hwsec_foundation::error::testing::IsOk; | |
| using ::hwsec_foundation::error::testing::ReturnError; | |
| using ::hwsec_foundation::error::testing::ReturnOk; | |
| using ::hwsec_foundation::error::testing::ReturnValue; | |
| using ::hwsec_foundation::status::MakeStatus; | |
| using ::hwsec_foundation::status::OkStatus; | |
| using ::hwsec_foundation::status::StatusChain; | |
| using ::std::string; | |
| using ::std::vector; | |
| using ::testing::_; | |
| using ::testing::AnyNumber; | |
| using ::testing::Invoke; | |
| using ::testing::InvokeWithoutArgs; | |
| using ::testing::Return; | |
| using ::testing::ReturnRef; | |
| using ::testing::SetArgPointee; | |
| using ::testing::StrictMock; | |
| using Result = chaps::ObjectPool::Result; | |
| using TPMRetryAction = ::hwsec::TPMRetryAction; | |
| namespace { | |
| hwsec::ECCPublicInfo GenerateECCPublicInfo() { | |
| hwsec_foundation::ScopedBN_CTX context = | |
| hwsec_foundation::CreateBigNumContext(); | |
| std::optional<hwsec_foundation::EllipticCurve> ec_256 = | |
| hwsec_foundation::EllipticCurve::Create( | |
| hwsec_foundation::EllipticCurve::CurveType::kPrime256, context.get()); | |
| CHECK(ec_256.has_value()); | |
| crypto::ScopedEC_KEY key = ec_256->GenerateKey(context.get()); | |
| crypto::ScopedBIGNUM x(BN_new()), y(BN_new()); | |
| CHECK_NE(x, nullptr); | |
| CHECK_NE(y, nullptr); | |
| const EC_POINT* ec_point = EC_KEY_get0_public_key(key.get()); | |
| CHECK_NE(ec_point, nullptr); | |
| CHECK(EC_POINT_get_affine_coordinates_GFp(ec_256->GetGroup(), ec_point, | |
| x.get(), y.get(), nullptr)); | |
| brillo::Blob x_point = | |
| brillo::BlobFromString(chaps::ConvertFromBIGNUM(x.get())); | |
| brillo::Blob y_point = | |
| brillo::BlobFromString(chaps::ConvertFromBIGNUM(y.get())); | |
| return hwsec::ECCPublicInfo{ | |
| .nid = NID_X9_62_prime256v1, | |
| .x_point = x_point, | |
| .y_point = y_point, | |
| }; | |
| } | |
| void ConfigureObjectPool(chaps::ObjectPoolMock* op, int handle_base) { | |
| op->SetupFake(handle_base); | |
| EXPECT_CALL(*op, Insert(_)).Times(AnyNumber()); | |
| EXPECT_CALL(*op, Find(_, _)).Times(AnyNumber()); | |
| EXPECT_CALL(*op, FindByHandle(_, _)).Times(AnyNumber()); | |
| EXPECT_CALL(*op, Delete(_)).Times(AnyNumber()); | |
| EXPECT_CALL(*op, Flush(_)).WillRepeatedly(Return(Result::Success)); | |
| } | |
| chaps::ObjectPool* CreateObjectPoolMock() { | |
| chaps::ObjectPoolMock* op = new chaps::ObjectPoolMock(); | |
| ConfigureObjectPool(op, 100); | |
| return op; | |
| } | |
| chaps::Object* CreateObjectMock() { | |
| chaps::ObjectMock* o = new chaps::ObjectMock(); | |
| o->SetupFake(); | |
| EXPECT_CALL(*o, GetObjectClass()).Times(AnyNumber()); | |
| EXPECT_CALL(*o, SetAttributes(_, _)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, FinalizeNewObject()).WillRepeatedly(Return(CKR_OK)); | |
| EXPECT_CALL(*o, Copy(_)).WillRepeatedly(Return(CKR_OK)); | |
| EXPECT_CALL(*o, IsTokenObject()).Times(AnyNumber()); | |
| EXPECT_CALL(*o, IsAttributePresent(_)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, GetAttributeString(_)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, GetAttributeInt(_, _)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, GetAttributeBool(_, _)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, SetAttributeString(_, _)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, SetAttributeInt(_, _)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, SetAttributeBool(_, _)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, set_handle(_)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, set_store_id(_)).Times(AnyNumber()); | |
| EXPECT_CALL(*o, handle()).Times(AnyNumber()); | |
| EXPECT_CALL(*o, store_id()).Times(AnyNumber()); | |
| EXPECT_CALL(*o, RemoveAttribute(_)).Times(AnyNumber()); | |
| return o; | |
| } | |
| void ConfigureHwsec(hwsec::MockChapsFrontend* hwsec) { | |
| EXPECT_CALL(*hwsec, GetRandomBlob(_)).WillRepeatedly([](size_t size) { | |
| return brillo::Blob(size); | |
| }); | |
| EXPECT_CALL(*hwsec, GetRandomSecureBlob(_)).WillRepeatedly([](size_t size) { | |
| return brillo::SecureBlob(size); | |
| }); | |
| } | |
| string bn2bin(const BIGNUM* bn) { | |
| string bin; | |
| bin.resize(BN_num_bytes(bn)); | |
| bin.resize(BN_bn2bin(bn, chaps::ConvertStringToByteBuffer(bin.data()))); | |
| return bin; | |
| } | |
| std::string GetRSAPSSParam(CK_MECHANISM_TYPE hashAlg, | |
| CK_RSA_PKCS_MGF_TYPE mgf, | |
| CK_ULONG sLen) { | |
| CK_RSA_PKCS_PSS_PARAMS params; | |
| params.hashAlg = hashAlg; | |
| params.mgf = mgf; | |
| params.sLen = sLen; | |
| std::string param_bytes(reinterpret_cast<char*>(¶ms), sizeof(params)); | |
| return param_bytes; | |
| } | |
| } // namespace | |
| namespace chaps { | |
| const char kChapsSessionDecrypt[] = "Platform.Chaps.Session.Decrypt"; | |
| const char kChapsSessionDigest[] = "Platform.Chaps.Session.Digest"; | |
| const char kChapsSessionEncrypt[] = "Platform.Chaps.Session.Encrypt"; | |
| const char kChapsSessionSign[] = "Platform.Chaps.Session.Sign"; | |
| const char kChapsSessionVerify[] = "Platform.Chaps.Session.Verify"; | |
| const char kChapsSessionDeriveKey[] = "Platform.Chaps.Session.DeriveKey"; | |
| const char kChapsSessionWrapKey[] = "Platform.Chaps.Session.WrapKey"; | |
| const char kChapsSessionWrapKeyWithChaps[] = | |
| "Platform.Chaps.Session.WrapKeyWithChaps"; | |
| const char kChapsSessionUnwrapKey[] = "Platform.Chaps.Session.UnwrapKey"; | |
| const char kChapsSessionUnwrapKeyWithChaps[] = | |
| "Platform.Chaps.Session.UnwrapKeyWithChaps"; | |
| const char kChapsRandomSeed[] = "random_seed"; | |
| // Test fixture for an initialized SessionImpl instance. | |
| class TestSession : public ::testing::Test { | |
| public: | |
| TestSession() { | |
| EXPECT_CALL(factory_, CreateObject()) | |
| .WillRepeatedly(InvokeWithoutArgs(CreateObjectMock)); | |
| EXPECT_CALL(factory_, CreateObjectPool(_, _, _)) | |
| .WillRepeatedly(InvokeWithoutArgs(CreateObjectPoolMock)); | |
| EXPECT_CALL(handle_generator_, CreateHandle()).WillRepeatedly(Return(1)); | |
| ConfigureObjectPool(&token_pool_, 0); | |
| ConfigureHwsec(&hwsec_); | |
| } | |
| void SetUp() { | |
| chaps_metrics_.set_metrics_library_for_testing(&mock_metrics_library_); | |
| session_.reset(new SessionImpl(1, &token_pool_, &hwsec_, &factory_, | |
| &handle_generator_, false, &chaps_metrics_)); | |
| } | |
| void GenerateSecretKey(CK_MECHANISM_TYPE mechanism, | |
| CK_ULONG size, | |
| const Object** obj) { | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_ATTRIBUTE encdec_template[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_ENCRYPT, &yes, sizeof(yes)}, | |
| {CKA_DECRYPT, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE signverify_template[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_SIGN, &yes, sizeof(yes)}, | |
| {CKA_VERIFY, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE_PTR attr = encdec_template; | |
| if (mechanism == CKM_GENERIC_SECRET_KEY_GEN) | |
| attr = signverify_template; | |
| int handle = 0; | |
| ASSERT_EQ(CKR_OK, session_->GenerateKey(mechanism, "", attr, 4, &handle)); | |
| ASSERT_TRUE(session_->GetObject(handle, obj)); | |
| } | |
| void GenerateRSAKeyPair(bool signing, | |
| CK_ULONG size, | |
| const Object** pub, | |
| const Object** priv) { | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_ENCRYPT, signing ? &no : &yes, sizeof(no)}, | |
| {CKA_VERIFY, signing ? &yes : &no, sizeof(no)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &no, sizeof(CK_BBOOL)}, | |
| {CKA_DECRYPT, signing ? &no : &yes, sizeof(no)}, | |
| {CKA_SIGN, signing ? &yes : &no, sizeof(no)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| ASSERT_TRUE(session_->GetObject(pubh, pub)); | |
| ASSERT_TRUE(session_->GetObject(privh, priv)); | |
| } | |
| string GetDERforNID(int openssl_nid) { | |
| // OBJ_nid2obj returns a pointer to an internal table and does not allocate | |
| // memory. No need to free. | |
| ASN1_OBJECT* obj = OBJ_nid2obj(NID_X9_62_prime256v1); | |
| int expected_size = i2d_ASN1_OBJECT(obj, nullptr); | |
| string output(expected_size, '\0'); | |
| unsigned char* buf = ConvertStringToByteBuffer(output.data()); | |
| int output_size = i2d_ASN1_OBJECT(obj, &buf); | |
| CHECK_EQ(output_size, expected_size); | |
| return output; | |
| } | |
| void GenerateECCKeyPair(bool use_token_object_for_pub, | |
| bool use_token_object_for_priv, | |
| const Object** pub, | |
| const Object** priv) { | |
| // Create DER encoded OID of P-256 for CKA_EC_PARAMS (prime256v1 or | |
| // secp256r1) | |
| string ec_params = GetDERforNID(NID_X9_62_prime256v1); | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_ATTRIBUTE pub_attr[] = { | |
| {CKA_TOKEN, use_token_object_for_pub ? &yes : &no, sizeof(CK_BBOOL)}, | |
| {CKA_ENCRYPT, &no, sizeof(CK_BBOOL)}, | |
| {CKA_VERIFY, &yes, sizeof(CK_BBOOL)}, | |
| {CKA_EC_PARAMS, ConvertStringToByteBuffer(ec_params.data()), | |
| ec_params.size()}}; | |
| CK_ATTRIBUTE priv_attr[] = { | |
| {CKA_TOKEN, use_token_object_for_priv ? &yes : &no, sizeof(CK_BBOOL)}, | |
| {CKA_DECRYPT, &no, sizeof(CK_BBOOL)}, | |
| {CKA_SIGN, &yes, sizeof(CK_BBOOL)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_EC_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| ASSERT_TRUE(session_->GetObject(pubh, pub)); | |
| ASSERT_TRUE(session_->GetObject(privh, priv)); | |
| } | |
| int CreateObject() { | |
| CK_OBJECT_CLASS c = CKO_DATA; | |
| CK_BBOOL no = CK_FALSE; | |
| CK_ATTRIBUTE attr[] = {{CKA_CLASS, &c, sizeof(c)}, | |
| {CKA_TOKEN, &no, sizeof(no)}}; | |
| int h; | |
| session_->CreateObject(attr, 2, &h); | |
| return h; | |
| } | |
| void TestSignVerify(const Object* pub, | |
| const Object* priv, | |
| size_t input_size, | |
| CK_MECHANISM_TYPE mech, | |
| const string& mechanism_parameter) { | |
| string in(input_size, 'A'); | |
| int len = 0; | |
| string sig; | |
| // Sign / verify with OperationSinglePart(). | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationInit(kSign, mech, mechanism_parameter, priv)); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationSinglePart(kSign, in, &len, &sig)); | |
| EXPECT_EQ(CKR_OK, session_->OperationSinglePart(kSign, in, &len, &sig)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationInit(kVerify, mech, mechanism_parameter, pub)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kVerify, in, nullptr, nullptr)); | |
| EXPECT_EQ(CKR_OK, session_->VerifyFinal(sig)); | |
| // Same stuff with OperationUpdate(). | |
| in = string(input_size, 'B'); | |
| string sig2; | |
| len = 0; | |
| size_t first_divide = input_size / 2; | |
| size_t second_divide = input_size / 5 * 2; | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationInit(kSign, mech, mechanism_parameter, priv)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate( | |
| kSign, in.substr(0, first_divide), nullptr, nullptr)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationUpdate( | |
| kSign, in.substr(first_divide, input_size - first_divide), | |
| nullptr, nullptr)); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationFinal(kSign, &len, &sig2)); | |
| EXPECT_EQ(CKR_OK, session_->OperationFinal(kSign, &len, &sig2)); | |
| // Test verification with OperationUpdate(). | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationInit(kVerify, mech, mechanism_parameter, pub)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationUpdate(kVerify, in.substr(0, second_divide), | |
| nullptr, nullptr)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationUpdate( | |
| kVerify, in.substr(second_divide, input_size - second_divide), | |
| nullptr, nullptr)); | |
| EXPECT_EQ(CKR_OK, session_->VerifyFinal(sig2)); | |
| } | |
| hwsec::ScopedKey GetTestScopedKey() { | |
| return hwsec::ScopedKey(hwsec::Key{.token = 42}, | |
| hwsec_.GetFakeMiddlewareDerivative()); | |
| } | |
| protected: | |
| ObjectPoolMock token_pool_; | |
| ChapsFactoryMock factory_; | |
| StrictMock<hwsec::MockChapsFrontend> hwsec_; | |
| HandleGeneratorMock handle_generator_; | |
| std::unique_ptr<SessionImpl> session_; | |
| StrictMock<MetricsLibraryMock> mock_metrics_library_; | |
| ChapsMetrics chaps_metrics_; | |
| const brillo::SecureBlob random_seed_ = brillo::SecureBlob(kChapsRandomSeed); | |
| }; | |
| // Session Test that uses real Object implementation (ObjectImpl) | |
| class TestSessionWithRealObject : public TestSession { | |
| public: | |
| TestSessionWithRealObject() { | |
| chaps::ChapsFactory* factory = &factory_; | |
| EXPECT_CALL(factory_, CreateObject()) | |
| .WillRepeatedly(InvokeWithoutArgs( | |
| [factory] { return new chaps::ObjectImpl(factory); })); | |
| EXPECT_CALL(factory_, CreateObjectPool(_, _, _)) | |
| .WillRepeatedly(InvokeWithoutArgs(CreateObjectPoolMock)); | |
| EXPECT_CALL(factory_, CreateObjectPolicy(_)) | |
| .WillRepeatedly(Invoke(ChapsFactoryImpl::GetObjectPolicyForType)); | |
| EXPECT_CALL(factory_, GetRandomSeed()) | |
| .WillRepeatedly(ReturnRef(random_seed_)); | |
| EXPECT_CALL(handle_generator_, CreateHandle()).WillRepeatedly(Return(1)); | |
| ConfigureObjectPool(&token_pool_, 0); | |
| ConfigureHwsec(&hwsec_); | |
| } | |
| }; | |
| // Session Test that uses real Object implementation (ObjectImpl) | |
| class TestSessionWithTpmSimulator : public TestSessionWithRealObject { | |
| public: | |
| using TestSessionWithRealObject::TestSessionWithRealObject; | |
| void SetUp() override { | |
| chaps_metrics_.set_metrics_library_for_testing(&mock_metrics_library_); | |
| session_.reset(); | |
| session_ = std::make_unique<SessionImpl>( | |
| 1, &token_pool_, hwsec_simulator_.get(), &factory_, &handle_generator_, | |
| false, &chaps_metrics_); | |
| } | |
| private: | |
| hwsec::Tpm2SimulatorFactoryForTest hwsec_simulator_factory_; | |
| std::unique_ptr<const hwsec::ChapsFrontend> hwsec_simulator_ = | |
| hwsec_simulator_factory_.GetChapsFrontend(); | |
| }; | |
| typedef TestSession TestSession_DeathTest; | |
| // Test that SessionImpl asserts as expected when not properly initialized. | |
| TEST(DeathTest, InvalidInit) { | |
| ObjectPoolMock pool; | |
| ChapsFactoryMock factory; | |
| hwsec::MockChapsFrontend hwsec; | |
| HandleGeneratorMock handle_generator; | |
| SessionImpl* session; | |
| ChapsMetrics chaps_metrics; | |
| EXPECT_CALL(factory, CreateObjectPool(_, _, _)).Times(AnyNumber()); | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session = new SessionImpl(1, nullptr, &hwsec, &factory, &handle_generator, | |
| false, &chaps_metrics), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session = new SessionImpl(1, &pool, &hwsec, nullptr, &handle_generator, | |
| false, &chaps_metrics), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session = new SessionImpl(1, &pool, &hwsec, &factory, nullptr, false, | |
| &chaps_metrics), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session = new SessionImpl(1, &pool, &hwsec, &factory, &handle_generator, | |
| false, nullptr), | |
| "Check failed"); | |
| (void)session; | |
| } | |
| // Test that SessionImpl asserts as expected when passed invalid arguments. | |
| TEST_F(TestSession_DeathTest, InvalidArgs) { | |
| OperationType invalid_op = kNumOperationTypes; | |
| EXPECT_DEATH_IF_SUPPORTED(session_->IsOperationActive(invalid_op), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->CreateObject(nullptr, 0, nullptr), | |
| "Check failed"); | |
| int i; | |
| EXPECT_DEATH_IF_SUPPORTED(session_->CreateObject(nullptr, 1, &i), | |
| "Check failed"); | |
| i = CreateObject(); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->CopyObject(nullptr, 0, i, nullptr), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->CopyObject(nullptr, 1, i, &i), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->FindObjects(invalid_op, nullptr), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->GetObject(1, nullptr), "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->OperationInit(invalid_op, 0, "", nullptr), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session_->OperationInit(kEncrypt, CKM_AES_CBC, "", nullptr), | |
| "Check failed"); | |
| string s; | |
| const Object* o; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 32, &o); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, static_cast<int>(CKR_OK))) | |
| .WillOnce(Return(true)); | |
| ASSERT_EQ(CKR_OK, session_->OperationInit(kEncrypt, CKM_AES_ECB, "", o)); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->OperationUpdate(invalid_op, "", &i, &s), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session_->OperationUpdate(kEncrypt, "", nullptr, &s), "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session_->OperationUpdate(kEncrypt, "", &i, nullptr), "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->OperationFinal(invalid_op, &i, &s), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->OperationFinal(kEncrypt, nullptr, &s), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->OperationFinal(kEncrypt, &i, nullptr), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session_->OperationSinglePart(invalid_op, "", &i, &s), "Check failed"); | |
| } | |
| // Test that SessionImpl asserts when out-of-memory during initialization. | |
| TEST(DeathTest, OutOfMemoryInit) { | |
| ObjectPoolMock pool; | |
| hwsec::MockChapsFrontend hwsec; | |
| ChapsFactoryMock factory; | |
| HandleGeneratorMock handle_generator; | |
| ObjectPool* null_pool = nullptr; | |
| ChapsMetrics chaps_metrics; | |
| EXPECT_CALL(factory, CreateObjectPool(_, _, _)) | |
| .WillRepeatedly(Return(null_pool)); | |
| Session* session; | |
| EXPECT_DEATH_IF_SUPPORTED( | |
| session = new SessionImpl(1, &pool, &hwsec, &factory, &handle_generator, | |
| false, &chaps_metrics), | |
| "Check failed"); | |
| (void)session; | |
| } | |
| // Test that SessionImpl asserts when out-of-memory during object creation. | |
| TEST_F(TestSession_DeathTest, OutOfMemoryObject) { | |
| Object* null_object = nullptr; | |
| EXPECT_CALL(factory_, CreateObject()).WillRepeatedly(Return(null_object)); | |
| int tmp; | |
| EXPECT_DEATH_IF_SUPPORTED(session_->CreateObject(nullptr, 0, &tmp), | |
| "Check failed"); | |
| EXPECT_DEATH_IF_SUPPORTED(session_->FindObjectsInit(nullptr, 0), | |
| "Check failed"); | |
| } | |
| // Test that default session properties are correctly reported. | |
| TEST_F(TestSession, DefaultSetup) { | |
| EXPECT_EQ(1, session_->GetSlot()); | |
| EXPECT_FALSE(session_->IsReadOnly()); | |
| EXPECT_FALSE(session_->IsOperationActive(kEncrypt)); | |
| } | |
| // Test object management: create / copy / find / destroy. | |
| TEST_F(TestSession, Objects) { | |
| EXPECT_CALL(token_pool_, Insert(_)).Times(2); | |
| EXPECT_CALL(token_pool_, Find(_, _)).Times(1); | |
| EXPECT_CALL(token_pool_, Delete(_)).Times(1); | |
| CK_OBJECT_CLASS oc = CKO_SECRET_KEY; | |
| CK_ATTRIBUTE attr[] = {{CKA_CLASS, &oc, sizeof(oc)}}; | |
| int handle = 0; | |
| int invalid_handle = -1; | |
| // Create a new object. | |
| ASSERT_EQ(CKR_OK, session_->CreateObject(attr, std::size(attr), &handle)); | |
| EXPECT_GT(handle, 0); | |
| const Object* o; | |
| // Get the new object from the new handle. | |
| EXPECT_TRUE(session_->GetObject(handle, &o)); | |
| int handle2 = 0; | |
| // Copy an object (try invalid and valid handles). | |
| EXPECT_EQ( | |
| CKR_OBJECT_HANDLE_INVALID, | |
| session_->CopyObject(attr, std::size(attr), invalid_handle, &handle2)); | |
| ASSERT_EQ(CKR_OK, | |
| session_->CopyObject(attr, std::size(attr), handle, &handle2)); | |
| // Ensure handles are unique. | |
| EXPECT_TRUE(handle != handle2); | |
| EXPECT_TRUE(session_->GetObject(handle2, &o)); | |
| EXPECT_FALSE(session_->GetObject(invalid_handle, &o)); | |
| vector<int> v; | |
| // Find objects with calls out-of-order. | |
| EXPECT_EQ(CKR_OPERATION_NOT_INITIALIZED, session_->FindObjects(1, &v)); | |
| EXPECT_EQ(CKR_OPERATION_NOT_INITIALIZED, session_->FindObjectsFinal()); | |
| // Find the objects we've created (there should be 2). | |
| EXPECT_EQ(CKR_OK, session_->FindObjectsInit(attr, std::size(attr))); | |
| EXPECT_EQ(CKR_OPERATION_ACTIVE, | |
| session_->FindObjectsInit(attr, std::size(attr))); | |
| // Test multi-step finds by only allowing 1 result at a time. | |
| EXPECT_EQ(CKR_OK, session_->FindObjects(1, &v)); | |
| EXPECT_EQ(1, v.size()); | |
| EXPECT_EQ(CKR_OK, session_->FindObjects(1, &v)); | |
| EXPECT_EQ(2, v.size()); | |
| // We have them all but we'll query again to make sure it behaves properly. | |
| EXPECT_EQ(CKR_OK, session_->FindObjects(1, &v)); | |
| ASSERT_EQ(2, v.size()); | |
| // Check that the handles found are the same ones we know about. | |
| EXPECT_TRUE(v[0] == handle || v[1] == handle); | |
| EXPECT_TRUE(v[0] == handle2 || v[1] == handle2); | |
| EXPECT_EQ(CKR_OK, session_->FindObjectsFinal()); | |
| // Destroy an object (try invalid and valid handles). | |
| EXPECT_EQ(CKR_OBJECT_HANDLE_INVALID, session_->DestroyObject(invalid_handle)); | |
| EXPECT_EQ(CKR_OK, session_->DestroyObject(handle)); | |
| // Once destroyed, we should not be able to use the handle. | |
| EXPECT_FALSE(session_->GetObject(handle, &o)); | |
| } | |
| // Test multi-part and single-part cipher operations. | |
| TEST_F(TestSession, Cipher) { | |
| const Object* key_object = nullptr; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 32, &key_object); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, static_cast<int>(CKR_OK))) | |
| .Times(3); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kEncrypt, CKM_AES_CBC_PAD, | |
| string(16, 'A'), key_object)); | |
| string in(22, 'B'); | |
| string out, tmp; | |
| int maxlen = 0; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(2); | |
| // Check buffer-too-small semantics (and for each call following). | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationUpdate(kEncrypt, in, &maxlen, &tmp)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kEncrypt, in, &maxlen, &tmp)); | |
| out += tmp; | |
| // The first block is ready, check that we've received it. | |
| EXPECT_EQ(16, out.length()); | |
| maxlen = 0; | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationFinal(kEncrypt, &maxlen, &tmp)); | |
| EXPECT_EQ(CKR_OK, session_->OperationFinal(kEncrypt, &maxlen, &tmp)); | |
| out += tmp; | |
| // Check that we've received the final block. | |
| EXPECT_EQ(32, out.length()); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDecrypt, static_cast<int>(CKR_OK))) | |
| .Times(2); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kDecrypt, CKM_AES_CBC_PAD, | |
| string(16, 'A'), key_object)); | |
| string in2; | |
| maxlen = 0; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDecrypt, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationSinglePart(kDecrypt, out, &maxlen, &in2)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationSinglePart(kDecrypt, out, &maxlen, &in2)); | |
| EXPECT_EQ(22, in2.length()); | |
| // Check that what has been decrypted matches our original plain-text. | |
| EXPECT_TRUE(in == in2); | |
| } | |
| // Test multi-part and single-part digest operations. | |
| TEST_F(TestSession, Digest) { | |
| string in(30, 'A'); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, static_cast<int>(CKR_OK))) | |
| .Times(7); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kDigest, CKM_SHA_1, "", nullptr)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kDigest, in.substr(0, 10), | |
| nullptr, nullptr)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kDigest, in.substr(10, 10), | |
| nullptr, nullptr)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kDigest, in.substr(20, 10), | |
| nullptr, nullptr)); | |
| int len = 0; | |
| string out; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(2); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationFinal(kDigest, &len, &out)); | |
| EXPECT_EQ(20, len); | |
| EXPECT_EQ(CKR_OK, session_->OperationFinal(kDigest, &len, &out)); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kDigest, CKM_SHA_1, "", nullptr)); | |
| string out2; | |
| len = 0; | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationSinglePart(kDigest, in, &len, &out2)); | |
| EXPECT_EQ(CKR_OK, session_->OperationSinglePart(kDigest, in, &len, &out2)); | |
| EXPECT_EQ(20, len); | |
| // Check that both operations computed the same digest. | |
| EXPECT_TRUE(out == out2); | |
| } | |
| // Test HMAC sign and verify operations. | |
| TEST_F(TestSession, HMAC) { | |
| const Object* key_object = nullptr; | |
| GenerateSecretKey(CKM_GENERIC_SECRET_KEY_GEN, 32, &key_object); | |
| string in(30, 'A'); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, static_cast<int>(CKR_OK))) | |
| .Times(5); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationInit(kSign, CKM_SHA256_HMAC, "", key_object)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kSign, in.substr(0, 10), nullptr, | |
| nullptr)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kSign, in.substr(10, 10), nullptr, | |
| nullptr)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kSign, in.substr(20, 10), nullptr, | |
| nullptr)); | |
| int len = 0; | |
| string out; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, session_->OperationFinal(kSign, &len, &out)); | |
| EXPECT_EQ(CKR_OK, session_->OperationFinal(kSign, &len, &out)); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionVerify, static_cast<int>(CKR_OK))) | |
| .Times(3); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationInit(kVerify, CKM_SHA256_HMAC, "", key_object)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kVerify, in, nullptr, nullptr)); | |
| // A successful verify implies both operations computed the same MAC. | |
| EXPECT_EQ(CKR_OK, session_->VerifyFinal(out)); | |
| } | |
| // Test empty multi-part operation. | |
| TEST_F(TestSession, FinalWithNoUpdate) { | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, static_cast<int>(CKR_OK))) | |
| .Times(2); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kDigest, CKM_SHA_1, "", nullptr)); | |
| int len = 20; | |
| string out; | |
| EXPECT_EQ(CKR_OK, session_->OperationFinal(kDigest, &len, &out)); | |
| EXPECT_EQ(20, len); | |
| } | |
| // Test multi-part and single-part operations inhibit each other. | |
| TEST_F(TestSession, UpdateOperationPreventsSinglePart) { | |
| string in(30, 'A'); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, static_cast<int>(CKR_OK))) | |
| .Times(2); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kDigest, CKM_SHA_1, "", nullptr)); | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kDigest, in.substr(0, 10), | |
| nullptr, nullptr)); | |
| int len = 0; | |
| string out; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_OPERATION_ACTIVE))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_OPERATION_ACTIVE, session_->OperationSinglePart( | |
| kDigest, in.substr(10, 20), &len, &out)); | |
| // The error also terminates the operation. | |
| len = 0; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_OPERATION_NOT_INITIALIZED))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_OPERATION_NOT_INITIALIZED, | |
| session_->OperationFinal(kDigest, &len, &out)); | |
| } | |
| TEST_F(TestSession, SinglePartOperationPreventsUpdate) { | |
| string in(30, 'A'); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, static_cast<int>(CKR_OK))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kDigest, CKM_SHA_1, "", nullptr)); | |
| string out; | |
| int len = 0; | |
| // Perform a single part operation but leave the output to be collected. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationSinglePart(kDigest, in, &len, &out)); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_OPERATION_ACTIVE))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ( | |
| CKR_OPERATION_ACTIVE, | |
| session_->OperationUpdate(kDigest, in.substr(10, 10), nullptr, nullptr)); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_OPERATION_NOT_INITIALIZED))) | |
| .WillOnce(Return(true)); | |
| // The error also terminates the operation. | |
| EXPECT_EQ(CKR_OPERATION_NOT_INITIALIZED, | |
| session_->OperationSinglePart(kDigest, in, &len, &out)); | |
| } | |
| TEST_F(TestSession, SinglePartOperationPreventsFinal) { | |
| string in(30, 'A'); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, static_cast<int>(CKR_OK))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kDigest, CKM_SHA_1, "", nullptr)); | |
| string out; | |
| int len = 0; | |
| // Perform a single part operation but leave the output to be collected. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationSinglePart(kDigest, in, &len, &out)); | |
| len = 0; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_OPERATION_ACTIVE))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_OPERATION_ACTIVE, | |
| session_->OperationFinal(kDigest, &len, &out)); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_OPERATION_NOT_INITIALIZED))) | |
| .WillOnce(Return(true)); | |
| // The error also terminates the operation. | |
| EXPECT_EQ(CKR_OPERATION_NOT_INITIALIZED, | |
| session_->OperationSinglePart(kDigest, in, &len, &out)); | |
| } | |
| // Test RSA PKCS #1 encryption. | |
| TEST_F(TestSession, RSAEncrypt) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateRSAKeyPair(false, 1024, &pub, &priv); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, static_cast<int>(CKR_OK))) | |
| .Times(2); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kEncrypt, CKM_RSA_PKCS, "", pub)); | |
| string in(100, 'A'); | |
| int len = 0; | |
| string out; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationSinglePart(kEncrypt, in, &len, &out)); | |
| EXPECT_EQ(CKR_OK, session_->OperationSinglePart(kEncrypt, in, &len, &out)); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDecrypt, static_cast<int>(CKR_OK))) | |
| .Times(3); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kDecrypt, CKM_RSA_PKCS, "", priv)); | |
| len = 0; | |
| string in2 = out; | |
| string out2; | |
| EXPECT_EQ(CKR_OK, session_->OperationUpdate(kDecrypt, in2, &len, &out2)); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDecrypt, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->OperationFinal(kDecrypt, &len, &out2)); | |
| EXPECT_EQ(CKR_OK, session_->OperationFinal(kDecrypt, &len, &out2)); | |
| EXPECT_EQ(in.length(), out2.length()); | |
| // Check that what has been decrypted matches our original plain-text. | |
| EXPECT_TRUE(in == out2); | |
| } | |
| // Test RSA PKCS #1 sign / verify. | |
| TEST_F(TestSession, RsaSign) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateRSAKeyPair(true, 1024, &pub, &priv); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(4); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, static_cast<int>(CKR_OK))) | |
| .Times(12); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionVerify, static_cast<int>(CKR_OK))) | |
| .Times(14); | |
| // Test the generic RSA mechanism. | |
| TestSignVerify(pub, priv, 100, CKM_RSA_PKCS, ""); | |
| // Test RSA mechanism with built-in hash. | |
| TestSignVerify(pub, priv, 100, CKM_SHA256_RSA_PKCS, ""); | |
| } | |
| // Test RSA PSS sign / verify. | |
| TEST_F(TestSession, RsaPSSSign) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateRSAKeyPair(true, 1024, &pub, &priv); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(4); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, static_cast<int>(CKR_OK))) | |
| .Times(12); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionVerify, static_cast<int>(CKR_OK))) | |
| .Times(14); | |
| // Test the generic RSA PSS mechanism. | |
| TestSignVerify(pub, priv, 20, CKM_RSA_PKCS_PSS, | |
| GetRSAPSSParam(CKM_SHA_1, CKG_MGF1_SHA1, 20)); | |
| // Test the version with built-in hash. | |
| TestSignVerify(pub, priv, 100, CKM_SHA256_RSA_PKCS_PSS, | |
| GetRSAPSSParam(CKM_SHA_1, CKG_MGF1_SHA1, 20)); | |
| } | |
| // Test ECC ECDSA sign / verify. | |
| TEST_F(TestSession, EcdsaSign) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateECCKeyPair(false, false, &pub, &priv); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(4); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, static_cast<int>(CKR_OK))) | |
| .Times(12); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionVerify, static_cast<int>(CKR_OK))) | |
| .Times(14); | |
| // Test the generic ECDSA. | |
| TestSignVerify(pub, priv, 100, CKM_ECDSA, ""); | |
| // Test ECDSA with built-in hash. | |
| TestSignVerify(pub, priv, 100, CKM_ECDSA_SHA1, ""); | |
| } | |
| // Test that requests for unsupported mechanisms are handled correctly. | |
| TEST_F(TestSession, MechanismInvalid) { | |
| const Object* key = nullptr; | |
| // Use a valid key so that key errors don't mask mechanism errors. | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 16, &key); | |
| // We don't support IDEA. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, | |
| static_cast<int>(CKR_MECHANISM_INVALID))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_MECHANISM_INVALID, | |
| session_->OperationInit(kEncrypt, CKM_IDEA_CBC, "", key)); | |
| // We don't support SHA-224. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, | |
| static_cast<int>(CKR_MECHANISM_INVALID))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_MECHANISM_INVALID, | |
| session_->OperationInit(kSign, CKM_SHA224_RSA_PKCS, "", key)); | |
| // We don't support MD2. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_MECHANISM_INVALID))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_MECHANISM_INVALID, | |
| session_->OperationInit(kDigest, CKM_MD2, "", nullptr)); | |
| } | |
| // Test that operation / mechanism mismatches are handled correctly. | |
| TEST_F(TestSession, MechanismMismatch) { | |
| const Object* hmac = nullptr; | |
| GenerateSecretKey(CKM_GENERIC_SECRET_KEY_GEN, 16, &hmac); | |
| const Object* aes = nullptr; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 16, &aes); | |
| // Encrypt with a sign/verify mechanism. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, | |
| static_cast<int>(CKR_MECHANISM_INVALID))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_MECHANISM_INVALID, | |
| session_->OperationInit(kEncrypt, CKM_SHA_1_HMAC, "", hmac)); | |
| // Sign with an encryption mechanism. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, | |
| static_cast<int>(CKR_MECHANISM_INVALID))) | |
| .Times(2); | |
| EXPECT_EQ(CKR_MECHANISM_INVALID, | |
| session_->OperationInit(kSign, CKM_AES_CBC_PAD, "", aes)); | |
| // Sign with a digest-only mechanism. | |
| EXPECT_EQ(CKR_MECHANISM_INVALID, | |
| session_->OperationInit(kSign, CKM_SHA_1, "", hmac)); | |
| // Digest with a sign+digest mechanism. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDigest, | |
| static_cast<int>(CKR_MECHANISM_INVALID))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_MECHANISM_INVALID, | |
| session_->OperationInit(kDigest, CKM_SHA1_RSA_PKCS, "", nullptr)); | |
| } | |
| // Test that mechanism / key type mismatches are handled correctly. | |
| TEST_F(TestSession, key_typeMismatch) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| const Object* aes = nullptr; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 16, &aes); | |
| const Object* rsapub = nullptr; | |
| const Object* rsapriv = nullptr; | |
| GenerateRSAKeyPair(true, 512, &rsapub, &rsapriv); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, | |
| static_cast<int>(CKR_KEY_TYPE_INCONSISTENT))) | |
| .Times(2); | |
| // DES3 with an AES key. | |
| EXPECT_EQ(CKR_KEY_TYPE_INCONSISTENT, | |
| session_->OperationInit(kEncrypt, CKM_DES3_CBC, "", aes)); | |
| // AES with an RSA key. | |
| EXPECT_EQ(CKR_KEY_TYPE_INCONSISTENT, | |
| session_->OperationInit(kEncrypt, CKM_AES_CBC, "", rsapriv)); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, | |
| static_cast<int>(CKR_KEY_TYPE_INCONSISTENT))) | |
| .Times(2); | |
| // HMAC with an RSA key. | |
| EXPECT_EQ(CKR_KEY_TYPE_INCONSISTENT, | |
| session_->OperationInit(kSign, CKM_SHA_1_HMAC, "", rsapriv)); | |
| // RSA with an AES key. | |
| EXPECT_EQ(CKR_KEY_TYPE_INCONSISTENT, | |
| session_->OperationInit(kSign, CKM_SHA1_RSA_PKCS, "", aes)); | |
| } | |
| // Test that key function permissions are correctly enforced. | |
| TEST_F(TestSession, KeyFunctionPermission) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| const Object* encpub = nullptr; | |
| const Object* encpriv = nullptr; | |
| GenerateRSAKeyPair(false, 512, &encpub, &encpriv); | |
| const Object* sigpub = nullptr; | |
| const Object* sigpriv = nullptr; | |
| GenerateRSAKeyPair(true, 512, &sigpub, &sigpriv); | |
| // Try decrypting with a sign-only key. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDecrypt, | |
| static_cast<int>(CKR_KEY_FUNCTION_NOT_PERMITTED))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_KEY_FUNCTION_NOT_PERMITTED, | |
| session_->OperationInit(kDecrypt, CKM_RSA_PKCS, "", sigpriv)); | |
| // Try signing with a decrypt-only key. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, | |
| static_cast<int>(CKR_KEY_FUNCTION_NOT_PERMITTED))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_KEY_FUNCTION_NOT_PERMITTED, | |
| session_->OperationInit(kSign, CKM_RSA_PKCS, "", encpriv)); | |
| } | |
| // Test that invalid mechanism parameters for ciphers are handled correctly. | |
| TEST_F(TestSession, BadIV) { | |
| const Object* aes = nullptr; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 16, &aes); | |
| const Object* des = nullptr; | |
| GenerateSecretKey(CKM_DES_KEY_GEN, 16, &des); | |
| const Object* des3 = nullptr; | |
| GenerateSecretKey(CKM_DES3_KEY_GEN, 16, &des3); | |
| // AES expects 16 bytes and DES/DES3 expects 8 bytes. | |
| string bad_iv(7, 0); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, | |
| static_cast<int>(CKR_MECHANISM_PARAM_INVALID))) | |
| .Times(3); | |
| EXPECT_EQ(CKR_MECHANISM_PARAM_INVALID, | |
| session_->OperationInit(kEncrypt, CKM_AES_CBC, bad_iv, aes)); | |
| EXPECT_EQ(CKR_MECHANISM_PARAM_INVALID, | |
| session_->OperationInit(kEncrypt, CKM_DES_CBC, bad_iv, des)); | |
| EXPECT_EQ(CKR_MECHANISM_PARAM_INVALID, | |
| session_->OperationInit(kEncrypt, CKM_DES3_CBC, bad_iv, des3)); | |
| } | |
| // Test that invalid key size ranges are handled correctly. | |
| TEST_F(TestSession, BadKeySize) { | |
| const Object* key = nullptr; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 16, &key); | |
| // AES keys can be 16, 24, or 32 bytes in length. | |
| const_cast<Object*>(key)->SetAttributeString(CKA_VALUE, string(33, 0)); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionEncrypt, | |
| static_cast<int>(CKR_KEY_SIZE_RANGE))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_KEY_SIZE_RANGE, | |
| session_->OperationInit(kEncrypt, CKM_AES_ECB, "", key)); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateRSAKeyPair(true, 512, &pub, &priv); | |
| // RSA keys can have a modulus size no smaller than 512. | |
| const_cast<Object*>(priv)->SetAttributeString(CKA_MODULUS, string(32, 0)); | |
| EXPECT_CALL( | |
| mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, static_cast<int>(CKR_KEY_SIZE_RANGE))) | |
| .WillOnce(Return(true)); | |
| EXPECT_EQ(CKR_KEY_SIZE_RANGE, | |
| session_->OperationInit(kSign, CKM_RSA_PKCS, "", priv)); | |
| } | |
| // Test that invalid attributes for key pair generation are handled correctly. | |
| TEST_F(TestSession, BadRSAGenerate) { | |
| CK_BBOOL no = CK_FALSE; | |
| int size = 1024; | |
| CK_BYTE pubexp[] = {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 12}, | |
| {CKA_MODULUS_BITS, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE priv_attr[] = { | |
| {CKA_TOKEN, &no, sizeof(no)}, | |
| }; | |
| int pub, priv; | |
| // CKA_PUBLIC_EXPONENT too large. | |
| EXPECT_EQ(CKR_FUNCTION_FAILED, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pub, &priv)); | |
| pub_attr[1].ulValueLen = 3; | |
| size = 20000; | |
| // CKA_MODULUS_BITS too large. | |
| EXPECT_EQ(CKR_KEY_SIZE_RANGE, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pub, &priv)); | |
| // CKA_MODULUS_BITS missing. | |
| EXPECT_EQ( | |
| CKR_TEMPLATE_INCOMPLETE, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, 2, | |
| priv_attr, std::size(priv_attr), &pub, &priv)); | |
| } | |
| // Test that invalid attributes for key generation are handled correctly. | |
| TEST_F(TestSession, BadAESGenerate) { | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| int size = 33; | |
| CK_ATTRIBUTE attr[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_ENCRYPT, &yes, sizeof(yes)}, | |
| {CKA_DECRYPT, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| int handle = 0; | |
| // CKA_VALUE_LEN missing. | |
| EXPECT_EQ(CKR_TEMPLATE_INCOMPLETE, | |
| session_->GenerateKey(CKM_AES_KEY_GEN, "", attr, 3, &handle)); | |
| // CKA_VALUE_LEN out of range. | |
| EXPECT_EQ(CKR_KEY_SIZE_RANGE, | |
| session_->GenerateKey(CKM_AES_KEY_GEN, "", attr, 4, &handle)); | |
| } | |
| // Test that signature verification fails as expected for invalid signatures. | |
| TEST_F(TestSession, BadSignature) { | |
| string input(100, 'A'); | |
| string signature(20, 0); | |
| const Object* hmac; | |
| GenerateSecretKey(CKM_GENERIC_SECRET_KEY_GEN, 32, &hmac); | |
| const Object *rsapub, *rsapriv; | |
| GenerateRSAKeyPair(true, 1024, &rsapub, &rsapriv); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionVerify, static_cast<int>(CKR_OK))) | |
| .Times(12); | |
| // HMAC with bad signature length. | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationInit(kVerify, CKM_SHA256_HMAC, "", hmac)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationUpdate(kVerify, input, nullptr, nullptr)); | |
| EXPECT_EQ(CKR_SIGNATURE_LEN_RANGE, session_->VerifyFinal(signature)); | |
| // HMAC with bad signature. | |
| signature.resize(32); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationInit(kVerify, CKM_SHA256_HMAC, "", hmac)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationUpdate(kVerify, input, nullptr, nullptr)); | |
| EXPECT_EQ(CKR_SIGNATURE_INVALID, session_->VerifyFinal(signature)); | |
| // RSA with bad signature length. | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kVerify, CKM_RSA_PKCS, "", rsapub)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationUpdate(kVerify, input, nullptr, nullptr)); | |
| EXPECT_EQ(CKR_SIGNATURE_LEN_RANGE, session_->VerifyFinal(signature)); | |
| // RSA with bad signature. | |
| signature.resize(128, 1); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kVerify, CKM_RSA_PKCS, "", rsapub)); | |
| EXPECT_EQ(CKR_OK, | |
| session_->OperationUpdate(kVerify, input, nullptr, nullptr)); | |
| EXPECT_EQ(CKR_SIGNATURE_INVALID, session_->VerifyFinal(signature)); | |
| } | |
| TEST_F(TestSession, Flush) { | |
| ObjectMock token_object; | |
| EXPECT_CALL(token_object, IsTokenObject()).WillRepeatedly(Return(true)); | |
| ObjectMock session_object; | |
| EXPECT_CALL(session_object, IsTokenObject()).WillRepeatedly(Return(false)); | |
| EXPECT_CALL(token_pool_, Flush(_)) | |
| .WillOnce(Return(Result::Failure)) | |
| .WillRepeatedly(Return(Result::Success)); | |
| EXPECT_NE(session_->FlushModifiableObject(&token_object), CKR_OK); | |
| EXPECT_EQ(session_->FlushModifiableObject(&token_object), CKR_OK); | |
| EXPECT_EQ(session_->FlushModifiableObject(&session_object), CKR_OK); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateRSAWithHWSec) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL( | |
| hwsec_, | |
| GenerateRSAKey(_, _, _, hwsec::ChapsFrontend::AllowSoftwareGen::kNotAllow, | |
| hwsec::ChapsFrontend::AllowDecrypt::kNotAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| EXPECT_CALL(hwsec_, GetRSAPublicKey(_)) | |
| .WillRepeatedly(ReturnValue(hwsec::RSAPublicInfo{})); | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| int size = 2048; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_ENCRYPT, &no, sizeof(no)}, | |
| {CKA_VERIFY, &yes, sizeof(yes)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_DECRYPT, &no, sizeof(no)}, | |
| {CKA_SIGN, &yes, sizeof(yes)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| // There are a few sensitive attributes that MUST not exist. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(privh, &object)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIVATE_EXPONENT)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIME_1)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIME_2)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_EXPONENT_1)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_EXPONENT_2)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_COEFFICIENT)); | |
| // Check attributes that store security element wrapped blob exists. | |
| EXPECT_TRUE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_FALSE(object->GetAttributeBool(kKeyInSoftwareAttribute, true)); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateRSAWithHWSecAndAllowSoftGen) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL( | |
| hwsec_, | |
| GenerateRSAKey(_, _, _, hwsec::ChapsFrontend::AllowSoftwareGen::kAllow, | |
| hwsec::ChapsFrontend::AllowDecrypt::kNotAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| EXPECT_CALL(hwsec_, GetRSAPublicKey(_)) | |
| .WillRepeatedly(ReturnValue(hwsec::RSAPublicInfo{})); | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| int size = 2048; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_ENCRYPT, &no, sizeof(no)}, | |
| {CKA_VERIFY, &yes, sizeof(yes)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_DECRYPT, &no, sizeof(no)}, | |
| {CKA_SIGN, &yes, sizeof(yes)}, | |
| {kAllowSoftwareGenAttribute, &yes, sizeof(yes)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| // There are a few sensitive attributes that MUST not exist. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(privh, &object)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIVATE_EXPONENT)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIME_1)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIME_2)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_EXPONENT_1)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_EXPONENT_2)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_COEFFICIENT)); | |
| // Check attributes that store security element wrapped blob exists. | |
| EXPECT_TRUE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_FALSE(object->GetAttributeBool(kKeyInSoftwareAttribute, true)); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateRSAWithHWsecInconsistentToken) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL( | |
| hwsec_, | |
| GenerateRSAKey(_, _, _, _, hwsec::ChapsFrontend::AllowDecrypt::kNotAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| EXPECT_CALL(hwsec_, GetRSAPublicKey(_)) | |
| .WillRepeatedly(ReturnValue(hwsec::RSAPublicInfo{})); | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| int size = 2048; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_ENCRYPT, &no, sizeof(no)}, | |
| {CKA_VERIFY, &yes, sizeof(yes)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_DECRYPT, &no, sizeof(no)}, | |
| {CKA_SIGN, &yes, sizeof(yes)}}; | |
| // Attempt to generate a private key on the token, but public key not on the | |
| // token. | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| const Object* public_object = nullptr; | |
| const Object* private_object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(pubh, &public_object)); | |
| ASSERT_TRUE(session_->GetObject(privh, &private_object)); | |
| EXPECT_FALSE(public_object->IsTokenObject()); | |
| EXPECT_TRUE(private_object->IsTokenObject()); | |
| // Destroy the objects. | |
| EXPECT_EQ(CKR_OK, session_->DestroyObject(pubh)); | |
| EXPECT_EQ(CKR_OK, session_->DestroyObject(privh)); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateRSAWithNoHWSec) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly( | |
| ReturnError<TPMError>("Not supported", TPMRetryAction::kNoRetry)); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(false)); | |
| EXPECT_CALL(hwsec_, GenerateRSAKey(_, _, _, _, _, _)).Times(0); | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| int size = 1024; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_ENCRYPT, &no, sizeof(no)}, | |
| {CKA_VERIFY, &yes, sizeof(yes)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_DECRYPT, &no, sizeof(no)}, | |
| {CKA_SIGN, &yes, sizeof(yes)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| // For a software key, the sensitive attributes should exist. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(privh, &object)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIVATE_EXPONENT)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIME_1)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIME_2)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_EXPONENT_1)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_EXPONENT_2)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_COEFFICIENT)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_TRUE(object->GetAttributeBool(kKeyInSoftwareAttribute, false)); | |
| // Check attributes that store security element wrapped blob doesn't exists. | |
| EXPECT_FALSE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_FALSE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateRSAWithForceSoftware) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, GenerateRSAKey(_, _, _, _, _, _)).Times(0); | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| int size = 1024; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_ENCRYPT, &no, sizeof(no)}, | |
| {CKA_VERIFY, &yes, sizeof(yes)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_DECRYPT, &no, sizeof(no)}, | |
| {CKA_SIGN, &yes, sizeof(yes)}, | |
| {kForceSoftwareAttribute, &yes, sizeof(yes)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| // For a software key, the sensitive attributes should exist. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(privh, &object)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIVATE_EXPONENT)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIME_1)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIME_2)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_EXPONENT_1)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_EXPONENT_2)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_COEFFICIENT)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_TRUE(object->GetAttributeBool(kKeyInSoftwareAttribute, false)); | |
| // Check attributes that store security element wrapped blob doesn't exists. | |
| EXPECT_FALSE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_FALSE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateECCWithHWSec) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, GenerateECCKey( | |
| _, _, hwsec::ChapsFrontend::AllowDecrypt::kNotAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| EXPECT_CALL(hwsec_, GetECCPublicKey(_)) | |
| .WillRepeatedly(ReturnValue(GenerateECCPublicInfo())); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateECCKeyPair(true, true, &pub, &priv); | |
| // Security element backed key object doesn't have CKA_VALUE which stored ECC | |
| // private key. | |
| EXPECT_FALSE(priv->IsAttributePresent(CKA_VALUE)); | |
| // Check attributes that store security element wrapped blob exists. | |
| EXPECT_TRUE(priv->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_TRUE(priv->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(priv->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_FALSE(priv->GetAttributeBool(kKeyInSoftwareAttribute, true)); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateECCWithHWSecInconsistentToken) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, GenerateECCKey( | |
| _, _, hwsec::ChapsFrontend::AllowDecrypt::kNotAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| EXPECT_CALL(hwsec_, GetECCPublicKey(_)) | |
| .WillRepeatedly(ReturnValue(GenerateECCPublicInfo())); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateECCKeyPair(false, true, &pub, &priv); | |
| EXPECT_FALSE(pub->IsTokenObject()); | |
| EXPECT_TRUE(priv->IsTokenObject()); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateECCWithNoHWSec) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(_)) | |
| .WillRepeatedly( | |
| ReturnError<TPMError>("Not supported", TPMRetryAction::kNoRetry)); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(false)); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateECCKeyPair(true, true, &pub, &priv); | |
| // For a software key, the sensitive attributes should exist. | |
| EXPECT_TRUE(priv->IsAttributePresent(CKA_VALUE)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(priv->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_TRUE(priv->GetAttributeBool(kKeyInSoftwareAttribute, false)); | |
| // Check attributes that store security element wrapped blob doesn't exists. | |
| EXPECT_FALSE(priv->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_FALSE(priv->IsAttributePresent(kKeyBlobAttribute)); | |
| } | |
| TEST_F(TestSessionWithRealObject, GenerateECCWithForceSoftware) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| const Object* priv = nullptr; | |
| // Create DER encoded OID of P-256 for CKA_EC_PARAMS (prime256v1 or | |
| // secp256r1) | |
| string ec_params = GetDERforNID(NID_X9_62_prime256v1); | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_ATTRIBUTE pub_attr[] = { | |
| {CKA_TOKEN, &yes, sizeof(CK_BBOOL)}, | |
| {CKA_ENCRYPT, &no, sizeof(CK_BBOOL)}, | |
| {CKA_VERIFY, &yes, sizeof(CK_BBOOL)}, | |
| {CKA_EC_PARAMS, ConvertStringToByteBuffer(ec_params.data()), | |
| ec_params.size()}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(CK_BBOOL)}, | |
| {CKA_DECRYPT, &no, sizeof(CK_BBOOL)}, | |
| {CKA_SIGN, &yes, sizeof(CK_BBOOL)}, | |
| {kForceSoftwareAttribute, &yes, sizeof(yes)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, session_->GenerateKeyPair( | |
| CKM_EC_KEY_PAIR_GEN, "", pub_attr, std::size(pub_attr), | |
| priv_attr, std::size(priv_attr), &pubh, &privh)); | |
| ASSERT_TRUE(session_->GetObject(privh, &priv)); | |
| // For a software key, the sensitive attributes should exist. | |
| EXPECT_TRUE(priv->IsAttributePresent(CKA_VALUE)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(priv->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_TRUE(priv->GetAttributeBool(kKeyInSoftwareAttribute, false)); | |
| // Check attributes that store security element wrapped blob doesn't exists. | |
| EXPECT_FALSE(priv->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_FALSE(priv->IsAttributePresent(kKeyBlobAttribute)); | |
| } | |
| TEST_F(TestSession, EcdsaSignWithHWSec) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, GenerateECCKey( | |
| _, _, hwsec::ChapsFrontend::AllowDecrypt::kNotAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| EXPECT_CALL(hwsec_, GetECCPublicKey(_)) | |
| .WillRepeatedly(ReturnValue(GenerateECCPublicInfo())); | |
| EXPECT_CALL(hwsec_, LoadKey(_, _)).WillRepeatedly([&](auto&&, auto&&) { | |
| return GetTestScopedKey(); | |
| }); | |
| EXPECT_CALL(hwsec_, Sign(_, _, _)) | |
| .WillRepeatedly(ReturnValue(brillo::Blob())); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateECCKeyPair(true, true, &pub, &priv); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, static_cast<int>(CKR_OK))) | |
| .Times(2); | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kSign, CKM_ECDSA_SHA1, "", priv)); | |
| string in(100, 'A'); | |
| int len = 0; | |
| string sig; | |
| EXPECT_EQ(CKR_OK, session_->OperationSinglePart(kSign, in, &len, &sig)); | |
| } | |
| TEST_F(TestSessionWithRealObject, ImportRSAWithHWSec) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, | |
| WrapRSAKey(_, _, _, _, hwsec::ChapsFrontend::AllowDecrypt::kAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| crypto::ScopedBIGNUM exponent(BN_new()); | |
| CHECK(exponent); | |
| EXPECT_TRUE(BN_set_word(exponent.get(), 0x10001)); | |
| crypto::ScopedRSA rsa(RSA_new()); | |
| CHECK(rsa); | |
| EXPECT_TRUE(RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr)); | |
| CK_OBJECT_CLASS priv_class = CKO_PRIVATE_KEY; | |
| CK_KEY_TYPE key_type = CKK_RSA; | |
| CK_BBOOL false_value = CK_FALSE; | |
| CK_BBOOL true_value = CK_TRUE; | |
| string id = "test_id"; | |
| string label = "test_label"; | |
| const BIGNUM* rsa_n; | |
| const BIGNUM* rsa_e; | |
| const BIGNUM* rsa_d; | |
| const BIGNUM* rsa_p; | |
| const BIGNUM* rsa_q; | |
| const BIGNUM* rsa_dmp1; | |
| const BIGNUM* rsa_dmq1; | |
| const BIGNUM* rsa_iqmp; | |
| RSA_get0_key(rsa.get(), &rsa_n, &rsa_e, &rsa_d); | |
| RSA_get0_factors(rsa.get(), &rsa_p, &rsa_q); | |
| RSA_get0_crt_params(rsa.get(), &rsa_dmp1, &rsa_dmq1, &rsa_iqmp); | |
| string n = bn2bin(rsa_n); | |
| string e = bn2bin(rsa_e); | |
| string d = bn2bin(rsa_d); | |
| string p = bn2bin(rsa_p); | |
| string q = bn2bin(rsa_q); | |
| string dmp1 = bn2bin(rsa_dmp1); | |
| string dmq1 = bn2bin(rsa_dmq1); | |
| string iqmp = bn2bin(rsa_iqmp); | |
| CK_ATTRIBUTE private_attributes[] = { | |
| {CKA_CLASS, &priv_class, sizeof(priv_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_DECRYPT, &true_value, sizeof(true_value)}, | |
| {CKA_SIGN, &true_value, sizeof(true_value)}, | |
| {CKA_UNWRAP, &false_value, sizeof(false_value)}, | |
| {CKA_SENSITIVE, &true_value, sizeof(true_value)}, | |
| {CKA_TOKEN, &true_value, sizeof(true_value)}, | |
| {CKA_PRIVATE, &true_value, sizeof(true_value)}, | |
| {CKA_ID, std::data(id), id.length()}, | |
| {CKA_LABEL, std::data(label), label.length()}, | |
| {CKA_MODULUS, std::data(n), n.length()}, | |
| {CKA_PUBLIC_EXPONENT, std::data(e), e.length()}, | |
| {CKA_PRIVATE_EXPONENT, std::data(d), d.length()}, | |
| {CKA_PRIME_1, std::data(p), p.length()}, | |
| {CKA_PRIME_2, std::data(q), q.length()}, | |
| {CKA_EXPONENT_1, std::data(dmp1), dmp1.length()}, | |
| {CKA_EXPONENT_2, std::data(dmq1), dmq1.length()}, | |
| {CKA_COEFFICIENT, std::data(iqmp), iqmp.length()}, | |
| }; | |
| int handle = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->CreateObject(private_attributes, | |
| std::size(private_attributes), &handle)); | |
| // There are a few sensitive attributes that MUST be removed. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(handle, &object)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIVATE_EXPONENT)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIME_1)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_PRIME_2)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_EXPONENT_1)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_EXPONENT_2)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_COEFFICIENT)); | |
| // Check attributes that store security element wrapped blob exists. | |
| EXPECT_TRUE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_FALSE(object->GetAttributeBool(kKeyInSoftwareAttribute, true)); | |
| } | |
| TEST_F(TestSessionWithRealObject, ImportRSAWithNoHWSec) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly( | |
| ReturnError<TPMError>("Not supported", TPMRetryAction::kNoRetry)); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(false)); | |
| crypto::ScopedBIGNUM exponent(BN_new()); | |
| CHECK(exponent); | |
| EXPECT_TRUE(BN_set_word(exponent.get(), 0x10001)); | |
| crypto::ScopedRSA rsa(RSA_new()); | |
| CHECK(rsa); | |
| EXPECT_TRUE(RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr)); | |
| CK_OBJECT_CLASS priv_class = CKO_PRIVATE_KEY; | |
| CK_KEY_TYPE key_type = CKK_RSA; | |
| CK_BBOOL false_value = CK_FALSE; | |
| CK_BBOOL true_value = CK_TRUE; | |
| string id = "test_id"; | |
| string label = "test_label"; | |
| const BIGNUM* rsa_n; | |
| const BIGNUM* rsa_e; | |
| const BIGNUM* rsa_d; | |
| const BIGNUM* rsa_p; | |
| const BIGNUM* rsa_q; | |
| const BIGNUM* rsa_dmp1; | |
| const BIGNUM* rsa_dmq1; | |
| const BIGNUM* rsa_iqmp; | |
| RSA_get0_key(rsa.get(), &rsa_n, &rsa_e, &rsa_d); | |
| RSA_get0_factors(rsa.get(), &rsa_p, &rsa_q); | |
| RSA_get0_crt_params(rsa.get(), &rsa_dmp1, &rsa_dmq1, &rsa_iqmp); | |
| string n = bn2bin(rsa_n); | |
| string e = bn2bin(rsa_e); | |
| string d = bn2bin(rsa_d); | |
| string p = bn2bin(rsa_p); | |
| string q = bn2bin(rsa_q); | |
| string dmp1 = bn2bin(rsa_dmp1); | |
| string dmq1 = bn2bin(rsa_dmq1); | |
| string iqmp = bn2bin(rsa_iqmp); | |
| CK_ATTRIBUTE private_attributes[] = { | |
| {CKA_CLASS, &priv_class, sizeof(priv_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_DECRYPT, &true_value, sizeof(true_value)}, | |
| {CKA_SIGN, &true_value, sizeof(true_value)}, | |
| {CKA_UNWRAP, &false_value, sizeof(false_value)}, | |
| {CKA_SENSITIVE, &true_value, sizeof(true_value)}, | |
| {CKA_TOKEN, &true_value, sizeof(true_value)}, | |
| {CKA_PRIVATE, &true_value, sizeof(true_value)}, | |
| {CKA_ID, std::data(id), id.length()}, | |
| {CKA_LABEL, std::data(label), label.length()}, | |
| {CKA_MODULUS, std::data(n), n.length()}, | |
| {CKA_PUBLIC_EXPONENT, std::data(e), e.length()}, | |
| {CKA_PRIVATE_EXPONENT, std::data(d), d.length()}, | |
| {CKA_PRIME_1, std::data(p), p.length()}, | |
| {CKA_PRIME_2, std::data(q), q.length()}, | |
| {CKA_EXPONENT_1, std::data(dmp1), dmp1.length()}, | |
| {CKA_EXPONENT_2, std::data(dmq1), dmq1.length()}, | |
| {CKA_COEFFICIENT, std::data(iqmp), iqmp.length()}, | |
| }; | |
| int handle = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->CreateObject(private_attributes, | |
| std::size(private_attributes), &handle)); | |
| // For a software key, the sensitive attributes should still exist. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(handle, &object)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIVATE_EXPONENT)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIME_1)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIME_2)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_EXPONENT_1)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_EXPONENT_2)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_COEFFICIENT)); | |
| // Software key should not have security element related attributes. | |
| EXPECT_FALSE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_FALSE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_TRUE(object->GetAttributeBool(kKeyInSoftwareAttribute, false)); | |
| } | |
| TEST_F(TestSessionWithRealObject, ImportRSAWithForceSoftware) { | |
| EXPECT_CALL(hwsec_, IsRSAModulusSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, WrapRSAKey(_, _, _, _, _, _)).Times(0); | |
| crypto::ScopedBIGNUM exponent(BN_new()); | |
| CHECK(exponent); | |
| EXPECT_TRUE(BN_set_word(exponent.get(), 0x10001)); | |
| crypto::ScopedRSA rsa(RSA_new()); | |
| CHECK(rsa); | |
| EXPECT_TRUE(RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr)); | |
| CK_OBJECT_CLASS priv_class = CKO_PRIVATE_KEY; | |
| CK_KEY_TYPE key_type = CKK_RSA; | |
| CK_BBOOL false_value = CK_FALSE; | |
| CK_BBOOL true_value = CK_TRUE; | |
| string id = "test_id"; | |
| string label = "test_label"; | |
| const BIGNUM* rsa_n; | |
| const BIGNUM* rsa_e; | |
| const BIGNUM* rsa_d; | |
| const BIGNUM* rsa_p; | |
| const BIGNUM* rsa_q; | |
| const BIGNUM* rsa_dmp1; | |
| const BIGNUM* rsa_dmq1; | |
| const BIGNUM* rsa_iqmp; | |
| RSA_get0_key(rsa.get(), &rsa_n, &rsa_e, &rsa_d); | |
| RSA_get0_factors(rsa.get(), &rsa_p, &rsa_q); | |
| RSA_get0_crt_params(rsa.get(), &rsa_dmp1, &rsa_dmq1, &rsa_iqmp); | |
| string n = bn2bin(rsa_n); | |
| string e = bn2bin(rsa_e); | |
| string d = bn2bin(rsa_d); | |
| string p = bn2bin(rsa_p); | |
| string q = bn2bin(rsa_q); | |
| string dmp1 = bn2bin(rsa_dmp1); | |
| string dmq1 = bn2bin(rsa_dmq1); | |
| string iqmp = bn2bin(rsa_iqmp); | |
| CK_ATTRIBUTE private_attributes[] = { | |
| {CKA_CLASS, &priv_class, sizeof(priv_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_DECRYPT, &true_value, sizeof(true_value)}, | |
| {CKA_SIGN, &true_value, sizeof(true_value)}, | |
| {CKA_UNWRAP, &false_value, sizeof(false_value)}, | |
| {CKA_SENSITIVE, &true_value, sizeof(true_value)}, | |
| {CKA_TOKEN, &true_value, sizeof(true_value)}, | |
| {CKA_PRIVATE, &true_value, sizeof(true_value)}, | |
| {CKA_ID, std::data(id), id.length()}, | |
| {CKA_LABEL, std::data(label), label.length()}, | |
| {CKA_MODULUS, std::data(n), n.length()}, | |
| {CKA_PUBLIC_EXPONENT, std::data(e), e.length()}, | |
| {CKA_PRIVATE_EXPONENT, std::data(d), d.length()}, | |
| {CKA_PRIME_1, std::data(p), p.length()}, | |
| {CKA_PRIME_2, std::data(q), q.length()}, | |
| {CKA_EXPONENT_1, std::data(dmp1), dmp1.length()}, | |
| {CKA_EXPONENT_2, std::data(dmq1), dmq1.length()}, | |
| {CKA_COEFFICIENT, std::data(iqmp), iqmp.length()}, | |
| {kForceSoftwareAttribute, &true_value, sizeof(true_value)}, | |
| }; | |
| int handle = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->CreateObject(private_attributes, | |
| std::size(private_attributes), &handle)); | |
| // For a software key, the sensitive attributes should still exist. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(handle, &object)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIVATE_EXPONENT)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIME_1)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_PRIME_2)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_EXPONENT_1)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_EXPONENT_2)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_COEFFICIENT)); | |
| // Software key should not have security element related attributes. | |
| EXPECT_FALSE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_FALSE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_TRUE(object->GetAttributeBool(kKeyInSoftwareAttribute, false)); | |
| } | |
| TEST_F(TestSessionWithRealObject, ImportECCWithHWSec) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(NID_X9_62_prime256v1)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, WrapECCKey(_, _, _, _, _, | |
| hwsec::ChapsFrontend::AllowDecrypt::kAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| crypto::ScopedEC_KEY key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); | |
| ASSERT_NE(key, nullptr); | |
| // Focus GetECParametersAsString() dump OID to CKA_EC_PARAMS | |
| EC_KEY_set_asn1_flag(key.get(), OPENSSL_EC_NAMED_CURVE); | |
| ASSERT_TRUE(EC_KEY_generate_key(key.get())); | |
| CK_OBJECT_CLASS priv_class = CKO_PRIVATE_KEY; | |
| CK_KEY_TYPE key_type = CKK_EC; | |
| CK_BBOOL false_value = CK_FALSE; | |
| CK_BBOOL true_value = CK_TRUE; | |
| string id = "test_id"; | |
| string label = "test_label"; | |
| string ec_params = GetECParametersAsString(key.get()); | |
| string private_value = bn2bin(EC_KEY_get0_private_key(key.get())); | |
| CK_ATTRIBUTE private_attributes[] = { | |
| {CKA_CLASS, &priv_class, sizeof(priv_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_DECRYPT, &true_value, sizeof(true_value)}, | |
| {CKA_SIGN, &true_value, sizeof(true_value)}, | |
| {CKA_UNWRAP, &false_value, sizeof(false_value)}, | |
| {CKA_SENSITIVE, &true_value, sizeof(true_value)}, | |
| {CKA_TOKEN, &true_value, sizeof(true_value)}, | |
| {CKA_PRIVATE, &true_value, sizeof(true_value)}, | |
| {CKA_ID, std::data(id), id.length()}, | |
| {CKA_LABEL, std::data(label), label.length()}, | |
| {CKA_EC_PARAMS, std::data(ec_params), ec_params.length()}, | |
| {CKA_VALUE, std::data(private_value), private_value.length()}, | |
| }; | |
| int handle = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->CreateObject(private_attributes, | |
| std::size(private_attributes), &handle)); | |
| // There are a few sensitive attributes that MUST be removed. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(handle, &object)); | |
| EXPECT_FALSE(object->IsAttributePresent(CKA_VALUE)); | |
| // Check attributes that store security element wrapped blob exists. | |
| EXPECT_TRUE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_FALSE(object->GetAttributeBool(kKeyInSoftwareAttribute, true)); | |
| } | |
| TEST_F(TestSessionWithRealObject, ImportECCWithNoHWSec) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(_)) | |
| .WillRepeatedly( | |
| ReturnError<TPMError>("Not supported", TPMRetryAction::kNoRetry)); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(false)); | |
| crypto::ScopedEC_KEY key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); | |
| ASSERT_NE(key, nullptr); | |
| ASSERT_TRUE(EC_KEY_generate_key(key.get())); | |
| CK_OBJECT_CLASS priv_class = CKO_PRIVATE_KEY; | |
| CK_KEY_TYPE key_type = CKK_EC; | |
| CK_BBOOL false_value = CK_FALSE; | |
| CK_BBOOL true_value = CK_TRUE; | |
| string id = "test_id"; | |
| string label = "test_label"; | |
| string ec_params = GetECParametersAsString(key.get()); | |
| string private_value = bn2bin(EC_KEY_get0_private_key(key.get())); | |
| CK_ATTRIBUTE private_attributes[] = { | |
| {CKA_CLASS, &priv_class, sizeof(priv_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_DECRYPT, &true_value, sizeof(true_value)}, | |
| {CKA_SIGN, &true_value, sizeof(true_value)}, | |
| {CKA_UNWRAP, &false_value, sizeof(false_value)}, | |
| {CKA_SENSITIVE, &true_value, sizeof(true_value)}, | |
| {CKA_TOKEN, &true_value, sizeof(true_value)}, | |
| {CKA_PRIVATE, &true_value, sizeof(true_value)}, | |
| {CKA_ID, std::data(id), id.length()}, | |
| {CKA_LABEL, std::data(label), label.length()}, | |
| {CKA_EC_PARAMS, std::data(ec_params), ec_params.length()}, | |
| {CKA_VALUE, std::data(private_value), private_value.length()}, | |
| }; | |
| int handle = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->CreateObject(private_attributes, | |
| std::size(private_attributes), &handle)); | |
| // For a software key, the sensitive attributes should still exist. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(handle, &object)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_VALUE)); | |
| // Software key should not have security element related attributes. | |
| EXPECT_FALSE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_FALSE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_TRUE(object->GetAttributeBool(kKeyInSoftwareAttribute, false)); | |
| } | |
| TEST_F(TestSessionWithRealObject, ImportECCWithForceSoftware) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(NID_X9_62_prime256v1)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, WrapECCKey(_, _, _, _, _, _, _)).Times(0); | |
| crypto::ScopedEC_KEY key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); | |
| ASSERT_NE(key, nullptr); | |
| ASSERT_TRUE(EC_KEY_generate_key(key.get())); | |
| CK_OBJECT_CLASS priv_class = CKO_PRIVATE_KEY; | |
| CK_KEY_TYPE key_type = CKK_EC; | |
| CK_BBOOL false_value = CK_FALSE; | |
| CK_BBOOL true_value = CK_TRUE; | |
| string id = "test_id"; | |
| string label = "test_label"; | |
| string ec_params = GetECParametersAsString(key.get()); | |
| string private_value = bn2bin(EC_KEY_get0_private_key(key.get())); | |
| CK_ATTRIBUTE private_attributes[] = { | |
| {CKA_CLASS, &priv_class, sizeof(priv_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_DECRYPT, &true_value, sizeof(true_value)}, | |
| {CKA_SIGN, &true_value, sizeof(true_value)}, | |
| {CKA_UNWRAP, &false_value, sizeof(false_value)}, | |
| {CKA_SENSITIVE, &true_value, sizeof(true_value)}, | |
| {CKA_TOKEN, &true_value, sizeof(true_value)}, | |
| {CKA_PRIVATE, &true_value, sizeof(true_value)}, | |
| {CKA_ID, std::data(id), id.length()}, | |
| {CKA_LABEL, std::data(label), label.length()}, | |
| {CKA_EC_PARAMS, std::data(ec_params), ec_params.length()}, | |
| {CKA_VALUE, std::data(private_value), private_value.length()}, | |
| {kForceSoftwareAttribute, &true_value, sizeof(true_value)}, | |
| }; | |
| int handle = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->CreateObject(private_attributes, | |
| std::size(private_attributes), &handle)); | |
| // For a software key, the sensitive attributes should still exist. | |
| const Object* object = nullptr; | |
| ASSERT_TRUE(session_->GetObject(handle, &object)); | |
| EXPECT_TRUE(object->IsAttributePresent(CKA_VALUE)); | |
| // Software key should not have security element related attributes. | |
| EXPECT_FALSE(object->IsAttributePresent(kAuthDataAttribute)); | |
| EXPECT_FALSE(object->IsAttributePresent(kKeyBlobAttribute)); | |
| // Check that kKeyInSoftwareAttribute attribute is correctly set. | |
| EXPECT_TRUE(object->IsAttributePresent(kKeyInSoftwareAttribute)); | |
| EXPECT_TRUE(object->GetAttributeBool(kKeyInSoftwareAttribute, false)); | |
| } | |
| TEST_F(TestSessionWithRealObject, DeriveKeySp800108) { | |
| const Object* base_key; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 32, &base_key); | |
| const_cast<Object*>(base_key)->SetAttributeBool(CKA_DERIVE, true); | |
| ASSERT_TRUE(base_key->GetAttributeBool(CKA_NEVER_EXTRACTABLE, true)); | |
| ASSERT_TRUE(base_key->GetAttributeBool(CKA_ALWAYS_SENSITIVE, true)); | |
| CK_BYTE label[] = {0xde, 0xad, 0xbe, 0xef}; | |
| CK_BYTE context[] = {0xfe, 0xed, 0xbe, 0xef}; | |
| CK_PRF_DATA_PARAM data_params[]{ | |
| {CK_SP800_108_BYTE_ARRAY, label, sizeof(label)}, | |
| {CK_SP800_108_BYTE_ARRAY, context, sizeof(context)}, | |
| }; | |
| CK_SP800_108_KDF_PARAMS kdf_params = {CKK_SHA256_HMAC, std::size(data_params), | |
| data_params, 0, nullptr}; | |
| Sp800108KdfParams kdf_params_proto = Sp800108KdfParamsToProto(&kdf_params); | |
| CK_OBJECT_CLASS key_class = CKO_SECRET_KEY; | |
| CK_KEY_TYPE key_type = CKK_AES; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_ULONG size = 32; | |
| CK_ATTRIBUTE derived_key_attr[] = { | |
| {CKA_CLASS, &key_class, sizeof(key_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_ENCRYPT, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| int handle = 0; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDeriveKey, static_cast<int>(CKR_OK))) | |
| .Times(1); | |
| EXPECT_EQ(CKR_OK, session_->DeriveKey(CKM_SP800_108_COUNTER_KDF, | |
| kdf_params_proto.SerializeAsString(), | |
| *base_key, derived_key_attr, | |
| std::size(derived_key_attr), &handle)); | |
| const Object* derived_key; | |
| ASSERT_TRUE(session_->GetObject(handle, &derived_key)); | |
| EXPECT_TRUE(derived_key->GetAttributeInt(CKA_KEY_GEN_MECHANISM, 0) == | |
| CK_UNAVAILABLE_INFORMATION); | |
| EXPECT_TRUE(derived_key->GetAttributeBool(CKA_NEVER_EXTRACTABLE, true)); | |
| EXPECT_TRUE(derived_key->GetAttributeBool(CKA_ALWAYS_SENSITIVE, true)); | |
| } | |
| TEST_F(TestSessionWithRealObject, DeriveKeySp800108InvalidKdfParams) { | |
| const Object* base_key; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 32, &base_key); | |
| const_cast<Object*>(base_key)->SetAttributeBool(CKA_DERIVE, true); | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_ULONG size = 32; | |
| CK_OBJECT_CLASS key_class = CKO_SECRET_KEY; | |
| CK_KEY_TYPE key_type = CKK_AES; | |
| CK_ATTRIBUTE derived_key_attr[] = { | |
| {CKA_CLASS, &key_class, sizeof(key_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_ENCRYPT, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| // The mechanism_parameter isn't serialized from a Sp800108KdfParams proto. | |
| int handle = 0; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDeriveKey, | |
| static_cast<int>(CKR_MECHANISM_PARAM_INVALID))) | |
| .Times(3); | |
| EXPECT_EQ(CKR_MECHANISM_PARAM_INVALID, | |
| session_->DeriveKey(CKM_SP800_108_COUNTER_KDF, "test", *base_key, | |
| derived_key_attr, std::size(derived_key_attr), | |
| &handle)); | |
| // An empty mechanism_parameter can be deserialized into a Sp800108KdfParams | |
| // proto, but it'll fail later when parsing label and context from | |
| // CK_PRF_DATA_PARAM. | |
| EXPECT_EQ(CKR_MECHANISM_PARAM_INVALID, | |
| session_->DeriveKey(CKM_SP800_108_COUNTER_KDF, "", *base_key, | |
| derived_key_attr, std::size(derived_key_attr), | |
| &handle)); | |
| // The CK_PRF_DATA_PARAM obeys our implementation assumption (having only two | |
| // entries, representing the label, and context in order). | |
| CK_SP800_108_COUNTER_FORMAT counter_format = {0, 16}; | |
| CK_SP800_108_DKM_LENGTH_FORMAT dkm_format = { | |
| CK_SP800_108_DKM_LENGTH_SUM_OF_KEYS, 0, 16}; | |
| CK_PRF_DATA_PARAM data_params[] = { | |
| {CK_SP800_108_ITERATION_VARIABLE, &counter_format, | |
| sizeof(counter_format)}, | |
| {CK_SP800_108_DKM_LENGTH, &dkm_format, sizeof(dkm_format)}}; | |
| CK_SP800_108_KDF_PARAMS kdf_params = {CKK_SHA256_HMAC, std::size(data_params), | |
| data_params, 0, nullptr}; | |
| Sp800108KdfParams kdf_params_proto = Sp800108KdfParamsToProto(&kdf_params); | |
| EXPECT_EQ(CKR_MECHANISM_PARAM_INVALID, | |
| session_->DeriveKey(CKM_SP800_108_COUNTER_KDF, "test", *base_key, | |
| derived_key_attr, std::size(derived_key_attr), | |
| &handle)); | |
| } | |
| TEST_F(TestSessionWithRealObject, DeriveKeySp800108InvalidAttributes) { | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_ULONG size = 32; | |
| int handle = 0; | |
| const Object* base_key; | |
| // Generate the base_key for encrypt/decrypt. | |
| GenerateSecretKey(CKM_AES_KEY_GEN, 32, &base_key); | |
| CK_OBJECT_CLASS key_class = CKO_SECRET_KEY; | |
| CK_KEY_TYPE key_type = CKK_AES; | |
| CK_ATTRIBUTE derived_key_attr[] = { | |
| {CKA_CLASS, &key_class, sizeof(key_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_ENCRYPT, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| // The base_key doesn't have CKA_DERIVE attribute set to CK_TRUE. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDeriveKey, | |
| static_cast<int>(CKR_KEY_FUNCTION_NOT_PERMITTED))) | |
| .Times(1); | |
| EXPECT_EQ(CKR_KEY_FUNCTION_NOT_PERMITTED, | |
| session_->DeriveKey(CKM_SP800_108_COUNTER_KDF, "test", *base_key, | |
| derived_key_attr, std::size(derived_key_attr), | |
| &handle)); | |
| // Set the base_key for derive. | |
| const_cast<Object*>(base_key)->SetAttributeBool(CKA_DERIVE, true); | |
| key_type = CKK_DES; | |
| CK_ATTRIBUTE derived_key_attr2[] = { | |
| {CKA_CLASS, &key_class, sizeof(key_class)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}, | |
| {CKA_ENCRYPT, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| // Currently we don't support deriving DES keys using SP800-108. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDeriveKey, | |
| static_cast<int>(CKR_FUNCTION_NOT_SUPPORTED))) | |
| .Times(1); | |
| EXPECT_EQ(CKR_FUNCTION_NOT_SUPPORTED, | |
| session_->DeriveKey(CKM_SP800_108_COUNTER_KDF, "test", *base_key, | |
| derived_key_attr2, std::size(derived_key_attr2), | |
| &handle)); | |
| key_class = CKO_PRIVATE_KEY; | |
| CK_ATTRIBUTE rsapriv_key_attr[] = {{CKA_CLASS, &key_class, sizeof(key_class)}, | |
| {CKA_ENCRYPT, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| // We cannot only derive symmetric keys using SP800-108. | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionDeriveKey, | |
| static_cast<int>(CKR_TEMPLATE_INCONSISTENT))) | |
| .Times(1); | |
| EXPECT_EQ(CKR_TEMPLATE_INCONSISTENT, | |
| session_->DeriveKey(CKM_SP800_108_COUNTER_KDF, "test", *base_key, | |
| rsapriv_key_attr, std::size(rsapriv_key_attr), | |
| &handle)); | |
| } | |
| TEST_F(TestSessionWithRealObject, WrapKeyRSAOAEPSoftware) { | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| int rsa_size = 2048; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_WRAP, &yes, sizeof(yes)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &rsa_size, sizeof(rsa_size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_UNWRAP, &yes, sizeof(yes)}, | |
| {kForceSoftwareAttribute, &yes, sizeof(yes)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| const Object *rsapub, *rsapriv; | |
| ASSERT_TRUE(session_->GetObject(pubh, &rsapub)); | |
| ASSERT_TRUE(session_->GetObject(privh, &rsapriv)); | |
| const Object* aeskey = nullptr; | |
| int aes_size = 32; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, aes_size, &aeskey); | |
| const_cast<Object*>(aeskey)->SetAttributeBool(CKA_EXTRACTABLE, true); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, static_cast<int>(CKR_OK))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionUnwrapKey, static_cast<int>(CKR_OK))) | |
| .Times(1); | |
| int len = 0; | |
| string wrapped_key; | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, &len, | |
| &wrapped_key)); | |
| EXPECT_EQ(CKR_OK, session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, | |
| &len, &wrapped_key)); | |
| CK_KEY_TYPE key_type = CKK_AES; | |
| CK_ATTRIBUTE attr[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}}; | |
| int handle = 0; | |
| EXPECT_EQ(CKR_OK, | |
| session_->UnwrapKey(CKM_RSA_PKCS_OAEP, "", rsapriv, wrapped_key, | |
| attr, std::size(attr), &handle)); | |
| const Object* aeskey_new = nullptr; | |
| session_->GetObject(handle, &aeskey_new); | |
| EXPECT_EQ(aes_size, aeskey_new->GetAttributeInt(CKA_VALUE_LEN, 0)); | |
| EXPECT_EQ(false, aeskey_new->GetAttributeBool(CKA_LOCAL, true)); | |
| EXPECT_EQ(false, aeskey_new->GetAttributeBool(CKA_ALWAYS_SENSITIVE, true)); | |
| EXPECT_EQ(false, aeskey_new->GetAttributeBool(CKA_NEVER_EXTRACTABLE, true)); | |
| EXPECT_EQ(true, aeskey_new->GetAttributeBool(CKA_EXTRACTABLE, false)); | |
| EXPECT_EQ(aeskey->GetAttributeString(CKA_VALUE), | |
| aeskey_new->GetAttributeString(CKA_VALUE)); | |
| } | |
| TEST_F(TestSessionWithTpmSimulator, WrapKeyRSAOAEPWithHWSec) { | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| int rsa_size = 2048; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_WRAP, &yes, sizeof(yes)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &rsa_size, sizeof(rsa_size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_UNWRAP, &yes, sizeof(yes)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| const Object *rsapub, *rsapriv; | |
| ASSERT_TRUE(session_->GetObject(pubh, &rsapub)); | |
| ASSERT_TRUE(session_->GetObject(privh, &rsapriv)); | |
| const Object* aeskey = nullptr; | |
| int aes_size = 32; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, aes_size, &aeskey); | |
| const_cast<Object*>(aeskey)->SetAttributeBool(CKA_EXTRACTABLE, true); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, static_cast<int>(CKR_OK))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionUnwrapKey, static_cast<int>(CKR_OK))) | |
| .Times(1); | |
| int len = 0; | |
| string wrapped_key; | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, &len, | |
| &wrapped_key)); | |
| EXPECT_EQ(CKR_OK, session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, | |
| &len, &wrapped_key)); | |
| CK_KEY_TYPE key_type = CKK_AES; | |
| CK_ATTRIBUTE attr[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}}; | |
| int handle = 0; | |
| EXPECT_EQ(CKR_OK, | |
| session_->UnwrapKey(CKM_RSA_PKCS_OAEP, "", rsapriv, wrapped_key, | |
| attr, std::size(attr), &handle)); | |
| const Object* aeskey_new = nullptr; | |
| session_->GetObject(handle, &aeskey_new); | |
| EXPECT_EQ(aes_size, aeskey_new->GetAttributeInt(CKA_VALUE_LEN, 0)); | |
| EXPECT_EQ(false, aeskey_new->GetAttributeBool(CKA_LOCAL, true)); | |
| EXPECT_EQ(false, aeskey_new->GetAttributeBool(CKA_ALWAYS_SENSITIVE, true)); | |
| EXPECT_EQ(false, aeskey_new->GetAttributeBool(CKA_NEVER_EXTRACTABLE, true)); | |
| EXPECT_EQ(true, aeskey_new->GetAttributeBool(CKA_EXTRACTABLE, false)); | |
| EXPECT_EQ(aeskey->GetAttributeString(CKA_VALUE), | |
| aeskey_new->GetAttributeString(CKA_VALUE)); | |
| } | |
| TEST_F(TestSessionWithRealObject, WrapKeyRSAOAEPInvalidAttributes) { | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| int rsa_size = 2048; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_WRAP, &no, sizeof(no)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &rsa_size, sizeof(rsa_size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_UNWRAP, &no, sizeof(no)}, | |
| {CKA_EXTRACTABLE, &yes, sizeof(yes)}, | |
| {kForceSoftwareAttribute, &yes, sizeof(yes)}}; | |
| int pubh = 0, privh = 0; | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| const Object *rsapub, *rsapriv; | |
| ASSERT_TRUE(session_->GetObject(pubh, &rsapub)); | |
| ASSERT_TRUE(session_->GetObject(privh, &rsapriv)); | |
| const Object* aeskey = nullptr; | |
| int aes_size = 32; | |
| GenerateSecretKey(CKM_AES_KEY_GEN, aes_size, &aeskey); | |
| const_cast<Object*>(aeskey)->SetAttributeBool(CKA_WRAP, true); | |
| const_cast<Object*>(aeskey)->SetAttributeBool(CKA_UNWRAP, true); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, | |
| static_cast<int>(CKR_KEY_UNEXTRACTABLE))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, | |
| static_cast<int>(CKR_KEY_FUNCTION_NOT_PERMITTED))) | |
| .Times(1); | |
| EXPECT_CALL( | |
| mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, | |
| static_cast<int>(CKR_WRAPPING_KEY_TYPE_INCONSISTENT))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, | |
| static_cast<int>(CKR_KEY_NOT_WRAPPABLE))) | |
| .Times(2); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKey, static_cast<int>(CKR_OK))) | |
| .Times(2); | |
| int len = 0; | |
| string wrapped_key; | |
| // The key being wrapped doesn't have CKA_EXTRACTABLE attribute set to | |
| // CK_TRUE. | |
| EXPECT_EQ(CKR_KEY_UNEXTRACTABLE, | |
| session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, &len, | |
| &wrapped_key)); | |
| const_cast<Object*>(aeskey)->SetAttributeBool(CKA_EXTRACTABLE, true); | |
| // The wrapping_key doesn't have CKA_WRAP attribute set to CK_TRUE. | |
| EXPECT_EQ(CKR_KEY_FUNCTION_NOT_PERMITTED, | |
| session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, &len, | |
| &wrapped_key)); | |
| const_cast<Object*>(rsapub)->SetAttributeBool(CKA_WRAP, true); | |
| // The wrapping_key should be a rsa public key. | |
| EXPECT_EQ(CKR_WRAPPING_KEY_TYPE_INCONSISTENT, | |
| session_->WrapKey(CKM_RSA_PKCS_OAEP, "", aeskey, *aeskey, &len, | |
| &wrapped_key)); | |
| // Cannot wrap private keys using CKM_RSA_PKCS_OAEP. | |
| EXPECT_EQ(CKR_KEY_NOT_WRAPPABLE, | |
| session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *rsapriv, &len, | |
| &wrapped_key)); | |
| // WrapKey should now success. | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, &len, | |
| &wrapped_key)); | |
| EXPECT_EQ(CKR_OK, session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, | |
| &len, &wrapped_key)); | |
| // Setting CKA_WRAP_WITH_TRUSTED=true for the target should disable WrapKey. | |
| const_cast<Object*>(aeskey)->SetAttributeBool(CKA_WRAP_WITH_TRUSTED, true); | |
| EXPECT_EQ(CKR_KEY_NOT_WRAPPABLE, | |
| session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, &len, | |
| &wrapped_key)); | |
| // Setting CKA_TRUSTED=true for the wrapping should enable WrapKey again. | |
| // (p.s. normally chaps cannot set CKA_TRUSTED=true for any key.) | |
| const_cast<Object*>(rsapub)->SetAttributeBool(CKA_TRUSTED, true); | |
| EXPECT_EQ(CKR_OK, session_->WrapKey(CKM_RSA_PKCS_OAEP, "", rsapub, *aeskey, | |
| &len, &wrapped_key)); | |
| CK_KEY_TYPE key_type = CKK_AES; | |
| CK_ATTRIBUTE attr[] = {{CKA_TOKEN, &no, sizeof(no)}, | |
| {CKA_VALUE_LEN, &aes_size, sizeof(aes_size)}, | |
| {CKA_KEY_TYPE, &key_type, sizeof(key_type)}}; | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionUnwrapKey, | |
| static_cast<int>(CKR_KEY_FUNCTION_NOT_PERMITTED))) | |
| .Times(1); | |
| EXPECT_CALL( | |
| mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionUnwrapKey, | |
| static_cast<int>(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionUnwrapKey, | |
| static_cast<int>(CKR_FUNCTION_FAILED))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionUnwrapKey, static_cast<int>(CKR_OK))) | |
| .Times(1); | |
| int handle = 0; | |
| // The unwrapping_key doesn't have CKA_WRAP attribute set to CK_TRUE. | |
| EXPECT_EQ(CKR_KEY_FUNCTION_NOT_PERMITTED, | |
| session_->UnwrapKey(CKM_RSA_PKCS_OAEP, "", rsapriv, wrapped_key, | |
| attr, std::size(attr), &handle)); | |
| const_cast<Object*>(rsapriv)->SetAttributeBool(CKA_UNWRAP, true); | |
| // The unwrapping_key should be a rsa private key. | |
| EXPECT_EQ(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, | |
| session_->UnwrapKey(CKM_RSA_PKCS_OAEP, "", aeskey, wrapped_key, | |
| attr, std::size(attr), &handle)); | |
| // Generate another rsa key pair and unwrap the blob using the new | |
| // (mismatched) private key. | |
| ASSERT_EQ(CKR_OK, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| std::size(pub_attr), priv_attr, | |
| std::size(priv_attr), &pubh, &privh)); | |
| const Object* rsapriv2; | |
| ASSERT_TRUE(session_->GetObject(privh, &rsapriv2)); | |
| const_cast<Object*>(rsapriv2)->SetAttributeBool(CKA_UNWRAP, true); | |
| EXPECT_EQ(CKR_FUNCTION_FAILED, | |
| session_->UnwrapKey(CKM_RSA_PKCS_OAEP, "", rsapriv2, wrapped_key, | |
| attr, std::size(attr), &handle)); | |
| // Unwrap should now succeed. | |
| EXPECT_EQ(CKR_OK, | |
| session_->UnwrapKey(CKM_RSA_PKCS_OAEP, "", rsapriv, wrapped_key, | |
| attr, std::size(attr), &handle)); | |
| } | |
| TEST_F(TestSessionWithTpmSimulator, WrapKeyWithChaps) { | |
| const Object *rsapub, *rsapriv; | |
| GenerateRSAKeyPair(true, 1024, &rsapub, &rsapriv); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKeyWithChaps, | |
| static_cast<int>(CKR_KEY_NOT_WRAPPABLE))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKeyWithChaps, | |
| static_cast<int>(CKR_BUFFER_TOO_SMALL))) | |
| .Times(1); | |
| EXPECT_CALL( | |
| mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionWrapKeyWithChaps, static_cast<int>(CKR_OK))) | |
| .Times(1); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionUnwrapKeyWithChaps, | |
| static_cast<int>(CKR_OK))) | |
| .Times(1); | |
| int len = 0; | |
| string wrapped_key; | |
| EXPECT_EQ(CKR_KEY_NOT_WRAPPABLE, | |
| session_->WrapKey(kChapsKeyWrapMechanism, "", nullptr, *rsapriv, | |
| &len, &wrapped_key)); | |
| const_cast<Object*>(rsapriv)->SetAttributeBool(kChapsWrappableAttribute, | |
| true); | |
| EXPECT_EQ(CKR_BUFFER_TOO_SMALL, | |
| session_->WrapKey(kChapsKeyWrapMechanism, "", nullptr, *rsapriv, | |
| &len, &wrapped_key)); | |
| EXPECT_EQ(CKR_OK, session_->WrapKey(kChapsKeyWrapMechanism, "", nullptr, | |
| *rsapriv, &len, &wrapped_key)); | |
| int handle = 0; | |
| EXPECT_EQ(CKR_OK, session_->UnwrapKey(kChapsKeyWrapMechanism, "", nullptr, | |
| wrapped_key, {}, 0, &handle)); | |
| const Object* rsapriv_new = nullptr; | |
| session_->GetObject(handle, &rsapriv_new); | |
| EXPECT_TRUE(rsapriv_new->GetAttributeBool(CKA_LOCAL, false)); | |
| EXPECT_TRUE(rsapriv_new->GetAttributeBool(kChapsWrappableAttribute, false)); | |
| EXPECT_TRUE(rsapriv_new->GetAttributeBool(CKA_ALWAYS_SENSITIVE, false)); | |
| EXPECT_TRUE(rsapriv_new->GetAttributeBool(CKA_NEVER_EXTRACTABLE, false)); | |
| EXPECT_EQ(rsapriv->GetAttributeString(kKeyBlobAttribute), | |
| rsapriv_new->GetAttributeString(kKeyBlobAttribute)); | |
| } | |
| TEST_F(TestSession, CreateObjectsNoPrivate) { | |
| EXPECT_CALL(token_pool_, Insert(_)) | |
| .WillRepeatedly(Return(ObjectPool::Result::WaitForPrivateObjects)); | |
| int handle = 0; | |
| int size = 2048; | |
| CK_BBOOL no = CK_FALSE; | |
| CK_BBOOL yes = CK_TRUE; | |
| CK_OBJECT_CLASS oc = CKO_SECRET_KEY; | |
| CK_ATTRIBUTE attr[] = {{CKA_CLASS, &oc, sizeof(oc)}}; | |
| EXPECT_EQ(CKR_WOULD_BLOCK_FOR_PRIVATE_OBJECTS, | |
| session_->CreateObject(attr, std::size(attr), &handle)); | |
| CK_ATTRIBUTE key_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_SIGN, &yes, sizeof(yes)}, | |
| {CKA_VERIFY, &yes, sizeof(yes)}, | |
| {CKA_VALUE_LEN, &size, sizeof(size)}}; | |
| EXPECT_EQ(CKR_WOULD_BLOCK_FOR_PRIVATE_OBJECTS, | |
| session_->GenerateKey(CKM_GENERIC_SECRET_KEY_GEN, "", key_attr, 4, | |
| &handle)); | |
| CK_BYTE pubexp[] = {1, 0, 1}; | |
| CK_ATTRIBUTE pub_attr[] = {{CKA_TOKEN, &yes, sizeof(yes)}, | |
| {CKA_ENCRYPT, &no, sizeof(no)}, | |
| {CKA_VERIFY, &yes, sizeof(yes)}, | |
| {CKA_PUBLIC_EXPONENT, pubexp, 3}, | |
| {CKA_MODULUS_BITS, &size, sizeof(size)}}; | |
| CK_ATTRIBUTE priv_attr[] = {{CKA_TOKEN, &no, sizeof(CK_BBOOL)}, | |
| {CKA_DECRYPT, &no, sizeof(no)}, | |
| {CKA_SIGN, &yes, sizeof(yes)}}; | |
| EXPECT_EQ(CKR_WOULD_BLOCK_FOR_PRIVATE_OBJECTS, | |
| session_->GenerateKeyPair(CKM_RSA_PKCS_KEY_PAIR_GEN, "", pub_attr, | |
| 5, priv_attr, 3, &handle, &handle)); | |
| } | |
| TEST_F(TestSession, FindObjectsNoPrivate) { | |
| EXPECT_CALL(token_pool_, Find(_, _)) | |
| .WillRepeatedly(Return(ObjectPool::Result::WaitForPrivateObjects)); | |
| CK_OBJECT_CLASS oc = CKO_PRIVATE_KEY; | |
| CK_ATTRIBUTE attr[] = {{CKA_CLASS, &oc, sizeof(oc)}}; | |
| EXPECT_EQ(CKR_WOULD_BLOCK_FOR_PRIVATE_OBJECTS, | |
| session_->FindObjectsInit(attr, std::size(attr))); | |
| } | |
| TEST_F(TestSession, DestroyObjectsNoPrivate) { | |
| EXPECT_CALL(token_pool_, Delete(_)) | |
| .WillRepeatedly(Return(ObjectPool::Result::WaitForPrivateObjects)); | |
| int handle = 0; | |
| CK_OBJECT_CLASS oc = CKO_SECRET_KEY; | |
| CK_ATTRIBUTE attr[] = {{CKA_CLASS, &oc, sizeof(oc)}}; | |
| ASSERT_EQ(CKR_OK, session_->CreateObject(attr, std::size(attr), &handle)); | |
| EXPECT_EQ(CKR_WOULD_BLOCK_FOR_PRIVATE_OBJECTS, | |
| session_->DestroyObject(handle)); | |
| } | |
| TEST_F(TestSession, FlushObjectsNoPrivate) { | |
| EXPECT_CALL(token_pool_, Flush(_)) | |
| .WillRepeatedly(Return(ObjectPool::Result::WaitForPrivateObjects)); | |
| ObjectMock token_object; | |
| EXPECT_CALL(token_object, IsTokenObject()).WillRepeatedly(Return(true)); | |
| EXPECT_EQ(CKR_WOULD_BLOCK_FOR_PRIVATE_OBJECTS, | |
| session_->FlushModifiableObject(&token_object)); | |
| } | |
| TEST_F(TestSession, MultipleSignWithHWSec) { | |
| EXPECT_CALL(hwsec_, IsECCurveSupported(_)) | |
| .WillRepeatedly(ReturnOk<TPMError>()); | |
| EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true)); | |
| EXPECT_CALL(hwsec_, GetECCPublicKey(_)) | |
| .WillRepeatedly(ReturnValue(GenerateECCPublicInfo())); | |
| EXPECT_CALL(hwsec_, GenerateECCKey( | |
| _, _, hwsec::ChapsFrontend::AllowDecrypt::kNotAllow, | |
| hwsec::ChapsFrontend::AllowSign::kAllow)) | |
| .WillOnce([&](auto&&, auto&&, auto&&, auto&&) { | |
| return hwsec::ChapsFrontend::CreateKeyResult{ | |
| .key = GetTestScopedKey(), | |
| }; | |
| }); | |
| const Object* pub = nullptr; | |
| const Object* priv = nullptr; | |
| GenerateECCKeyPair(true, true, &pub, &priv); | |
| EXPECT_CALL(hwsec_, LoadKey(_, _)).WillRepeatedly([&](auto&&, auto&&) { | |
| return GetTestScopedKey(); | |
| }); | |
| EXPECT_CALL(hwsec_, Sign(_, _, _)) | |
| .WillRepeatedly(ReturnValue(brillo::Blob())); | |
| EXPECT_CALL(mock_metrics_library_, | |
| SendSparseToUMA(kChapsSessionSign, static_cast<int>(CKR_OK))) | |
| .WillRepeatedly(Return(true)); | |
| for (int i = 0; i < 100; i++) { | |
| EXPECT_EQ(CKR_OK, session_->OperationInit(kSign, CKM_ECDSA_SHA1, "", priv)); | |
| string in(100, 'A'); | |
| int len = 0; | |
| string sig; | |
| EXPECT_EQ(CKR_OK, session_->OperationSinglePart(kSign, in, &len, &sig)); | |
| } | |
| EXPECT_EQ(session_->get_object_key_map_size_for_testing(), 0); | |
| } | |
| } // namespace chaps | |
| int main(int argc, char** argv) { | |
| ::testing::InitGoogleMock(&argc, argv); | |
| OpenSSL_add_all_algorithms(); | |
| ERR_load_crypto_strings(); | |
| return RUN_ALL_TESTS(); | |
| } |