0

Use net::SSLInfo and friends in Blink

This CL reduces the amount of boilerplate needed to route TLS features
from the net stack to DevTools. There is something to be said for not
leaking this to the renderer in the first place, but that would require
a DevTools protocol change. This CL does not address that, but fixing it
could further reduce boilerploate. (In some configurations, the server
certificate we observed is PII. It's not web-platform-exposed, but it
seems odd to send non-platform-exposed things to the rendewrer.)

Blink currently has SecurityDetails/WebSecurityDetails, which contain a
slightly different version of the information in net::SSLInfo. These are
only used by DevTools. Due to historical reasons (before the repos were
merged, it was difficult to call into //net's logic to interpret TLS
codepoints), this information is stringified fairly early. This meant:

1. DevTools-specific display logic was split between ResourceResponse
   and InspectorNetworkAgent.

2. Blink retained string versions of codepoints which are much more
   compactly described with integers and enums in //net.

Meanwhile, the navigation stack was refactored, and now SecurityDetails
for navigation requests must be routed completely differently, up in
//content/browser. There, network_handler.cc has logic to directly
convert from net::SSLInfo to the DevTools protocol message.

Align the two paths and have Blink store a net::SSLInfo, instead of
maintaining parallel WebSecurityDetails and SecurityDetails structures.
This avoids needing to add each field two extra structs and plumb
between the two. It also stores a more compact representation.  Note
that net::X509Certificate is backed by CRYPTO_BUFFER, which is already a
certificate-specific AtomicString-like BoringSSL type. So retaining a
net::X509Certificate means we already get the memory savings of
AtomicString.

Unfortunately, we cannot quite deduplicate the Blink and content copies
of this logic, because the DevTools bindings are different between the
two. However, now the logic is at least analogous.

Since, as a result, we're losing some tests of the Blink half of the
logic (web_url_loader_unittest.cc checking different SAN combinations is
no longer relevant at that layer), I've added some new tests in
devtools_protocol_browsertest.cc, next to the very similar (but slightly
different) CertificateSecurityState test. These tests cannot use the
usual Blink machinery because they require configuring specific TLS
servers, so I've moved them up the stack.

Bug: none
Change-Id: I0b66f6c7e56395e1df2579103fe0540f9798d3ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3258063
Commit-Queue: David Benjamin <davidben@chromium.org>
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/main@{#938477}
This commit is contained in:
David Benjamin
2021-11-04 20:43:25 +00:00
committed by Chromium LUCI CQ
parent 7b59923253
commit 316373d9a7
21 changed files with 435 additions and 518 deletions

@ -31,12 +31,16 @@
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/test/extension_test_message_listener.h"
#include "net/base/ip_address.h"
#include "net/dns/mock_host_resolver.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_config.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_server_config.h"
#include "printing/buildflags/buildflags.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/boringssl/src/include/openssl/nid.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
using DevToolsProtocolTest = DevToolsProtocolTestBase;
@ -358,6 +362,240 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, VisibleSecurityStateSecureState) {
ASSERT_FALSE(params.FindPath("visibleSecurityState.safetyTipInfo"));
}
class NetworkResponseProtocolTest : public DevToolsProtocolTest {
protected:
base::Value FetchAndWaitForResponse(const GURL& url) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
std::string script =
content::JsReplace("fetch($1).then(r => r.status)", url.spec());
content::EvalJsResult status = content::EvalJs(web_contents, script);
EXPECT_EQ(200, status);
if (!(200 == status)) {
return base::Value();
}
// Look for the requestId.
auto matches_url = [](const GURL& url, const base::Value& params) {
const std::string* got_url = params.FindStringPath("request.url");
return got_url && *got_url == url.spec();
};
base::Value request = WaitForMatchingNotification(
"Network.requestWillBeSent", base::BindRepeating(matches_url, url));
const std::string* request_id = request.FindStringPath("requestId");
if (!request_id) {
ADD_FAILURE() << "Could not find request ID";
return base::Value();
}
// Look for the response.
auto matches_id = [](const std::string& request_id,
const base::Value& params) {
const std::string* id = params.FindStringPath("requestId");
return id && *id == request_id;
};
return WaitForMatchingNotification(
"Network.responseReceived",
base::BindRepeating(matches_id, *request_id));
}
};
// Test that the SecurityDetails field of the resource response matches the
// server.
IN_PROC_BROWSER_TEST_F(NetworkResponseProtocolTest, SecurityDetails) {
// Configure a specific TLS configuration to compare against.
net::SSLServerConfig server_config;
server_config.version_min = net::SSL_PROTOCOL_VERSION_TLS1_2;
server_config.version_max = net::SSL_PROTOCOL_VERSION_TLS1_2;
// TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
server_config.cipher_suite_for_testing = 0xc02f;
server_config.curves_for_testing = {NID_X25519};
net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS);
server.SetSSLConfig(net::EmbeddedTestServer::ServerCertificate::CERT_OK,
server_config);
server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(server.Start());
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), server.GetURL("/title1.html"), 1);
Attach();
SendCommand("Network.enable");
base::Value response = FetchAndWaitForResponse(server.GetURL("/empty.html"));
const std::string* protocol =
response.FindStringPath("response.securityDetails.protocol");
ASSERT_TRUE(protocol);
EXPECT_EQ("TLS 1.2", *protocol);
const std::string* key_exchange =
response.FindStringPath("response.securityDetails.keyExchange");
ASSERT_TRUE(key_exchange);
EXPECT_EQ("ECDHE_RSA", *key_exchange);
const std::string* cipher =
response.FindStringPath("response.securityDetails.cipher");
ASSERT_TRUE(cipher);
EXPECT_EQ("AES_128_GCM", *cipher);
// AEAD ciphers should not report a MAC.
EXPECT_FALSE(response.FindStringPath("response.securityDetails.mac"));
const std::string* group =
response.FindStringPath("response.securityDetails.keyExchangeGroup");
ASSERT_TRUE(group);
EXPECT_EQ("X25519", *group);
const std::string* subject =
response.FindStringPath("response.securityDetails.subjectName");
ASSERT_TRUE(subject);
EXPECT_EQ(server.GetCertificate()->subject().common_name, *subject);
const std::string* issuer =
response.FindStringPath("response.securityDetails.issuer");
ASSERT_TRUE(issuer);
EXPECT_EQ(server.GetCertificate()->issuer().common_name, *issuer);
// The default certificate has a single SAN, 127.0.0.1.
const base::Value* sans =
response.FindListPath("response.securityDetails.sanList");
ASSERT_TRUE(sans);
ASSERT_EQ(1u, sans->GetList().size());
EXPECT_EQ(base::Value("127.0.0.1"), sans->GetList()[0]);
absl::optional<double> valid_from =
response.FindDoublePath("response.securityDetails.validFrom");
EXPECT_EQ(server.GetCertificate()->valid_start().ToDoubleT(), valid_from);
absl::optional<double> valid_to =
response.FindDoublePath("response.securityDetails.validTo");
EXPECT_EQ(server.GetCertificate()->valid_expiry().ToDoubleT(), valid_to);
}
// Test SecurityDetails, but with a TLS 1.3 cipher suite, which should not
// report a key exchange component.
IN_PROC_BROWSER_TEST_F(NetworkResponseProtocolTest, SecurityDetailsTLS13) {
// Configure a specific TLS configuration to compare against.
net::SSLServerConfig server_config;
server_config.version_min = net::SSL_PROTOCOL_VERSION_TLS1_3;
server_config.version_max = net::SSL_PROTOCOL_VERSION_TLS1_3;
server_config.curves_for_testing = {NID_X25519};
net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS);
server.SetSSLConfig(net::EmbeddedTestServer::ServerCertificate::CERT_OK,
server_config);
server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(server.Start());
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), server.GetURL("/title1.html"), 1);
Attach();
SendCommand("Network.enable");
base::Value response = FetchAndWaitForResponse(server.GetURL("/empty.html"));
const std::string* protocol =
response.FindStringPath("response.securityDetails.protocol");
ASSERT_TRUE(protocol);
EXPECT_EQ("TLS 1.3", *protocol);
const std::string* key_exchange =
response.FindStringPath("response.securityDetails.keyExchange");
ASSERT_TRUE(key_exchange);
EXPECT_EQ("", *key_exchange);
const std::string* cipher =
response.FindStringPath("response.securityDetails.cipher");
ASSERT_TRUE(cipher);
// Depending on whether the host machine has AES hardware, the server may
// pick AES-GCM or ChaCha20-Poly1305.
EXPECT_TRUE(*cipher == "AES_128_GCM" || *cipher == "CHACHA20_POLY1305");
// AEAD ciphers should not report a MAC.
EXPECT_FALSE(response.FindStringPath("response.securityDetails.mac"));
const std::string* group =
response.FindStringPath("response.securityDetails.keyExchangeGroup");
ASSERT_TRUE(group);
EXPECT_EQ("X25519", *group);
}
// Test SecurityDetails, but with a legacy cipher suite, which should report a
// separate MAC component and no group.
IN_PROC_BROWSER_TEST_F(NetworkResponseProtocolTest,
SecurityDetailsLegacyCipher) {
// Configure a specific TLS configuration to compare against.
net::SSLServerConfig server_config;
server_config.version_min = net::SSL_PROTOCOL_VERSION_TLS1_2;
server_config.version_max = net::SSL_PROTOCOL_VERSION_TLS1_2;
// TLS_RSA_WITH_AES_128_CBC_SHA
server_config.cipher_suite_for_testing = 0x002f;
net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS);
server.SetSSLConfig(net::EmbeddedTestServer::ServerCertificate::CERT_OK,
server_config);
server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(server.Start());
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), server.GetURL("/title1.html"), 1);
Attach();
SendCommand("Network.enable");
base::Value response = FetchAndWaitForResponse(server.GetURL("/empty.html"));
const std::string* key_exchange =
response.FindStringPath("response.securityDetails.keyExchange");
ASSERT_TRUE(key_exchange);
EXPECT_EQ("RSA", *key_exchange);
const std::string* cipher =
response.FindStringPath("response.securityDetails.cipher");
ASSERT_TRUE(cipher);
EXPECT_EQ("AES_128_CBC", *cipher);
const std::string* mac =
response.FindStringPath("response.securityDetails.mac");
ASSERT_TRUE(mac);
EXPECT_EQ("HMAC-SHA1", *mac);
// RSA ciphers should not report a MAC.
EXPECT_FALSE(
response.FindStringPath("response.securityDetails.keyExchangeGroup"));
}
// Test that complex certificate SAN lists are reported in SecurityDetails.
IN_PROC_BROWSER_TEST_F(NetworkResponseProtocolTest, SecurityDetailsSAN) {
net::EmbeddedTestServer::ServerCertificateConfig cert_config;
cert_config.dns_names = {"a.example", "b.example", "*.c.example"};
cert_config.ip_addresses = {net::IPAddress::IPv4Localhost(),
net::IPAddress::IPv6Localhost(),
net::IPAddress(1, 2, 3, 4)};
net::EmbeddedTestServer server(net::EmbeddedTestServer::TYPE_HTTPS);
server.SetSSLConfig(cert_config);
server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(server.Start());
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), server.GetURL("/title1.html"), 1);
Attach();
SendCommand("Network.enable");
base::Value response = FetchAndWaitForResponse(server.GetURL("/empty.html"));
const base::Value* sans =
response.FindListPath("response.securityDetails.sanList");
ASSERT_TRUE(sans);
ASSERT_EQ(6u, sans->GetList().size());
EXPECT_EQ(base::Value("a.example"), sans->GetList()[0]);
EXPECT_EQ(base::Value("b.example"), sans->GetList()[1]);
EXPECT_EQ(base::Value("*.c.example"), sans->GetList()[2]);
EXPECT_EQ(base::Value("127.0.0.1"), sans->GetList()[3]);
EXPECT_EQ(base::Value("::1"), sans->GetList()[4]);
EXPECT_EQ(base::Value("1.2.3.4"), sans->GetList()[5]);
}
class ExtensionProtocolTest : public DevToolsProtocolTest {
protected:
void SetUpOnMainThread() override {

@ -1642,6 +1642,8 @@ namespace {
std::unique_ptr<protocol::Network::SecurityDetails> BuildSecurityDetails(
const net::SSLInfo& ssl_info) {
// This function should be kept in sync with the corresponding function in
// inspector_network_agent.cc in //third_party/blink.
if (!ssl_info.cert)
return nullptr;
auto signed_certificate_timestamp_list =

@ -407,6 +407,10 @@ base::StringPiece CryptoBufferAsStringPiece(const CRYPTO_BUFFER* buffer) {
CRYPTO_BUFFER_len(buffer));
}
base::span<const uint8_t> CryptoBufferAsSpan(const CRYPTO_BUFFER* buffer) {
return base::make_span(CRYPTO_BUFFER_data(buffer), CRYPTO_BUFFER_len(buffer));
}
scoped_refptr<X509Certificate> CreateX509CertificateFromBuffers(
const STACK_OF(CRYPTO_BUFFER) * buffers) {
if (sk_CRYPTO_BUFFER_num(buffers) == 0) {

@ -123,6 +123,10 @@ NET_EXPORT bool CryptoBufferEqual(const CRYPTO_BUFFER* a,
NET_EXPORT base::StringPiece CryptoBufferAsStringPiece(
const CRYPTO_BUFFER* buffer);
// Returns a span pointing to the data in |buffer|.
NET_EXPORT base::span<const uint8_t> CryptoBufferAsSpan(
const CRYPTO_BUFFER* buffer);
// Creates a new X509Certificate from the chain in |buffers|, which must have at
// least one element.
NET_EXPORT scoped_refptr<X509Certificate> CreateX509CertificateFromBuffers(

@ -499,8 +499,8 @@ bool EmbeddedTestServer::GenerateCertAndKey() {
intermediate->SetCertificatePolicies(cert_config_.policy_oids);
}
if (!cert_config_.dns_names.empty()) {
leaf->SetSubjectAltNames(cert_config_.dns_names, {});
if (!cert_config_.dns_names.empty() || !cert_config_.ip_addresses.empty()) {
leaf->SetSubjectAltNames(cert_config_.dns_names, cert_config_.ip_addresses);
}
const std::string leaf_serial_text =

@ -303,6 +303,9 @@ class EmbeddedTestServer {
// A list of DNS names to include in the leaf subjectAltName extension.
std::vector<std::string> dns_names;
// A list of IP addresses to include in the leaf subjectAltName extension.
std::vector<net::IPAddress> ip_addresses;
};
typedef base::RepeatingCallback<std::unique_ptr<HttpResponse>(

@ -52,6 +52,10 @@ class LoadTimingInfo;
}
} // namespace network
namespace net {
class SSLInfo;
}
namespace blink {
class ResourceResponse;
@ -68,83 +72,6 @@ class WebURLResponse {
kHTTPVersion_2_0
};
struct SignedCertificateTimestamp {
SignedCertificateTimestamp() = default;
SignedCertificateTimestamp(WebString status,
WebString origin,
WebString log_description,
WebString log_id,
int64_t timestamp,
WebString hash_algorithm,
WebString signature_algorithm,
WebString signature_data)
: status(status),
origin(origin),
log_description(log_description),
log_id(log_id),
timestamp(timestamp),
hash_algorithm(hash_algorithm),
signature_algorithm(signature_algorithm),
signature_data(signature_data) {}
WebString status;
WebString origin;
WebString log_description;
WebString log_id;
int64_t timestamp;
WebString hash_algorithm;
WebString signature_algorithm;
WebString signature_data;
};
using SignedCertificateTimestampList = WebVector<SignedCertificateTimestamp>;
struct WebSecurityDetails {
WebSecurityDetails(const WebString& protocol,
const WebString& key_exchange,
const WebString& key_exchange_group,
const WebString& cipher,
const WebString& mac,
const WebString& subject_name,
const WebVector<WebString>& san_list,
const WebString& issuer,
double valid_from,
double valid_to,
const WebVector<WebString>& certificate,
const SignedCertificateTimestampList& sct_list)
: protocol(protocol),
key_exchange(key_exchange),
key_exchange_group(key_exchange_group),
cipher(cipher),
mac(mac),
subject_name(subject_name),
san_list(san_list),
issuer(issuer),
valid_from(valid_from),
valid_to(valid_to),
certificate(certificate),
sct_list(sct_list) {}
// All strings are human-readable values.
WebString protocol;
// keyExchange is the empty string if not applicable for the connection's
// protocol.
WebString key_exchange;
// keyExchangeGroup is the empty string if not applicable for the
// connection's key exchange.
WebString key_exchange_group;
WebString cipher;
// mac is the empty string when the connection cipher suite does not
// have a separate MAC value (i.e. if the cipher suite is AEAD).
WebString mac;
WebString subject_name;
WebVector<WebString> san_list;
WebString issuer;
double valid_from;
double valid_to;
// DER-encoded X509Certificate certificate chain.
WebVector<WebString> certificate;
SignedCertificateTimestampList sct_list;
};
BLINK_PLATFORM_EXPORT ~WebURLResponse();
BLINK_PLATFORM_EXPORT WebURLResponse();
@ -220,9 +147,7 @@ class WebURLResponse {
BLINK_PLATFORM_EXPORT void SetSecurityStyle(SecurityStyle);
BLINK_PLATFORM_EXPORT void SetSecurityDetails(const WebSecurityDetails&);
BLINK_PLATFORM_EXPORT absl::optional<WebSecurityDetails>
SecurityDetailsForTesting();
BLINK_PLATFORM_EXPORT void SetSSLInfo(const net::SSLInfo&);
BLINK_PLATFORM_EXPORT void SetAsyncRevalidationRequested(bool);
BLINK_PLATFORM_EXPORT void SetNetworkAccessed(bool);

@ -12,9 +12,14 @@ include_rules = [
"+cc/trees/transform_node.h",
"+net/base/ip_address.h",
"+net/base/ip_endpoint.h",
"+third_party/inspector_protocol/crdtp",
"+third_party/icu/source/common/unicode/locid.h",
"+net/cert/ct_sct_to_string.h",
"+net/cert/x509_certificate.h",
"+net/cert/x509_util.h",
"+net/http/http_status_code.h",
"+net/ssl/ssl_cipher_suite_names.h",
"+net/ssl/ssl_connection_status_flags.h",
"+third_party/icu/source/common/unicode/locid.h",
"+third_party/inspector_protocol/crdtp",
]
specific_include_rules = {

@ -38,7 +38,11 @@
#include "build/build_config.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "net/cert/ct_sct_to_string.h"
#include "net/cert/x509_util.h"
#include "net/http/http_status_code.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_connection_status_flags.h"
#include "services/network/public/cpp/cors/cors_error_status.h"
#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
#include "services/network/public/mojom/trust_tokens.mojom-blink.h"
@ -95,6 +99,7 @@
#include "third_party/blink/renderer/platform/wtf/text/base64.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"
#include "third_party/inspector_protocol/crdtp/json.h"
using crdtp::SpanFrom;
@ -151,17 +156,22 @@ bool LoadsFromCacheOnly(const ResourceRequest& request) {
}
protocol::Network::CertificateTransparencyCompliance
SerializeCTPolicyCompliance(
ResourceResponse::CTPolicyCompliance ct_compliance) {
SerializeCTPolicyCompliance(net::ct::CTPolicyCompliance ct_compliance) {
switch (ct_compliance) {
case ResourceResponse::kCTPolicyComplianceDetailsNotAvailable:
return protocol::Network::CertificateTransparencyComplianceEnum::Unknown;
case ResourceResponse::kCTPolicyComplies:
case net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS:
return protocol::Network::CertificateTransparencyComplianceEnum::
Compliant;
case ResourceResponse::kCTPolicyDoesNotComply:
case net::ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS:
case net::ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS:
return protocol::Network::CertificateTransparencyComplianceEnum::
NotCompliant;
case net::ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY:
case net::ct::CTPolicyCompliance::
CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE:
return protocol::Network::CertificateTransparencyComplianceEnum::Unknown;
case net::ct::CTPolicyCompliance::CT_POLICY_COUNT:
NOTREACHED();
// Fallthrough to default.
}
NOTREACHED();
return protocol::Network::CertificateTransparencyComplianceEnum::Unknown;
@ -760,6 +770,107 @@ static bool FormDataToString(
return true;
}
static String StringFromASCII(const std::string& str) {
String ret(str);
DCHECK(ret.ContainsOnlyASCIIOrEmpty());
return ret;
}
static std::unique_ptr<protocol::Network::SecurityDetails> BuildSecurityDetails(
const net::SSLInfo& ssl_info) {
// This function should be kept in sync with the corresponding function in
// network_handler.cc in //content.
if (!ssl_info.cert)
return nullptr;
auto signed_certificate_timestamp_list = std::make_unique<
protocol::Array<protocol::Network::SignedCertificateTimestamp>>();
for (auto const& sct : ssl_info.signed_certificate_timestamps) {
std::unique_ptr<protocol::Network::SignedCertificateTimestamp>
signed_certificate_timestamp =
protocol::Network::SignedCertificateTimestamp::create()
.setStatus(StringFromASCII(net::ct::StatusToString(sct.status)))
.setOrigin(
StringFromASCII(net::ct::OriginToString(sct.sct->origin)))
.setLogDescription(String::FromUTF8(sct.sct->log_description))
.setLogId(StringFromASCII(base::HexEncode(
sct.sct->log_id.c_str(), sct.sct->log_id.length())))
.setTimestamp(sct.sct->timestamp.ToJavaTime())
.setHashAlgorithm(
StringFromASCII(net::ct::HashAlgorithmToString(
sct.sct->signature.hash_algorithm)))
.setSignatureAlgorithm(
StringFromASCII(net::ct::SignatureAlgorithmToString(
sct.sct->signature.signature_algorithm)))
.setSignatureData(StringFromASCII(base::HexEncode(
sct.sct->signature.signature_data.c_str(),
sct.sct->signature.signature_data.length())))
.build();
signed_certificate_timestamp_list->emplace_back(
std::move(signed_certificate_timestamp));
}
std::vector<std::string> san_dns;
std::vector<std::string> san_ip;
ssl_info.cert->GetSubjectAltName(&san_dns, &san_ip);
auto san_list = std::make_unique<protocol::Array<String>>();
for (const std::string& san : san_dns) {
// DNS names in a SAN list are always ASCII.
san_list->push_back(StringFromASCII(san));
}
for (const std::string& san : san_ip) {
net::IPAddress ip(reinterpret_cast<const uint8_t*>(san.data()), san.size());
san_list->push_back(StringFromASCII(ip.ToString()));
}
const char* protocol = "";
const char* key_exchange = "";
const char* cipher = "";
const char* mac = nullptr;
if (ssl_info.connection_status) {
net::SSLVersion ssl_version =
net::SSLConnectionStatusToVersion(ssl_info.connection_status);
net::SSLVersionToString(&protocol, ssl_version);
bool is_aead;
bool is_tls13;
uint16_t cipher_suite =
net::SSLConnectionStatusToCipherSuite(ssl_info.connection_status);
net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
&is_tls13, cipher_suite);
if (key_exchange == nullptr) {
DCHECK(is_tls13);
key_exchange = "";
}
}
std::unique_ptr<protocol::Network::SecurityDetails> security_details =
protocol::Network::SecurityDetails::create()
.setProtocol(protocol)
.setKeyExchange(key_exchange)
.setCipher(cipher)
.setSubjectName(
String::FromUTF8(ssl_info.cert->subject().common_name))
.setSanList(std::move(san_list))
.setIssuer(String::FromUTF8(ssl_info.cert->issuer().common_name))
.setValidFrom(ssl_info.cert->valid_start().ToDoubleT())
.setValidTo(ssl_info.cert->valid_expiry().ToDoubleT())
.setCertificateId(0) // Keep this in protocol for compatibility.
.setSignedCertificateTimestampList(
std::move(signed_certificate_timestamp_list))
.setCertificateTransparencyCompliance(
SerializeCTPolicyCompliance(ssl_info.ct_policy_compliance))
.build();
if (ssl_info.key_exchange_group != 0) {
const char* key_exchange_group =
SSL_get_curve_name(ssl_info.key_exchange_group);
if (key_exchange_group)
security_details->setKeyExchangeGroup(key_exchange_group);
}
if (mac)
security_details->setMac(mac);
return security_details;
}
static std::unique_ptr<protocol::Network::Request>
BuildObjectForResourceRequest(const ResourceRequest& request,
scoped_refptr<EncodedFormData> post_data,
@ -904,55 +1015,9 @@ BuildObjectForResourceResponse(const ResourceResponse& response,
}
response_object->setProtocol(protocol);
const absl::optional<ResourceResponse::SecurityDetails>&
response_security_details = response.GetSecurityDetails();
if (response_security_details.has_value()) {
auto san_list = std::make_unique<protocol::Array<String>>(
response_security_details->san_list.begin(),
response_security_details->san_list.end());
auto signed_certificate_timestamp_list = std::make_unique<
protocol::Array<protocol::Network::SignedCertificateTimestamp>>();
for (auto const& sct : response_security_details->sct_list) {
std::unique_ptr<protocol::Network::SignedCertificateTimestamp>
signed_certificate_timestamp =
protocol::Network::SignedCertificateTimestamp::create()
.setStatus(sct.status_)
.setOrigin(sct.origin_)
.setLogDescription(sct.log_description_)
.setLogId(sct.log_id_)
.setTimestamp(sct.timestamp_)
.setHashAlgorithm(sct.hash_algorithm_)
.setSignatureAlgorithm(sct.signature_algorithm_)
.setSignatureData(sct.signature_data_)
.build();
signed_certificate_timestamp_list->emplace_back(
std::move(signed_certificate_timestamp));
}
std::unique_ptr<protocol::Network::SecurityDetails> security_details =
protocol::Network::SecurityDetails::create()
.setProtocol(response_security_details->protocol)
.setKeyExchange(response_security_details->key_exchange)
.setCipher(response_security_details->cipher)
.setSubjectName(response_security_details->subject_name)
.setSanList(std::move(san_list))
.setIssuer(response_security_details->issuer)
.setValidFrom(response_security_details->valid_from)
.setValidTo(response_security_details->valid_to)
.setCertificateId(0) // Keep this in protocol for compatability.
.setSignedCertificateTimestampList(
std::move(signed_certificate_timestamp_list))
.setCertificateTransparencyCompliance(
SerializeCTPolicyCompliance(response.GetCTPolicyCompliance()))
.build();
if (response_security_details->key_exchange_group.length() > 0)
security_details->setKeyExchangeGroup(
response_security_details->key_exchange_group);
if (response_security_details->mac.length() > 0)
security_details->setMac(response_security_details->mac);
response_object->setSecurityDetails(std::move(security_details));
const absl::optional<net::SSLInfo>& ssl_info = response.GetSSLInfo();
if (ssl_info.has_value()) {
response_object->setSecurityDetails(BuildSecurityDetails(*ssl_info));
}
return response_object;
@ -1308,11 +1373,9 @@ void InspectorNetworkAgent::DidReceiveResourceResponse(
resources_data_->ResponseReceived(request_id, frame_id, response);
resources_data_->SetResourceType(request_id, type);
const absl::optional<ResourceResponse::SecurityDetails>&
response_security_details = response.GetSecurityDetails();
if (response_security_details.has_value()) {
resources_data_->SetCertificate(request_id,
response_security_details->certificate);
const absl::optional<net::SSLInfo>& ssl_info = response.GetSSLInfo();
if (ssl_info.has_value() && ssl_info->cert) {
resources_data_->SetCertificate(request_id, ssl_info->cert);
}
if (IsNavigation(loader, identifier))
@ -2011,12 +2074,15 @@ Response InspectorNetworkAgent::getCertificate(
for (auto& resource : resources_data_->Resources()) {
scoped_refptr<const SecurityOrigin> resource_origin =
SecurityOrigin::Create(resource->RequestedURL());
if (resource_origin->IsSameOriginWith(security_origin.get()) &&
resource->Certificate().size()) {
for (auto& cert : resource->Certificate()) {
net::X509Certificate* cert = resource->Certificate();
if (resource_origin->IsSameOriginWith(security_origin.get()) && cert) {
(*certificate)
->push_back(Base64Encode(
net::x509_util::CryptoBufferAsSpan(cert->cert_buffer())));
for (const auto& buf : cert->intermediate_buffers()) {
(*certificate)
->emplace_back(
Base64Encode(base::as_bytes(base::make_span(cert.Latin1()))));
->push_back(
Base64Encode(net::x509_util::CryptoBufferAsSpan(buf.get())));
}
return Response::Success();
}

@ -356,11 +356,11 @@ XHRReplayData* NetworkResourcesData::XhrReplayData(const String& request_id) {
void NetworkResourcesData::SetCertificate(
const String& request_id,
const Vector<AtomicString>& certificate) {
scoped_refptr<net::X509Certificate> certificate) {
ResourceData* resource_data = ResourceDataForRequestId(request_id);
if (!resource_data)
return;
resource_data->SetCertificate(certificate);
resource_data->SetCertificate(std::move(certificate));
}
void NetworkResourcesData::SetXHRReplayData(const String& request_id,

@ -29,6 +29,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_NETWORK_RESOURCES_DATA_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_NETWORK_RESOURCES_DATA_H_
#include "net/cert/x509_certificate.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/html/parser/text_resource_decoder.h"
#include "third_party/blink/renderer/core/inspector/inspector_page_agent.h"
@ -151,9 +152,9 @@ class NetworkResourcesData final
int64_t RawHeaderSize() const { return raw_header_size_; }
void SetRawHeaderSize(int64_t size) { raw_header_size_ = size; }
Vector<AtomicString> Certificate() { return certificate_; }
void SetCertificate(const Vector<AtomicString>& certificate) {
certificate_ = certificate;
net::X509Certificate* Certificate() { return certificate_.get(); }
void SetCertificate(scoped_refptr<net::X509Certificate> certificate) {
certificate_ = std::move(certificate);
}
int64_t PendingEncodedDataLength() const {
return pending_encoded_data_length_;
@ -163,7 +164,7 @@ class NetworkResourcesData final
pending_encoded_data_length_ += encoded_data_length;
}
void SetPostData(scoped_refptr<EncodedFormData> post_data) {
post_data_ = post_data;
post_data_ = std::move(post_data);
}
EncodedFormData* PostData() const { return post_data_.get(); }
@ -204,7 +205,7 @@ class NetworkResourcesData final
UntracedMember<const Resource> cached_resource_;
scoped_refptr<BlobDataHandle> downloaded_file_blob_;
Vector<AtomicString> certificate_;
scoped_refptr<net::X509Certificate> certificate_;
scoped_refptr<EncodedFormData> post_data_;
};
@ -238,7 +239,7 @@ class NetworkResourcesData final
void SetXHRReplayData(const String& request_id, XHRReplayData*);
XHRReplayData* XhrReplayData(const String& request_id);
void SetCertificate(const String& request_id,
const Vector<AtomicString>& certificate);
scoped_refptr<net::X509Certificate>);
HeapVector<Member<ResourceData>> Resources();
int64_t GetAndClearPendingEncodedDataLength(const String& request_id);

@ -1,6 +1,7 @@
include_rules = [
"+net/base/ip_endpoint.h",
"+net/base/load_flags.h",
"+net/ssl/ssl_info.h",
"+net/url_request/redirect_info.h",
"+services/network/public/cpp/features.h",
"+services/network/public/mojom/fetch_api.mojom.h",

@ -36,6 +36,7 @@
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "net/ssl/ssl_info.h"
#include "services/network/public/mojom/ip_address_space.mojom-shared.h"
#include "services/network/public/mojom/load_timing_info.mojom.h"
#include "third_party/blink/public/platform/web_http_header_visitor.h"
@ -262,52 +263,8 @@ void WebURLResponse::SetSecurityStyle(SecurityStyle security_style) {
resource_response_->SetSecurityStyle(security_style);
}
void WebURLResponse::SetSecurityDetails(
const WebSecurityDetails& web_security_details) {
ResourceResponse::SignedCertificateTimestampList sct_list;
for (const auto& iter : web_security_details.sct_list) {
sct_list.push_back(
static_cast<ResourceResponse::SignedCertificateTimestamp>(iter));
}
Vector<String> san_list;
san_list.Append(
web_security_details.san_list.Data(),
base::checked_cast<wtf_size_t>(web_security_details.san_list.size()));
Vector<AtomicString> certificate;
for (const auto& iter : web_security_details.certificate) {
AtomicString cert = iter;
certificate.push_back(cert);
}
resource_response_->SetSecurityDetails(
web_security_details.protocol, web_security_details.key_exchange,
web_security_details.key_exchange_group, web_security_details.cipher,
web_security_details.mac, web_security_details.subject_name, san_list,
web_security_details.issuer,
static_cast<time_t>(web_security_details.valid_from),
static_cast<time_t>(web_security_details.valid_to), certificate,
sct_list);
}
absl::optional<WebURLResponse::WebSecurityDetails>
WebURLResponse::SecurityDetailsForTesting() {
const absl::optional<ResourceResponse::SecurityDetails>& security_details =
resource_response_->GetSecurityDetails();
if (!security_details.has_value())
return absl::nullopt;
SignedCertificateTimestampList sct_list;
for (const auto& iter : security_details->sct_list) {
sct_list.emplace_back(SignedCertificateTimestamp(
iter.status_, iter.origin_, iter.log_description_, iter.log_id_,
iter.timestamp_, iter.hash_algorithm_, iter.signature_algorithm_,
iter.signature_data_));
}
return WebSecurityDetails(
security_details->protocol, security_details->key_exchange,
security_details->key_exchange_group, security_details->cipher,
security_details->mac, security_details->subject_name,
security_details->san_list, security_details->issuer,
security_details->valid_from, security_details->valid_to,
security_details->certificate, sct_list);
void WebURLResponse::SetSSLInfo(const net::SSLInfo& ssl_info) {
resource_response_->SetSSLInfo(ssl_info);
}
const ResourceResponse& WebURLResponse::ToResourceResponse() const {

@ -5,6 +5,7 @@ include_rules = [
"+net/dns/public",
"+net/filter/source_stream.h",
"+net/http/http_response_info.h",
"+net/ssl/ssl_info.h",
"+services/network/public/cpp/fetch_api_utils.h",
"+services/network/public/cpp/optional_trust_token_params.h",
"+third_party/blink/renderer/platform/mojo",

@ -32,6 +32,7 @@
#include <string>
#include "net/http/structured_headers.h"
#include "net/ssl/ssl_info.h"
#include "services/network/public/cpp/cors/cors.h"
#include "services/network/public/mojom/fetch_api.mojom-blink.h"
#include "third_party/blink/public/platform/web_url_response.h"
@ -61,26 +62,6 @@ static const char kPragmaHeader[] = "pragma";
} // namespace
ResourceResponse::SignedCertificateTimestamp::SignedCertificateTimestamp(
const blink::WebURLResponse::SignedCertificateTimestamp& sct)
: status_(sct.status),
origin_(sct.origin),
log_description_(sct.log_description),
log_id_(sct.log_id),
timestamp_(sct.timestamp),
hash_algorithm_(sct.hash_algorithm),
signature_algorithm_(sct.signature_algorithm),
signature_data_(sct.signature_data) {}
ResourceResponse::SignedCertificateTimestamp
ResourceResponse::SignedCertificateTimestamp::IsolatedCopy() const {
return SignedCertificateTimestamp(
status_.IsolatedCopy(), origin_.IsolatedCopy(),
log_description_.IsolatedCopy(), log_id_.IsolatedCopy(), timestamp_,
hash_algorithm_.IsolatedCopy(), signature_algorithm_.IsolatedCopy(),
signature_data_.IsolatedCopy());
}
ResourceResponse::ResourceResponse()
: was_cached_(false),
connection_reused_(false),
@ -247,24 +228,10 @@ void ResourceResponse::UpdateHeaderParsedState(const AtomicString& name) {
have_parsed_last_modified_header_ = false;
}
void ResourceResponse::SetSecurityDetails(
const String& protocol,
const String& key_exchange,
const String& key_exchange_group,
const String& cipher,
const String& mac,
const String& subject_name,
const Vector<String>& san_list,
const String& issuer,
time_t valid_from,
time_t valid_to,
const Vector<AtomicString>& certificate,
const SignedCertificateTimestampList& sct_list) {
void ResourceResponse::SetSSLInfo(const net::SSLInfo& ssl_info) {
DCHECK_NE(security_style_, SecurityStyle::kUnknown);
DCHECK_NE(security_style_, SecurityStyle::kNeutral);
security_details_ = SecurityDetails(
protocol, key_exchange, key_exchange_group, cipher, mac, subject_name,
san_list, issuer, valid_from, valid_to, certificate, sct_list);
ssl_info_ = ssl_info;
}
bool ResourceResponse::IsCorsSameOrigin() const {

@ -33,6 +33,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/time/time.h"
#include "net/base/ip_endpoint.h"
#include "net/ssl/ssl_info.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-shared.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "services/network/public/mojom/ip_address_space.mojom-shared.h"
@ -45,7 +46,6 @@
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
@ -76,91 +76,6 @@ class PLATFORM_EXPORT ResourceResponse final {
kCTPolicyDoesNotComply
};
class PLATFORM_EXPORT SignedCertificateTimestamp final {
DISALLOW_NEW();
public:
SignedCertificateTimestamp(String status,
String origin,
String log_description,
String log_id,
int64_t timestamp,
String hash_algorithm,
String signature_algorithm,
String signature_data)
: status_(status),
origin_(origin),
log_description_(log_description),
log_id_(log_id),
timestamp_(timestamp),
hash_algorithm_(hash_algorithm),
signature_algorithm_(signature_algorithm),
signature_data_(signature_data) {}
explicit SignedCertificateTimestamp(
const struct blink::WebURLResponse::SignedCertificateTimestamp&);
SignedCertificateTimestamp IsolatedCopy() const;
String status_;
String origin_;
String log_description_;
String log_id_;
int64_t timestamp_;
String hash_algorithm_;
String signature_algorithm_;
String signature_data_;
};
using SignedCertificateTimestampList =
WTF::Vector<SignedCertificateTimestamp>;
struct SecurityDetails {
DISALLOW_NEW();
SecurityDetails(const String& protocol,
const String& key_exchange,
const String& key_exchange_group,
const String& cipher,
const String& mac,
const String& subject_name,
const Vector<String>& san_list,
const String& issuer,
time_t valid_from,
time_t valid_to,
const Vector<AtomicString>& certificate,
const SignedCertificateTimestampList& sct_list)
: protocol(protocol),
key_exchange(key_exchange),
key_exchange_group(key_exchange_group),
cipher(cipher),
mac(mac),
subject_name(subject_name),
san_list(san_list),
issuer(issuer),
valid_from(valid_from),
valid_to(valid_to),
certificate(certificate),
sct_list(sct_list) {}
// All strings are human-readable values.
String protocol;
// keyExchange is the empty string if not applicable for the connection's
// protocol.
String key_exchange;
// keyExchangeGroup is the empty string if not applicable for the
// connection's key exchange.
String key_exchange_group;
String cipher;
// mac is the empty string when the connection cipher suite does not
// have a separate MAC value (i.e. if the cipher suite is AEAD).
String mac;
String subject_name;
Vector<String> san_list;
String issuer;
time_t valid_from;
time_t valid_to;
// DER-encoded X509Certificate certificate chain.
Vector<AtomicString> certificate;
SignedCertificateTimestampList sct_list;
};
ResourceResponse();
explicit ResourceResponse(const KURL& current_request_url);
ResourceResponse(const ResourceResponse&);
@ -300,21 +215,8 @@ class PLATFORM_EXPORT ResourceResponse final {
security_style_ = security_style;
}
const absl::optional<SecurityDetails>& GetSecurityDetails() const {
return security_details_;
}
void SetSecurityDetails(const String& protocol,
const String& key_exchange,
const String& key_exchange_group,
const String& cipher,
const String& mac,
const String& subject_name,
const Vector<String>& san_list,
const String& issuer,
time_t valid_from,
time_t valid_to,
const Vector<AtomicString>& certificate,
const SignedCertificateTimestampList& sct_list);
const absl::optional<net::SSLInfo>& GetSSLInfo() const { return ssl_info_; }
void SetSSLInfo(const net::SSLInfo& ssl_info);
const KURL& WebBundleURL() const { return web_bundle_url_; }
void SetWebBundleURL(const KURL& url) { web_bundle_url_ = url; }
@ -653,7 +555,7 @@ class PLATFORM_EXPORT ResourceResponse final {
SecurityStyle security_style_ = SecurityStyle::kUnknown;
// Security details of this request's connection.
absl::optional<SecurityDetails> security_details_;
absl::optional<net::SSLInfo> ssl_info_;
scoped_refptr<ResourceLoadTiming> resource_load_timing_;

@ -45,30 +45,6 @@ void RunInThread() {
} // namespace
TEST(ResourceResponseTest, SignedCertificateTimestampIsolatedCopy) {
ResourceResponse::SignedCertificateTimestamp src(
"status", "origin", "logDescription", "logId", 7, "hashAlgorithm",
"signatureAlgorithm", "signatureData");
ResourceResponse::SignedCertificateTimestamp dest = src.IsolatedCopy();
EXPECT_EQ(src.status_, dest.status_);
EXPECT_NE(src.status_.Impl(), dest.status_.Impl());
EXPECT_EQ(src.origin_, dest.origin_);
EXPECT_NE(src.origin_.Impl(), dest.origin_.Impl());
EXPECT_EQ(src.log_description_, dest.log_description_);
EXPECT_NE(src.log_description_.Impl(), dest.log_description_.Impl());
EXPECT_EQ(src.log_id_, dest.log_id_);
EXPECT_NE(src.log_id_.Impl(), dest.log_id_.Impl());
EXPECT_EQ(src.timestamp_, dest.timestamp_);
EXPECT_EQ(src.hash_algorithm_, dest.hash_algorithm_);
EXPECT_NE(src.hash_algorithm_.Impl(), dest.hash_algorithm_.Impl());
EXPECT_EQ(src.signature_algorithm_, dest.signature_algorithm_);
EXPECT_NE(src.signature_algorithm_.Impl(), dest.signature_algorithm_.Impl());
EXPECT_EQ(src.signature_data_, dest.signature_data_);
EXPECT_NE(src.signature_data_.Impl(), dest.signature_data_.Impl());
}
// This test checks that AtomicStrings in ResourceResponse doesn't cause the
// failure of ThreadRestrictionVerifier check.
TEST(ResourceResponseTest, CrossThreadAtomicStrings) {

@ -22,6 +22,7 @@
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/platform/web_navigation_body_loader.h"
#include "third_party/blink/public/web/web_navigation_params.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
namespace blink {
@ -352,7 +353,7 @@ TEST_F(NavigationBodyLoaderTest, FillResponseWithSecurityDetails) {
/*resource_load_info_notifier=*/nullptr),
/*is_main_frame=*/true, &navigation_params);
EXPECT_TRUE(
navigation_params.response.SecurityDetailsForTesting().has_value());
navigation_params.response.ToResourceResponse().GetSSLInfo().has_value());
}
} // namespace

@ -144,33 +144,6 @@ net::RequestPriority ConvertWebKitPriorityToNetPriority(
}
}
// Convert a net::SignedCertificateTimestampAndStatus object to a
// WebURLResponse::SignedCertificateTimestamp object.
WebURLResponse::SignedCertificateTimestamp NetSCTToBlinkSCT(
const net::SignedCertificateTimestampAndStatus& sct_and_status) {
return WebURLResponse::SignedCertificateTimestamp(
WebString::FromASCII(net::ct::StatusToString(sct_and_status.status)),
WebString::FromASCII(net::ct::OriginToString(sct_and_status.sct->origin)),
WebString::FromUTF8(sct_and_status.sct->log_description),
WebString::FromASCII(
base::HexEncode(sct_and_status.sct->log_id.c_str(),
sct_and_status.sct->log_id.length())),
sct_and_status.sct->timestamp.ToJavaTime(),
WebString::FromASCII(net::ct::HashAlgorithmToString(
sct_and_status.sct->signature.hash_algorithm)),
WebString::FromASCII(net::ct::SignatureAlgorithmToString(
sct_and_status.sct->signature.signature_algorithm)),
WebString::FromASCII(base::HexEncode(
sct_and_status.sct->signature.signature_data.c_str(),
sct_and_status.sct->signature.signature_data.length())));
}
WebString CryptoBufferAsWebString(const CRYPTO_BUFFER* buffer) {
base::StringPiece sp = net::x509_util::CryptoBufferAsStringPiece(buffer);
return WebString::FromLatin1(reinterpret_cast<const WebLChar*>(sp.begin()),
sp.size());
}
void SetSecurityStyleAndDetails(const GURL& url,
const network::mojom::URLResponseHead& head,
WebURLResponse* response,
@ -198,91 +171,19 @@ void SetSecurityStyleAndDetails(const GURL& url,
}
const net::SSLInfo& ssl_info = *head.ssl_info;
const char* protocol = "";
const char* key_exchange = "";
const char* cipher = "";
const char* mac = "";
const char* key_exchange_group = "";
if (ssl_info.connection_status) {
int ssl_version =
net::SSLConnectionStatusToVersion(ssl_info.connection_status);
net::SSLVersionToString(&protocol, ssl_version);
bool is_aead;
bool is_tls13;
uint16_t cipher_suite =
net::SSLConnectionStatusToCipherSuite(ssl_info.connection_status);
net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
&is_tls13, cipher_suite);
if (!key_exchange) {
DCHECK(is_tls13);
key_exchange = "";
}
if (!mac) {
DCHECK(is_aead);
mac = "";
}
if (ssl_info.key_exchange_group != 0) {
// Historically the field was named 'curve' rather than 'group'.
key_exchange_group = SSL_get_curve_name(ssl_info.key_exchange_group);
if (!key_exchange_group) {
NOTREACHED();
key_exchange_group = "";
}
}
}
if (net::IsCertStatusError(head.cert_status)) {
response->SetSecurityStyle(SecurityStyle::kInsecure);
} else {
response->SetSecurityStyle(SecurityStyle::kSecure);
}
WebURLResponse::SignedCertificateTimestampList sct_list(
ssl_info.signed_certificate_timestamps.size());
for (size_t i = 0; i < sct_list.size(); ++i)
sct_list[i] = NetSCTToBlinkSCT(ssl_info.signed_certificate_timestamps[i]);
if (!ssl_info.cert) {
NOTREACHED();
response->SetSecurityStyle(SecurityStyle::kUnknown);
return;
}
std::vector<std::string> san_dns;
std::vector<std::string> san_ip;
ssl_info.cert->GetSubjectAltName(&san_dns, &san_ip);
WebVector<WebString> web_san(san_dns.size() + san_ip.size());
std::transform(san_dns.begin(), san_dns.end(), web_san.begin(),
[](const std::string& h) { return WebString::FromLatin1(h); });
std::transform(san_ip.begin(), san_ip.end(), web_san.begin() + san_dns.size(),
[](const std::string& h) {
net::IPAddress ip(reinterpret_cast<const uint8_t*>(h.data()),
h.size());
return WebString::FromLatin1(ip.ToString());
});
WebVector<WebString> web_cert;
web_cert.reserve(ssl_info.cert->intermediate_buffers().size() + 1);
web_cert.emplace_back(CryptoBufferAsWebString(ssl_info.cert->cert_buffer()));
for (const auto& cert : ssl_info.cert->intermediate_buffers())
web_cert.emplace_back(CryptoBufferAsWebString(cert.get()));
WebURLResponse::WebSecurityDetails webSecurityDetails(
WebString::FromASCII(protocol), WebString::FromASCII(key_exchange),
WebString::FromASCII(key_exchange_group), WebString::FromASCII(cipher),
WebString::FromASCII(mac),
WebString::FromUTF8(ssl_info.cert->subject().common_name), web_san,
WebString::FromUTF8(ssl_info.cert->issuer().common_name),
ssl_info.cert->valid_start().ToDoubleT(),
ssl_info.cert->valid_expiry().ToDoubleT(), web_cert, sct_list);
response->SetSecurityDetails(webSecurityDetails);
response->SetSSLInfo(ssl_info);
}
bool IsBannedCrossSiteAuth(

@ -54,6 +54,7 @@
#include "third_party/blink/public/platform/web_url_request_extra_data.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
#include "third_party/blink/renderer/platform/loader/fetch/url_loader/sync_load_response.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "url/gurl.h"
@ -576,7 +577,7 @@ TEST_F(WebURLLoaderTest, ResponseAddressSpaceConsidersResponseUrl) {
EXPECT_EQ(network::mojom::IPAddressSpace::kLocal, response.AddressSpace());
}
TEST_F(WebURLLoaderTest, ResponseCert) {
TEST_F(WebURLLoaderTest, SSLInfo) {
KURL url("https://test.example/");
net::CertificateList certs;
@ -600,56 +601,11 @@ TEST_F(WebURLLoaderTest, ResponseCert) {
WebURLResponse web_url_response;
WebURLLoader::PopulateURLResponse(url, head, &web_url_response, true, -1);
absl::optional<WebURLResponse::WebSecurityDetails> security_details =
web_url_response.SecurityDetailsForTesting();
ASSERT_TRUE(security_details.has_value());
EXPECT_EQ("TLS 1.2", security_details->protocol);
EXPECT_EQ("127.0.0.1", security_details->subject_name);
EXPECT_EQ("127.0.0.1", security_details->issuer);
ASSERT_EQ(3U, security_details->san_list.size());
EXPECT_EQ("test.example", security_details->san_list[0]);
EXPECT_EQ("127.0.0.2", security_details->san_list[1]);
EXPECT_EQ("fe80::1", security_details->san_list[2]);
EXPECT_EQ(certs[0]->valid_start().ToTimeT(), security_details->valid_from);
EXPECT_EQ(certs[0]->valid_expiry().ToTimeT(), security_details->valid_to);
ASSERT_EQ(2U, security_details->certificate.size());
EXPECT_EQ(WebString::FromLatin1(std::string(cert0_der)),
security_details->certificate[0]);
EXPECT_EQ(WebString::FromLatin1(std::string(cert1_der)),
security_details->certificate[1]);
}
TEST_F(WebURLLoaderTest, ResponseCertWithNoSANs) {
KURL url("https://test.example/");
net::CertificateList certs;
ASSERT_TRUE(net::LoadCertificateFiles({"multi-root-B-by-C.pem"}, &certs));
ASSERT_EQ(1U, certs.size());
base::StringPiece cert0_der =
net::x509_util::CryptoBufferAsStringPiece(certs[0]->cert_buffer());
net::SSLInfo ssl_info;
net::SSLConnectionStatusSetVersion(net::SSL_CONNECTION_VERSION_TLS1_2,
&ssl_info.connection_status);
ssl_info.cert = certs[0];
network::mojom::URLResponseHead head;
head.ssl_info = ssl_info;
WebURLResponse web_url_response;
WebURLLoader::PopulateURLResponse(url, head, &web_url_response, true, -1);
absl::optional<WebURLResponse::WebSecurityDetails> security_details =
web_url_response.SecurityDetailsForTesting();
ASSERT_TRUE(security_details.has_value());
EXPECT_EQ("TLS 1.2", security_details->protocol);
EXPECT_EQ("B CA - Multi-root", security_details->subject_name);
EXPECT_EQ("C CA - Multi-root", security_details->issuer);
EXPECT_EQ(0U, security_details->san_list.size());
EXPECT_EQ(certs[0]->valid_start().ToTimeT(), security_details->valid_from);
EXPECT_EQ(certs[0]->valid_expiry().ToTimeT(), security_details->valid_to);
ASSERT_EQ(1U, security_details->certificate.size());
EXPECT_EQ(WebString::FromLatin1(std::string(cert0_der)),
security_details->certificate[0]);
const absl::optional<net::SSLInfo>& got_ssl_info =
web_url_response.ToResourceResponse().GetSSLInfo();
ASSERT_TRUE(got_ssl_info.has_value());
EXPECT_EQ(ssl_info.connection_status, got_ssl_info->connection_status);
EXPECT_TRUE(ssl_info.cert->EqualsIncludingChain(got_ssl_info->cert.get()));
}
// Verifies that the lengths used by the PerformanceResourceTiming API are

@ -915,6 +915,9 @@ _CONFIG = [
# [C]h[R]ome [D]ev[T]ools [P]rotocol implementation support library
# (see third_party/inspector_protocol/crdtp).
'crdtp::.+',
# DevTools manages certificates from the net stack.
'net::X509Certificate',
'net::x509_util::CryptoBufferAsSpan',
],
},
{
@ -952,8 +955,12 @@ _CONFIG = [
'third_party/blink/renderer/core/inspector/inspector_network_agent.cc'
],
'allowed': [
'net::SourceStream',
'base::flat_set',
'base::HexEncode',
'net::ct::.+',
'net::IPAddress',
'net::SourceStream',
'net::SSL.+',
],
},
{