blob: 5806bebc13c44319cfcc0e27ce708f9f54d15aae [file] [log] [blame]
// Copyright 2015 The Chromium 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 "components/security_state/security_state_model.h"
#include <stdint.h>
#include "components/security_state/security_state_model_client.h"
#include "net/cert/x509_certificate.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/test/cert_test_util.h"
#include "net/test/test_certificate_data.h"
#include "net/test/test_data_directory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace security_state {
namespace {
const char kUrl[] = "https://foo.test";
class TestSecurityStateModelClient : public SecurityStateModelClient {
public:
TestSecurityStateModelClient()
: initial_security_level_(SecurityStateModel::SECURE),
connection_status_(net::SSL_CONNECTION_VERSION_TLS1_2
<< net::SSL_CONNECTION_VERSION_SHIFT),
cert_status_(net::CERT_STATUS_SHA1_SIGNATURE_PRESENT),
displayed_mixed_content_(false),
ran_mixed_content_(false),
fails_malware_check_(false) {
cert_ =
net::ImportCertFromFile(net::GetTestCertsDirectory(), "sha1_2016.pem");
}
~TestSecurityStateModelClient() override {}
void set_connection_status(int connection_status) {
connection_status_ = connection_status;
}
void SetCipherSuite(uint16_t ciphersuite) {
net::SSLConnectionStatusSetCipherSuite(ciphersuite, &connection_status_);
}
void AddCertStatus(net::CertStatus cert_status) {
cert_status_ |= cert_status;
}
void SetDisplayedMixedContent(bool displayed_mixed_content) {
displayed_mixed_content_ = displayed_mixed_content;
}
void SetRanMixedContent(bool ran_mixed_content) {
ran_mixed_content_ = ran_mixed_content;
}
void set_fails_malware_check(bool fails_malware_check) {
fails_malware_check_ = fails_malware_check;
}
void set_initial_security_level(
SecurityStateModel::SecurityLevel security_level) {
initial_security_level_ = security_level;
}
// SecurityStateModelClient:
void GetVisibleSecurityState(
SecurityStateModel::VisibleSecurityState* state) override {
state->connection_info_initialized = true;
state->url = GURL(kUrl);
state->initial_security_level = initial_security_level_;
state->cert_id = 1;
state->cert_status = cert_status_;
state->connection_status = connection_status_;
state->security_bits = 256;
state->displayed_mixed_content = displayed_mixed_content_;
state->ran_mixed_content = ran_mixed_content_;
state->fails_malware_check = fails_malware_check_;
}
bool RetrieveCert(scoped_refptr<net::X509Certificate>* cert) override {
*cert = cert_;
return true;
}
bool UsedPolicyInstalledCertificate() override { return false; }
// Always returns true because all unit tests in this file test
// scenarios in which the origin is secure.
bool IsOriginSecure(const GURL& url) override { return true; }
private:
SecurityStateModel::SecurityLevel initial_security_level_;
scoped_refptr<net::X509Certificate> cert_;
int connection_status_;
net::CertStatus cert_status_;
bool displayed_mixed_content_;
bool ran_mixed_content_;
bool fails_malware_check_;
};
// Tests that SHA1-signed certificates expiring in 2016 downgrade the
// security state of the page.
TEST(SecurityStateModelTest, SHA1Warning) {
TestSecurityStateModelClient client;
SecurityStateModel model;
model.SetClient(&client);
const SecurityStateModel::SecurityInfo& security_info =
model.GetSecurityInfo();
EXPECT_EQ(SecurityStateModel::DEPRECATED_SHA1_MINOR,
security_info.sha1_deprecation_status);
EXPECT_EQ(SecurityStateModel::NONE, security_info.security_level);
}
// Tests that SHA1 warnings don't interfere with the handling of mixed
// content.
TEST(SecurityStateModelTest, SHA1WarningMixedContent) {
TestSecurityStateModelClient client;
SecurityStateModel model;
model.SetClient(&client);
client.SetDisplayedMixedContent(true);
const SecurityStateModel::SecurityInfo& security_info1 =
model.GetSecurityInfo();
EXPECT_EQ(SecurityStateModel::DEPRECATED_SHA1_MINOR,
security_info1.sha1_deprecation_status);
EXPECT_EQ(SecurityStateModel::CONTENT_STATUS_DISPLAYED,
security_info1.mixed_content_status);
EXPECT_EQ(SecurityStateModel::NONE, security_info1.security_level);
client.set_initial_security_level(SecurityStateModel::SECURITY_ERROR);
client.SetDisplayedMixedContent(false);
client.SetRanMixedContent(true);
const SecurityStateModel::SecurityInfo& security_info2 =
model.GetSecurityInfo();
EXPECT_EQ(SecurityStateModel::DEPRECATED_SHA1_MINOR,
security_info2.sha1_deprecation_status);
EXPECT_EQ(SecurityStateModel::CONTENT_STATUS_RAN,
security_info2.mixed_content_status);
EXPECT_EQ(SecurityStateModel::SECURITY_ERROR, security_info2.security_level);
}
// Tests that SHA1 warnings don't interfere with the handling of major
// cert errors.
TEST(SecurityStateModelTest, SHA1WarningBrokenHTTPS) {
TestSecurityStateModelClient client;
SecurityStateModel model;
model.SetClient(&client);
client.set_initial_security_level(SecurityStateModel::SECURITY_ERROR);
client.AddCertStatus(net::CERT_STATUS_DATE_INVALID);
const SecurityStateModel::SecurityInfo& security_info =
model.GetSecurityInfo();
EXPECT_EQ(SecurityStateModel::DEPRECATED_SHA1_MINOR,
security_info.sha1_deprecation_status);
EXPECT_EQ(SecurityStateModel::SECURITY_ERROR, security_info.security_level);
}
// Tests that |security_info.is_secure_protocol_and_ciphersuite| is
// computed correctly.
TEST(SecurityStateModelTest, SecureProtocolAndCiphersuite) {
TestSecurityStateModelClient client;
SecurityStateModel model;
model.SetClient(&client);
// TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-4
const uint16_t ciphersuite = 0xc02f;
client.set_connection_status(net::SSL_CONNECTION_VERSION_TLS1_2
<< net::SSL_CONNECTION_VERSION_SHIFT);
client.SetCipherSuite(ciphersuite);
const SecurityStateModel::SecurityInfo& security_info =
model.GetSecurityInfo();
EXPECT_EQ(net::OBSOLETE_SSL_NONE, security_info.obsolete_ssl_status);
}
TEST(SecurityStateModelTest, NonsecureProtocol) {
TestSecurityStateModelClient client;
SecurityStateModel model;
model.SetClient(&client);
// TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-4
const uint16_t ciphersuite = 0xc02f;
client.set_connection_status(net::SSL_CONNECTION_VERSION_TLS1_1
<< net::SSL_CONNECTION_VERSION_SHIFT);
client.SetCipherSuite(ciphersuite);
const SecurityStateModel::SecurityInfo& security_info =
model.GetSecurityInfo();
EXPECT_EQ(net::OBSOLETE_SSL_MASK_PROTOCOL, security_info.obsolete_ssl_status);
}
TEST(SecurityStateModelTest, NonsecureCiphersuite) {
TestSecurityStateModelClient client;
SecurityStateModel model;
model.SetClient(&client);
// TLS_RSA_WITH_AES_128_CCM_8 from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-4
const uint16_t ciphersuite = 0xc0a0;
client.set_connection_status(net::SSL_CONNECTION_VERSION_TLS1_2
<< net::SSL_CONNECTION_VERSION_SHIFT);
client.SetCipherSuite(ciphersuite);
const SecurityStateModel::SecurityInfo& security_info =
model.GetSecurityInfo();
EXPECT_EQ(net::OBSOLETE_SSL_MASK_KEY_EXCHANGE | net::OBSOLETE_SSL_MASK_CIPHER,
security_info.obsolete_ssl_status);
}
// Tests that the malware/phishing status is set, and it overrides valid HTTPS.
TEST(SecurityStateModelTest, MalwareOverride) {
TestSecurityStateModelClient client;
SecurityStateModel model;
model.SetClient(&client);
// TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-4
const uint16_t ciphersuite = 0xc02f;
client.set_connection_status(net::SSL_CONNECTION_VERSION_TLS1_2
<< net::SSL_CONNECTION_VERSION_SHIFT);
client.SetCipherSuite(ciphersuite);
client.set_fails_malware_check(true);
const SecurityStateModel::SecurityInfo& security_info =
model.GetSecurityInfo();
EXPECT_TRUE(security_info.fails_malware_check);
EXPECT_EQ(SecurityStateModel::SECURITY_ERROR, security_info.security_level);
}
// Tests that the malware/phishing status is set, even if other connection info
// is not available.
TEST(SecurityStateModelTest, MalwareWithoutCOnnectionState) {
TestSecurityStateModelClient client;
SecurityStateModel model;
model.SetClient(&client);
client.set_fails_malware_check(true);
const SecurityStateModel::SecurityInfo& security_info =
model.GetSecurityInfo();
EXPECT_TRUE(security_info.fails_malware_check);
EXPECT_EQ(SecurityStateModel::SECURITY_ERROR, security_info.security_level);
}
} // namespace
} // namespace security_state