[privacy] Use the Poisson privacy mechanism on the client.
Bug: 93951
Change-Id: I0f47011ad9f7b73e996f081092b13ec672ae75a9
Reviewed-on: https://fuchsia-review.googlesource.com/c/cobalt/+/659766
Reviewed-by: Laura Peskin <pesk@google.com>
Commit-Queue: Alexandre Zani <azani@google.com>
Fuchsia-Auto-Submit: Alexandre Zani <azani@google.com>
diff --git a/src/logger/BUILD.gn b/src/logger/BUILD.gn
index 1be703f..2c4893d 100644
--- a/src/logger/BUILD.gn
+++ b/src/logger/BUILD.gn
@@ -483,6 +483,7 @@
":event_vector_index",
"$cobalt_root/src/algorithms/privacy:count_min",
"$cobalt_root/src/algorithms/privacy:numeric_encoding",
+ "$cobalt_root/src/algorithms/privacy:poisson",
"$cobalt_root/src/algorithms/privacy:rappor",
"$cobalt_root/src/algorithms/random:random",
"$cobalt_root/src/lib/client/cpp:buckets_config",
diff --git a/src/logger/privacy_encoder.cc b/src/logger/privacy_encoder.cc
index 4849809..07c6529 100644
--- a/src/logger/privacy_encoder.cc
+++ b/src/logger/privacy_encoder.cc
@@ -2,6 +2,7 @@
#include "src/algorithms/privacy/count_min.h"
#include "src/algorithms/privacy/numeric_encoding.h"
+#include "src/algorithms/privacy/poisson.h"
#include "src/algorithms/privacy/rappor.h"
#include "src/algorithms/random/random.h"
#include "src/lib/client/cpp/buckets_config.h"
@@ -246,7 +247,7 @@
double p = report_def.prob_bit_flip();
if (p < 0 || p > 1) {
- return Status(StatusCode::INVALID_ARGUMENT, "prob_bit_flip is not between 0 and 1");
+ return Status(StatusCode::INVALID_ARGUMENT, "prob_bit_flip is not between 0 and 1.");
}
for (auto index : indices) {
@@ -255,7 +256,15 @@
}
}
- return ApplyRapporNoise(indices, max_index, Probability(p), secure_gen_.get());
+ std::vector<uint64_t> with_rappor_noise =
+ ApplyRapporNoise(indices, max_index, Probability(p), secure_gen_.get());
+
+ double lambda = report_def.poisson_mean();
+ if (lambda < 0) {
+ return Status(StatusCode::INVALID_ARGUMENT, "poisson_mean is negative.");
+ }
+ return ApplyPoissonNoise(with_rappor_noise, max_index, PoissonParameter(lambda),
+ secure_gen_.get());
}
lib::statusor::StatusOr<std::vector<uint64_t>>
diff --git a/src/logger/privacy_encoder.h b/src/logger/privacy_encoder.h
index 2b86b47..a12662a 100644
--- a/src/logger/privacy_encoder.h
+++ b/src/logger/privacy_encoder.h
@@ -105,6 +105,10 @@
static std::vector<std::unique_ptr<Observation>> ObservationsFromIndices(
const std::vector<uint64_t> &indices);
+ // Adds noise based upon fields in the supplied |report_def|.
+ // If |prob_bit_flip| is set noise is added using the RAPPOR mechanism.
+ // If |poisson_mean| is set noise is added using the Poisson mechanism.
+ // If both are set, the RAPPOR noise is added first, then the Poisson noise.
lib::statusor::StatusOr<std::vector<uint64_t>> AddNoise(const std::vector<uint64_t> &indices,
const MetricDefinition &metric_def,
const ReportDefinition &report_def);
diff --git a/src/logger/privacy_encoder_test.cc b/src/logger/privacy_encoder_test.cc
index f6d52c5..5cf28c1 100644
--- a/src/logger/privacy_encoder_test.cc
+++ b/src/logger/privacy_encoder_test.cc
@@ -994,6 +994,20 @@
EXPECT_EQ(status_or.status().error_code(), StatusCode::INVALID_ARGUMENT);
}
+TEST_F(PrivacyEncoderTest, AddNoisePoissonNoiseOutOfRange) {
+ MetricDefinition metric_def;
+ ReportDefinition report_def;
+ report_def.set_report_type(ReportDefinition::UNIQUE_DEVICE_COUNTS);
+ std::vector<uint64_t> indices;
+
+ // Check that a negative poisson mean is rejected.
+ report_def.set_prob_bit_flip(0.0);
+ report_def.set_poisson_mean(-1.0);
+ auto status_or = AddNoise(indices, metric_def, report_def);
+ ASSERT_FALSE(status_or.ok());
+ EXPECT_EQ(status_or.status().error_code(), StatusCode::INVALID_ARGUMENT);
+}
+
TEST_F(PrivacyEncoderTest, AddNoiseIndexOutOfRange) {
MetricDefinition metric_def;
ReportDefinition report_def;
@@ -1011,7 +1025,7 @@
EXPECT_EQ(status_or.status().error_code(), StatusCode::INVALID_ARGUMENT);
}
-TEST_F(PrivacyEncoderTest, AddNoise) {
+TEST_F(PrivacyEncoderTest, AddNoiseRapporOnly) {
MetricDefinition metric_def;
ReportDefinition report_def;
uint32_t max_event_code = 10;
@@ -1021,8 +1035,40 @@
dim->set_max_event_code(max_event_code);
std::vector<uint64_t> indices = {1, 2, 3};
- double p = 1.0;
- report_def.set_prob_bit_flip(p);
+ report_def.set_prob_bit_flip(1.0);
+ report_def.set_poisson_mean(0.0);
+ auto status_or = AddNoise(indices, metric_def, report_def);
+ EXPECT_TRUE(status_or.ok());
+}
+
+TEST_F(PrivacyEncoderTest, AddNoisePoissonOnly) {
+ MetricDefinition metric_def;
+ ReportDefinition report_def;
+ uint32_t max_event_code = 10;
+ report_def.set_report_type(ReportDefinition::UNIQUE_DEVICE_COUNTS);
+ MetricDefinition::MetricDimension *dim = metric_def.add_metric_dimensions();
+ dim->set_dimension("dimension 1");
+ dim->set_max_event_code(max_event_code);
+ std::vector<uint64_t> indices = {1, 2, 3};
+
+ report_def.set_prob_bit_flip(0.0);
+ report_def.set_poisson_mean(2.0);
+ auto status_or = AddNoise(indices, metric_def, report_def);
+ EXPECT_TRUE(status_or.ok());
+}
+
+TEST_F(PrivacyEncoderTest, AddNoiseRapporAndPoisson) {
+ MetricDefinition metric_def;
+ ReportDefinition report_def;
+ uint32_t max_event_code = 10;
+ report_def.set_report_type(ReportDefinition::UNIQUE_DEVICE_COUNTS);
+ MetricDefinition::MetricDimension *dim = metric_def.add_metric_dimensions();
+ dim->set_dimension("dimension 1");
+ dim->set_max_event_code(max_event_code);
+ std::vector<uint64_t> indices = {1, 2, 3};
+
+ report_def.set_prob_bit_flip(1.0);
+ report_def.set_poisson_mean(2.0);
auto status_or = AddNoise(indices, metric_def, report_def);
EXPECT_TRUE(status_or.ok());
}