Index: chrome/browser/net/certificate_error_reporter.cc |
diff --git a/chrome/browser/net/certificate_error_reporter.cc b/chrome/browser/net/certificate_error_reporter.cc |
index 15dad135b373587ed318c3ac384c801275367e4b..18d3ea46e4ca7c01d62320fdf74c17e4ca3274fd 100644 |
--- a/chrome/browser/net/certificate_error_reporter.cc |
+++ b/chrome/browser/net/certificate_error_reporter.cc |
@@ -10,6 +10,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" |
@@ -18,15 +24,223 @@ |
#include "net/ssl/ssl_info.h" |
#include "net/url_request/url_request_context.h" |
+namespace { |
+// Constants used for crypto |
+// TODO(estark): insert the production public key |
felt
2015/04/14 04:30:16
Why's it missing here?
estark
2015/04/14 04:39:16
Um, I don't know. I thought I had a reason for not
estark
2015/04/16 15:04:24
Done.
|
+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 size_t kAesKeySize = 16; |
+static const size_t kAeadNonceSize = 12; |
+static const size_t kAeadTagSize = 32; |
+static const size_t kAesBlockSize = 16; |
+static const size_t kAeadKeySize = 48; |
+static const size_t kSha256BlockSize = 64; |
+ |
+void CalculateAeadKey(const uint8 client_private_key[32], |
+ const uint8 server_public_key[32], |
+ std::string* aead_key) { |
+ // Compute the shared secret, and then hash it once with a 0 prepended |
+ // and once with a 1 prepended. |
+ uint8 shared_secret[crypto::curve25519::kBytes + 1]; |
+ |
+ crypto::curve25519::ScalarMult(client_private_key, server_public_key, |
+ shared_secret + 1); |
+ |
+ std::string shared_secret_str((char*)shared_secret, sizeof(shared_secret)); |
+ |
+ shared_secret_str[0] = 0; |
+ *aead_key = crypto::SHA256HashString(shared_secret_str); |
+ shared_secret[0] = 1; |
+ // Take only as many bytes of the second hash as we need to get to |
+ // |kAeadKeySize|. |
+ *aead_key += crypto::SHA256HashString(shared_secret_str) |
+ .substr(0, kAeadKeySize - aead_key->size()); |
+} |
+ |
+void CopyUint64(uint64 value, std::string* input) { |
+ unsigned i; |
+ uint8 bytes[8]; |
+ for (i = 0; i < sizeof(bytes); i++) { |
+ bytes[i] = value & 0xff; |
+ value >>= 8; |
+ } |
+ *input += std::string((char*)bytes, 8); |
+} |
+ |
+void ComputeHmacInput(const std::string& ciphertext, |
+ const std::string& nonce, |
+ std::string* hmac_input) { |
+ CopyUint64(0, hmac_input); |
+ CopyUint64(ciphertext.size(), hmac_input); |
+ |
+ *hmac_input += nonce; |
+ |
+ // Pad with zeroes to the end of the SHA256 block |
+ const size_t num_padding = |
+ (kSha256BlockSize - |
+ ((sizeof(uint64) * 2 + kAeadNonceSize) % kSha256BlockSize)) % |
+ kSha256BlockSize; |
+ uint8 padding[kSha256BlockSize]; |
+ memset(padding, 0, num_padding); |
+ *hmac_input += std::string((char*)padding, num_padding); |
+ |
+ *hmac_input += ciphertext; |
+} |
+ |
+// Used only by tests (by way of |DecryptCertificateErrorReport|). |
+bool Open(const std::string& aead_key, |
+ const std::string& nonce, |
+ const std::string& ciphertext, |
+ std::string* const plaintext) { |
+ crypto::Encryptor encryptor; |
+ uint8 counter[kAesBlockSize]; |
+ scoped_ptr<crypto::SymmetricKey> aes_key(crypto::SymmetricKey::Import( |
+ crypto::SymmetricKey::AES, aead_key.substr(0, kAesKeySize))); |
+ |
+ memset(counter, 0, kAesBlockSize); |
+ memcpy(counter, nonce.data(), kAeadNonceSize); |
+ |
+ std::string aes_ciphertext = |
+ ciphertext.substr(0, ciphertext.size() - kAeadTagSize); |
+ std::string tag = ciphertext.substr(ciphertext.size() - kAeadTagSize); |
+ |
+ // Compute HMAC(ad_len || ct_len || nonce || ciphertext) |
+ crypto::HMAC hmac(crypto::HMAC::SHA256); |
+ if (!hmac.Init(aead_key.substr(kAesKeySize))) { |
+ LOG(ERROR) << "Failed to initialize HMAC"; |
+ return false; |
+ } |
+ |
+ std::string hmac_input; |
+ ComputeHmacInput(aes_ciphertext, nonce, &hmac_input); |
+ if (!hmac.Verify(hmac_input, tag)) |
+ return false; |
+ |
+ encryptor.Init(aes_key.get(), crypto::Encryptor::CTR, ""); |
+ encryptor.SetCounter(std::string((char*)counter, kAesBlockSize)); |
+ encryptor.Decrypt(aes_ciphertext, plaintext); |
+ return true; |
+} |
+ |
+bool EncryptSerializedReport( |
+ const uint8* server_public_key, |
+ uint32 server_public_key_version, |
+ const std::string& report, |
+ chrome_browser_net::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); |
+ |
+ std::string aead_key; |
+ std::string ciphertext; |
+ CalculateAeadKey(private_key, server_public_key, &aead_key); |
+ |
+ // Use an all-zero nonce because the key is random per-message. |
+ std::string nonce(kAeadNonceSize, 0); |
+ if (!chrome_browser_net::Seal(aead_key, nonce, report, &ciphertext)) |
+ return false; |
+ |
+ encrypted_report->set_encrypted_report(ciphertext); |
+ encrypted_report->set_server_public_key_version(server_public_key_version); |
+ encrypted_report->set_client_public_key( |
+ std::string((char*)public_key, crypto::curve25519::kBytes)); |
+ encrypted_report->set_algorithm( |
+ chrome_browser_net::EncryptedCertLoggerRequest:: |
+ AEAD_ECDH_AES_128_CTR_HMAC_SHA256); |
+ return true; |
+} |
+ |
+} // namespace |
+ |
namespace chrome_browser_net { |
+// Seal |plaintext| with AES-CTR-128-HMAC-SHA256. Support for AD |
+// (additional authenticated data) is not implemented because it's not |
+// needed for cert reporting. |
+bool Seal(const std::string& aead_key, |
estark
2015/04/13 23:43:57
I wasn't sure whether to put the AEAD code here or
|
+ const std::string& nonce, |
+ const std::string& plaintext, |
+ std::string* const ciphertext) { |
+ crypto::Encryptor encryptor; |
+ uint8 counter[kAesBlockSize]; |
+ scoped_ptr<crypto::SymmetricKey> aes_key(crypto::SymmetricKey::Import( |
+ crypto::SymmetricKey::AES, aead_key.substr(0, kAesKeySize))); |
+ |
+ memset(counter, 0, kAesBlockSize); |
+ memcpy(counter, nonce.data(), kAeadNonceSize); |
+ |
+ encryptor.Init(aes_key.get(), crypto::Encryptor::CTR, ""); |
+ encryptor.SetCounter(std::string((char*)counter, sizeof(counter))); |
+ encryptor.Encrypt(plaintext, ciphertext); |
+ |
+ // Compute HMAC(ad_len || ct_len || nonce || ad || padding || ciphertext) |
+ crypto::HMAC hmac(crypto::HMAC::SHA256); |
+ unsigned char digest[32]; |
+ CHECK_EQ(32u, hmac.DigestLength()); |
+ if (!hmac.Init(aead_key.substr(kAesKeySize))) { |
+ LOG(ERROR) << "Failed to initialize HMAC"; |
+ return false; |
+ } |
+ |
+ std::string hmac_input; |
+ ComputeHmacInput(*ciphertext, nonce, &hmac_input); |
+ if (!hmac.Sign(hmac_input, digest, sizeof(digest))) { |
+ LOG(ERROR) << "Failed to compute HMAC."; |
+ return false; |
+ } |
+ |
+ *ciphertext += std::string((char*)digest, kAeadTagSize); |
+ return true; |
+} |
+ |
+// Used only by tests. |
+bool DecryptCertificateErrorReport( |
+ const uint8 server_private_key[32], |
+ const EncryptedCertLoggerRequest& encrypted_report, |
+ CertLoggerRequest* decrypted_report) { |
+ std::string aead_key; |
+ std::string plaintext; |
+ CalculateAeadKey(server_private_key, |
+ (uint8*)encrypted_report.client_public_key().data(), |
+ &aead_key); |
+ |
+ // Use an all-zero nonce because the key is random per-message. |
+ std::string nonce(kAeadNonceSize, 0); |
+ if (!Open(aead_key, nonce, encrypted_report.encrypted_report(), &plaintext)) |
+ return false; |
+ |
+ decrypted_report->ParseFromString(plaintext); |
+ return true; |
+} |
+ |
CertificateErrorReporter::CertificateErrorReporter( |
net::URLRequestContext* request_context, |
const GURL& upload_url, |
CookiesPreference cookies_preference) |
+ : CertificateErrorReporter(request_context, |
+ upload_url, |
+ cookies_preference, |
+ kServerPublicKey, |
+ kServerPublicKeyVersion) { |
+} |
+ |
+CertificateErrorReporter::CertificateErrorReporter( |
+ net::URLRequestContext* request_context, |
+ const GURL& upload_url, |
+ CookiesPreference cookies_preference, |
+ const uint8 server_public_key[32], |
+ const uint32 server_public_key_version) |
: request_context_(request_context), |
upload_url_(upload_url), |
- cookies_preference_(cookies_preference) { |
+ cookies_preference_(cookies_preference), |
+ server_public_key_(server_public_key), |
+ server_public_key_version_(server_public_key_version) { |
DCHECK(!upload_url.is_empty()); |
} |
@@ -38,8 +252,6 @@ void CertificateErrorReporter::SendReport(ReportType type, |
const std::string& hostname, |
const net::SSLInfo& ssl_info) { |
CertLoggerRequest request; |
- std::string out; |
- |
BuildReport(hostname, ssl_info, &request); |
switch (type) { |
@@ -47,9 +259,22 @@ void CertificateErrorReporter::SendReport(ReportType type, |
SendCertLoggerRequest(request); |
break; |
case REPORT_TYPE_EXTENDED_REPORTING: |
- // TODO(estark): Encrypt the report if not sending over HTTPS |
- DCHECK(upload_url_.SchemeIsSecure()); |
- SendCertLoggerRequest(request); |
+ if (upload_url_.SchemeIsSecure()) { |
+ SendCertLoggerRequest(request); |
+ } else { |
+ EncryptedCertLoggerRequest encrypted_report; |
+ std::string serialized_report; |
+ request.SerializeToString(&serialized_report); |
+ if (!EncryptSerializedReport(server_public_key_, |
+ server_public_key_version_, |
+ serialized_report, &encrypted_report)) { |
+ LOG(ERROR) << "Failed to encrypt serialized report."; |
+ return; |
+ } |
+ std::string serialized_encrypted_report; |
+ encrypted_report.SerializeToString(&serialized_encrypted_report); |
+ SendSerializedRequest(serialized_encrypted_report); |
+ } |
break; |
default: |
NOTREACHED(); |
@@ -88,7 +313,11 @@ void CertificateErrorReporter::SendCertLoggerRequest( |
const CertLoggerRequest& request) { |
std::string serialized_request; |
request.SerializeToString(&serialized_request); |
+ SendSerializedRequest(serialized_request); |
+} |
+void CertificateErrorReporter::SendSerializedRequest( |
+ const std::string& serialized_request) { |
scoped_ptr<net::URLRequest> url_request = CreateURLRequest(request_context_); |
url_request->set_method("POST"); |