| // Copyright 2016 The Fuchsia Authors |
| // |
| // 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 |
| // |
| // http://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 COBALT_ALGORITHMS_FORCULUS_FORCULUS_ANALYZER_H_ |
| #define COBALT_ALGORITHMS_FORCULUS_FORCULUS_ANALYZER_H_ |
| |
| #include <unordered_map> |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "./observation.pb.h" |
| #include "algorithms/forculus/forculus_decrypter.h" |
| #include "config/encodings.pb.h" |
| |
| namespace cobalt { |
| namespace forculus { |
| |
| // A ForculusAnalyzer is constructed for the purpose of performing a single |
| // Forculus analysis. |
| // |
| // (1) Construct a ForculusAnalyzer passing in a ForculusConfig. |
| // |
| // (2) Repeatedly invoke AddObservation() to add the set of observations to |
| // be analyzed. The observations must all be for the same metric part and |
| // must have been encoded using the same encoding configuration. More |
| // precisely this means they must be associated with the same customer_id, |
| // project_id, metric_id, encoding_config_id and metric_part_name. |
| // |
| // (3) Invoke observation_errors() to check that all observations were added |
| // without any errors. Invoke num_observations() for the count of observations |
| // added. |
| // |
| // (4) Invoke TakeResults() to take the results. |
| // |
| // (5) Delete the ForculusAnalyzer as it should not be used any more. |
| // |
| // Note that the number of observations that are still left unencrypted may |
| // be computed as the value of num_observations() minus the sum of the values of |
| // |total_count| in each of the |ResultInfo|s in the map returned by |
| // TakeResults(). |
| // |
| // An instance of ForculusAnalyzer is not thread-safe. |
| class ForculusAnalyzer { |
| public: |
| // Constructs a ForculusAnalyzer for the given config. All of the observations |
| // added via AddObservation() must have been encoded using this config. |
| explicit ForculusAnalyzer(const cobalt::ForculusConfig& config); |
| |
| // Adds an additional observation to be analyzed. All of the observations |
| // added must be for the same metric part and must have been encoded using |
| // the same encoding configuration. See comments at the top of this file for |
| // more details. Furthermore the observations must have been encoded using |
| // the ForculusConfig passed to the constructor. |
| // |
| // |day_index| is the index of the day that the observation was encoded at |
| // the client. It is used to compute an epoch_index. The observations are |
| // grouped by (epoch_index, ciphertext) before Forculus decryption is applied. |
| // |
| // Returns true to indicate the observation was added without error and |
| // so num_observations() was incremented or false to indicate there was |
| // an error and so observation_errors() was incremented. |
| bool AddObservation(uint32_t day_index, |
| const ForculusObservation& obs); |
| |
| // The number of times that AddObservation() was invoked minus the value |
| // observation_errors(). |
| size_t num_observations() { |
| return num_observations_; |
| } |
| |
| // The number of times that AddObservation() was invoked and the observation |
| // was discarded due to an error. If this number is not zero it indicates |
| // that the Analyzer received data that was not created by a legitimate |
| // Cobalt client. See the error logs for details of the errors. |
| size_t observation_errors() { |
| return observation_errors_; |
| } |
| |
| // A ResultInfo contains info about one particular recovered plaintext. |
| struct ResultInfo { |
| explicit ResultInfo(size_t total_count) : |
| total_count(total_count), num_epochs(1) {} |
| |
| // The total number of observations added to this ForculusAnalyzer that |
| // decrypted to the plaintext. This is not the number of *distinct encoder |
| // clients* that sent this value. For example if 100 observations from the |
| // same encoder client that decrypt to this value were all added, then all |
| // 100 will be included in the count. (But the number of observations from |
| // distinct encoder clients must have been at least equal to the threshold |
| // or the value would not have been decrypted at all.) |
| size_t total_count; |
| |
| // The number of different epochs that were analyzed that contributed |
| // to total_count. For example if the report period were one week and |
| // the Forculus epoch were one day then the report period would contain 7 |
| // different Forculus epochs. Suppose that in 4 of the 7 epochs there |
| // were more than the threshold number of observations that decrypted to |
| // the plaintext but in the remaining three epochs there were not. Then this |
| // value would be 4. |
| size_t num_epochs; |
| }; |
| |
| // Returns the results of the Forculus analysis as a map. |
| // |
| // The keys to the map are all of the recovered plaintexts that were |
| // successfully decrypted by the analysis. The values are pointers to |
| // information about the recovered plaintext. |
| // |
| // After this method is invoked this ForculusAnalyzer should be deleted. |
| // This is because the contents of the returned map have been moved out |
| // of the ForculusAnalyzer leaving the ForculusAnalyzer in an undefined |
| // state. |
| std::map<std::string, std::unique_ptr<ResultInfo>> TakeResults() { |
| return std::move(results_); |
| } |
| |
| private: |
| ForculusConfig config_; |
| size_t num_observations_ = 0; |
| size_t observation_errors_ = 0; |
| std::map<std::string, std::unique_ptr<ResultInfo>> results_; |
| |
| // The type of the keys of |decryption_map_|. Represents a group of |
| // observations that all come from the same epoch and have the same |
| // ciphertext. |
| struct DecrypterGroupKey{ |
| DecrypterGroupKey(uint32_t epoch_index, std::string ciphertext) : |
| epoch_index(epoch_index), ciphertext(std::move(ciphertext)) {} |
| |
| bool operator==(const DecrypterGroupKey& other) const { |
| return other.epoch_index == epoch_index && other.ciphertext == ciphertext; |
| } |
| |
| // An eopch index. Forculus decryption operates on a set of observations |
| // that are all from the same epoch. |
| uint32_t epoch_index; |
| |
| // A ciphertext to be decrypted. |
| std::string ciphertext; |
| }; |
| |
| // The type of the values of |decryption_map_|. |
| struct DecrypterResult { |
| // Constructs a new DecrypterResult with the given decrypter and a null |
| // result_info. |
| explicit DecrypterResult(std::unique_ptr<ForculusDecrypter>&& decrypter) : |
| decrypter(std::move(decrypter)), |
| result_info(nullptr) {} |
| |
| // The ForculusDecrypter corresponding to the key if the ciphertext has |
| // not yet been decrypted, or NULL if the ciphertext has already been |
| // decrypted or if the ForculusDecrypter was previously corrupted. |
| std::unique_ptr<ForculusDecrypter> decrypter; |
| |
| // A pointer to the ResultInfo for the recovered plain text |
| // corresponding to the key if the ciphertext has already been decrypted, |
| // or NULL if the ciphertext has not yet been decrypted. |
| ResultInfo* result_info; |
| }; |
| |
| // Hash function for DecrypterGroupKey. |
| class KeyHasher { |
| public: |
| size_t operator()(const DecrypterGroupKey &key) const; |
| }; |
| |
| // A map from DecrypterGroupKeys to their DecrypterResults. |
| std::unordered_map<DecrypterGroupKey, DecrypterResult, KeyHasher> |
| decryption_map_; |
| }; |
| |
| } // namespace forculus |
| } // namespace cobalt |
| |
| #endif // COBALT_ALGORITHMS_FORCULUS_FORCULUS_ANALYZER_H_ |