Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1567)

Unified Diff: chrome/browser/net/certificate_error_reporter.cc

Issue 1083493003: Encrypt certificate reports before uploading to HTTP URLs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: more cleanup Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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");
« no previous file with comments | « chrome/browser/net/certificate_error_reporter.h ('k') | chrome/browser/net/certificate_error_reporter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698