[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());
 }