14408: Drop PROCHLO.

Fixes https://github.com/brave/brave-browser/issues/14408
This commit is contained in:
Ivan Efremov
2021-11-03 22:03:35 +07:00
parent 1f70989921
commit 9c9fafdd59
16 changed files with 163 additions and 1217 deletions
+3 -20
View File
@@ -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 = "."
-3
View File
@@ -1,3 +0,0 @@
include_rules = [
"+third_party/boringssl",
]
-4
View File
@@ -1,4 +0,0 @@
Name: PROCHLO
URL: https://github.com/google/prochlo
License: Apache-2.0
License File: /brave/common/licenses/Apache-2.0
@@ -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<char>& 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<char>& 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<char>& bytes) {
bssl::UniquePtr<BIO> bio(
BIO_new_mem_buf(bytes.data(), static_cast<int>(bytes.size())));
if (!bio) {
return nullptr;
}
return PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr);
}
} // namespace prochlo
@@ -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 <vector>
#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<char>& bytes);
// Load the public key for the Analyzer from bytes.
bool load_shuffler_key_from_bytes(const std::vector<char>& bytes);
private:
// Load a public key returning the structure.
EVP_PKEY* load_public_key_from_bytes(const std::vector<char>& bytes);
DISALLOW_COPY_AND_ASSIGN(BraveProchloCrypto);
};
} // namespace prochlo
#endif // BRAVE_COMPONENTS_BRAVE_PROCHLO_BRAVE_PROCHLO_CRYPTO_H_
@@ -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 <vector>
#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<char> shuffler_key(
&kShufflerKey[0], &kShufflerKey[0] + base::size(kShufflerKey));
if (!crypto.load_shuffler_key_from_bytes(shuffler_key)) {
return false;
}
const std::vector<char> 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<std::string> 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<std::string> const kNotableRefcodes(
{"BRV001", "GDB255", "APP709", "GBW423", "BRT001", "VNI569", "ICO964",
"ILY758"});
static base::flat_set<std::string> 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
-516
View File
@@ -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 <assert.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <cstring>
#include <iostream>
#include <string>
#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<uint8_t*>(&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<const uint8_t*>(&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<char**>(&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
-114
View File
@@ -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 <memory>
#include <string>
#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_
-167
View File
@@ -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 <cstdint>
#include <cstddef>
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_
@@ -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;
}
+3 -1
View File
@@ -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",
]
}
+23 -27
View File
@@ -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() {
+4 -4
View File
@@ -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<BraveP3AService>,
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<BraveP3AService>,
base::TimeDelta rotation_interval_;
GURL upload_server_url_;
prochlo::MessageMetainfo pyxis_meta_;
MessageMetainfo message_meta_;
// Components:
std::unique_ptr<BraveP3ALogStore> log_store_;
+121
View File
@@ -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 <vector>
#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<std::string> 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<std::string> const kNotableRefcodes(
{"BRV001", "GDB255", "APP709", "GBW423", "BRT001", "VNI569", "ICO964",
"ILY758"});
static base::flat_set<std::string> 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
@@ -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 <cstdint>
#include <string>
#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_
-1
View File
@@ -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)