Avi Drissman | 8ba1bad | 2022-09-13 19:22:36 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Zach Trudo | 059a3d1 | 2021-01-29 21:47:06 | [diff] [blame] | 5 | #include "components/reporting/encryption/decryption.h" |
| 6 | |
Leonid Baraz | dcdb66690 | 2021-01-15 23:46:49 | [diff] [blame] | 7 | #include <limits> |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 8 | #include <memory> |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 9 | #include <string> |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 10 | #include <utility> |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 11 | |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 12 | #include "base/containers/span.h" |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 13 | #include "base/hash/hash.h" |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 14 | #include "base/memory/ptr_util.h" |
Leonid Baraz | 6a50913 | 2020-10-30 19:30:00 | [diff] [blame] | 15 | #include "base/rand_util.h" |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 16 | #include "base/strings/strcat.h" |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 17 | #include "base/strings/string_number_conversions.h" |
| 18 | #include "base/strings/string_piece.h" |
Patrick Monette | 643cdf6 | 2021-10-15 19:13:42 | [diff] [blame] | 19 | #include "base/task/task_runner.h" |
Gabriel Charette | 99f5df3 | 2021-03-19 19:55:55 | [diff] [blame] | 20 | #include "base/task/thread_pool.h" |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 21 | #include "base/types/expected.h" |
Zach Trudo | 059a3d1 | 2021-01-29 21:47:06 | [diff] [blame] | 22 | #include "components/reporting/encryption/encryption.h" |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 23 | #include "components/reporting/encryption/primitives.h" |
| 24 | #include "components/reporting/encryption/testing_primitives.h" |
Zach Trudo | 059a3d1 | 2021-01-29 21:47:06 | [diff] [blame] | 25 | #include "components/reporting/util/status.h" |
| 26 | #include "components/reporting/util/statusor.h" |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 27 | |
| 28 | namespace reporting { |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 29 | namespace test { |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 30 | |
Josh Hilke | 4996b60 | 2023-07-18 17:49:04 | [diff] [blame] | 31 | Decryptor::Handle::Handle(std::string_view shared_secret, |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 32 | scoped_refptr<Decryptor> decryptor) |
| 33 | : shared_secret_(shared_secret), decryptor_(decryptor) {} |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 34 | |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 35 | Decryptor::Handle::~Handle() = default; |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 36 | |
Josh Hilke | 4996b60 | 2023-07-18 17:49:04 | [diff] [blame] | 37 | void Decryptor::Handle::AddToRecord(std::string_view data, |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 38 | base::OnceCallback<void(Status)> cb) { |
| 39 | // Add piece of data to the record. |
David Benjamin | 2eb24c24 | 2023-05-31 15:29:50 | [diff] [blame] | 40 | record_.append(data); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 41 | std::move(cb).Run(Status::StatusOK()); |
| 42 | } |
| 43 | |
| 44 | void Decryptor::Handle::CloseRecord( |
Josh Hilke | 4996b60 | 2023-07-18 17:49:04 | [diff] [blame] | 45 | base::OnceCallback<void(StatusOr<std::string_view>)> cb) { |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 46 | // Make sure the record self-destructs when returning from this method. |
| 47 | const auto self_destruct = base::WrapUnique(this); |
| 48 | |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 49 | // Produce symmetric key from shared secret using HKDF. |
| 50 | // Since the original keys were only used once, no salt and context is needed. |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 51 | uint8_t out_symmetric_key[kKeySize]; |
| 52 | if (!ProduceSymmetricKey( |
| 53 | reinterpret_cast<const uint8_t*>(shared_secret_.data()), |
| 54 | out_symmetric_key)) { |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 55 | std::move(cb).Run(base::unexpected( |
| 56 | Status(error::INTERNAL, "Symmetric key extraction failed"))); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 57 | return; |
| 58 | } |
| 59 | |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 60 | std::string decrypted; |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 61 | PerformSymmetricDecryption(out_symmetric_key, record_, &decrypted); |
| 62 | record_.clear(); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 63 | |
| 64 | // Return decrypted record. |
| 65 | std::move(cb).Run(decrypted); |
| 66 | } |
| 67 | |
Josh Hilke | 4996b60 | 2023-07-18 17:49:04 | [diff] [blame] | 68 | void Decryptor::OpenRecord(std::string_view shared_secret, |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 69 | base::OnceCallback<void(StatusOr<Handle*>)> cb) { |
| 70 | std::move(cb).Run(new Handle(shared_secret, this)); |
| 71 | } |
| 72 | |
| 73 | StatusOr<std::string> Decryptor::DecryptSecret( |
Josh Hilke | 4996b60 | 2023-07-18 17:49:04 | [diff] [blame] | 74 | std::string_view private_key, |
| 75 | std::string_view peer_public_value) { |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 76 | // Verify the keys. |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 77 | if (private_key.size() != kKeySize) { |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 78 | return base::unexpected(Status( |
| 79 | error::FAILED_PRECONDITION, |
| 80 | base::StrCat({"Private key size mismatch, expected=", |
| 81 | base::NumberToString(kKeySize), |
| 82 | " actual=", base::NumberToString(private_key.size())}))); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 83 | } |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 84 | if (peer_public_value.size() != kKeySize) { |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 85 | return base::unexpected( |
| 86 | Status(error::FAILED_PRECONDITION, |
| 87 | base::StrCat({"Public key size mismatch, expected=", |
| 88 | base::NumberToString(kKeySize), " actual=", |
| 89 | base::NumberToString(peer_public_value.size())}))); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | // Compute shared secret. |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 93 | uint8_t out_shared_value[kKeySize]; |
| 94 | RestoreSharedSecret( |
| 95 | reinterpret_cast<const uint8_t*>(private_key.data()), |
| 96 | reinterpret_cast<const uint8_t*>(peer_public_value.data()), |
| 97 | out_shared_value); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 98 | |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 99 | return std::string(reinterpret_cast<const char*>(out_shared_value), kKeySize); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | Decryptor::Decryptor() |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 103 | : keys_sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner( |
| 104 | {base::TaskPriority::BEST_EFFORT, base::MayBlock()})) { |
| 105 | DETACH_FROM_SEQUENCE(keys_sequence_checker_); |
| 106 | } |
| 107 | |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 108 | Decryptor::~Decryptor() = default; |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 109 | |
Leonid Baraz | 4f79df41 | 2020-11-11 23:17:52 | [diff] [blame] | 110 | void Decryptor::RecordKeyPair( |
Josh Hilke | 4996b60 | 2023-07-18 17:49:04 | [diff] [blame] | 111 | std::string_view private_key, |
| 112 | std::string_view public_key, |
Leonid Baraz | 4f79df41 | 2020-11-11 23:17:52 | [diff] [blame] | 113 | base::OnceCallback<void(StatusOr<Encryptor::PublicKeyId>)> cb) { |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 114 | // Schedule key recording on the sequenced task runner. |
| 115 | keys_sequenced_task_runner_->PostTask( |
| 116 | FROM_HERE, |
| 117 | base::BindOnce( |
| 118 | [](std::string public_key, KeyInfo key_info, |
Leonid Baraz | 4f79df41 | 2020-11-11 23:17:52 | [diff] [blame] | 119 | base::OnceCallback<void(StatusOr<Encryptor::PublicKeyId>)> cb, |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 120 | scoped_refptr<Decryptor> decryptor) { |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 121 | DCHECK_CALLED_ON_VALID_SEQUENCE(decryptor->keys_sequence_checker_); |
Hong Xu | 6e3b270 | 2023-12-18 22:52:29 | [diff] [blame] | 122 | StatusOr<Encryptor::PublicKeyId> result = |
| 123 | CreateUnknownErrorStatusOr(); |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 124 | if (key_info.private_key.size() != kKeySize) { |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 125 | result = base::unexpected(Status( |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 126 | error::FAILED_PRECONDITION, |
| 127 | base::StrCat( |
| 128 | {"Private key size mismatch, expected=", |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 129 | base::NumberToString(kKeySize), " actual=", |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 130 | base::NumberToString(key_info.private_key.size())}))); |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 131 | } else if (public_key.size() != kKeySize) { |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 132 | result = base::unexpected(Status( |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 133 | error::FAILED_PRECONDITION, |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 134 | base::StrCat({"Public key size mismatch, expected=", |
| 135 | base::NumberToString(kKeySize), " actual=", |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 136 | base::NumberToString(public_key.size())}))); |
Leonid Baraz | 6a50913 | 2020-10-30 19:30:00 | [diff] [blame] | 137 | } else { |
| 138 | // Assign a random number to be public key id for testing purposes |
Leonid Baraz | 5fe6f45 | 2021-01-08 01:53:40 | [diff] [blame] | 139 | // only (in production it will be retrieved from the server as |
| 140 | // 'int32'). |
Leonid Baraz | dcdb66690 | 2021-01-15 23:46:49 | [diff] [blame] | 141 | const Encryptor::PublicKeyId public_key_id = base::RandGenerator( |
| 142 | std::numeric_limits<Encryptor::PublicKeyId>::max()); |
Leonid Baraz | 6a50913 | 2020-10-30 19:30:00 | [diff] [blame] | 143 | if (!decryptor->keys_.emplace(public_key_id, key_info).second) { |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 144 | result = base::unexpected( |
| 145 | Status(error::ALREADY_EXISTS, |
| 146 | base::StrCat({"Public key='", public_key, |
| 147 | "' already recorded"}))); |
Leonid Baraz | 6a50913 | 2020-10-30 19:30:00 | [diff] [blame] | 148 | } else { |
| 149 | result = public_key_id; |
| 150 | } |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 151 | } |
| 152 | // Schedule response on a generic thread pool. |
| 153 | base::ThreadPool::PostTask( |
Leonid Baraz | 4f79df41 | 2020-11-11 23:17:52 | [diff] [blame] | 154 | FROM_HERE, base::BindOnce( |
| 155 | [](base::OnceCallback<void( |
| 156 | StatusOr<Encryptor::PublicKeyId>)> cb, |
| 157 | StatusOr<Encryptor::PublicKeyId> result) { |
| 158 | std::move(cb).Run(result); |
| 159 | }, |
| 160 | std::move(cb), result)); |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 161 | }, |
| 162 | std::string(public_key), |
| 163 | KeyInfo{.private_key = std::string(private_key), |
| 164 | .time_stamp = base::Time::Now()}, |
| 165 | std::move(cb), base::WrapRefCounted(this))); |
| 166 | } |
| 167 | |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 168 | void Decryptor::RetrieveMatchingPrivateKey( |
Leonid Baraz | 4f79df41 | 2020-11-11 23:17:52 | [diff] [blame] | 169 | Encryptor::PublicKeyId public_key_id, |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 170 | base::OnceCallback<void(StatusOr<std::string>)> cb) { |
| 171 | // Schedule key retrieval on the sequenced task runner. |
| 172 | keys_sequenced_task_runner_->PostTask( |
| 173 | FROM_HERE, |
| 174 | base::BindOnce( |
Leonid Baraz | 4f79df41 | 2020-11-11 23:17:52 | [diff] [blame] | 175 | [](Encryptor::PublicKeyId public_key_id, |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 176 | base::OnceCallback<void(StatusOr<std::string>)> cb, |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 177 | scoped_refptr<Decryptor> decryptor) { |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 178 | DCHECK_CALLED_ON_VALID_SEQUENCE(decryptor->keys_sequence_checker_); |
| 179 | auto key_info_it = decryptor->keys_.find(public_key_id); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 180 | if (key_info_it != decryptor->keys_.end()) { |
Leonid Baraz | 48447f14 | 2023-07-31 19:46:21 | [diff] [blame] | 181 | CHECK_EQ(key_info_it->second.private_key.size(), |
| 182 | static_cast<size_t>(kKeySize)); |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 183 | } |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 184 | // Schedule response on a generic thread pool. |
| 185 | base::ThreadPool::PostTask( |
| 186 | FROM_HERE, |
| 187 | base::BindOnce( |
| 188 | [](base::OnceCallback<void(StatusOr<std::string>)> cb, |
| 189 | StatusOr<std::string> result) { |
| 190 | std::move(cb).Run(result); |
| 191 | }, |
| 192 | std::move(cb), |
| 193 | key_info_it == decryptor->keys_.end() |
Hong Xu | a7c9ad8 | 2023-10-30 23:41:54 | [diff] [blame] | 194 | ? StatusOr<std::string>(base::unexpected(Status( |
| 195 | error::NOT_FOUND, "Matching key not found"))) |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 196 | : key_info_it->second.private_key)); |
| 197 | }, |
| 198 | public_key_id, std::move(cb), base::WrapRefCounted(this))); |
| 199 | } |
| 200 | |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 201 | StatusOr<scoped_refptr<Decryptor>> Decryptor::Create() { |
Leonid Baraz | 679cfd0 | 2020-08-24 23:03:29 | [diff] [blame] | 202 | return base::WrapRefCounted(new Decryptor()); |
| 203 | } |
| 204 | |
Leonid Baraz | 06b4bed | 2021-03-24 06:19:44 | [diff] [blame] | 205 | } // namespace test |
Leonid Baraz | 2cb92bb | 2020-08-12 00:09:24 | [diff] [blame] | 206 | } // namespace reporting |