Chromium Code Reviews| Index: chrome/browser/net/chrome_fraudulent_certificate_reporter.cc |
| diff --git a/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc b/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc |
| index 0e3a1d658533a4678f51c441ac05704cf2651547..e3f43613170f3157b583743f15270b634e2d2ed7 100644 |
| --- a/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc |
| +++ b/chrome/browser/net/chrome_fraudulent_certificate_reporter.cc |
| @@ -12,6 +12,12 @@ |
| #include "base/stl_util.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/net/cert_logger.pb.h" |
| +#include "crypto/curve25519.h" |
| +#include "crypto/encryptor.h" |
| +#include "crypto/hmac.h" |
| +#include "crypto/random.h" |
| +#include "crypto/sha2.h" |
| +#include "crypto/symmetric_key.h" |
| #include "net/base/elements_upload_data_stream.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/request_priority.h" |
| @@ -29,6 +35,14 @@ static const char kFraudulentCertificateUploadEndpoint[] = |
| static const char kInvalidCertificateChainUploadEndpoint[] = ""; |
| +// Constants used for crypto |
| +static const uint8 kServerPublicKey[] = { |
| + 0xc5, 0x3e, 0x58, 0x9e, 0x43, 0xec, 0xbc, 0x84, 0xff, 0xec, 0x8d, |
| + 0x57, 0x20, 0xa3, 0x61, 0x60, 0xe1, 0x0b, 0x7d, 0x30, 0x5d, 0x3b, |
| + 0x2a, 0x90, 0xcf, 0x73, 0xe7, 0x61, 0xa8, 0x92, 0xa1, 0x79}; |
| +static const uint32 kServerPublicKeyVersion = 1; |
| +static const uint32 kAesNonceSize = 16; |
| + |
| ChromeFraudulentCertificateReporter::ChromeFraudulentCertificateReporter( |
| net::URLRequestContext* request_context) |
| : request_context_(request_context), |
| @@ -40,6 +54,76 @@ ChromeFraudulentCertificateReporter::~ChromeFraudulentCertificateReporter() { |
| STLDeleteElements(&inflight_requests_); |
| } |
| +void CalculateSymmetricKeys(const uint8* client_private_key, |
| + const uint8* server_public_key, |
| + std::string& aes_key, |
| + std::string& hmac_key) { |
| + uint8 shared_secret[crypto::curve25519::kBytes]; |
| + uint8 symmetric_key[crypto::kSHA256Length]; |
| + crypto::curve25519::ScalarMult(client_private_key, server_public_key, |
| + shared_secret); |
| + crypto::SHA256HashString( |
| + std::string((char*)shared_secret, sizeof(shared_secret)), symmetric_key, |
| + sizeof(symmetric_key)); |
| + |
| + aes_key = std::string((char*)symmetric_key, sizeof(symmetric_key) / 2); |
| + 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
|
| + sizeof(symmetric_key) / 2); |
| +} |
| + |
| +bool EncryptSerializedReport(const uint8* server_public_key, |
| + uint32 server_public_key_version, |
| + const std::string& report, |
| + EncryptedCertLoggerRequest& encrypted_report) { |
| + // Generate an ephemeral key pair to generate a shared secret. |
| + uint8 public_key[crypto::curve25519::kBytes]; |
| + uint8 private_key[crypto::curve25519::kScalarBytes]; |
| + crypto::RandBytes(private_key, sizeof(private_key)); |
| + crypto::curve25519::ScalarBaseMult(private_key, public_key); |
| + |
| + // Calculate the shared symmetric keys. |
| + std::string aes_key_str; |
| + std::string hmac_key; |
| + CalculateSymmetricKeys(private_key, server_public_key, aes_key_str, hmac_key); |
| + scoped_ptr<crypto::SymmetricKey> aes_key( |
| + crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, aes_key_str)); |
| + |
| + // Encrypt the serialized report with AES-CTR. |
| + crypto::Encryptor encryptor; |
| + char counter[kAesNonceSize]; |
|
agl
2015/03/05 19:22:25
s/char/uint8/
|
| + std::string ciphertext; |
| + crypto::RandBytes(counter, kAesNonceSize); |
|
agl
2015/03/05 19:22:25
as noted, the nonce can be all zeros because the k
|
| + encryptor.Init(aes_key.get(), crypto::Encryptor::CTR, ""); |
| + encryptor.SetCounter(std::string(counter, kAesNonceSize)); |
| + encryptor.Encrypt(report, &ciphertext); |
| + |
| + // Compute HMAC-SHA256(nonce || ciphertext). |
| + std::string hmac_input = std::string(counter, kAesNonceSize) + ciphertext; |
| + crypto::HMAC hmac(crypto::HMAC::SHA256); |
| + bool init_result = hmac.Init(hmac_key); |
|
agl
2015/03/05 19:22:25
why does |init_result| and |hmac_result| exist? Yo
|
| + if (!init_result) { |
| + LOG(ERROR) << "Failed to initialize HMAC."; |
| + return init_result; |
| + } |
| + |
| + unsigned char digest[hmac.DigestLength()]; |
|
agl
2015/03/05 19:22:25
Aren't variable-length arrays a compiler extension
|
| + bool hmac_result = hmac.Sign(hmac_input, digest, sizeof(digest)); |
| + if (!hmac_result) { |
| + LOG(ERROR) << "Failed to compute HMAC."; |
| + return hmac_result; |
| + } |
| + |
| + encrypted_report.set_encrypted_report(ciphertext); |
| + encrypted_report.set_server_public_key(server_public_key_version); |
| + encrypted_report.set_client_public_key( |
| + std::string((char*)public_key, crypto::curve25519::kBytes)); |
| + encrypted_report.set_nonce(std::string(counter, kAesNonceSize)); |
| + encrypted_report.set_mac(std::string((char*)digest, sizeof(digest))); |
| + encrypted_report.set_algorithm( |
| + EncryptedCertLoggerRequest::ECDH_AES_CTR_128_HMAC_SHA256); |
| + return true; |
| +} |
| + |
| static std::string BuildReport(const std::string& hostname, |
| const net::SSLInfo& ssl_info) { |
| CertLoggerRequest request; |
| @@ -77,24 +161,37 @@ void ChromeFraudulentCertificateReporter::SendReport( |
| ReportType type, |
| const std::string& hostname, |
| const net::SSLInfo& ssl_info) { |
| + // We do silent/automatic reporting ONLY for Google properties. For other |
| + // domains (when we start supporting that), we will ask for user permission. |
| + if (type == REPORT_TYPE_PIN_VIOLATION && |
| + !net::TransportSecurityState::IsGooglePinnedProperty(hostname)) { |
| + return; |
| + } |
| + |
| + std::string report = BuildReport(hostname, ssl_info); |
| + |
| if (type == REPORT_TYPE_EXTENDED_REPORTING) { |
| // TODO(estark): Double-check that the user is opted in. |
| // TODO(estark): Temporarily, since there is no upload endpoint, just log |
| // the information. |
| - LOG(ERROR) << "SSL report for " << hostname << ":\n" |
| - << BuildReport(hostname, ssl_info) << "\n\n"; |
| - return; |
| - } |
| + LOG(ERROR) << "SSL report for " << hostname << ":\n" << report << "\n\n"; |
| + |
| + EncryptedCertLoggerRequest encrypted_request; |
| + bool result = EncryptSerializedReport( |
| + kServerPublicKey, kServerPublicKeyVersion, report, encrypted_request); |
| + if (!result) { |
| + LOG(ERROR) << "Failed to encrypt serialized report."; |
| + return; |
| + } |
| + |
| + std::string encrypted_report; |
| + encrypted_request.SerializeToString(&encrypted_report); |
| + LOG(ERROR) << "Encrypted: " << encrypted_report; |
| - // We do silent/automatic reporting ONLY for Google properties. For other |
| - // domains (when we start supporting that), we will ask for user permission. |
| - if (!net::TransportSecurityState::IsGooglePinnedProperty(hostname)) { |
| return; |
| } |
| - std::string report = BuildReport(hostname, ssl_info); |
| - |
| scoped_ptr<net::URLRequest> url_request = |
| CreateURLRequest(request_context_, pinning_violation_upload_url_); |
| url_request->set_method("POST"); |