1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/net/certificate_error_reporter.h"
9 #include "base/logging.h"
10 #include "chrome/browser/net/encrypted_cert_logger.pb.h"
12 #if defined(USE_OPENSSL)
13 #include "crypto/aead_openssl.h"
16 #include "crypto/curve25519.h"
17 #include "crypto/hkdf.h"
18 #include "crypto/random.h"
19 #include "net/base/elements_upload_data_stream.h"
20 #include "net/base/load_flags.h"
21 #include "net/base/request_priority.h"
22 #include "net/base/upload_bytes_element_reader.h"
23 #include "net/url_request/url_request_context.h"
27 // Constants used for crypto
28 static const uint8 kServerPublicKey
[] = {
29 0x51, 0xcc, 0x52, 0x67, 0x42, 0x47, 0x3b, 0x10, 0xe8, 0x63, 0x18,
30 0x3c, 0x61, 0xa7, 0x96, 0x76, 0x86, 0x91, 0x40, 0x71, 0x39, 0x5f,
31 0x31, 0x1a, 0x39, 0x5b, 0x76, 0xb1, 0x6b, 0x3d, 0x6a, 0x2b};
32 static const uint32 kServerPublicKeyVersion
= 1;
34 #if defined(USE_OPENSSL)
36 static const char kHkdfLabel
[] = "certificate report";
38 bool EncryptSerializedReport(
39 const uint8
* server_public_key
,
40 uint32 server_public_key_version
,
41 const std::string
& report
,
42 chrome_browser_net::EncryptedCertLoggerRequest
* encrypted_report
) {
43 // Generate an ephemeral key pair to generate a shared secret.
44 uint8 public_key
[crypto::curve25519::kBytes
];
45 uint8 private_key
[crypto::curve25519::kScalarBytes
];
46 uint8 shared_secret
[crypto::curve25519::kBytes
];
48 crypto::RandBytes(private_key
, sizeof(private_key
));
49 crypto::curve25519::ScalarBaseMult(private_key
, public_key
);
50 crypto::curve25519::ScalarMult(private_key
, server_public_key
, shared_secret
);
52 crypto::Aead
aead(crypto::Aead::AES_128_CTR_HMAC_SHA256
);
53 crypto::HKDF
hkdf(std::string(reinterpret_cast<char*>(shared_secret
),
54 sizeof(shared_secret
)),
56 base::StringPiece(kHkdfLabel
, sizeof(kHkdfLabel
)), 0, 0,
59 const std::string
key(hkdf
.subkey_secret().data(),
60 hkdf
.subkey_secret().size());
63 // Use an all-zero nonce because the key is random per-message.
64 std::string
nonce(aead
.NonceLength(), 0);
66 std::string ciphertext
;
67 if (!aead
.Seal(report
, nonce
, std::string(), &ciphertext
)) {
68 LOG(ERROR
) << "Error sealing certificate report.";
72 encrypted_report
->set_encrypted_report(ciphertext
);
73 encrypted_report
->set_server_public_key_version(server_public_key_version
);
74 encrypted_report
->set_client_public_key(
75 std::string(reinterpret_cast<char*>(public_key
), sizeof(public_key
)));
76 encrypted_report
->set_algorithm(
77 chrome_browser_net::EncryptedCertLoggerRequest::
78 AEAD_ECDH_AES_128_CTR_HMAC_SHA256
);
85 namespace chrome_browser_net
{
87 CertificateErrorReporter::CertificateErrorReporter(
88 net::URLRequestContext
* request_context
,
89 const GURL
& upload_url
,
90 CookiesPreference cookies_preference
)
91 : CertificateErrorReporter(request_context
,
95 kServerPublicKeyVersion
) {
98 CertificateErrorReporter::CertificateErrorReporter(
99 net::URLRequestContext
* request_context
,
100 const GURL
& upload_url
,
101 CookiesPreference cookies_preference
,
102 const uint8 server_public_key
[32],
103 const uint32 server_public_key_version
)
104 : request_context_(request_context
),
105 upload_url_(upload_url
),
106 cookies_preference_(cookies_preference
),
107 server_public_key_(server_public_key
),
108 server_public_key_version_(server_public_key_version
) {
109 DCHECK(!upload_url
.is_empty());
112 CertificateErrorReporter::~CertificateErrorReporter() {
113 STLDeleteElements(&inflight_requests_
);
116 void CertificateErrorReporter::SendReport(
118 const std::string
& serialized_report
) {
120 case REPORT_TYPE_PINNING_VIOLATION
:
121 SendSerializedRequest(serialized_report
);
123 case REPORT_TYPE_EXTENDED_REPORTING
:
124 if (upload_url_
.SchemeIsCryptographic()) {
125 SendSerializedRequest(serialized_report
);
127 DCHECK(IsHttpUploadUrlSupported());
128 #if defined(USE_OPENSSL)
129 EncryptedCertLoggerRequest encrypted_report
;
130 if (!EncryptSerializedReport(server_public_key_
,
131 server_public_key_version_
,
132 serialized_report
, &encrypted_report
)) {
133 LOG(ERROR
) << "Failed to encrypt serialized report.";
136 std::string serialized_encrypted_report
;
137 encrypted_report
.SerializeToString(&serialized_encrypted_report
);
138 SendSerializedRequest(serialized_encrypted_report
);
147 void CertificateErrorReporter::OnResponseStarted(net::URLRequest
* request
) {
148 const net::URLRequestStatus
& status(request
->status());
149 if (!status
.is_success()) {
150 LOG(WARNING
) << "Certificate upload failed"
151 << " status:" << status
.status()
152 << " error:" << status
.error();
153 } else if (request
->GetResponseCode() != 200) {
154 LOG(WARNING
) << "Certificate upload HTTP status: "
155 << request
->GetResponseCode();
157 RequestComplete(request
);
160 void CertificateErrorReporter::OnReadCompleted(net::URLRequest
* request
,
164 scoped_ptr
<net::URLRequest
> CertificateErrorReporter::CreateURLRequest(
165 net::URLRequestContext
* context
) {
166 scoped_ptr
<net::URLRequest
> request
=
167 context
->CreateRequest(upload_url_
, net::DEFAULT_PRIORITY
, this);
168 if (cookies_preference_
!= SEND_COOKIES
) {
169 request
->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES
|
170 net::LOAD_DO_NOT_SAVE_COOKIES
);
172 return request
.Pass();
175 bool CertificateErrorReporter::IsHttpUploadUrlSupported() {
176 #if defined(USE_OPENSSL)
183 // Used only by tests.
184 #if defined(USE_OPENSSL)
185 bool CertificateErrorReporter::DecryptCertificateErrorReport(
186 const uint8 server_private_key
[32],
187 const EncryptedCertLoggerRequest
& encrypted_report
,
188 std::string
* decrypted_serialized_report
) {
189 uint8 shared_secret
[crypto::curve25519::kBytes
];
190 crypto::curve25519::ScalarMult(
191 server_private_key
, reinterpret_cast<const uint8
*>(
192 encrypted_report
.client_public_key().data()),
195 crypto::Aead
aead(crypto::Aead::AES_128_CTR_HMAC_SHA256
);
196 crypto::HKDF
hkdf(std::string(reinterpret_cast<char*>(shared_secret
),
197 sizeof(shared_secret
)),
199 base::StringPiece(kHkdfLabel
, sizeof(kHkdfLabel
)), 0, 0,
202 const std::string
key(hkdf
.subkey_secret().data(),
203 hkdf
.subkey_secret().size());
206 // Use an all-zero nonce because the key is random per-message.
207 std::string
nonce(aead
.NonceLength(), 0);
209 return aead
.Open(encrypted_report
.encrypted_report(), nonce
, std::string(),
210 decrypted_serialized_report
);
214 void CertificateErrorReporter::SendSerializedRequest(
215 const std::string
& serialized_request
) {
216 scoped_ptr
<net::URLRequest
> url_request
= CreateURLRequest(request_context_
);
217 url_request
->set_method("POST");
219 scoped_ptr
<net::UploadElementReader
> reader(
220 net::UploadOwnedBytesElementReader::CreateWithString(serialized_request
));
221 url_request
->set_upload(
222 net::ElementsUploadDataStream::CreateWithReader(reader
.Pass(), 0));
224 net::HttpRequestHeaders headers
;
225 headers
.SetHeader(net::HttpRequestHeaders::kContentType
,
226 "x-application/chrome-fraudulent-cert-report");
227 url_request
->SetExtraRequestHeaders(headers
);
229 net::URLRequest
* raw_url_request
= url_request
.get();
230 inflight_requests_
.insert(url_request
.release());
231 raw_url_request
->Start();
234 void CertificateErrorReporter::RequestComplete(net::URLRequest
* request
) {
235 std::set
<net::URLRequest
*>::iterator i
= inflight_requests_
.find(request
);
236 DCHECK(i
!= inflight_requests_
.end());
237 scoped_ptr
<net::URLRequest
> url_request(*i
);
238 inflight_requests_
.erase(i
);
241 } // namespace chrome_browser_net