| // Copyright 2021 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cryptohome/cryptorecovery/recovery_crypto_hsm_cbor_serialization.h" |
| |
| #include <map> |
| #include <optional> |
| #include <utility> |
| #include <vector> |
| |
| #include <brillo/secure_blob.h> |
| #include <chromeos/cbor/writer.h> |
| #include <crypto/scoped_openssl_types.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <libhwsec-foundation/crypto/big_num_util.h> |
| #include <libhwsec-foundation/crypto/elliptic_curve.h> |
| #include <libhwsec-foundation/crypto/secure_blob_util.h> |
| #include <openssl/bn.h> |
| |
| #include "cryptohome/cryptorecovery/recovery_crypto_util.h" |
| |
| using ::hwsec_foundation::BigNumFromValue; |
| using ::hwsec_foundation::BigNumToSecureBlob; |
| using ::hwsec_foundation::CreateBigNumContext; |
| using ::hwsec_foundation::CreateSecureRandomBlob; |
| using ::hwsec_foundation::EllipticCurve; |
| using ::hwsec_foundation::ScopedBN_CTX; |
| using ::hwsec_foundation::SecureBlobToBigNum; |
| using ::testing::Eq; |
| |
| namespace cryptohome { |
| namespace cryptorecovery { |
| |
| namespace { |
| |
| constexpr EllipticCurve::CurveType kCurve = EllipticCurve::CurveType::kPrime256; |
| // Size of public/private key for EllipticCurve::CurveType::kPrime256. |
| constexpr size_t kEc256PubKeySize = 65; |
| constexpr size_t kEc256PrivKeySize = 32; |
| |
| const char kFakeUserId[] = "fake user id"; |
| const char kFakeAccessToken[] = "fake access token"; |
| const char kFakeRapt[] = "fake RAPT"; |
| const char kFakeHsmPayloadCipherText[] = "fake hsm payload cipher text"; |
| const char kFakeHsmPayloadAd[] = "fake hsm payload ad"; |
| const char kFakeHsmPayloadIv[] = "fake hsm payload iv"; |
| const char kFakeHsmPayloadTag[] = "fake hsm payload tag"; |
| const char kFakePayloadCipherText[] = "fake cipher text"; |
| const char kFakePayloadAd[] = "fake ad"; |
| const char kFakePayloadIv[] = "fake iv"; |
| const char kFakePayloadTag[] = "fake tag"; |
| const char kFakeResponseSalt[] = "fake response salt"; |
| const char kFakeMetadataCborKey[] = "fake metadata cbor key"; |
| const char kFakeMetadataCborValue[] = "fake metadata cbor value"; |
| // Generated by RecoveryResponseCborHelperTest.DeserializeRecoveryResponse with |
| // field order manually changed. |
| const char kFakeRecoveryResponseWithOutOfOrderKeyHex[] = |
| "A36A6572726F725F636F6465026C6572726F725F737472696E677166616B65206572726F72" |
| "20737472696E6769726573705F61656164A46261644766616B652061646263745066616B65" |
| "2063697068657220746578746269764766616B65206976637461674866616B6520746167"; |
| |
| bool CreateCborMapForTesting(const cbor::Value::MapValue& map, |
| brillo::SecureBlob* serialized_cbor_map) { |
| return SerializeCborForTesting(cbor::Value(map), serialized_cbor_map); |
| } |
| |
| bool FindMapValueInCborMap(const cbor::Value::MapValue& map, |
| const std::string& key, |
| cbor::Value* value) { |
| const auto entry = map.find(cbor::Value(key)); |
| if (entry == map.end() || !entry->second.is_map()) { |
| return false; |
| } |
| |
| *value = entry->second.Clone(); |
| return true; |
| } |
| |
| MATCHER_P2(SerializedCborMapContainsSecureBlobValue, key, expected_value, "") { |
| brillo::SecureBlob deserialized_value; |
| if (!GetBytestringValueFromCborMapByKeyForTesting(arg, key, |
| &deserialized_value)) { |
| return false; |
| } |
| return deserialized_value == expected_value; |
| } |
| |
| MATCHER_P2(CborMapContainsSecureBlobValue, key, expected_value, "") { |
| const auto entry = arg.find(cbor::Value(key)); |
| if (entry == arg.end() || !entry->second.is_bytestring()) { |
| return false; |
| } |
| |
| brillo::SecureBlob result(entry->second.GetBytestring().begin(), |
| entry->second.GetBytestring().end()); |
| return result == expected_value; |
| } |
| |
| MATCHER_P2(CborMapContainsIntegerValue, key, expected_value, "") { |
| const auto entry = arg.find(cbor::Value(key)); |
| if (entry == arg.end() || !entry->second.is_integer()) { |
| return false; |
| } |
| |
| return entry->second.GetInteger() == expected_value; |
| } |
| |
| MATCHER_P2(CborMapContainsStringValue, key, expected_value, "") { |
| const auto entry = arg.find(cbor::Value(key)); |
| if (entry == arg.end() || !entry->second.is_string()) { |
| return false; |
| } |
| |
| return entry->second.GetString() == expected_value; |
| } |
| |
| MATCHER_P(CborIntegerEq, expected_value, "") { |
| return arg.is_integer() && arg.GetInteger() == expected_value; |
| } |
| |
| } // namespace |
| |
| class HsmPayloadCborHelperTest : public testing::Test { |
| public: |
| void SetUp() override { |
| context_ = CreateBigNumContext(); |
| ASSERT_TRUE(context_); |
| ec_ = EllipticCurve::Create(kCurve, context_.get()); |
| ASSERT_TRUE(ec_); |
| ASSERT_TRUE(ec_->GenerateKeysAsSecureBlobs( |
| &publisher_pub_key_, &publisher_priv_key_, context_.get())); |
| ASSERT_TRUE(ec_->GenerateKeysAsSecureBlobs( |
| &channel_pub_key_, &channel_priv_key_, context_.get())); |
| ASSERT_TRUE(ec_->GenerateKeysAsSecureBlobs( |
| &dealer_pub_key_, &dealer_priv_key_, context_.get())); |
| |
| onboarding_meta_data_.cryptohome_user_type = UserType::kGaiaId; |
| onboarding_meta_data_.cryptohome_user = "User ID"; |
| onboarding_meta_data_.device_user_id = "Device User ID"; |
| onboarding_meta_data_.board_name = "Board Name"; |
| onboarding_meta_data_.model_name = "Model Name"; |
| onboarding_meta_data_.recovery_id = "Recovery ID"; |
| } |
| |
| protected: |
| ScopedBN_CTX context_; |
| std::optional<EllipticCurve> ec_; |
| brillo::SecureBlob publisher_pub_key_; |
| brillo::SecureBlob publisher_priv_key_; |
| brillo::SecureBlob channel_pub_key_; |
| brillo::SecureBlob channel_priv_key_; |
| brillo::SecureBlob dealer_pub_key_; |
| brillo::SecureBlob dealer_priv_key_; |
| brillo::SecureBlob rsa_public_key_; |
| OnboardingMetadata onboarding_meta_data_; |
| }; |
| |
| class AeadPayloadHelper { |
| public: |
| AeadPayloadHelper() { |
| fake_aead_payload_.cipher_text = brillo::SecureBlob(kFakePayloadCipherText); |
| fake_aead_payload_.associated_data = brillo::SecureBlob(kFakePayloadAd); |
| fake_aead_payload_.iv = brillo::SecureBlob(kFakePayloadIv); |
| fake_aead_payload_.tag = brillo::SecureBlob(kFakePayloadTag); |
| } |
| |
| const AeadPayload& GetFakePayload() const { return fake_aead_payload_; } |
| |
| cbor::Value::MapValue GetFakePayloadCborMap() const { |
| cbor::Value::MapValue payload; |
| payload.emplace(kAeadCipherText, fake_aead_payload_.cipher_text); |
| payload.emplace(kAeadAd, fake_aead_payload_.associated_data); |
| payload.emplace(kAeadIv, fake_aead_payload_.iv); |
| payload.emplace(kAeadTag, fake_aead_payload_.tag); |
| return payload; |
| } |
| |
| void ExpectEqualsToFakeAeadPayload(const AeadPayload& payload) const { |
| EXPECT_EQ(payload.cipher_text, fake_aead_payload_.cipher_text); |
| EXPECT_EQ(payload.associated_data, fake_aead_payload_.associated_data); |
| EXPECT_EQ(payload.iv, fake_aead_payload_.iv); |
| EXPECT_EQ(payload.tag, fake_aead_payload_.tag); |
| } |
| |
| void ExpectEqualsToFakeAeadPayload( |
| const cbor::Value::MapValue& cbor_map) const { |
| EXPECT_THAT(cbor_map, CborMapContainsSecureBlobValue( |
| kAeadCipherText, fake_aead_payload_.cipher_text)); |
| EXPECT_THAT(cbor_map, CborMapContainsSecureBlobValue( |
| kAeadAd, fake_aead_payload_.associated_data)); |
| EXPECT_THAT(cbor_map, |
| CborMapContainsSecureBlobValue(kAeadIv, fake_aead_payload_.iv)); |
| EXPECT_THAT(cbor_map, CborMapContainsSecureBlobValue( |
| kAeadTag, fake_aead_payload_.tag)); |
| } |
| |
| private: |
| AeadPayload fake_aead_payload_; |
| }; |
| |
| class RecoveryRequestCborHelperTest : public testing::Test { |
| public: |
| RecoveryRequestCborHelperTest() { |
| request_meta_data_.requestor_user_id = kFakeUserId; |
| request_meta_data_.requestor_user_id_type = UserType::kGaiaId; |
| AuthClaim auth_claim; |
| auth_claim.gaia_access_token = kFakeAccessToken; |
| auth_claim.gaia_reauth_proof_token = kFakeRapt; |
| request_meta_data_.auth_claim = auth_claim; |
| |
| cbor::Value::MapValue meta_data_cbor; |
| meta_data_cbor.emplace(kFakeMetadataCborKey, kFakeMetadataCborValue); |
| epoch_meta_data_.meta_data_cbor = cbor::Value(meta_data_cbor); |
| } |
| ~RecoveryRequestCborHelperTest() = default; |
| |
| void SetUp() override { |
| context_ = CreateBigNumContext(); |
| ASSERT_TRUE(context_); |
| ec_ = EllipticCurve::Create(kCurve, context_.get()); |
| ASSERT_TRUE(ec_); |
| ASSERT_TRUE(ec_->GenerateKeysAsSecureBlobs( |
| &epoch_pub_key_, &epoch_priv_key_, context_.get())); |
| } |
| |
| protected: |
| AeadPayloadHelper aead_helper_; |
| ScopedBN_CTX context_; |
| std::optional<EllipticCurve> ec_; |
| brillo::SecureBlob epoch_pub_key_; |
| brillo::SecureBlob epoch_priv_key_; |
| RequestMetadata request_meta_data_; |
| EpochMetadata epoch_meta_data_; |
| }; |
| |
| class RecoveryResponseCborHelperTest : public testing::Test { |
| protected: |
| AeadPayloadHelper aead_helper_; |
| const int fake_error_code_ = 2; |
| const std::string fake_error_string_ = "fake error string"; |
| const brillo::SecureBlob fake_pub_key_ = |
| CreateSecureRandomBlob(kEc256PubKeySize); |
| const brillo::SecureBlob fake_priv_key_ = |
| CreateSecureRandomBlob(kEc256PrivKeySize); |
| const brillo::SecureBlob salt_{kFakeResponseSalt}; |
| }; |
| |
| // Verifies serialization of HSM payload associated data to CBOR. |
| TEST_F(HsmPayloadCborHelperTest, GenerateAdCborWithEmptyRsaPublicKey) { |
| brillo::SecureBlob cbor_output; |
| HsmAssociatedData args; |
| args.publisher_pub_key = publisher_pub_key_; |
| args.channel_pub_key = channel_pub_key_; |
| args.onboarding_meta_data = onboarding_meta_data_; |
| |
| ASSERT_TRUE(SerializeHsmAssociatedDataToCbor(args, &cbor_output)); |
| |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kPublisherPublicKey, publisher_pub_key_)); |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kChannelPublicKey, channel_pub_key_)); |
| |
| cbor::Value deserialized_schema_version; |
| EXPECT_TRUE(GetValueFromCborMapByKeyForTesting(cbor_output, kSchemaVersion, |
| &deserialized_schema_version)); |
| EXPECT_THAT(deserialized_schema_version, |
| CborIntegerEq(kHsmAssociatedDataSchemaVersion)); |
| |
| cbor::Value deserialized_onboarding_metadata; |
| EXPECT_TRUE(GetValueFromCborMapByKeyForTesting( |
| cbor_output, kOnboardingMetaData, &deserialized_onboarding_metadata)); |
| ASSERT_TRUE(deserialized_onboarding_metadata.is_map()); |
| EXPECT_THAT(deserialized_onboarding_metadata.GetMap(), |
| CborMapContainsStringValue( |
| kCryptohomeUser, onboarding_meta_data_.cryptohome_user)); |
| EXPECT_THAT(deserialized_onboarding_metadata.GetMap(), |
| CborMapContainsStringValue(kDeviceUserId, |
| onboarding_meta_data_.device_user_id)); |
| EXPECT_THAT( |
| deserialized_onboarding_metadata.GetMap(), |
| CborMapContainsStringValue(kBoardName, onboarding_meta_data_.board_name)); |
| EXPECT_THAT( |
| deserialized_onboarding_metadata.GetMap(), |
| CborMapContainsStringValue(kModelName, onboarding_meta_data_.model_name)); |
| EXPECT_THAT(deserialized_onboarding_metadata.GetMap(), |
| CborMapContainsStringValue(kRecoveryId, |
| onboarding_meta_data_.recovery_id)); |
| EXPECT_THAT( |
| deserialized_onboarding_metadata.GetMap(), |
| CborMapContainsIntegerValue( |
| kCryptohomeUserType, |
| static_cast<int>(onboarding_meta_data_.cryptohome_user_type))); |
| EXPECT_THAT(deserialized_onboarding_metadata.GetMap(), |
| CborMapContainsIntegerValue(kSchemaVersion, |
| kOnboardingMetaDataSchemaVersion)); |
| EXPECT_EQ(deserialized_onboarding_metadata.GetMap().size(), 7); |
| |
| // 3 fields + schema version: |
| EXPECT_EQ(GetCborMapSize(cbor_output), 4); |
| } |
| |
| // Verifies serialization of HSM payload plain text encrypted payload to CBOR |
| // with key auth value. |
| TEST_F(HsmPayloadCborHelperTest, GeneratePlainTextHsmPayloadCborWithKav) { |
| brillo::SecureBlob key_auth_value("key auth value"); |
| brillo::SecureBlob mediator_share; |
| brillo::SecureBlob cbor_output; |
| |
| crypto::ScopedBIGNUM scalar = BigNumFromValue(123123123u); |
| ASSERT_TRUE(scalar); |
| ASSERT_TRUE(BigNumToSecureBlob(*scalar, 10, &mediator_share)); |
| |
| // Serialize plain text payload with kav. |
| HsmPlainText hsm_plain_text; |
| hsm_plain_text.mediator_share = mediator_share; |
| hsm_plain_text.dealer_pub_key = dealer_pub_key_; |
| hsm_plain_text.key_auth_value = key_auth_value; |
| ASSERT_TRUE(SerializeHsmPlainTextToCbor(hsm_plain_text, &cbor_output)); |
| |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kDealerPublicKey, dealer_pub_key_)); |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kKeyAuthValue, key_auth_value)); |
| brillo::SecureBlob deserialized_mediator_share; |
| EXPECT_TRUE(GetBytestringValueFromCborMapByKeyForTesting( |
| cbor_output, kMediatorShare, &deserialized_mediator_share)); |
| EXPECT_EQ(BN_get_word(SecureBlobToBigNum(deserialized_mediator_share).get()), |
| BN_get_word(scalar.get())); |
| EXPECT_EQ(GetCborMapSize(cbor_output), 3); |
| } |
| |
| // Verifies serialization of HSM payload plain text encrypted payload to CBOR |
| // without key auth value. |
| TEST_F(HsmPayloadCborHelperTest, GeneratePlainTextHsmPayloadCbor) { |
| brillo::SecureBlob mediator_share; |
| brillo::SecureBlob cbor_output; |
| |
| crypto::ScopedBIGNUM scalar = BigNumFromValue(123123123u); |
| ASSERT_TRUE(scalar); |
| ASSERT_TRUE(BigNumToSecureBlob(*scalar, 10, &mediator_share)); |
| |
| // Serialize plain text payload without kav. |
| HsmPlainText hsm_plain_text; |
| hsm_plain_text.mediator_share = mediator_share; |
| hsm_plain_text.dealer_pub_key = dealer_pub_key_; |
| ASSERT_TRUE(SerializeHsmPlainTextToCbor(hsm_plain_text, &cbor_output)); |
| |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kDealerPublicKey, dealer_pub_key_)); |
| brillo::SecureBlob deserialized_mediator_share; |
| EXPECT_TRUE(GetBytestringValueFromCborMapByKeyForTesting( |
| cbor_output, kMediatorShare, &deserialized_mediator_share)); |
| EXPECT_EQ(BN_get_word(SecureBlobToBigNum(deserialized_mediator_share).get()), |
| BN_get_word(scalar.get())); |
| EXPECT_EQ(GetCborMapSize(cbor_output), 2); |
| } |
| |
| // Verifies deserialization of HSM associated data from CBOR. |
| TEST_F(HsmPayloadCborHelperTest, DeserializeAssociatedDataHsmPayload) { |
| brillo::SecureBlob mediator_share; |
| brillo::SecureBlob cbor_output; |
| |
| crypto::ScopedBIGNUM scalar = BigNumFromValue(123123123u); |
| ASSERT_TRUE(scalar); |
| ASSERT_TRUE(BigNumToSecureBlob(*scalar, 10, &mediator_share)); |
| |
| // Serialize plain text payload with empty kav. |
| cbor::Value::MapValue fake_map; |
| fake_map.emplace(kPublisherPublicKey, publisher_pub_key_); |
| fake_map.emplace(kChannelPublicKey, channel_pub_key_); |
| fake_map.emplace(kRsaPublicKey, rsa_public_key_); |
| brillo::SecureBlob hsm_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(fake_map, &hsm_cbor)); |
| |
| HsmAssociatedData hsm_associated_data; |
| EXPECT_TRUE( |
| DeserializeHsmAssociatedDataFromCbor(hsm_cbor, &hsm_associated_data)); |
| |
| EXPECT_EQ(hsm_associated_data.publisher_pub_key, publisher_pub_key_); |
| EXPECT_EQ(hsm_associated_data.channel_pub_key, channel_pub_key_); |
| EXPECT_EQ(hsm_associated_data.rsa_public_key, rsa_public_key_); |
| } |
| |
| // Verifies that the deserialization of HSM associated data text from CBOR fails |
| // if input is not a CBOR. |
| TEST_F(HsmPayloadCborHelperTest, DeserializeAssociatedDataHsmPayloadNotCbor) { |
| HsmAssociatedData hsm_associated_data; |
| brillo::SecureBlob hsm_cbor("actually not a CBOR"); |
| EXPECT_FALSE( |
| DeserializeHsmAssociatedDataFromCbor(hsm_cbor, &hsm_associated_data)); |
| } |
| |
| // Verifies that the deserialization of HSM payload plain text from CBOR fails |
| // if input is not a CBOR map. |
| TEST_F(HsmPayloadCborHelperTest, DeserializeAssociatedDataHsmPayloadNotMap) { |
| HsmAssociatedData hsm_associated_data; |
| std::optional<std::vector<uint8_t>> serialized = |
| cbor::Writer::Write(cbor::Value("a CBOR but not a map")); |
| ASSERT_TRUE(serialized.has_value()); |
| brillo::SecureBlob hsm_cbor(serialized.value().begin(), |
| serialized.value().end()); |
| EXPECT_FALSE( |
| DeserializeHsmAssociatedDataFromCbor(hsm_cbor, &hsm_associated_data)); |
| } |
| |
| // Verifies that the deserialization of HSM payload plain text from CBOR fails |
| // if CBOR has wrong format. |
| TEST_F(HsmPayloadCborHelperTest, |
| DeserializeAssociatedDataHsmPayloadWrongFormat) { |
| HsmAssociatedData hsm_associated_data; |
| brillo::SecureBlob cbor_output; |
| cbor::Value::MapValue fake_map; |
| fake_map.emplace(kMediatorShare, "a string value instead of bytes"); |
| fake_map.emplace(kDealerPublicKey, dealer_pub_key_); |
| fake_map.emplace(kKeyAuthValue, brillo::SecureBlob()); |
| brillo::SecureBlob hsm_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(fake_map, &hsm_cbor)); |
| EXPECT_FALSE( |
| DeserializeHsmAssociatedDataFromCbor(hsm_cbor, &hsm_associated_data)); |
| } |
| |
| // Verifies deserialization of HSM payload plain text from CBOR. |
| TEST_F(HsmPayloadCborHelperTest, DeserializePlainTextHsmPayload) { |
| brillo::SecureBlob mediator_share; |
| brillo::SecureBlob cbor_output; |
| |
| crypto::ScopedBIGNUM scalar = BigNumFromValue(123123123u); |
| ASSERT_TRUE(scalar); |
| ASSERT_TRUE(BigNumToSecureBlob(*scalar, 10, &mediator_share)); |
| |
| // Serialize plain text payload with empty kav. |
| cbor::Value::MapValue fake_map; |
| fake_map.emplace(kDealerPublicKey, dealer_pub_key_); |
| fake_map.emplace(kMediatorShare, mediator_share); |
| fake_map.emplace(kKeyAuthValue, brillo::SecureBlob()); |
| brillo::SecureBlob hsm_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(fake_map, &hsm_cbor)); |
| |
| HsmPlainText hsm_plain_text; |
| DeserializeHsmPlainTextFromCbor(hsm_cbor, &hsm_plain_text); |
| |
| EXPECT_EQ(hsm_plain_text.dealer_pub_key, dealer_pub_key_); |
| EXPECT_EQ(hsm_plain_text.mediator_share, mediator_share); |
| EXPECT_TRUE(hsm_plain_text.key_auth_value.empty()); |
| } |
| |
| // Verifies that the deserialization of HSM payload plain text from CBOR fails |
| // if input is not a CBOR. |
| TEST_F(HsmPayloadCborHelperTest, DeserializePlainTextHsmPayloadNotCbor) { |
| HsmPlainText hsm_plain_text; |
| brillo::SecureBlob hsm_cbor("actually not a CBOR"); |
| EXPECT_FALSE(DeserializeHsmPlainTextFromCbor(hsm_cbor, &hsm_plain_text)); |
| } |
| |
| // Verifies that the deserialization of HSM payload plain text from CBOR fails |
| // if input is not a CBOR map. |
| TEST_F(HsmPayloadCborHelperTest, DeserializePlainTextHsmPayloadNotMap) { |
| HsmPlainText hsm_plain_text; |
| std::optional<std::vector<uint8_t>> serialized = |
| cbor::Writer::Write(cbor::Value("a CBOR but not a map")); |
| ASSERT_TRUE(serialized.has_value()); |
| brillo::SecureBlob hsm_cbor(serialized.value().begin(), |
| serialized.value().end()); |
| EXPECT_FALSE(DeserializeHsmPlainTextFromCbor(hsm_cbor, &hsm_plain_text)); |
| } |
| |
| // Verifies that the deserialization of HSM payload plain text from CBOR fails |
| // if CBOR has wrong format. |
| TEST_F(HsmPayloadCborHelperTest, DeserializePlainTextHsmPayloadWrongFormat) { |
| HsmPlainText hsm_plain_text; |
| brillo::SecureBlob cbor_output; |
| cbor::Value::MapValue fake_map; |
| fake_map.emplace(kMediatorShare, "a string value instead of bytes"); |
| fake_map.emplace(kDealerPublicKey, dealer_pub_key_); |
| fake_map.emplace(kKeyAuthValue, brillo::SecureBlob()); |
| brillo::SecureBlob hsm_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(fake_map, &hsm_cbor)); |
| EXPECT_FALSE(DeserializeHsmPlainTextFromCbor(hsm_cbor, &hsm_plain_text)); |
| } |
| |
| // Simulates failed attempt to get dealer public key from the Hsm payload |
| // associated data. |
| TEST_F(HsmPayloadCborHelperTest, FailedAttemptToGetPlainTextFieldFromAd) { |
| brillo::SecureBlob cbor_output; |
| HsmAssociatedData args; |
| args.publisher_pub_key = publisher_pub_key_; |
| args.channel_pub_key = channel_pub_key_; |
| args.onboarding_meta_data = onboarding_meta_data_; |
| ASSERT_TRUE(SerializeHsmAssociatedDataToCbor(args, &cbor_output)); |
| brillo::SecureBlob deserialized_dealer_pub_key; |
| EXPECT_FALSE(GetBytestringValueFromCborMapByKeyForTesting( |
| cbor_output, kDealerPublicKey, &deserialized_dealer_pub_key)); |
| } |
| |
| // Verifies serialization of Recovery Request payload associated data to CBOR. |
| TEST_F(RecoveryRequestCborHelperTest, GenerateAd) { |
| brillo::SecureBlob salt("fake salt"); |
| brillo::SecureBlob cbor_output; |
| |
| HsmPayload hsm_payload; |
| hsm_payload.cipher_text = brillo::SecureBlob(kFakeHsmPayloadCipherText); |
| hsm_payload.associated_data = brillo::SecureBlob(kFakeHsmPayloadAd); |
| hsm_payload.iv = brillo::SecureBlob(kFakeHsmPayloadIv); |
| hsm_payload.tag = brillo::SecureBlob(kFakeHsmPayloadTag); |
| |
| RecoveryRequestAssociatedData request_ad; |
| request_ad.hsm_payload = std::move(hsm_payload); |
| request_ad.request_meta_data = request_meta_data_; |
| request_ad.epoch_meta_data = epoch_meta_data_; |
| request_ad.epoch_pub_key = epoch_pub_key_; |
| request_ad.request_payload_salt = salt; |
| ASSERT_TRUE( |
| SerializeRecoveryRequestAssociatedDataToCbor(request_ad, &cbor_output)); |
| |
| HsmPayload deserialized_hsm_payload; |
| ASSERT_TRUE(GetHsmPayloadFromRequestAdForTesting(cbor_output, |
| &deserialized_hsm_payload)); |
| EXPECT_EQ(deserialized_hsm_payload.cipher_text.to_string(), |
| kFakeHsmPayloadCipherText); |
| EXPECT_EQ(deserialized_hsm_payload.associated_data.to_string(), |
| kFakeHsmPayloadAd); |
| EXPECT_EQ(deserialized_hsm_payload.iv.to_string(), kFakeHsmPayloadIv); |
| EXPECT_EQ(deserialized_hsm_payload.tag.to_string(), kFakeHsmPayloadTag); |
| |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kEpochPublicKey, epoch_pub_key_)); |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kRequestPayloadSalt, salt)); |
| |
| cbor::Value deserialized_request_meta_data; |
| EXPECT_TRUE(GetValueFromCborMapByKeyForTesting( |
| cbor_output, kRequestMetaData, &deserialized_request_meta_data)); |
| ASSERT_TRUE(deserialized_request_meta_data.is_map()); |
| EXPECT_THAT(deserialized_request_meta_data.GetMap(), |
| CborMapContainsStringValue(kRequestorUser, |
| request_meta_data_.requestor_user_id)); |
| EXPECT_THAT(deserialized_request_meta_data.GetMap(), |
| CborMapContainsIntegerValue( |
| kRequestorUserType, |
| static_cast<int>(request_meta_data_.requestor_user_id_type))); |
| EXPECT_THAT(deserialized_request_meta_data.GetMap(), |
| CborMapContainsIntegerValue(kSchemaVersion, |
| kRequestMetaDataSchemaVersion)); |
| |
| cbor::Value deserialized_auth_claim; |
| EXPECT_TRUE(FindMapValueInCborMap(deserialized_request_meta_data.GetMap(), |
| kAuthClaim, &deserialized_auth_claim)); |
| EXPECT_THAT( |
| deserialized_auth_claim.GetMap(), |
| CborMapContainsStringValue( |
| kGaiaAccessToken, request_meta_data_.auth_claim.gaia_access_token)); |
| EXPECT_THAT(deserialized_auth_claim.GetMap(), |
| CborMapContainsStringValue( |
| kGaiaReauthProofToken, |
| request_meta_data_.auth_claim.gaia_reauth_proof_token)); |
| EXPECT_EQ(deserialized_auth_claim.GetMap().size(), 2); |
| |
| EXPECT_EQ(deserialized_request_meta_data.GetMap().size(), 4); |
| |
| cbor::Value deserialized_epoch_meta_data; |
| EXPECT_TRUE(GetValueFromCborMapByKeyForTesting( |
| cbor_output, kEpochMetaData, &deserialized_epoch_meta_data)); |
| |
| brillo::SecureBlob epoch_meta_data_blob, expected_epoch_meta_data_blob; |
| ASSERT_TRUE(SerializeCborForTesting(deserialized_epoch_meta_data, |
| &epoch_meta_data_blob)); |
| ASSERT_TRUE(SerializeCborForTesting(epoch_meta_data_.meta_data_cbor, |
| &expected_epoch_meta_data_blob)); |
| EXPECT_EQ(epoch_meta_data_blob, expected_epoch_meta_data_blob); |
| |
| EXPECT_EQ(GetCborMapSize(cbor_output), 5); |
| } |
| |
| // Verifies serialization of Recovery Request payload plain text encrypted |
| // payload to CBOR. |
| TEST_F(RecoveryRequestCborHelperTest, GeneratePlainText) { |
| brillo::SecureBlob ephemeral_inverse_key; |
| crypto::ScopedBIGNUM scalar = BigNumFromValue(123u); |
| ASSERT_TRUE(scalar); |
| BN_set_negative(scalar.get(), 1); |
| crypto::ScopedEC_POINT inverse_point = |
| ec_->MultiplyWithGenerator(*scalar, context_.get()); |
| ASSERT_TRUE(ec_->PointToSecureBlob(*inverse_point, &ephemeral_inverse_key, |
| context_.get())); |
| |
| brillo::SecureBlob cbor_output; |
| RecoveryRequestPlainText plain_text; |
| plain_text.ephemeral_pub_inv_key = ephemeral_inverse_key; |
| ASSERT_TRUE( |
| SerializeRecoveryRequestPlainTextToCbor(plain_text, &cbor_output)); |
| |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kEphemeralPublicInvKey, ephemeral_inverse_key)); |
| EXPECT_EQ(GetCborMapSize(cbor_output), 1); |
| } |
| |
| // Verifies serialization of Recovery Request and Request Payload to CBOR. |
| TEST_F(RecoveryRequestCborHelperTest, SerializeRecoveryRequest) { |
| RecoveryRequest request; |
| RequestPayload request_payload = aead_helper_.GetFakePayload(); |
| ASSERT_TRUE(SerializeRecoveryRequestPayloadToCbor(request_payload, |
| &request.request_payload)); |
| |
| brillo::SecureBlob cbor_output; |
| EXPECT_TRUE(SerializeRecoveryRequestToCbor(request, &cbor_output)); |
| |
| RequestPayload deserialized_request_payload; |
| ASSERT_TRUE(DeserializeRecoveryRequestPayloadFromCbor( |
| request.request_payload, &deserialized_request_payload)); |
| aead_helper_.ExpectEqualsToFakeAeadPayload(deserialized_request_payload); |
| } |
| |
| // Verifies deserialization of Recovery Request from CBOR. |
| TEST_F(RecoveryRequestCborHelperTest, DeserializeRecoveryRequestPayload) { |
| brillo::SecureBlob request_payload_cbor; |
| ASSERT_TRUE(SerializeRecoveryRequestPayloadToCbor( |
| aead_helper_.GetFakePayload(), &request_payload_cbor)); |
| |
| RequestPayload request_payload; |
| EXPECT_TRUE(DeserializeRecoveryRequestPayloadFromCbor(request_payload_cbor, |
| &request_payload)); |
| aead_helper_.ExpectEqualsToFakeAeadPayload(request_payload); |
| } |
| |
| // Verifies that deserialization of Recovery Request from CBOR fails if payload |
| // has a missing field. |
| TEST_F(RecoveryRequestCborHelperTest, |
| DeserializeRecoveryRequestPayloadMissingField) { |
| cbor::Value::MapValue payload = aead_helper_.GetFakePayloadCborMap(); |
| // Remove `iv` value. |
| payload.erase(payload.find(cbor::Value(kAeadIv))); |
| |
| brillo::SecureBlob request_payload_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(payload, &request_payload_cbor)); |
| RequestPayload request_payload; |
| EXPECT_FALSE(DeserializeRecoveryRequestPayloadFromCbor(request_payload_cbor, |
| &request_payload)); |
| } |
| |
| // Verifies deserialization of Recovery Request from CBOR. |
| TEST_F(RecoveryRequestCborHelperTest, DeserializeRecoveryRequest) { |
| cbor::Value::MapValue request; |
| RequestPayload payload = aead_helper_.GetFakePayload(); |
| brillo::SecureBlob request_payload_cbor; |
| ASSERT_TRUE( |
| SerializeRecoveryRequestPayloadToCbor(payload, &request_payload_cbor)); |
| request.emplace(kRequestAead, request_payload_cbor); |
| brillo::SecureBlob request_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(request, &request_cbor)); |
| |
| RecoveryRequest recovery_request; |
| EXPECT_TRUE( |
| DeserializeRecoveryRequestFromCbor(request_cbor, &recovery_request)); |
| RequestPayload request_payload; |
| EXPECT_TRUE(DeserializeRecoveryRequestPayloadFromCbor( |
| recovery_request.request_payload, &request_payload)); |
| aead_helper_.ExpectEqualsToFakeAeadPayload(request_payload); |
| } |
| |
| // Verifies that deserialization of Recovery Request from CBOR fails if payload |
| // has wrong format. |
| TEST_F(RecoveryRequestCborHelperTest, DeserializeRecoveryRequestWrongFormat) { |
| cbor::Value::MapValue request; |
| // Field value is nested map instead of serialized CBOR. |
| request.emplace(kRequestAead, aead_helper_.GetFakePayloadCborMap()); |
| brillo::SecureBlob request_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(request, &request_cbor)); |
| |
| RecoveryRequest recovery_request; |
| EXPECT_FALSE( |
| DeserializeRecoveryRequestFromCbor(request_cbor, &recovery_request)); |
| } |
| |
| // Verifies that deserialization of Recovery Request from CBOR fails if payload |
| // has a missing field. |
| TEST_F(RecoveryRequestCborHelperTest, DeserializeRecoveryRequestMissingField) { |
| cbor::Value::MapValue request; |
| // Does not emplace `kRequestAead` value. |
| brillo::SecureBlob request_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(request, &request_cbor)); |
| |
| RecoveryRequest recovery_request; |
| EXPECT_FALSE( |
| DeserializeRecoveryRequestFromCbor(request_cbor, &recovery_request)); |
| } |
| |
| // Verifies serialization of Response payload associated data to CBOR. |
| TEST_F(RecoveryResponseCborHelperTest, SerializeAssociatedData) { |
| HsmResponseAssociatedData response_ad; |
| response_ad.response_payload_salt = salt_; |
| brillo::SecureBlob cbor_output; |
| ASSERT_TRUE( |
| SerializeHsmResponseAssociatedDataToCbor(response_ad, &cbor_output)); |
| |
| cbor::Value deserialized_hsm_meta_data; |
| EXPECT_TRUE(GetValueFromCborMapByKeyForTesting( |
| cbor_output, kResponseHsmMetaData, &deserialized_hsm_meta_data)); |
| ASSERT_TRUE(deserialized_hsm_meta_data.is_map()); |
| |
| EXPECT_THAT( |
| deserialized_hsm_meta_data.GetMap(), |
| CborMapContainsIntegerValue(kSchemaVersion, kHsmMetaDataSchemaVersion)); |
| EXPECT_EQ(deserialized_hsm_meta_data.GetMap().size(), 1); |
| |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kResponsePayloadSalt, salt_)); |
| EXPECT_EQ(GetCborMapSize(cbor_output), 2); |
| } |
| |
| // Verifies serialization of Response payload plain text to CBOR, without kav. |
| TEST_F(RecoveryResponseCborHelperTest, SerializePlainTextWithoutKav) { |
| brillo::SecureBlob mediated_point("mediated point"); |
| |
| HsmResponsePlainText response_plain_text; |
| response_plain_text.mediated_point = mediated_point; |
| response_plain_text.dealer_pub_key = fake_pub_key_; |
| response_plain_text.key_auth_value = brillo::SecureBlob(); |
| brillo::SecureBlob cbor_output; |
| ASSERT_TRUE( |
| SerializeHsmResponsePlainTextToCbor(response_plain_text, &cbor_output)); |
| |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kMediatedShare, mediated_point)); |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kDealerPublicKey, fake_pub_key_)); |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kKeyAuthValue, brillo::SecureBlob())); |
| EXPECT_EQ(GetCborMapSize(cbor_output), 3); |
| } |
| |
| // Verifies serialization of Response payload plain text to CBOR. |
| TEST_F(RecoveryResponseCborHelperTest, SerializePlainText) { |
| brillo::SecureBlob mediated_point("mediated point"); |
| brillo::SecureBlob key_auth_value("key auth value"); |
| |
| HsmResponsePlainText response_plain_text; |
| response_plain_text.mediated_point = mediated_point; |
| response_plain_text.dealer_pub_key = fake_pub_key_; |
| response_plain_text.key_auth_value = key_auth_value; |
| brillo::SecureBlob cbor_output; |
| ASSERT_TRUE( |
| SerializeHsmResponsePlainTextToCbor(response_plain_text, &cbor_output)); |
| |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kMediatedShare, mediated_point)); |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kDealerPublicKey, fake_pub_key_)); |
| EXPECT_THAT(cbor_output, SerializedCborMapContainsSecureBlobValue( |
| kKeyAuthValue, key_auth_value)); |
| EXPECT_EQ(GetCborMapSize(cbor_output), 3); |
| } |
| |
| // Verifies deserialization of Response payload associated data from CBOR. |
| TEST_F(RecoveryResponseCborHelperTest, DeserializeAssociatedData) { |
| cbor::Value::MapValue fake_map; |
| cbor::Value::MapValue hsm_meta_data; |
| fake_map.emplace(kResponseHsmMetaData, hsm_meta_data); |
| fake_map.emplace(kResponsePayloadSalt, salt_); |
| brillo::SecureBlob response_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(fake_map, &response_cbor)); |
| |
| HsmResponseAssociatedData response_ad; |
| EXPECT_TRUE(DeserializeHsmResponseAssociatedDataFromCbor(response_cbor, |
| &response_ad)); |
| EXPECT_EQ(response_ad.response_payload_salt, salt_); |
| } |
| |
| // Verifies that deserialization of Response payload associated data from CBOR |
| // fails when input is not a map. |
| TEST_F(RecoveryResponseCborHelperTest, DeserializeAssociatedDataNotMap) { |
| std::optional<std::vector<uint8_t>> serialized = |
| cbor::Writer::Write(cbor::Value("a CBOR but not a map")); |
| ASSERT_TRUE(serialized.has_value()); |
| brillo::SecureBlob response_cbor(serialized.value().begin(), |
| serialized.value().end()); |
| |
| HsmResponseAssociatedData response_ad; |
| EXPECT_FALSE(DeserializeHsmResponseAssociatedDataFromCbor(response_cbor, |
| &response_ad)); |
| } |
| |
| // Verifies that deserialization of Response payload associated data from CBOR |
| // fails when a field has a wrong format. |
| TEST_F(RecoveryResponseCborHelperTest, DeserializeAssociatedDataWrongFormat) { |
| cbor::Value::MapValue fake_map; |
| fake_map.emplace(kResponseHsmMetaData, "a string value instead of bytes"); |
| fake_map.emplace(kResponsePayloadSalt, salt_); |
| brillo::SecureBlob response_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(fake_map, &response_cbor)); |
| |
| HsmResponseAssociatedData response_ad; |
| EXPECT_FALSE(DeserializeHsmResponseAssociatedDataFromCbor(response_cbor, |
| &response_ad)); |
| } |
| |
| // Verifies deserialization of Response payload plain text from CBOR. |
| TEST_F(RecoveryResponseCborHelperTest, DeserializePlainText) { |
| brillo::SecureBlob mediated_point("mediated point"); |
| cbor::Value::MapValue fake_map; |
| fake_map.emplace(kMediatedShare, mediated_point); |
| fake_map.emplace(kDealerPublicKey, fake_pub_key_); |
| fake_map.emplace(kKeyAuthValue, brillo::SecureBlob()); |
| brillo::SecureBlob response_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(fake_map, &response_cbor)); |
| |
| HsmResponsePlainText response_plain_text; |
| EXPECT_TRUE(DeserializeHsmResponsePlainTextFromCbor(response_cbor, |
| &response_plain_text)); |
| EXPECT_EQ(response_plain_text.mediated_point, mediated_point); |
| EXPECT_EQ(response_plain_text.dealer_pub_key, fake_pub_key_); |
| EXPECT_EQ(response_plain_text.key_auth_value, brillo::SecureBlob()); |
| } |
| |
| // Verifies that deserialization of Response payload plain text from CBOR fails |
| // when input is not a map. |
| TEST_F(RecoveryResponseCborHelperTest, DeserializePlainTextNotMap) { |
| std::optional<std::vector<uint8_t>> serialized = |
| cbor::Writer::Write(cbor::Value("a CBOR but not a map")); |
| ASSERT_TRUE(serialized.has_value()); |
| brillo::SecureBlob response_cbor(serialized.value().begin(), |
| serialized.value().end()); |
| |
| HsmResponsePlainText response_plain_text; |
| EXPECT_FALSE(DeserializeHsmResponsePlainTextFromCbor(response_cbor, |
| &response_plain_text)); |
| } |
| |
| // Verifies that deserialization of Response payload plain text from CBOR fails |
| // when a field has a wrong format. |
| TEST_F(RecoveryResponseCborHelperTest, DeserializePlainTextWrongFormat) { |
| cbor::Value::MapValue fake_map; |
| fake_map.emplace(kMediatedShare, "a string value instead of bytes"); |
| fake_map.emplace(kDealerPublicKey, fake_pub_key_); |
| fake_map.emplace(kKeyAuthValue, brillo::SecureBlob()); |
| brillo::SecureBlob response_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(fake_map, &response_cbor)); |
| |
| HsmResponsePlainText response_plain_text; |
| EXPECT_FALSE(DeserializeHsmResponsePlainTextFromCbor(response_cbor, |
| &response_plain_text)); |
| } |
| |
| // Verifies serialization of Recovery Response to CBOR. |
| TEST_F(RecoveryResponseCborHelperTest, SerializeRecoveryResponse) { |
| RecoveryResponse response; |
| response.response_payload = aead_helper_.GetFakePayload(); |
| response.error_code = fake_error_code_; |
| response.error_string = fake_error_string_; |
| |
| brillo::SecureBlob cbor_output; |
| EXPECT_TRUE(SerializeRecoveryResponseToCbor(response, &cbor_output)); |
| |
| cbor::Value deserialized_error_code; |
| EXPECT_TRUE(GetValueFromCborMapByKeyForTesting( |
| cbor_output, kResponseErrorCode, &deserialized_error_code)); |
| EXPECT_THAT(deserialized_error_code, CborIntegerEq(response.error_code)); |
| |
| cbor::Value deserialized_error_string; |
| EXPECT_TRUE(GetValueFromCborMapByKeyForTesting( |
| cbor_output, kResponseErrorString, &deserialized_error_string)); |
| ASSERT_TRUE(deserialized_error_string.is_string()); |
| EXPECT_EQ(deserialized_error_string.GetString(), response.error_string); |
| |
| cbor::Value deserialized_response_payload; |
| EXPECT_TRUE(GetValueFromCborMapByKeyForTesting( |
| cbor_output, kResponseAead, &deserialized_response_payload)); |
| ASSERT_TRUE(deserialized_response_payload.is_map()); |
| aead_helper_.ExpectEqualsToFakeAeadPayload( |
| deserialized_response_payload.GetMap()); |
| EXPECT_EQ(GetCborMapSize(cbor_output), 3); |
| } |
| |
| // Verifies deserialization of Recovery Response from CBOR. |
| TEST_F(RecoveryResponseCborHelperTest, DeserializeRecoveryResponse) { |
| cbor::Value::MapValue response; |
| response.emplace(kResponseAead, aead_helper_.GetFakePayloadCborMap()); |
| response.emplace(kResponseErrorCode, fake_error_code_); |
| response.emplace(kResponseErrorString, fake_error_string_); |
| brillo::SecureBlob response_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(response, &response_cbor)); |
| |
| RecoveryResponse recovery_response; |
| EXPECT_TRUE( |
| DeserializeRecoveryResponseFromCbor(response_cbor, &recovery_response)); |
| EXPECT_EQ(recovery_response.error_code, fake_error_code_); |
| EXPECT_EQ(recovery_response.error_string, fake_error_string_); |
| aead_helper_.ExpectEqualsToFakeAeadPayload( |
| recovery_response.response_payload); |
| } |
| |
| // Verifies that deserialization of Recovery Response from CBOR fails if payload |
| // has a wrong format. |
| TEST_F(RecoveryResponseCborHelperTest, DeserializeRecoveryResponseWrongFormat) { |
| brillo::SecureBlob payload_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(aead_helper_.GetFakePayloadCborMap(), |
| &payload_cbor)); |
| |
| cbor::Value::MapValue response; |
| // Field value is serialized CBOR instead of nested map. |
| response.emplace(kResponseAead, payload_cbor); |
| response.emplace(kResponseErrorCode, fake_error_code_); |
| response.emplace(kResponseErrorString, fake_error_string_); |
| brillo::SecureBlob response_cbor; |
| ASSERT_TRUE(CreateCborMapForTesting(response, &response_cbor)); |
| |
| RecoveryResponse recovery_response; |
| EXPECT_FALSE( |
| DeserializeRecoveryResponseFromCbor(response_cbor, &recovery_response)); |
| } |
| |
| TEST_F(RecoveryResponseCborHelperTest, DeserializeFakeRecoveryResponse) { |
| brillo::SecureBlob response_cbor; |
| ASSERT_TRUE(brillo::SecureBlob::HexStringToSecureBlob( |
| kFakeRecoveryResponseWithOutOfOrderKeyHex, &response_cbor)); |
| |
| RecoveryResponse recovery_response; |
| EXPECT_TRUE( |
| DeserializeRecoveryResponseFromCbor(response_cbor, &recovery_response)); |
| EXPECT_EQ(recovery_response.error_code, fake_error_code_); |
| EXPECT_EQ(recovery_response.error_string, fake_error_string_); |
| aead_helper_.ExpectEqualsToFakeAeadPayload( |
| recovery_response.response_payload); |
| } |
| |
| } // namespace cryptorecovery |
| } // namespace cryptohome |