Chromium Code Reviews| 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 |