1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <string>
6
7 #include "base/containers/span.h"
8 #include "base/hash/hash.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/rand_util.h"
11 #include "base/strings/strcat.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/task/post_task.h"
15 #include "base/task_runner.h"
16 #include "chrome/browser/policy/messaging_layer/encryption/decryption.h"
17 #include "chrome/browser/policy/messaging_layer/encryption/encryption.h"
18 #include "chrome/browser/policy/messaging_layer/util/status.h"
19 #include "chrome/browser/policy/messaging_layer/util/statusor.h"
20 #include "crypto/aead.h"
21 #include "crypto/openssl_util.h"
22 #include "third_party/boringssl/src/include/openssl/curve25519.h"
23 #include "third_party/boringssl/src/include/openssl/digest.h"
24 #include "third_party/boringssl/src/include/openssl/hkdf.h"
25
26 namespace reporting {
27
Handle(base::StringPiece shared_secret,scoped_refptr<Decryptor> decryptor)28 Decryptor::Handle::Handle(base::StringPiece shared_secret,
29 scoped_refptr<Decryptor> decryptor)
30 : shared_secret_(shared_secret), decryptor_(decryptor) {}
31
32 Decryptor::Handle::~Handle() = default;
33
AddToRecord(base::StringPiece data,base::OnceCallback<void (Status)> cb)34 void Decryptor::Handle::AddToRecord(base::StringPiece data,
35 base::OnceCallback<void(Status)> cb) {
36 // Add piece of data to the record.
37 record_.append(data.data(), data.size());
38 std::move(cb).Run(Status::StatusOK());
39 }
40
CloseRecord(base::OnceCallback<void (StatusOr<base::StringPiece>)> cb)41 void Decryptor::Handle::CloseRecord(
42 base::OnceCallback<void(StatusOr<base::StringPiece>)> cb) {
43 // Make sure the record self-destructs when returning from this method.
44 const auto self_destruct = base::WrapUnique(this);
45
46 // Decrypt the data with symmetric key using AEAD interface.
47 crypto::Aead aead(crypto::Aead::CHACHA20_POLY1305);
48
49 // Produce symmetric key from shared secret using HKDF.
50 // Since the original keys were only used once, no salt and context is needed.
51 const auto out_symmetric_key = std::make_unique<uint8_t[]>(aead.KeyLength());
52 if (!HKDF(out_symmetric_key.get(), aead.KeyLength(), /*digest=*/EVP_sha256(),
53 reinterpret_cast<const uint8_t*>(shared_secret_.data()),
54 shared_secret_.size(),
55 /*salt=*/nullptr, /*salt_len=*/0,
56 /*info=*/nullptr, /*info_len=*/0)) {
57 std::move(cb).Run(
58 Status(error::INTERNAL, "Symmetric key extraction failed"));
59 return;
60 }
61
62 // Use the symmetric key for data decryption.
63 aead.Init(base::make_span(out_symmetric_key.get(), aead.KeyLength()));
64
65 // Set nonce to 0s, since a symmetric key is only used once.
66 // Note: if we ever start reusing the same symmetric key, we will need
67 // to generate new nonce for every record and transfer it to the peer.
68 std::string nonce(aead.NonceLength(), 0);
69
70 // Decrypt collected record.
71 std::string decrypted;
72 if (!aead.Open(record_, nonce, std::string(), &decrypted)) {
73 std::move(cb).Run(Status(error::INTERNAL, "Failed to decrypt"));
74 return;
75 }
76 record_.clear(); // Free unused memory.
77
78 // Return decrypted record.
79 std::move(cb).Run(decrypted);
80 }
81
OpenRecord(base::StringPiece shared_secret,base::OnceCallback<void (StatusOr<Handle * >)> cb)82 void Decryptor::OpenRecord(base::StringPiece shared_secret,
83 base::OnceCallback<void(StatusOr<Handle*>)> cb) {
84 std::move(cb).Run(new Handle(shared_secret, this));
85 }
86
DecryptSecret(base::StringPiece private_key,base::StringPiece peer_public_value)87 StatusOr<std::string> Decryptor::DecryptSecret(
88 base::StringPiece private_key,
89 base::StringPiece peer_public_value) {
90 // Verify the keys.
91 if (private_key.size() != X25519_PRIVATE_KEY_LEN) {
92 return Status(
93 error::FAILED_PRECONDITION,
94 base::StrCat({"Private key size mismatch, expected=",
95 base::NumberToString(X25519_PRIVATE_KEY_LEN),
96 " actual=", base::NumberToString(private_key.size())}));
97 }
98 if (peer_public_value.size() != X25519_PUBLIC_VALUE_LEN) {
99 return Status(
100 error::FAILED_PRECONDITION,
101 base::StrCat({"Public key size mismatch, expected=",
102 base::NumberToString(X25519_PUBLIC_VALUE_LEN), " actual=",
103 base::NumberToString(peer_public_value.size())}));
104 }
105
106 // Compute shared secret.
107 uint8_t out_shared_value[X25519_SHARED_KEY_LEN];
108 if (!X25519(out_shared_value,
109 reinterpret_cast<const uint8_t*>(private_key.data()),
110 reinterpret_cast<const uint8_t*>(peer_public_value.data()))) {
111 return Status(error::DATA_LOSS, "Curve25519 decryption failed");
112 }
113
114 return std::string(reinterpret_cast<const char*>(out_shared_value),
115 X25519_SHARED_KEY_LEN);
116 }
117
Decryptor()118 Decryptor::Decryptor()
119 : keys_sequenced_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
120 {base::TaskPriority::BEST_EFFORT, base::MayBlock()})) {
121 DETACH_FROM_SEQUENCE(keys_sequence_checker_);
122 }
123
124 Decryptor::~Decryptor() = default;
125
RecordKeyPair(base::StringPiece private_key,base::StringPiece public_key,base::OnceCallback<void (StatusOr<Encryptor::PublicKeyId>)> cb)126 void Decryptor::RecordKeyPair(
127 base::StringPiece private_key,
128 base::StringPiece public_key,
129 base::OnceCallback<void(StatusOr<Encryptor::PublicKeyId>)> cb) {
130 // Schedule key recording on the sequenced task runner.
131 keys_sequenced_task_runner_->PostTask(
132 FROM_HERE,
133 base::BindOnce(
134 [](std::string public_key, KeyInfo key_info,
135 base::OnceCallback<void(StatusOr<Encryptor::PublicKeyId>)> cb,
136 scoped_refptr<Decryptor> decryptor) {
137 DCHECK_CALLED_ON_VALID_SEQUENCE(decryptor->keys_sequence_checker_);
138 StatusOr<Encryptor::PublicKeyId> result;
139 if (key_info.private_key.size() != X25519_PRIVATE_KEY_LEN) {
140 result = Status(
141 error::FAILED_PRECONDITION,
142 base::StrCat(
143 {"Private key size mismatch, expected=",
144 base::NumberToString(X25519_PRIVATE_KEY_LEN), " actual=",
145 base::NumberToString(key_info.private_key.size())}));
146 } else if (public_key.size() != X25519_PUBLIC_VALUE_LEN) {
147 result = Status(
148 error::FAILED_PRECONDITION,
149 base::StrCat(
150 {"Public key size mismatch, expected=",
151 base::NumberToString(X25519_PUBLIC_VALUE_LEN),
152 " actual=", base::NumberToString(public_key.size())}));
153 } else {
154 // Assign a random number to be public key id for testing purposes
155 // only (in production it will be Java Fingerprint2011 which is
156 // 'long').
157 Encryptor::PublicKeyId public_key_id;
158 base::RandBytes(&public_key_id, sizeof(public_key_id));
159 if (!decryptor->keys_.emplace(public_key_id, key_info).second) {
160 result = Status(error::ALREADY_EXISTS,
161 base::StrCat({"Public key='", public_key,
162 "' already recorded"}));
163 } else {
164 result = public_key_id;
165 }
166 }
167 // Schedule response on a generic thread pool.
168 base::ThreadPool::PostTask(
169 FROM_HERE, base::BindOnce(
170 [](base::OnceCallback<void(
171 StatusOr<Encryptor::PublicKeyId>)> cb,
172 StatusOr<Encryptor::PublicKeyId> result) {
173 std::move(cb).Run(result);
174 },
175 std::move(cb), result));
176 },
177 std::string(public_key),
178 KeyInfo{.private_key = std::string(private_key),
179 .time_stamp = base::Time::Now()},
180 std::move(cb), base::WrapRefCounted(this)));
181 }
182
RetrieveMatchingPrivateKey(Encryptor::PublicKeyId public_key_id,base::OnceCallback<void (StatusOr<std::string>)> cb)183 void Decryptor::RetrieveMatchingPrivateKey(
184 Encryptor::PublicKeyId public_key_id,
185 base::OnceCallback<void(StatusOr<std::string>)> cb) {
186 // Schedule key retrieval on the sequenced task runner.
187 keys_sequenced_task_runner_->PostTask(
188 FROM_HERE,
189 base::BindOnce(
190 [](Encryptor::PublicKeyId public_key_id,
191 base::OnceCallback<void(StatusOr<std::string>)> cb,
192 scoped_refptr<Decryptor> decryptor) {
193 DCHECK_CALLED_ON_VALID_SEQUENCE(decryptor->keys_sequence_checker_);
194 auto key_info_it = decryptor->keys_.find(public_key_id);
195 if (key_info_it != decryptor->keys_.end()) {
196 DCHECK_EQ(key_info_it->second.private_key.size(),
197 static_cast<size_t>(X25519_PRIVATE_KEY_LEN));
198 }
199 // Schedule response on a generic thread pool.
200 base::ThreadPool::PostTask(
201 FROM_HERE,
202 base::BindOnce(
203 [](base::OnceCallback<void(StatusOr<std::string>)> cb,
204 StatusOr<std::string> result) {
205 std::move(cb).Run(result);
206 },
207 std::move(cb),
208 key_info_it == decryptor->keys_.end()
209 ? StatusOr<std::string>(Status(
210 error::NOT_FOUND, "Matching key not found"))
211 : key_info_it->second.private_key));
212 },
213 public_key_id, std::move(cb), base::WrapRefCounted(this)));
214 }
215
Create()216 StatusOr<scoped_refptr<Decryptor>> Decryptor::Create() {
217 // Make sure OpenSSL is initialized, in order to avoid data races later.
218 crypto::EnsureOpenSSLInit();
219 return base::WrapRefCounted(new Decryptor());
220 }
221
222 } // namespace reporting
223