OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/net/chrome_fraudulent_certificate_reporter.h" | 5 #include "chrome/browser/net/chrome_fraudulent_certificate_reporter.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 | 8 |
9 #include "base/base64.h" | 9 #include "base/base64.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/profiler/scoped_tracker.h" | 11 #include "base/profiler/scoped_tracker.h" |
12 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
13 #include "base/time/time.h" | 13 #include "base/time/time.h" |
14 #include "chrome/browser/net/cert_logger.pb.h" | 14 #include "chrome/browser/net/cert_logger.pb.h" |
15 #include "crypto/curve25519.h" | |
16 #include "crypto/encryptor.h" | |
17 #include "crypto/hmac.h" | |
18 #include "crypto/random.h" | |
19 #include "crypto/sha2.h" | |
20 #include "crypto/symmetric_key.h" | |
15 #include "net/base/elements_upload_data_stream.h" | 21 #include "net/base/elements_upload_data_stream.h" |
16 #include "net/base/load_flags.h" | 22 #include "net/base/load_flags.h" |
17 #include "net/base/request_priority.h" | 23 #include "net/base/request_priority.h" |
18 #include "net/base/upload_bytes_element_reader.h" | 24 #include "net/base/upload_bytes_element_reader.h" |
19 #include "net/cert/x509_certificate.h" | 25 #include "net/cert/x509_certificate.h" |
20 #include "net/ssl/ssl_info.h" | 26 #include "net/ssl/ssl_info.h" |
21 #include "net/url_request/url_request_context.h" | 27 #include "net/url_request/url_request_context.h" |
22 | 28 |
23 namespace chrome_browser_net { | 29 namespace chrome_browser_net { |
24 | 30 |
25 // TODO(palmer): Switch to HTTPS when the error handling delegate is more | 31 // TODO(palmer): Switch to HTTPS when the error handling delegate is more |
26 // sophisticated. Ultimately we plan to attempt the report on many transports. | 32 // sophisticated. Ultimately we plan to attempt the report on many transports. |
27 static const char kFraudulentCertificateUploadEndpoint[] = | 33 static const char kFraudulentCertificateUploadEndpoint[] = |
28 "http://clients3.google.com/log_cert_error"; | 34 "http://clients3.google.com/log_cert_error"; |
29 | 35 |
30 static const char kInvalidCertificateChainUploadEndpoint[] = ""; | 36 static const char kInvalidCertificateChainUploadEndpoint[] = ""; |
31 | 37 |
38 // Constants used for crypto | |
39 static const uint8 kServerPublicKey[] = { | |
40 0xc5, 0x3e, 0x58, 0x9e, 0x43, 0xec, 0xbc, 0x84, 0xff, 0xec, 0x8d, | |
41 0x57, 0x20, 0xa3, 0x61, 0x60, 0xe1, 0x0b, 0x7d, 0x30, 0x5d, 0x3b, | |
42 0x2a, 0x90, 0xcf, 0x73, 0xe7, 0x61, 0xa8, 0x92, 0xa1, 0x79}; | |
43 static const uint32 kServerPublicKeyVersion = 1; | |
44 static const uint32 kAesNonceSize = 16; | |
45 | |
32 ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter( | 46 ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter( |
33 net::URLRequestContext* request_context) | 47 net::URLRequestContext* request_context) |
34 : request_context_(request_context), | 48 : request_context_(request_context), |
35 pinning_violation_upload_url_(kFraudulentCertificateUploadEndpoint), | 49 pinning_violation_upload_url_(kFraudulentCertificateUploadEndpoint), |
36 invalid_chain_upload_url_(kInvalidCertificateChainUploadEndpoint) { | 50 invalid_chain_upload_url_(kInvalidCertificateChainUploadEndpoint) { |
37 } | 51 } |
38 | 52 |
39 ChromeFraudulentCertificateReporter::~ChromeFraudulentCertificateReporter() { | 53 ChromeFraudulentCertificateReporter::~ChromeFraudulentCertificateReporter() { |
40 STLDeleteElements(&inflight_requests_); | 54 STLDeleteElements(&inflight_requests_); |
41 } | 55 } |
42 | 56 |
57 void CalculateSymmetricKeys(const uint8* client_private_key, | |
58 const uint8* server_public_key, | |
59 std::string& aes_key, | |
60 std::string& hmac_key) { | |
61 uint8 shared_secret[crypto::curve25519::kBytes]; | |
62 uint8 symmetric_key[crypto::kSHA256Length]; | |
63 crypto::curve25519::ScalarMult(client_private_key, server_public_key, | |
64 shared_secret); | |
65 crypto::SHA256HashString( | |
66 std::string((char*)shared_secret, sizeof(shared_secret)), symmetric_key, | |
67 sizeof(symmetric_key)); | |
68 | |
69 aes_key = std::string((char*)symmetric_key, sizeof(symmetric_key) / 2); | |
70 hmac_key = std::string((char*)(symmetric_key + sizeof(symmetric_key) / 2), | |
agl
2015/03/05 19:22:25
An HMAC-SHA256 key is generally 32 bytes. Also, if
| |
71 sizeof(symmetric_key) / 2); | |
72 } | |
73 | |
74 bool EncryptSerializedReport(const uint8* server_public_key, | |
75 uint32 server_public_key_version, | |
76 const std::string& report, | |
77 EncryptedCertLoggerRequest& encrypted_report) { | |
78 // Generate an ephemeral key pair to generate a shared secret. | |
79 uint8 public_key[crypto::curve25519::kBytes]; | |
80 uint8 private_key[crypto::curve25519::kScalarBytes]; | |
81 crypto::RandBytes(private_key, sizeof(private_key)); | |
82 crypto::curve25519::ScalarBaseMult(private_key, public_key); | |
83 | |
84 // Calculate the shared symmetric keys. | |
85 std::string aes_key_str; | |
86 std::string hmac_key; | |
87 CalculateSymmetricKeys(private_key, server_public_key, aes_key_str, hmac_key); | |
88 scoped_ptr<crypto::SymmetricKey> aes_key( | |
89 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, aes_key_str)); | |
90 | |
91 // Encrypt the serialized report with AES-CTR. | |
92 crypto::Encryptor encryptor; | |
93 char counter[kAesNonceSize]; | |
agl
2015/03/05 19:22:25
s/char/uint8/
| |
94 std::string ciphertext; | |
95 crypto::RandBytes(counter, kAesNonceSize); | |
agl
2015/03/05 19:22:25
as noted, the nonce can be all zeros because the k
| |
96 encryptor.Init(aes_key.get(), crypto::Encryptor::CTR, ""); | |
97 encryptor.SetCounter(std::string(counter, kAesNonceSize)); | |
98 encryptor.Encrypt(report, &ciphertext); | |
99 | |
100 // Compute HMAC-SHA256(nonce || ciphertext). | |
101 std::string hmac_input = std::string(counter, kAesNonceSize) + ciphertext; | |
102 crypto::HMAC hmac(crypto::HMAC::SHA256); | |
103 bool init_result = hmac.Init(hmac_key); | |
agl
2015/03/05 19:22:25
why does |init_result| and |hmac_result| exist? Yo
| |
104 if (!init_result) { | |
105 LOG(ERROR) << "Failed to initialize HMAC."; | |
106 return init_result; | |
107 } | |
108 | |
109 unsigned char digest[hmac.DigestLength()]; | |
agl
2015/03/05 19:22:25
Aren't variable-length arrays a compiler extension
| |
110 bool hmac_result = hmac.Sign(hmac_input, digest, sizeof(digest)); | |
111 if (!hmac_result) { | |
112 LOG(ERROR) << "Failed to compute HMAC."; | |
113 return hmac_result; | |
114 } | |
115 | |
116 encrypted_report.set_encrypted_report(ciphertext); | |
117 encrypted_report.set_server_public_key(server_public_key_version); | |
118 encrypted_report.set_client_public_key( | |
119 std::string((char*)public_key, crypto::curve25519::kBytes)); | |
120 encrypted_report.set_nonce(std::string(counter, kAesNonceSize)); | |
121 encrypted_report.set_mac(std::string((char*)digest, sizeof(digest))); | |
122 encrypted_report.set_algorithm( | |
123 EncryptedCertLoggerRequest::ECDH_AES_CTR_128_HMAC_SHA256); | |
124 return true; | |
125 } | |
126 | |
43 static std::string BuildReport(const std::string& hostname, | 127 static std::string BuildReport(const std::string& hostname, |
44 const net::SSLInfo& ssl_info) { | 128 const net::SSLInfo& ssl_info) { |
45 CertLoggerRequest request; | 129 CertLoggerRequest request; |
46 base::Time now = base::Time::Now(); | 130 base::Time now = base::Time::Now(); |
47 request.set_time_usec(now.ToInternalValue()); | 131 request.set_time_usec(now.ToInternalValue()); |
48 request.set_hostname(hostname); | 132 request.set_hostname(hostname); |
49 | 133 |
50 std::vector<std::string> pem_encoded_chain; | 134 std::vector<std::string> pem_encoded_chain; |
51 if (!ssl_info.cert->GetPEMEncodedChain(&pem_encoded_chain)) { | 135 if (!ssl_info.cert->GetPEMEncodedChain(&pem_encoded_chain)) { |
52 LOG(ERROR) << "Could not get PEM encoded chain."; | 136 LOG(ERROR) << "Could not get PEM encoded chain."; |
(...skipping 17 matching lines...) Expand all Loading... | |
70 context->CreateRequest(upload_url, net::DEFAULT_PRIORITY, this, NULL); | 154 context->CreateRequest(upload_url, net::DEFAULT_PRIORITY, this, NULL); |
71 request->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | 155 request->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | |
72 net::LOAD_DO_NOT_SAVE_COOKIES); | 156 net::LOAD_DO_NOT_SAVE_COOKIES); |
73 return request.Pass(); | 157 return request.Pass(); |
74 } | 158 } |
75 | 159 |
76 void ChromeFraudulentCertificateReporter::SendReport( | 160 void ChromeFraudulentCertificateReporter::SendReport( |
77 ReportType type, | 161 ReportType type, |
78 const std::string& hostname, | 162 const std::string& hostname, |
79 const net::SSLInfo& ssl_info) { | 163 const net::SSLInfo& ssl_info) { |
164 // We do silent/automatic reporting ONLY for Google properties. For other | |
165 // domains (when we start supporting that), we will ask for user permission. | |
166 if (type == REPORT_TYPE_PIN_VIOLATION && | |
167 !net::TransportSecurityState::IsGooglePinnedProperty(hostname)) { | |
168 return; | |
169 } | |
170 | |
171 std::string report = BuildReport(hostname, ssl_info); | |
172 | |
80 if (type == REPORT_TYPE_EXTENDED_REPORTING) { | 173 if (type == REPORT_TYPE_EXTENDED_REPORTING) { |
81 // TODO(estark): Double-check that the user is opted in. | 174 // TODO(estark): Double-check that the user is opted in. |
82 | 175 |
83 // TODO(estark): Temporarily, since there is no upload endpoint, just log | 176 // TODO(estark): Temporarily, since there is no upload endpoint, just log |
84 // the information. | 177 // the information. |
85 LOG(ERROR) << "SSL report for " << hostname << ":\n" | 178 LOG(ERROR) << "SSL report for " << hostname << ":\n" << report << "\n\n"; |
86 << BuildReport(hostname, ssl_info) << "\n\n"; | 179 |
180 EncryptedCertLoggerRequest encrypted_request; | |
181 bool result = EncryptSerializedReport( | |
182 kServerPublicKey, kServerPublicKeyVersion, report, encrypted_request); | |
183 if (!result) { | |
184 LOG(ERROR) << "Failed to encrypt serialized report."; | |
185 return; | |
186 } | |
187 | |
188 std::string encrypted_report; | |
189 encrypted_request.SerializeToString(&encrypted_report); | |
190 LOG(ERROR) << "Encrypted: " << encrypted_report; | |
191 | |
87 return; | 192 return; |
88 } | 193 } |
89 | 194 |
90 // We do silent/automatic reporting ONLY for Google properties. For other | |
91 // domains (when we start supporting that), we will ask for user permission. | |
92 if (!net::TransportSecurityState::IsGooglePinnedProperty(hostname)) { | |
93 return; | |
94 } | |
95 | |
96 std::string report = BuildReport(hostname, ssl_info); | |
97 | |
98 scoped_ptr<net::URLRequest> url_request = | 195 scoped_ptr<net::URLRequest> url_request = |
99 CreateURLRequest(request_context_, pinning_violation_upload_url_); | 196 CreateURLRequest(request_context_, pinning_violation_upload_url_); |
100 url_request->set_method("POST"); | 197 url_request->set_method("POST"); |
101 | 198 |
102 scoped_ptr<net::UploadElementReader> reader( | 199 scoped_ptr<net::UploadElementReader> reader( |
103 net::UploadOwnedBytesElementReader::CreateWithString(report)); | 200 net::UploadOwnedBytesElementReader::CreateWithString(report)); |
104 url_request->set_upload( | 201 url_request->set_upload( |
105 net::ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0)); | 202 net::ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0)); |
106 | 203 |
107 net::HttpRequestHeaders headers; | 204 net::HttpRequestHeaders headers; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
141 LOG(WARNING) << "Certificate upload HTTP status: " | 238 LOG(WARNING) << "Certificate upload HTTP status: " |
142 << request->GetResponseCode(); | 239 << request->GetResponseCode(); |
143 } | 240 } |
144 RequestComplete(request); | 241 RequestComplete(request); |
145 } | 242 } |
146 | 243 |
147 void ChromeFraudulentCertificateReporter::OnReadCompleted( | 244 void ChromeFraudulentCertificateReporter::OnReadCompleted( |
148 net::URLRequest* request, int bytes_read) {} | 245 net::URLRequest* request, int bytes_read) {} |
149 | 246 |
150 } // namespace chrome_browser_net | 247 } // namespace chrome_browser_net |
OLD | NEW |