diff --git a/components/brave_prochlo/BUILD.gn b/components/brave_prochlo/BUILD.gn index 7f46342dbb5..326a4d0f111 100644 --- a/components/brave_prochlo/BUILD.gn +++ b/components/brave_prochlo/BUILD.gn @@ -1,25 +1,8 @@ import("//third_party/protobuf/proto_library.gni") -static_library("brave_prochlo") { - sources = [ - "brave_prochlo_crypto.cc", - "brave_prochlo_crypto.h", - "brave_prochlo_message.cc", - "brave_prochlo_message.h", - "prochlo_crypto.cc", - "prochlo_crypto.h", - "prochlo_data.h", - ] - - public_deps = [ - ":prochlo_proto", - "//base:base", - "//crypto", - "//third_party/boringssl", - "//third_party/protobuf:protobuf_lite", - ] -} - +# This component is a leftover that we will cleanup after transition +# to the new p3a ping format. +# https://github.com/brave/brave-browser/issues/15967 proto_library("prochlo_proto") { sources = [ "prochlo_message.proto" ] proto_in_dir = "." diff --git a/components/brave_prochlo/DEPS b/components/brave_prochlo/DEPS deleted file mode 100644 index a861ae98c80..00000000000 --- a/components/brave_prochlo/DEPS +++ /dev/null @@ -1,3 +0,0 @@ -include_rules = [ - "+third_party/boringssl", -] diff --git a/components/brave_prochlo/README.chromium b/components/brave_prochlo/README.chromium deleted file mode 100644 index 6af425976a8..00000000000 --- a/components/brave_prochlo/README.chromium +++ /dev/null @@ -1,4 +0,0 @@ -Name: PROCHLO -URL: https://github.com/google/prochlo -License: Apache-2.0 -License File: /brave/common/licenses/Apache-2.0 diff --git a/components/brave_prochlo/brave_prochlo_crypto.cc b/components/brave_prochlo/brave_prochlo_crypto.cc deleted file mode 100644 index 4d524215794..00000000000 --- a/components/brave_prochlo/brave_prochlo_crypto.cc +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright 2019 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "brave/components/brave_prochlo/brave_prochlo_crypto.h" - -#include "third_party/boringssl/src/include/openssl/pem.h" - -namespace prochlo { - -BraveProchloCrypto::BraveProchloCrypto() = default; - -bool BraveProchloCrypto::load_analyzer_key_from_bytes( - const std::vector& bytes) { - public_analyzer_key_ = load_public_key_from_bytes(bytes); - return public_analyzer_key_ != nullptr; -} - -bool BraveProchloCrypto::load_shuffler_key_from_bytes( - const std::vector& bytes) { - public_shuffler_key_ = load_public_key_from_bytes(bytes); - return public_shuffler_key_ != nullptr; -} - -EVP_PKEY* BraveProchloCrypto::load_public_key_from_bytes( - const std::vector& bytes) { - bssl::UniquePtr bio( - BIO_new_mem_buf(bytes.data(), static_cast(bytes.size()))); - if (!bio) { - return nullptr; - } - return PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr); -} - -} // namespace prochlo diff --git a/components/brave_prochlo/brave_prochlo_crypto.h b/components/brave_prochlo/brave_prochlo_crypto.h deleted file mode 100644 index 03c313a8a54..00000000000 --- a/components/brave_prochlo/brave_prochlo_crypto.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright 2019 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef BRAVE_COMPONENTS_BRAVE_PROCHLO_BRAVE_PROCHLO_CRYPTO_H_ -#define BRAVE_COMPONENTS_BRAVE_PROCHLO_BRAVE_PROCHLO_CRYPTO_H_ - -#include - -#include "base/macros.h" -#include "brave/components/brave_prochlo/prochlo_crypto.h" - -namespace prochlo { - -class BraveProchloCrypto : public Crypto { - public: - BraveProchloCrypto(); - - // Load the public key for the Analyzer from bytes. - bool load_analyzer_key_from_bytes(const std::vector& bytes); - - // Load the public key for the Analyzer from bytes. - bool load_shuffler_key_from_bytes(const std::vector& bytes); - - private: - // Load a public key returning the structure. - EVP_PKEY* load_public_key_from_bytes(const std::vector& bytes); - - DISALLOW_COPY_AND_ASSIGN(BraveProchloCrypto); -}; - -} // namespace prochlo - -#endif // BRAVE_COMPONENTS_BRAVE_PROCHLO_BRAVE_PROCHLO_CRYPTO_H_ diff --git a/components/brave_prochlo/brave_prochlo_message.cc b/components/brave_prochlo/brave_prochlo_message.cc deleted file mode 100644 index e5bef6d5bbe..00000000000 --- a/components/brave_prochlo/brave_prochlo_message.cc +++ /dev/null @@ -1,260 +0,0 @@ -/* Copyright 2019 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "brave/components/brave_prochlo/brave_prochlo_message.h" - -#include - -#include "base/containers/flat_set.h" -#include "base/cxx17_backports.h" -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "base/trace_event/trace_event.h" -#include "brave/components/brave_prochlo/brave_prochlo_crypto.h" -#include "brave/components/brave_prochlo/prochlo_data.h" -#include "brave/components/brave_prochlo/prochlo_message.pb.h" -#include "crypto/sha2.h" - -namespace prochlo { - -namespace { - -// TODO(iefremov): Make it possible to use testing keys. -// TODO(iefremov): Key versioning? -constexpr char kShufflerKey[] = R"( ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB+tJ1w8nSnusfxfXV1pq+teKmsb+ -kH5op6DjhJABBiLWDhTXyLB38noi7BMwNC3fAcrlVAYPj4ejQ8ohHuSSRA== ------END PUBLIC KEY-----)"; - -constexpr char kAnalyzerKey[] = R"( ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQCeVJbcADloHb8bwftIi1UO0smiz -8ObdAFQ8j3U9cMehGqI3zXgS8APvBW/9XxMkb4XWQe+t9h6qHq82P6zcBg== ------END PUBLIC KEY-----)"; - -bool MakeProchlomation(uint64_t metric, - const uint8_t* data, - const uint8_t* crowd_id, - ShufflerItem* shuffler_item) { - DCHECK(data); - DCHECK(crowd_id); - DCHECK(shuffler_item); - // TODO(iefremov): - create patch for adding `brave_p3a` - // to src/base/trace_event/builtin_categories.h - // TRACE_EVENT0("brave_p3a", "MakeProchlomation"); - - BraveProchloCrypto crypto; - - const std::vector shuffler_key( - &kShufflerKey[0], &kShufflerKey[0] + base::size(kShufflerKey)); - if (!crypto.load_shuffler_key_from_bytes(shuffler_key)) { - return false; - } - - const std::vector analyzer_key( - &kAnalyzerKey[0], &kAnalyzerKey[0] + base::size(kAnalyzerKey)); - if (!crypto.load_analyzer_key_from_bytes(analyzer_key)) { - return false; - } - - // We have to create a Prochlomation and a PlainShufflerItem to encrypt them - // both into an AnalyzerItea and a ShufflerItem, respectively. We'll stage - // those here. We can probably do this more efficiently to avoid copies. - Prochlomation prochlomation; - PlainShufflerItem plain_shuffler_item; - - // First the prochlomation - prochlomation.metric = metric; - memcpy(prochlomation.data, data, kProchlomationDataLength); - - // Then the AnalyzerItem of the PlainShufflerItem - if (!crypto.EncryptForAnalyzer(prochlomation, - &plain_shuffler_item.analyzer_item)) { - NOTREACHED(); - return false; - } - - // Now prepare the PlainShufflerItem - memcpy(plain_shuffler_item.crowd_id, crowd_id, kCrowdIdLength); - - // And create the ShufflerItem - if (!crypto.EncryptForShuffler(plain_shuffler_item, shuffler_item)) { - NOTREACHED(); - return false; - } - - return true; -} - -void InitProchloMessage(uint64_t metric_hash, - const ShufflerItem& item, - brave_pyxis::PyxisMessage* pyxis_message) { - DCHECK(pyxis_message); - brave_pyxis::PyxisValue* value = pyxis_message->add_pyxis_values(); - value->set_ciphertext(item.ciphertext, kPlainShufflerItemLength); - value->set_tag(item.tag, kTagLength); - value->set_nonce(item.nonce, kNonceLength); - value->set_metric_id(metric_hash); - value->set_client_public_key(item.client_public_key, kPublicKeyLength); -} - -} // namespace - -MessageMetainfo::MessageMetainfo() = default; -MessageMetainfo::~MessageMetainfo() = default; - -void GenerateProchloMessage(uint64_t metric_hash, - uint64_t metric_value, - const MessageMetainfo& meta, - brave_pyxis::PyxisMessage* pyxis_message) { - // TODO(iefremov): - create patch for adding `brave_p3a` - // to src/base/trace_event/builtin_categories.h - // TRACE_EVENT0("brave_p3a", "GenerateProchloMessage"); - ShufflerItem item; - uint8_t data[kProchlomationDataLength] = {0}; - uint8_t crowd_id[kCrowdIdLength] = {0}; - - // First byte contains the 4 booleans. - const char daily = 1; - const char weekly = 0; - const char monthly = 2; - const char first = 0; - data[0] = daily | weekly | monthly | first; - uint8_t* ptr = data; - ptr++; - - // Find out years of install and survey. - base::Time::Exploded exploded; - meta.date_of_survey.LocalExplode(&exploded); - DCHECK_GE(exploded.year, 999); - const std::string yos = base::NumberToString(exploded.year).substr(2, 4); - meta.date_of_install.LocalExplode(&exploded); - DCHECK_GE(exploded.year, 999); - const std::string yoi = base::NumberToString(exploded.year).substr(2, 4); - - const std::string metastring = - "," + meta.country_code + "," + meta.platform + "," + meta.version + "," + - meta.channel + "," + yoi + base::NumberToString(meta.woi) + "," + yos + - base::NumberToString(meta.wos) + "," + meta.refcode + ","; - - const std::string metric_value_str = base::NumberToString(metric_value); - - // TODO(iefremov): replace with 'if'? - CHECK_LE(metastring.size() + metric_value_str.size(), - kProchlomationDataLength - 1); - - memcpy(ptr, metastring.data(), metastring.size()); - ptr += metastring.size(); - memcpy(ptr, metric_value_str.data(), metric_value_str.size()); - - // TODO(iefremov): Salt? - crypto::SHA256HashString( - base::NumberToString(metric_hash) + base::NumberToString(metric_value), - crowd_id, kCrowdIdLength); - MakeProchlomation(metric_hash, data, crowd_id, &item); - - InitProchloMessage(metric_hash, item, pyxis_message); -} - -void GenerateP3AMessage(uint64_t metric_hash, - uint64_t metric_value, - const MessageMetainfo& meta, - brave_pyxis::RawP3AValue* p3a_message) { - // TODO(iefremov): - create patch for adding `brave_p3a` - // to src/base/trace_event/builtin_categories.h - // TRACE_EVENT0("brave_p3a", "GenerateP3AMessage"); - uint8_t data[kProchlomationDataLength] = {0}; - - // First byte contains the 4 booleans. - const char daily = 1; - const char weekly = 0; - const char monthly = 2; - const char first = 0; - data[0] = daily | weekly | monthly | first; - uint8_t* ptr = data; - ptr++; - - // Find out years of install and survey. - base::Time::Exploded exploded; - meta.date_of_survey.LocalExplode(&exploded); - DCHECK_GE(exploded.year, 999); - const std::string yos = base::NumberToString(exploded.year).substr(2, 4); - meta.date_of_install.LocalExplode(&exploded); - DCHECK_GE(exploded.year, 999); - const std::string yoi = base::NumberToString(exploded.year).substr(2, 4); - - const std::string metastring = - "," + meta.country_code + "," + meta.platform + "," + meta.version + "," + - meta.channel + "," + yoi + base::NumberToString(meta.woi) + "," + yos + - base::NumberToString(meta.wos) + "," + meta.refcode + ","; - - const std::string metric_value_str = base::NumberToString(metric_value); - - // TODO(iefremov): replace with 'if'? - CHECK_LE(metastring.size() + metric_value_str.size(), - kProchlomationDataLength - 1); - - memcpy(ptr, metastring.data(), metastring.size()); - ptr += metastring.size(); - memcpy(ptr, metric_value_str.data(), metric_value_str.size()); - - // Init the message. - p3a_message->set_metric_id(metric_hash); - p3a_message->set_p3a_info(data, kProchlomationDataLength); -} - -void MaybeStripRefcodeAndCountry(prochlo::MessageMetainfo* meta) { - const std::string& refcode = meta->refcode; - const std::string& country = meta->country_code; - constexpr char kRefcodeNone[] = "none"; - constexpr char kCountryOther[] = "other"; - constexpr char kRefcodeOther[] = "other"; - - static base::flat_set const kLinuxCountries( - {"US", "FR", "DE", "GB", "IN", "BR", "PL", "NL", "ES", "CA", "IT", "AU", - "MX", "CH", "RU", "ZA", "SE", "BE", "JP"}); - - static base::flat_set const kNotableRefcodes( - {"BRV001", "GDB255", "APP709", "GBW423", "BRT001", "VNI569", "ICO964", - "ILY758"}); - - static base::flat_set const kNotableCountries( - {"FR", "PH", "GB", "IN", "DE", "BR", "CA", "IT", "ES", "NL", "MX", "AU", - "RU", "JP", "PL", "ID", "KR", "AR"}); - - DCHECK(meta); - if (meta->platform == "linux-bc") { - // Because Linux has no refcodes, ignore, and if we have more than - // 3/0.05 = 60 users in a country for a week of install, we can send - // country. - meta->refcode = kRefcodeNone; - if (kLinuxCountries.count(country) == 0) { - meta->country_code = kCountryOther; - } - } else { - // Now the minimum platform is MacOS at ~3%, so cut off for a group under - // here becomes 3/(0.05*0.03) = 2000. - if (country == "US" || country.empty()) { - const bool us_and_ref = - country == "US" && (refcode == kRefcodeNone || refcode == "GDB255" || - refcode == "BRV001"); - const bool unknown_and_ref = - country.empty() && - (kNotableRefcodes.count(refcode) > 0 || refcode == kRefcodeNone); - - if (!(us_and_ref || unknown_and_ref)) { - meta->refcode = kRefcodeOther; - } - } else if (kNotableCountries.count(country) > 0) { - meta->refcode = kRefcodeOther; - } else { - meta->country_code = kCountryOther; - meta->refcode = kRefcodeOther; - } - } -} - -} // namespace prochlo diff --git a/components/brave_prochlo/prochlo_crypto.cc b/components/brave_prochlo/prochlo_crypto.cc deleted file mode 100644 index 3bc39b9d1da..00000000000 --- a/components/brave_prochlo/prochlo_crypto.cc +++ /dev/null @@ -1,516 +0,0 @@ -// Copyright 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -#include -#include -#include - -#include "third_party/boringssl/src/include/openssl/ecdh.h" -#include "third_party/boringssl/src/include/openssl/err.h" -#include "third_party/boringssl/src/include/openssl/hmac.h" -#include "third_party/boringssl/src/include/openssl/pem.h" -#include "third_party/boringssl/src/include/openssl/rand.h" - -#include "brave/components/brave_prochlo/prochlo_crypto.h" - -namespace prochlo { - -bool Crypto::load_analyzer_key(const std::string& keyfile) { - public_analyzer_key_ = load_public_key(keyfile); - return public_analyzer_key_ != nullptr; -} - -bool Crypto::load_shuffler_key(const std::string& keyfile) { - public_shuffler_key_ = load_public_key(keyfile); - return public_shuffler_key_ != nullptr; -} - -EVP_PKEY* Crypto::load_public_key(const std::string& keyfile) { - FILE* fp = fopen(keyfile.c_str(), "r"); - if (fp == nullptr) { - // warn("fopen()"); - return nullptr; - } - - EVP_PKEY* key; - key = PEM_read_PUBKEY(fp, NULL, NULL, NULL); - fclose(fp); - - if (key == nullptr) { - ERR_print_errors_fp(stderr); - } - return key; -} - -Crypto::Crypto() - : public_shuffler_key_(nullptr), public_analyzer_key_(nullptr) { - // Pedantically check that we have the same endianness everywhere - // uint32_t number = 1; - // assert(reinterpret_cast(&number)[0] == 1); - - // Ensure the AES128-GCM default nonce length is kNonceLength - assert(EVP_CIPHER_iv_length(EVP_aes_128_gcm()) == kNonceLength); -} - -Crypto::~Crypto() { - if (public_shuffler_key_ != nullptr) { - EVP_PKEY_free(public_shuffler_key_); - } - if (public_analyzer_key_ != nullptr) { - EVP_PKEY_free(public_analyzer_key_); - } -} - -Crypto::ProchlomationToAnalyzerItemEncryption:: - ProchlomationToAnalyzerItemEncryption(EVP_PKEY* peer_key, - const Prochlomation& prochlomation, - AnalyzerItem* analyzer_item) - : Encryption(peer_key), - prochlomation(prochlomation), - analyzer_item(analyzer_item) {} - -uint8_t* Crypto::ProchlomationToAnalyzerItemEncryption::ToPublicKey() { - return analyzer_item->client_public_key; -} - -const char* Crypto::ProchlomationToAnalyzerItemEncryption::TypeString() { - return "Prochlomation->AnalyzerItem"; -} - -uint8_t* Crypto::ProchlomationToAnalyzerItemEncryption::ToNonce() { - return analyzer_item->nonce; -} - -uint8_t* Crypto::ProchlomationToAnalyzerItemEncryption::ToTag() { - return analyzer_item->tag; -} - -bool Crypto::ProchlomationToAnalyzerItemEncryption::StreamDataForEncryption( - EVP_CIPHER_CTX* ctx) { - // Stream the proclomation to the cipher and write out the ciphertext. - uint8_t* next_byte = nullptr; - size_t ciphertext_byte_count = 0; - int32_t out_length; - - // First the metric - next_byte = &analyzer_item->ciphertext[ciphertext_byte_count]; - const uint8_t* to_metric = - reinterpret_cast(&prochlomation.metric); - if (EVP_EncryptUpdate(ctx, next_byte, &out_length, to_metric, - sizeof(prochlomation.metric)) != 1) { - // warn("Couldn't encrypt metric with AES128-GCM."); - ERR_print_errors_fp(stderr); - return false; - } - ciphertext_byte_count += out_length; - - // And the data - next_byte = &analyzer_item->ciphertext[ciphertext_byte_count]; - const uint8_t* to_data = prochlomation.data; - if (EVP_EncryptUpdate(ctx, next_byte, &out_length, to_data, - kProchlomationDataLength) != 1) { - // warn("Couldn't encrypt data with AES128-GCM."); - ERR_print_errors_fp(stderr); - return false; - } - ciphertext_byte_count += out_length; - assert(ciphertext_byte_count == kProchlomationCiphertextLength); - return true; -} - -Crypto::PlainShufflerItemToShufflerItemEncryption:: - PlainShufflerItemToShufflerItemEncryption( - EVP_PKEY* peer_key, - const PlainShufflerItem& plain_shuffler_item, - ShufflerItem* shuffler_item) - : Encryption(peer_key), - plain_shuffler_item(plain_shuffler_item), - shuffler_item(shuffler_item) {} - -uint8_t* Crypto::PlainShufflerItemToShufflerItemEncryption::ToPublicKey() { - return shuffler_item->client_public_key; -} - -const char* Crypto::PlainShufflerItemToShufflerItemEncryption::TypeString() { - return "PlainShufflerItem->ShufflerItem"; -} - -uint8_t* Crypto::PlainShufflerItemToShufflerItemEncryption::ToNonce() { - return shuffler_item->nonce; -} - -uint8_t* Crypto::PlainShufflerItemToShufflerItemEncryption::ToTag() { - return shuffler_item->tag; -} - -bool Crypto::PlainShufflerItemToShufflerItemEncryption::StreamDataForEncryption( - EVP_CIPHER_CTX* ctx) { - // Stream the PlainShufflerItem to the CIPHER and write out the ciphertext. - uint8_t* next_byte = nullptr; - size_t ciphertext_byte_count = 0; - int32_t out_length; - - // First the analyzer item (i.e., its innards). - next_byte = &shuffler_item->ciphertext[ciphertext_byte_count]; - const uint8_t* to_analyzer_item_ciphertext = - plain_shuffler_item.analyzer_item.ciphertext; - if (EVP_EncryptUpdate(ctx, next_byte, &out_length, - to_analyzer_item_ciphertext, - kProchlomationCiphertextLength) != 1) { - // warn("Couldn't encrypt analyzer item ciphertext with AES128-GCM."); - ERR_print_errors_fp(stderr); - return false; - } - ciphertext_byte_count += out_length; - - next_byte = &shuffler_item->ciphertext[ciphertext_byte_count]; - const uint8_t* to_analyzer_item_tag = plain_shuffler_item.analyzer_item.tag; - if (EVP_EncryptUpdate(ctx, next_byte, &out_length, to_analyzer_item_tag, - kTagLength) != 1) { - // warn("Couldn't encrypt analyzer item tag with AES128-GCM."); - ERR_print_errors_fp(stderr); - return false; - } - ciphertext_byte_count += out_length; - - next_byte = &shuffler_item->ciphertext[ciphertext_byte_count]; - const uint8_t* to_analyzer_item_nonce = - plain_shuffler_item.analyzer_item.nonce; - if (EVP_EncryptUpdate(ctx, next_byte, &out_length, to_analyzer_item_nonce, - kNonceLength) != 1) { - // warn("Couldn't encrypt analyzer item nonce with AES128-GCM."); - ERR_print_errors_fp(stderr); - return false; - } - ciphertext_byte_count += out_length; - - next_byte = &shuffler_item->ciphertext[ciphertext_byte_count]; - const uint8_t* to_analyzer_item_client_public_key = - plain_shuffler_item.analyzer_item.client_public_key; - if (EVP_EncryptUpdate(ctx, next_byte, &out_length, - to_analyzer_item_client_public_key, - kPublicKeyLength) != 1) { - // warn("Couldn't encrypt analyzer item client public key with - // AES128-GCM."); - ERR_print_errors_fp(stderr); - return false; - } - ciphertext_byte_count += out_length; - - // And now finish with the crowd ID - next_byte = &shuffler_item->ciphertext[ciphertext_byte_count]; - const uint8_t* to_crowd_id = plain_shuffler_item.crowd_id; - if (EVP_EncryptUpdate(ctx, next_byte, &out_length, to_crowd_id, - kCrowdIdLength) != 1) { - // warn("Couldn't encrypt crowd IDwith AES128-GCM."); - ERR_print_errors_fp(stderr); - return false; - } - ciphertext_byte_count += out_length; - - assert(ciphertext_byte_count == kPlainShufflerItemLength); - return true; -} - -bool Crypto::MakeEncryptedMessage(Encryption* encryption) { - EVP_PKEY* my_key = nullptr; - EVP_PKEY* peer_key = encryption->ToPeerKey(); - - do { // Using BoringSSL's scoped EVP_PKEY pointers would be a lot more - // exciting here that the do {} while(false) kludge. - if (!GenerateKeyPair(peer_key, &my_key, encryption->ToPublicKey())) { - // warn("Couldn't generate an ephemeral keypair during %s message - // creation.", - // encryption->TypeString()); - break; - } - - uint8_t symmetric_key[kSymmetricKeyLength]; - if (!DeriveSecretSymmetricKey(my_key, peer_key, symmetric_key)) { - // warn("Couldn't generate a symmetric key during %s message creation.", - // encryption->TypeString()); - break; - } - - if (!Encrypt(symmetric_key, encryption)) { - // warn("Couldn't encrypt for %s.", encryption->TypeString()); - break; - } - - if (my_key != nullptr) { - EVP_PKEY_free(my_key); - } - return true; - } while (false); - - if (my_key != nullptr) { - EVP_PKEY_free(my_key); - } - return false; -} - -bool Crypto::EncryptForAnalyzer(const Prochlomation& prochlomation, - AnalyzerItem* analyzer_item) { - ProchlomationToAnalyzerItemEncryption encryption( - public_analyzer_key_, prochlomation, analyzer_item); - return MakeEncryptedMessage(&encryption); -} - -bool Crypto::EncryptForShuffler(const PlainShufflerItem& plain_shuffler_item, - ShufflerItem* shuffler_item) { - PlainShufflerItemToShufflerItemEncryption encryption( - public_shuffler_key_, plain_shuffler_item, shuffler_item); - return MakeEncryptedMessage(&encryption); -} - -bool Crypto::GenerateKeyPair(EVP_PKEY* peer_public_key, - EVP_PKEY** key_out, - uint8_t* binary_key) { - assert(peer_public_key != nullptr); - assert(key_out != nullptr); - assert(binary_key != nullptr); - assert(*key_out == nullptr); - - EVP_PKEY_CTX* ctx = nullptr; - EVP_PKEY* key = nullptr; - BIO* bio = NULL; - - do { - // Generate a key based on the peer's key parameters. - ctx = EVP_PKEY_CTX_new(peer_public_key, /*e=*/nullptr); - if (ctx == nullptr) { - // warn("Couldn't create an EVP_PKEY_CTX."); - ERR_print_errors_fp(stderr); - break; - } - - if (EVP_PKEY_keygen_init(ctx) != 1) { - // warn("Couldn't initialize the key-pair generation."); - ERR_print_errors_fp(stderr); - break; - } - - if (EVP_PKEY_keygen(ctx, &key) != 1) { - // warn("Couldn't generate a key pair."); - ERR_print_errors_fp(stderr); - break; - } - - // Serialize the key. - bio = BIO_new(BIO_s_mem()); - if (bio == nullptr) { - // warn("Couldn't allocate an OpenSSL buffer."); - ERR_print_errors_fp(stderr); - break; - } - - if (i2d_PUBKEY_bio(bio, key) != 1) { - // warn("Couldn't serialize a key pair."); - ERR_print_errors_fp(stderr); - break; - } - - uint8_t* serialized_buffer = nullptr; - size_t serialized_key_length = - BIO_get_mem_data(bio, reinterpret_cast(&serialized_buffer)); - // We'd better have provisioned enough space for the serialized public key. - assert(serialized_key_length <= kPublicKeyLength); - - // Now write the results out (they OpenSSL key and the serialized key) - memcpy(binary_key, serialized_buffer, serialized_key_length); - *key_out = key; - - // Successful return. - EVP_PKEY_CTX_free(ctx); - BIO_free(bio); - return true; - } while (false); - - // Unsuccessful return. - *key_out = nullptr; - if (key != nullptr) { - EVP_PKEY_free(key); - } - if (ctx != nullptr) { - EVP_PKEY_CTX_free(ctx); - } - if (bio != nullptr) { - BIO_free(bio); - } - return false; -} - -bool Crypto::DeriveSecretSymmetricKey(EVP_PKEY* local_key, - EVP_PKEY* peer_public_key, - uint8_t* secret_key) { - assert(local_key != nullptr); - assert(peer_public_key != nullptr); - assert(secret_key != nullptr); - - EVP_PKEY_CTX* ctx = nullptr; - - do { - ctx = EVP_PKEY_CTX_new(local_key, /*e=*/nullptr); - if (ctx == nullptr) { - // warn("Couldn't create an EVP_PKEY_CTX for secret derivation."); - ERR_print_errors_fp(stderr); - break; - } - - if (EVP_PKEY_derive_init(ctx) != 1) { - // warn("Couldn't initiate a secret derivation."); - ERR_print_errors_fp(stderr); - break; - } - - if (EVP_PKEY_derive_set_peer(ctx, peer_public_key) != 1) { - // warn("Couldn't set the public key of my peer for a secret - // derivation."); - ERR_print_errors_fp(stderr); - break; - } - - size_t derived_secret_length = 0; - if (EVP_PKEY_derive(ctx, nullptr, &derived_secret_length) != 1) { - // warn("Couldn't find the length of the derived secret."); - ERR_print_errors_fp(stderr); - break; - } - assert(derived_secret_length <= kSharedSecretLength); - uint8_t derived_secret[kSharedSecretLength]; - - if (EVP_PKEY_derive(ctx, derived_secret, &derived_secret_length) != 1) { - // warn("Couldn't derive a shared secret."); - ERR_print_errors_fp(stderr); - break; - } - - // Now turn it into a key, using a HKDF. - // 1. Extract - uint8_t expansion[kSharedSecretExpansionLength]; - // Zero it out, don't use a fancy salt. - memset(expansion, 0, kSharedSecretExpansionLength); - // First HMAC the shared secret with the expansion as the key (initially - // zero). - uint32_t hmac_length; - uint8_t* hmac = HMAC(EVP_sha256(), - /* key = */ expansion, kSharedSecretExpansionLength, - /* d = */ derived_secret, derived_secret_length, - /* md = */ expansion, &hmac_length); - if (hmac == nullptr) { - // warn("Couldn't HMAC the derived secret."); - ERR_print_errors_fp(stderr); - break; - } - assert(hmac_length == kSharedSecretExpansionLength); - // Now HMAC the previous HMAC result with itself as a key, and some - // well-defined additional data (namely, 1). - uint8_t one = 1; - hmac = HMAC(EVP_sha256(), - /* key = */ expansion, kSharedSecretExpansionLength, - /* d = */ &one, sizeof(one), // arbitrary choice - /* md = */ expansion, /* md_len= */ nullptr); // No need to - // obtain the - // length of the - // md yet again. - if (hmac == nullptr) { - // warn("Couldn't HMAC to expand the symmetric key."); - ERR_print_errors_fp(stderr); - break; - } - // Now we have good key material in |expansion|. Strip it down to the - // keysize of AES128. - assert(kSharedSecretExpansionLength > kSymmetricKeyLength); - memcpy(secret_key, expansion, kSymmetricKeyLength); - - EVP_PKEY_CTX_free(ctx); - return true; - } while (false); - - if (ctx != nullptr) { - EVP_PKEY_CTX_free(ctx); - } - return false; -} - -bool Crypto::Encrypt(const uint8_t* symmetric_key, Encryption* encryption) { - assert(encryption != nullptr); - - EVP_CIPHER_CTX* ctx = nullptr; - do { - ctx = EVP_CIPHER_CTX_new(); - if (ctx == nullptr) { - // warn("Couldn't create a new EVP_CIPHER_CTX."); - ERR_print_errors_fp(stderr); - break; - } - - EVP_CIPHER_CTX_init(ctx); - - // Set up a random nonce - if (RAND_bytes(encryption->ToNonce(), kNonceLength) != 1) { - // warn("Couldn't generate random nonce."); - ERR_print_errors_fp(stderr); - break; - } - - if (EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), - /* impl= */ nullptr, symmetric_key, - /* iv= */ encryption->ToNonce()) != 1) { - // warn("Couldn't initialize for AES128-GCM encryption."); - ERR_print_errors_fp(stderr); - break; - } - - if (!encryption->StreamDataForEncryption(ctx)) { - // warn("Couldn't stream data for %s AES128-GCM encryption.", - // encryption->TypeString()); - break; - } - - // Now finalize to obtain the tag. We should have no pending ciphertext data - // at this point. - int32_t out_length; - if (EVP_EncryptFinal_ex(ctx, /* out= */ nullptr, &out_length) != 1) { - // warn("Couldn't finalize the prochlomation encryption."); - ERR_print_errors_fp(stderr); - break; - } - assert(out_length == 0); - - // We have filled in the ciphertext. Now we also need to fill in the tag. - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, kTagLength, - encryption->ToTag()) != 1) { - // warn("Couldn't obtain the AEAD tag from the prochlomation - // encryption."); - ERR_print_errors_fp(stderr); - break; - } - - EVP_CIPHER_CTX_free(ctx); - return true; - } while (false); - - if (ctx != nullptr) { - EVP_CIPHER_CTX_free(ctx); - } - return false; -} - -} // namespace prochlo diff --git a/components/brave_prochlo/prochlo_crypto.h b/components/brave_prochlo/prochlo_crypto.h deleted file mode 100644 index 9cb4b58035d..00000000000 --- a/components/brave_prochlo/prochlo_crypto.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef BRAVE_COMPONENTS_BRAVE_PROCHLO_PROCHLO_CRYPTO_H_ -#define BRAVE_COMPONENTS_BRAVE_PROCHLO_PROCHLO_CRYPTO_H_ - -#include -#include - -#include "third_party/boringssl/src/include/openssl/evp.h" - -#include "brave/components/brave_prochlo/prochlo_data.h" - -namespace prochlo { - -class Crypto { - public: - // Load the public key for the Analyzer - bool load_analyzer_key(const std::string& keyfile); - - // Load the public key for the Analyzer - bool load_shuffler_key(const std::string& keyfile); - - Crypto(); - ~Crypto(); - - bool EncryptForAnalyzer(const Prochlomation& prochlomation, - AnalyzerItem* analyzer_item); - - bool EncryptForShuffler(const PlainShufflerItem& plain_shuffler_item, - ShufflerItem* shuffler_item); - - private: - friend class BraveProchloCrypto; - - // A convenient interface for encrypting between pairs of Prochlo messages - // without producing a separate serialized copy of the input. - class Encryption { - public: - explicit Encryption(EVP_PKEY* peer_key) : peer_key(peer_key) {} - - EVP_PKEY* ToPeerKey() { return peer_key; } - - virtual bool StreamDataForEncryption(EVP_CIPHER_CTX* ctx) = 0; - virtual uint8_t* ToPublicKey() = 0; - virtual uint8_t* ToNonce() = 0; - virtual uint8_t* ToTag() = 0; - virtual const char* TypeString() = 0; - - EVP_PKEY* peer_key; - }; - - class ProchlomationToAnalyzerItemEncryption : public Crypto::Encryption { - public: - ProchlomationToAnalyzerItemEncryption(EVP_PKEY* peer_key, - const Prochlomation& prochlomation, - AnalyzerItem* analyzer_item); - const Prochlomation& prochlomation; - AnalyzerItem* analyzer_item; - - uint8_t* ToPublicKey() override; - const char* TypeString() override; - uint8_t* ToNonce() override; - uint8_t* ToTag() override; - bool StreamDataForEncryption(EVP_CIPHER_CTX* ctx) override; - }; - - class PlainShufflerItemToShufflerItemEncryption : public Crypto::Encryption { - public: - PlainShufflerItemToShufflerItemEncryption( - EVP_PKEY* peer_key, - const PlainShufflerItem& plain_shuffler_item, - ShufflerItem* shuffler_item); - const PlainShufflerItem& plain_shuffler_item; - ShufflerItem* shuffler_item; - - uint8_t* ToPublicKey() override; - const char* TypeString() override; - uint8_t* ToNonce() override; - uint8_t* ToTag() override; - bool StreamDataForEncryption(EVP_CIPHER_CTX* ctx) override; - }; - - bool MakeEncryptedMessage(Encryption* encryption); - bool GenerateKeyPair(EVP_PKEY* peer_public_key, - EVP_PKEY** key_out, - uint8_t* binary_key); - - bool DeriveSecretSymmetricKey(EVP_PKEY* local_key, - EVP_PKEY* peer_public_key, - uint8_t* secret_key); - bool Encrypt(const uint8_t* symmetric_key, Encryption* encryption); - - // Load a public key returning the structure - EVP_PKEY* load_public_key(const std::string& keyfile); - - EVP_PKEY* public_shuffler_key_; - EVP_PKEY* public_analyzer_key_; -}; - -} // namespace prochlo - -#endif // BRAVE_COMPONENTS_BRAVE_PROCHLO_PROCHLO_CRYPTO_H_ diff --git a/components/brave_prochlo/prochlo_data.h b/components/brave_prochlo/prochlo_data.h deleted file mode 100644 index aaf59760f8c..00000000000 --- a/components/brave_prochlo/prochlo_data.h +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2017 Google Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef BRAVE_COMPONENTS_BRAVE_PROCHLO_PROCHLO_DATA_H_ -#define BRAVE_COMPONENTS_BRAVE_PROCHLO_PROCHLO_DATA_H_ - -#include -#include - -namespace prochlo { - -// Nomenclature for message structures: -// -// The client's encoder produces a Proclomation, i.e., an EncoderItem, which it -// wishes to deliver to the Analyzer, via the shuffler. -// -// A ShufflerItem travels from the Client to the Shuffler. -// -// An AnalyzerItem travels from the Shuffler to the Analyzer. -// -// The Shuffler stores the intermediate state of its shuffle on local -// (untrusted) storage, in the shape of an IntermediateShufflerItem. -// -// A Proclomation contains just the type of data (|metric|) and the value. The -// Client encrypts using AES128-GCM, with a key derived from its ephemeral key -// pair and the Analyzer's public key. That's the inner layer of the nested -// encryption, and constitutes the AnalyzerItem. -// -// Next, the Client constructs the outer layer of the nested encryption, by -// adding the value of the crowd id to the AnalyzerItem, and encrypting it, -// again using AES128-GCM, with a key derived from (another) ephemeral key pair, -// and the Shuffler's public key. That's the outer layer of the nested -// encryption, and constitutes the ShufflerItem. This is what the Client -// transmits to the Shuffler. -// -// The StashShuffler shuffles ShufflerItems in at least two rounds, storing -// intermediate results on local (but untrusted) storage. To ensure the shuffle -// is oblivious, the Shuffler decrypts ShufflerItems (as collected from -// Clients), and re-encrypts them unchanged using an ephemeral symmetric key of -// its choosing (again using AES128-GCM). The resulting structure is an -// IntermediateShufflerItem. In addition to the AnalyzerItem and |crowd_id|, and -// IntermediateShufflerItem also contains some StashShuffler metadata (e.g., -// whether an IntermediateShufflerItem is a dummy). -// -// In a production setting, all of these message structures are represented by -// Protocol Buffers, which are not shown here. -// -// Note that we use struct sizes as the sizes of messages (i.e., sizeof(type)), -// rather than the number of bytes they'd take when marshalled. Due to -// alignment, the former may be larger than the latter. - -// Default lengths -#define DATA_LENGTH 64 -#define CROWD_ID_LENGTH 8 - -// Problem-specific lengths, in bytes. -constexpr size_t kProchlomationDataLength = DATA_LENGTH; -constexpr size_t kCrowdIdLength = CROWD_ID_LENGTH; - -// Crypto-specific lengths. -constexpr size_t kPublicKeyLength = 91; // This is the maximum length we devote - // for storing a DER-encoded NIST - // P-256 public key. -constexpr size_t kSharedSecretLength = 256 / 8; // The length of the derived - // shared secret from Diffie - // Hellman key exchange on NIST - // P-256. -constexpr size_t kSymmetricKeyLength = 128 / 8; // The length of an AES128 key. -constexpr size_t kSharedSecretExpansionLength = 256 / 8; // The length of the - // pseudo-random space - // used to derive a - // shared symmetric - // key from a shared - // DH secret. It's - // determined by the - // length of SHA256. - -constexpr size_t kNonceLength = 12; // The recommended nonce (i.e., IV) length - // for AES128-GCM is 12 bytes. - -constexpr size_t kTagLength = 16; // The maximum tag length for AES128-GCM is - // 16 bytes. - -//////////////////////////////////////////////////////////////////////////////// -// Prochlomation -//////////////////////////////////////////////////////////////////////////////// -// A prochlomation is the plain encoded data that a Client's Encoder generates -// and intends to deliver to an Analyzer. -struct Prochlomation { - uint64_t metric; - uint8_t data[kProchlomationDataLength]; // e.g., 64 -}; -constexpr size_t kProchlomationLength = sizeof(Prochlomation); // e.g., 72 -constexpr size_t kProchlomationCiphertextLength = - kProchlomationLength; // The ciphertext is the same length, but it is - // augmented by the MAC stored in |tag| below. - -//////////////////////////////////////////////////////////////////////////////// -// AnalyzerItem -//////////////////////////////////////////////////////////////////////////////// -struct EncryptedProchlomation { - // The result of encrypting a Prochlomation using AES128-GCM is |ciphertext|, - // with MAC |tag|, starting with the IV in |nonce|. - uint8_t ciphertext[kProchlomationCiphertextLength]; - uint8_t tag[kTagLength]; - uint8_t nonce[kNonceLength]; - - // The key used to produce |ciphertext| is derived from the analyzer's key - // pair and the client's ephermeral key pair. The public key of the client's - // key pair is |client_public_key|. - uint8_t client_public_key[kPublicKeyLength]; -}; -constexpr size_t kEncryptedProchlomationLength = sizeof(EncryptedProchlomation); - -// Aliases for EncryptedProchlomations -typedef EncryptedProchlomation AnalyzerItem; -constexpr size_t kAnalyzerItemLength = kEncryptedProchlomationLength; - -//////////////////////////////////////////////////////////////////////////////// -// ShufflerItem -//////////////////////////////////////////////////////////////////////////////// -// This is the item that the Shuffler handles, and it contains the AnalyzerItem -// and the crowd ID. -struct PlainShufflerItem { - AnalyzerItem analyzer_item; - - // The crowd ID for the Prochlomation included in |analyzer_item|. - uint8_t crowd_id[kCrowdIdLength]; -}; -constexpr size_t kPlainShufflerItemLength = sizeof(PlainShufflerItem); - -struct EncryptedPlainShufflerItem { - // The result of encrypting an PlainShufflerItem using AES128-GCM is - // |ciphertext|, with MAC |tag|, starting with the IV in |nonce|. - uint8_t ciphertext[kPlainShufflerItemLength]; - uint8_t tag[kTagLength]; - uint8_t nonce[kNonceLength]; - - // The key used to produce |ciphertext| is derived from the shuffler's key - // pair and the client's ephermeral key pair. The public key of the client's - // key pair is |client_public_key|. Note that the client may (in fact, might - // as well) use two different ephemeral key pairs, one for the shuffler and - // one for the analyzer. So this may not be the same as the - // |client_public_key| in EncryptedProchlomation. - uint8_t client_public_key[kPublicKeyLength]; -}; -constexpr size_t kEncryptedPlainShufflerItemLength = - sizeof(EncryptedPlainShufflerItem); - -// The ShufflerItem is just an EncryptedPlainShufflerItem -typedef EncryptedPlainShufflerItem ShufflerItem; -constexpr size_t kShufflerItemLength = kEncryptedPlainShufflerItemLength; - -} // namespace prochlo - -#endif // BRAVE_COMPONENTS_BRAVE_PROCHLO_PROCHLO_DATA_H_ diff --git a/components/brave_prochlo/prochlo_message.proto b/components/brave_prochlo/prochlo_message.proto index a8cc6e2756b..6d2b469f7bb 100644 --- a/components/brave_prochlo/prochlo_message.proto +++ b/components/brave_prochlo/prochlo_message.proto @@ -9,20 +9,7 @@ option optimize_for = LITE_RUNTIME; package brave_pyxis; -message PyxisValue { - bytes ciphertext = 1; - bytes tag = 2; - bytes nonce = 3; - fixed64 metric_id = 4; - - bytes client_public_key = 5; -} - message RawP3AValue { fixed64 metric_id = 1; bytes p3a_info = 2; } - -message PyxisMessage { - repeated PyxisValue pyxis_values = 1; -} diff --git a/components/p3a/BUILD.gn b/components/p3a/BUILD.gn index 2f70ed0a97a..ecaf6ed794e 100644 --- a/components/p3a/BUILD.gn +++ b/components/p3a/BUILD.gn @@ -22,13 +22,14 @@ static_library("p3a") { "brave_p3a_utils.h", "histograms_braveizer.cc", "histograms_braveizer.h", + "p3a_message.cc", + "p3a_message.h", "pref_names.cc", "pref_names.h", ] deps = [ "//base", - "//brave/components/brave_prochlo", "//brave/components/brave_prochlo:prochlo_proto", "//brave/components/brave_referrals/common", "//brave/components/brave_stats/browser", @@ -41,6 +42,7 @@ static_library("p3a") { "//content/public/common", "//services/network/public/cpp", "//services/network/public/mojom", + "//third_party/protobuf:protobuf_lite", "//url", ] } diff --git a/components/p3a/brave_p3a_service.cc b/components/p3a/brave_p3a_service.cc index 7d5cd844309..12a6db9a81a 100644 --- a/components/p3a/brave_p3a_service.cc +++ b/components/p3a/brave_p3a_service.cc @@ -248,7 +248,7 @@ void BraveP3AService::Init( << ", upload_server_url_ = " << upload_server_url_.spec() << ", rotation_interval_ = " << rotation_interval_; - InitPyxisMeta(); + InitMessageMeta(); // Init log store. log_store_.reset(new BraveP3ALogStore(this, local_state_)); @@ -300,15 +300,9 @@ std::string BraveP3AService::Serialize(base::StringPiece histogram_name, // point when the actual histogram is not ready yet. const uint64_t histogram_name_hash = base::HashMetricName(histogram_name); - // TODO(iefremov): Restore when PROCHLO/PYXIS is ready. - // brave_pyxis::PyxisMessage message; - // prochlo::GenerateProchloMessage(histogram_name_hash, value, pyxis_meta_, - // &message); - - UpdatePyxisMeta(); + UpdateMessageMeta(); brave_pyxis::RawP3AValue message; - prochlo::GenerateP3AMessage(histogram_name_hash, value, pyxis_meta_, - &message); + GenerateP3AMessage(histogram_name_hash, value, message_meta_, &message); return message.SerializeAsString(); } @@ -354,35 +348,37 @@ void BraveP3AService::MaybeOverrideSettingsFromCommandLine() { } } -void BraveP3AService::InitPyxisMeta() { - pyxis_meta_.platform = brave_stats::GetPlatformIdentifier(); - pyxis_meta_.channel = channel_; - pyxis_meta_.version = +void BraveP3AService::InitMessageMeta() { + message_meta_.platform = brave_stats::GetPlatformIdentifier(); + message_meta_.channel = channel_; + message_meta_.version = version_info::GetBraveVersionWithoutChromiumMajorVersion(); if (!week_of_install_.empty()) { - pyxis_meta_.date_of_install = brave_stats::GetYMDAsDate(week_of_install_); + message_meta_.date_of_install = brave_stats::GetYMDAsDate(week_of_install_); } else { - pyxis_meta_.date_of_install = base::Time::Now(); + message_meta_.date_of_install = base::Time::Now(); } - pyxis_meta_.woi = brave_stats::GetIsoWeekNumber(pyxis_meta_.date_of_install); + message_meta_.woi = + brave_stats::GetIsoWeekNumber(message_meta_.date_of_install); - pyxis_meta_.country_code = + message_meta_.country_code = base::ToUpperASCII(base::CountryCodeForCurrentTimezone()); - pyxis_meta_.refcode = local_state_->GetString(kReferralPromoCode); - MaybeStripRefcodeAndCountry(&pyxis_meta_); + message_meta_.refcode = local_state_->GetString(kReferralPromoCode); + MaybeStripRefcodeAndCountry(&message_meta_); - UpdatePyxisMeta(); + UpdateMessageMeta(); - VLOG(2) << "Pyxis meta: " << pyxis_meta_.platform << " " - << pyxis_meta_.channel << " " << pyxis_meta_.version << " " - << pyxis_meta_.woi << " " << pyxis_meta_.wos << " " - << pyxis_meta_.country_code << " " << pyxis_meta_.refcode; + VLOG(2) << "Message meta: " << message_meta_.platform << " " + << message_meta_.channel << " " << message_meta_.version << " " + << message_meta_.woi << " " << message_meta_.wos << " " + << message_meta_.country_code << " " << message_meta_.refcode; } -void BraveP3AService::UpdatePyxisMeta() { - pyxis_meta_.date_of_survey = base::Time::Now(); - pyxis_meta_.wos = brave_stats::GetIsoWeekNumber(pyxis_meta_.date_of_survey); +void BraveP3AService::UpdateMessageMeta() { + message_meta_.date_of_survey = base::Time::Now(); + message_meta_.wos = + brave_stats::GetIsoWeekNumber(message_meta_.date_of_survey); } void BraveP3AService::StartScheduledUpload() { diff --git a/components/p3a/brave_p3a_service.h b/components/p3a/brave_p3a_service.h index 972fa45d616..29a3bbd3772 100644 --- a/components/p3a/brave_p3a_service.h +++ b/components/p3a/brave_p3a_service.h @@ -15,8 +15,8 @@ #include "base/metrics/histogram_base.h" #include "base/metrics/statistics_recorder.h" #include "base/timer/timer.h" -#include "brave/components/brave_prochlo/brave_prochlo_message.h" #include "brave/components/p3a/brave_p3a_log_store.h" +#include "brave/components/p3a/p3a_message.h" #include "url/gurl.h" class PrefRegistrySimple; @@ -64,10 +64,10 @@ class BraveP3AService : public base::RefCountedThreadSafe, void MaybeOverrideSettingsFromCommandLine(); - void InitPyxisMeta(); + void InitMessageMeta(); // Updates things that change over time: week of survey, etc. - void UpdatePyxisMeta(); + void UpdateMessageMeta(); void StartScheduledUpload(); @@ -105,7 +105,7 @@ class BraveP3AService : public base::RefCountedThreadSafe, base::TimeDelta rotation_interval_; GURL upload_server_url_; - prochlo::MessageMetainfo pyxis_meta_; + MessageMetainfo message_meta_; // Components: std::unique_ptr log_store_; diff --git a/components/p3a/p3a_message.cc b/components/p3a/p3a_message.cc new file mode 100644 index 00000000000..7c9022c8dd0 --- /dev/null +++ b/components/p3a/p3a_message.cc @@ -0,0 +1,121 @@ +/* Copyright 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/p3a/p3a_message.h" + +#include + +#include "base/containers/flat_set.h" +#include "base/cxx17_backports.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/trace_event/trace_event.h" +#include "brave/components/brave_prochlo/prochlo_message.pb.h" +#include "crypto/sha2.h" + +namespace brave { + +MessageMetainfo::MessageMetainfo() = default; +MessageMetainfo::~MessageMetainfo() = default; + +void GenerateP3AMessage(uint64_t metric_hash, + uint64_t metric_value, + const MessageMetainfo& meta, + brave_pyxis::RawP3AValue* p3a_message) { + // TODO(iefremov): - create patch for adding `brave_p3a` + // to src/base/trace_event/builtin_categories.h + // TRACE_EVENT0("brave_p3a", "GenerateP3AMessage"); + constexpr size_t kDataLength = 64; + uint8_t data[kDataLength] = {0}; + + // First byte contains the 4 booleans. + const char daily = 1; + const char weekly = 0; + const char monthly = 2; + const char first = 0; + data[0] = daily | weekly | monthly | first; + uint8_t* ptr = data; + ptr++; + + // Find out years of install and survey. + base::Time::Exploded exploded; + meta.date_of_survey.LocalExplode(&exploded); + DCHECK_GE(exploded.year, 999); + const std::string yos = base::NumberToString(exploded.year).substr(2, 4); + meta.date_of_install.LocalExplode(&exploded); + DCHECK_GE(exploded.year, 999); + const std::string yoi = base::NumberToString(exploded.year).substr(2, 4); + + const std::string metastring = + "," + meta.country_code + "," + meta.platform + "," + meta.version + "," + + meta.channel + "," + yoi + base::NumberToString(meta.woi) + "," + yos + + base::NumberToString(meta.wos) + "," + meta.refcode + ","; + + const std::string metric_value_str = base::NumberToString(metric_value); + + // TODO(iefremov): replace with 'if'? + CHECK_LE(metastring.size() + metric_value_str.size(), kDataLength - 1); + + memcpy(ptr, metastring.data(), metastring.size()); + ptr += metastring.size(); + memcpy(ptr, metric_value_str.data(), metric_value_str.size()); + + // Init the message. + p3a_message->set_metric_id(metric_hash); + p3a_message->set_p3a_info(data, kDataLength); +} + +void MaybeStripRefcodeAndCountry(MessageMetainfo* meta) { + const std::string& refcode = meta->refcode; + const std::string& country = meta->country_code; + constexpr char kRefcodeNone[] = "none"; + constexpr char kCountryOther[] = "other"; + constexpr char kRefcodeOther[] = "other"; + + static base::flat_set const kLinuxCountries( + {"US", "FR", "DE", "GB", "IN", "BR", "PL", "NL", "ES", "CA", "IT", "AU", + "MX", "CH", "RU", "ZA", "SE", "BE", "JP"}); + + static base::flat_set const kNotableRefcodes( + {"BRV001", "GDB255", "APP709", "GBW423", "BRT001", "VNI569", "ICO964", + "ILY758"}); + + static base::flat_set const kNotableCountries( + {"FR", "PH", "GB", "IN", "DE", "BR", "CA", "IT", "ES", "NL", "MX", "AU", + "RU", "JP", "PL", "ID", "KR", "AR"}); + + DCHECK(meta); + if (meta->platform == "linux-bc") { + // Because Linux has no refcodes, ignore, and if we have more than + // 3/0.05 = 60 users in a country for a week of install, we can send + // country. + meta->refcode = kRefcodeNone; + if (kLinuxCountries.count(country) == 0) { + meta->country_code = kCountryOther; + } + } else { + // Now the minimum platform is MacOS at ~3%, so cut off for a group under + // here becomes 3/(0.05*0.03) = 2000. + if (country == "US" || country.empty()) { + const bool us_and_ref = + country == "US" && (refcode == kRefcodeNone || refcode == "GDB255" || + refcode == "BRV001"); + const bool unknown_and_ref = + country.empty() && + (kNotableRefcodes.count(refcode) > 0 || refcode == kRefcodeNone); + + if (!(us_and_ref || unknown_and_ref)) { + meta->refcode = kRefcodeOther; + } + } else if (kNotableCountries.count(country) > 0) { + meta->refcode = kRefcodeOther; + } else { + meta->country_code = kCountryOther; + meta->refcode = kRefcodeOther; + } + } +} + +} // namespace brave diff --git a/components/brave_prochlo/brave_prochlo_message.h b/components/p3a/p3a_message.h similarity index 56% rename from components/brave_prochlo/brave_prochlo_message.h rename to components/p3a/p3a_message.h index e80641d26c3..39af30073f6 100644 --- a/components/brave_prochlo/brave_prochlo_message.h +++ b/components/p3a/p3a_message.h @@ -1,22 +1,20 @@ -/* Copyright 2019 The Brave Authors. All rights reserved. +/* Copyright 2021 The Brave Authors. All rights reserved. * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef BRAVE_COMPONENTS_BRAVE_PROCHLO_BRAVE_PROCHLO_MESSAGE_H_ -#define BRAVE_COMPONENTS_BRAVE_PROCHLO_BRAVE_PROCHLO_MESSAGE_H_ +#ifndef BRAVE_COMPONENTS_P3A_P3A_MESSAGE_H_ +#define BRAVE_COMPONENTS_P3A_P3A_MESSAGE_H_ #include #include #include "base/time/time.h" namespace brave_pyxis { -class PyxisMessage; class RawP3AValue; } -// TODO(iefremov): prochlo -> pyxis everywhere. -namespace prochlo { +namespace brave { struct MessageMetainfo { MessageMetainfo(); @@ -25,19 +23,14 @@ struct MessageMetainfo { std::string platform; std::string version; std::string channel; - base::Time date_of_install; - base::Time date_of_survey; + base::Time date_of_install; + base::Time date_of_survey; int woi; // Week of install. Remove this occasionally and extract from above. int wos; // Week of survey. std::string country_code; std::string refcode; }; -void GenerateProchloMessage(uint64_t metric_hash, - uint64_t metric_value, - const MessageMetainfo& meta, - brave_pyxis::PyxisMessage* prochlo_message); - void GenerateP3AMessage(uint64_t metric_hash, uint64_t metric_value, const MessageMetainfo& meta, @@ -45,8 +38,8 @@ void GenerateP3AMessage(uint64_t metric_hash, // Ensures that country/refcode represent the big enough cohort that will not // let anybody identify the sender. -void MaybeStripRefcodeAndCountry(prochlo::MessageMetainfo* meta); +void MaybeStripRefcodeAndCountry(MessageMetainfo* meta); -} // namespace prochlo +} // namespace brave -#endif // BRAVE_COMPONENTS_BRAVE_PROCHLO_BRAVE_PROCHLO_MESSAGE_H_ +#endif // BRAVE_COMPONENTS_P3A_P3A_MESSAGE_H_ diff --git a/script/brave_license_helper.py b/script/brave_license_helper.py index 8439fa42b9c..e82b6c3003c 100644 --- a/script/brave_license_helper.py +++ b/script/brave_license_helper.py @@ -127,7 +127,6 @@ def AddBraveCredits(prune_paths, special_cases, prune_dirs, additional_paths): # contained under a "third_party" or "vendor" directory. additional_list = list(additional_paths) additional_list += [ - os.path.join('brave', 'components', 'brave_prochlo'), os.path.join('brave', 'components', 'brave_new_tab_ui', 'data'), ] additional_paths = tuple(additional_list)