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 "chrome/browser/policy/messaging_layer/encryption/encryption.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/containers/span.h"
13 #include "base/hash/hash.h"
14 #include "base/memory/ptr_util.h"
15 #include "base/strings/strcat.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/task/post_task.h"
18 #include "base/task_runner.h"
19 #include "chrome/browser/policy/messaging_layer/util/status.h"
20 #include "chrome/browser/policy/messaging_layer/util/statusor.h"
21 #include "crypto/aead.h"
22 #include "crypto/openssl_util.h"
23 #include "third_party/boringssl/src/include/openssl/curve25519.h"
24 #include "third_party/boringssl/src/include/openssl/digest.h"
25 #include "third_party/boringssl/src/include/openssl/hkdf.h"
26 
27 namespace reporting {
28 
Handle(scoped_refptr<Encryptor> encryptor)29 Encryptor::Handle::Handle(scoped_refptr<Encryptor> encryptor)
30     : encryptor_(encryptor) {}
31 
32 Encryptor::Handle::~Handle() = default;
33 
AddToRecord(base::StringPiece data,base::OnceCallback<void (Status)> cb)34 void Encryptor::Handle::AddToRecord(base::StringPiece data,
35                                     base::OnceCallback<void(Status)> cb) {
36   // Append new 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<EncryptedRecord>)> cb)41 void Encryptor::Handle::CloseRecord(
42     base::OnceCallback<void(StatusOr<EncryptedRecord>)> cb) {
43   // Retrieves asymmetric public key to use.
44   encryptor_->RetrieveAsymmetricKey(base::BindOnce(
45       &Handle::ProduceEncryptedRecord, base::Unretained(this), std::move(cb)));
46 }
47 
ProduceEncryptedRecord(base::OnceCallback<void (StatusOr<EncryptedRecord>)> cb,StatusOr<std::pair<std::string,PublicKeyId>> asymmetric_key_result)48 void Encryptor::Handle::ProduceEncryptedRecord(
49     base::OnceCallback<void(StatusOr<EncryptedRecord>)> cb,
50     StatusOr<std::pair<std::string, PublicKeyId>> asymmetric_key_result) {
51   // Make sure the record self-destructs when returning from this method.
52   const auto self_destruct = base::WrapUnique(this);
53 
54   // Validate keys.
55   if (!asymmetric_key_result.ok()) {
56     std::move(cb).Run(asymmetric_key_result.status());
57     return;
58   }
59   const auto& asymmetric_key = asymmetric_key_result.ValueOrDie();
60   if (asymmetric_key.first.size() != X25519_PUBLIC_VALUE_LEN) {
61     std::move(cb).Run(Status(
62         error::INTERNAL,
63         base::StrCat({"Asymmetric key size mismatch, expected=",
64                       base::NumberToString(X25519_PUBLIC_VALUE_LEN), " actual=",
65                       base::NumberToString(asymmetric_key.first.size())})));
66     return;
67   }
68 
69   // Generate new pair of private key and public value.
70   uint8_t out_public_value[X25519_PUBLIC_VALUE_LEN];
71   uint8_t out_private_key[X25519_PRIVATE_KEY_LEN];
72   X25519_keypair(out_public_value, out_private_key);
73 
74   // Compute shared secret.
75   uint8_t out_shared_secret[X25519_SHARED_KEY_LEN];
76   if (!X25519(out_shared_secret, out_private_key,
77               reinterpret_cast<const uint8_t*>(asymmetric_key.first.data()))) {
78     std::move(cb).Run(Status(error::DATA_LOSS, "Curve25519 encryption failed"));
79     return;
80   }
81 
82   // Encrypt the data with symmetric key using AEAD interface.
83   crypto::Aead aead(crypto::Aead::CHACHA20_POLY1305);
84 
85   // Produce symmetric key from shared secret using HKDF.
86   // Since the keys above are only used once, no salt and context is provided.
87   const auto out_symmetric_key = std::make_unique<uint8_t[]>(aead.KeyLength());
88   if (!HKDF(out_symmetric_key.get(), aead.KeyLength(), /*digest=*/EVP_sha256(),
89             out_shared_secret, X25519_SHARED_KEY_LEN,
90             /*salt=*/nullptr, /*salt_len=*/0,
91             /*info=*/nullptr, /*info_len=*/0)) {
92     std::move(cb).Run(
93         Status(error::INTERNAL, "Symmetric key extraction failed"));
94     return;
95   }
96 
97   // Use the symmetric key for data encryption.
98   aead.Init(base::make_span(out_symmetric_key.get(), aead.KeyLength()));
99 
100   // Set nonce to 0s, since a symmetric key is only used once.
101   // Note: if we ever start reusing the same symmetric key, we will need
102   // to generate new nonce for every record and transfer it to the peer.
103   std::string nonce(aead.NonceLength(), 0);
104 
105   // Prepare encrypted record.
106   EncryptedRecord encrypted_record;
107   encrypted_record.mutable_encryption_info()->set_public_key_id(
108       asymmetric_key.second);
109   encrypted_record.mutable_encryption_info()->set_encryption_key(
110       reinterpret_cast<const char*>(out_public_value), X25519_PUBLIC_VALUE_LEN);
111 
112   // Encrypt the whole record.
113   if (!aead.Seal(record_, nonce, std::string(),
114                  encrypted_record.mutable_encrypted_wrapped_record()) ||
115       encrypted_record.encrypted_wrapped_record().empty()) {
116     std::move(cb).Run(Status(error::INTERNAL, "Failed to encrypt the record"));
117     return;
118   }
119   record_.clear();  // Free unused memory.
120 
121   // Return EncryptedRecord.
122   std::move(cb).Run(encrypted_record);
123 }
124 
Encryptor()125 Encryptor::Encryptor()
126     : asymmetric_key_sequenced_task_runner_(
127           base::ThreadPool::CreateSequencedTaskRunner(
128               {base::TaskPriority::BEST_EFFORT, base::MayBlock()})) {
129   DETACH_FROM_SEQUENCE(asymmetric_key_sequence_checker_);
130 }
131 
132 Encryptor::~Encryptor() = default;
133 
UpdateAsymmetricKey(base::StringPiece new_public_key,PublicKeyId new_public_key_id,base::OnceCallback<void (Status)> response_cb)134 void Encryptor::UpdateAsymmetricKey(
135     base::StringPiece new_public_key,
136     PublicKeyId new_public_key_id,
137     base::OnceCallback<void(Status)> response_cb) {
138   if (new_public_key.empty()) {
139     std::move(response_cb)
140         .Run(Status(error::INVALID_ARGUMENT, "Provided key is empty"));
141     return;
142   }
143 
144   // Schedule key update on the sequenced task runner.
145   asymmetric_key_sequenced_task_runner_->PostTask(
146       FROM_HERE,
147       base::BindOnce(
148           [](base::StringPiece new_public_key, PublicKeyId new_public_key_id,
149              scoped_refptr<Encryptor> encryptor) {
150             encryptor->asymmetric_key_ =
151                 std::make_pair(std::string(new_public_key), new_public_key_id);
152           },
153           std::string(new_public_key), new_public_key_id,
154           base::WrapRefCounted(this)));
155 
156   // Response OK not waiting for the update.
157   std::move(response_cb).Run(Status::StatusOK());
158 }
159 
OpenRecord(base::OnceCallback<void (StatusOr<Handle * >)> cb)160 void Encryptor::OpenRecord(base::OnceCallback<void(StatusOr<Handle*>)> cb) {
161   std::move(cb).Run(new Handle(this));
162 }
163 
RetrieveAsymmetricKey(base::OnceCallback<void (StatusOr<std::pair<std::string,PublicKeyId>>)> cb)164 void Encryptor::RetrieveAsymmetricKey(
165     base::OnceCallback<void(StatusOr<std::pair<std::string, PublicKeyId>>)>
166         cb) {
167   // Schedule key retrieval on the sequenced task runner.
168   asymmetric_key_sequenced_task_runner_->PostTask(
169       FROM_HERE,
170       base::BindOnce(
171           [](base::OnceCallback<void(
172                  StatusOr<std::pair<std::string, PublicKeyId>>)> cb,
173              scoped_refptr<Encryptor> encryptor) {
174             DCHECK_CALLED_ON_VALID_SEQUENCE(
175                 encryptor->asymmetric_key_sequence_checker_);
176             StatusOr<std::pair<std::string, PublicKeyId>> response;
177             // Schedule response on regular thread pool.
178             base::ThreadPool::PostTask(
179                 FROM_HERE,
180                 base::BindOnce(
181                     [](base::OnceCallback<void(
182                            StatusOr<std::pair<std::string, PublicKeyId>>)> cb,
183                        StatusOr<std::pair<std::string, PublicKeyId>> response) {
184                       std::move(cb).Run(response);
185                     },
186                     std::move(cb),
187                     !encryptor->asymmetric_key_.has_value()
188                         ? StatusOr<std::pair<std::string, PublicKeyId>>(Status(
189                               error::NOT_FOUND, "Asymmetric key not set"))
190                         : encryptor->asymmetric_key_.value()));
191           },
192           std::move(cb), base::WrapRefCounted(this)));
193 }
194 
Create()195 StatusOr<scoped_refptr<Encryptor>> Encryptor::Create() {
196   // Make sure OpenSSL is initialized, in order to avoid data races later.
197   crypto::EnsureOpenSSLInit();
198   return base::WrapRefCounted(new Encryptor());
199 }
200 
201 }  // namespace reporting
202