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/nearby_sharing/certificates/nearby_share_private_certificate.h"
6
7 #include <utility>
8
9 #include "base/base64url.h"
10 #include "base/rand_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_piece.h"
13 #include "base/util/values/values_util.h"
14 #include "chrome/browser/nearby_sharing/certificates/common.h"
15 #include "chrome/browser/nearby_sharing/certificates/constants.h"
16 #include "chrome/browser/nearby_sharing/logging/logging.h"
17 #include "chrome/browser/nearby_sharing/proto/timestamp.pb.h"
18 #include "crypto/aead.h"
19 #include "crypto/ec_private_key.h"
20 #include "crypto/ec_signature_creator.h"
21 #include "crypto/encryptor.h"
22 #include "crypto/hmac.h"
23 #include "crypto/sha2.h"
24 #include "crypto/symmetric_key.h"
25
26 namespace {
27
28 // Dictionary keys used in ToDictionary, FromDictionary
29 const char kVisibility[] = "visibility";
30 const char kNotBefore[] = "not_before";
31 const char kNotAfter[] = "not_after";
32 const char kKeyPair[] = "key_pair";
33 const char kSecretKey[] = "secret_key";
34 const char kMetadataEncryptionKey[] = "metadata_encryption_key";
35 const char kId[] = "id";
36 const char kUnencryptedMetadata[] = "unencrypted_metadata";
37 const char kConsumedSalts[] = "consumed_salts";
38
39 // Generates a random validity bound offset in the interval
40 // [0, kNearbyShareMaxPrivateCertificateValidityBoundOffset).
GenerateRandomOffset()41 base::TimeDelta GenerateRandomOffset() {
42 return base::TimeDelta::FromMicroseconds(base::RandGenerator(
43 kNearbyShareMaxPrivateCertificateValidityBoundOffset.InMicroseconds()));
44 }
45
46 // Generates a certificate identifier by hashing the input secret |key|.
CreateCertificateIdFromSecretKey(const crypto::SymmetricKey & key)47 std::vector<uint8_t> CreateCertificateIdFromSecretKey(
48 const crypto::SymmetricKey& key) {
49 DCHECK_EQ(crypto::kSHA256Length, kNearbyShareNumBytesCertificateId);
50 std::vector<uint8_t> id(kNearbyShareNumBytesCertificateId);
51 crypto::SHA256HashString(key.key(), id.data(), id.size());
52
53 return id;
54 }
55
56 // Creates an HMAC from |metadata_encryption_key| to be used as a key commitment
57 // in certificates.
CreateMetadataEncryptionKeyTag(base::span<const uint8_t> metadata_encryption_key)58 base::Optional<std::vector<uint8_t>> CreateMetadataEncryptionKeyTag(
59 base::span<const uint8_t> metadata_encryption_key) {
60 // This array of 0x00 is used to conform with the GmsCore implementation.
61 std::vector<uint8_t> key(kNearbyShareNumBytesMetadataEncryptionKeyTag, 0x00);
62
63 std::vector<uint8_t> result(kNearbyShareNumBytesMetadataEncryptionKeyTag);
64 crypto::HMAC hmac(crypto::HMAC::HashAlgorithm::SHA256);
65 if (!hmac.Init(key) || !hmac.Sign(metadata_encryption_key, result))
66 return base::nullopt;
67
68 return result;
69 }
70
EncodeString(const std::string & unencoded_string)71 std::string EncodeString(const std::string& unencoded_string) {
72 std::string encoded_string;
73 base::Base64UrlEncode(unencoded_string,
74 base::Base64UrlEncodePolicy::INCLUDE_PADDING,
75 &encoded_string);
76
77 return encoded_string;
78 }
79
DecodeString(const std::string * encoded_string)80 base::Optional<std::string> DecodeString(const std::string* encoded_string) {
81 if (!encoded_string)
82 return base::nullopt;
83
84 std::string decoded_string;
85 if (!base::Base64UrlDecode(*encoded_string,
86 base::Base64UrlDecodePolicy::REQUIRE_PADDING,
87 &decoded_string)) {
88 return base::nullopt;
89 }
90
91 return decoded_string;
92 }
93
BytesToEncodedString(const std::vector<uint8_t> & bytes)94 std::string BytesToEncodedString(const std::vector<uint8_t>& bytes) {
95 return EncodeString(std::string(bytes.begin(), bytes.end()));
96 }
97
EncodedStringToBytes(const std::string * str)98 base::Optional<std::vector<uint8_t>> EncodedStringToBytes(
99 const std::string* str) {
100 base::Optional<std::string> decoded_str = DecodeString(str);
101 return decoded_str ? base::make_optional<std::vector<uint8_t>>(
102 decoded_str->begin(), decoded_str->end())
103 : base::nullopt;
104 }
105
SaltsToString(const std::set<std::vector<uint8_t>> & salts)106 std::string SaltsToString(const std::set<std::vector<uint8_t>>& salts) {
107 std::string str;
108 str.reserve(salts.size() * 2 * kNearbyShareNumBytesMetadataEncryptionKeySalt);
109 for (const std::vector<uint8_t>& salt : salts) {
110 str += base::HexEncode(salt);
111 }
112 return str;
113 }
114
StringToSalts(const std::string & str)115 std::set<std::vector<uint8_t>> StringToSalts(const std::string& str) {
116 const size_t chars_per_salt =
117 2 * kNearbyShareNumBytesMetadataEncryptionKeySalt;
118 DCHECK(str.size() % chars_per_salt == 0);
119 std::set<std::vector<uint8_t>> salts;
120 for (size_t i = 0; i < str.size(); i += chars_per_salt) {
121 std::vector<uint8_t> salt;
122 base::HexStringToBytes(base::StringPiece(&str[i], chars_per_salt), &salt);
123 salts.insert(std::move(salt));
124 }
125 return salts;
126 }
127
128 } // namespace
129
NearbySharePrivateCertificate(nearby_share::mojom::Visibility visibility,base::Time not_before,nearbyshare::proto::EncryptedMetadata unencrypted_metadata)130 NearbySharePrivateCertificate::NearbySharePrivateCertificate(
131 nearby_share::mojom::Visibility visibility,
132 base::Time not_before,
133 nearbyshare::proto::EncryptedMetadata unencrypted_metadata)
134 : visibility_(visibility),
135 not_before_(not_before),
136 not_after_(not_before_ + kNearbyShareCertificateValidityPeriod),
137 key_pair_(crypto::ECPrivateKey::Create()),
138 secret_key_(crypto::SymmetricKey::GenerateRandomKey(
139 crypto::SymmetricKey::Algorithm::AES,
140 /*key_size_in_bits=*/8 * kNearbyShareNumBytesSecretKey)),
141 metadata_encryption_key_(
142 GenerateRandomBytes(kNearbyShareNumBytesMetadataEncryptionKey)),
143 id_(CreateCertificateIdFromSecretKey(*secret_key_)),
144 unencrypted_metadata_(std::move(unencrypted_metadata)) {
145 DCHECK_NE(visibility, nearby_share::mojom::Visibility::kNoOne);
146 }
147
NearbySharePrivateCertificate(nearby_share::mojom::Visibility visibility,base::Time not_before,base::Time not_after,std::unique_ptr<crypto::ECPrivateKey> key_pair,std::unique_ptr<crypto::SymmetricKey> secret_key,std::vector<uint8_t> metadata_encryption_key,std::vector<uint8_t> id,nearbyshare::proto::EncryptedMetadata unencrypted_metadata,std::set<std::vector<uint8_t>> consumed_salts)148 NearbySharePrivateCertificate::NearbySharePrivateCertificate(
149 nearby_share::mojom::Visibility visibility,
150 base::Time not_before,
151 base::Time not_after,
152 std::unique_ptr<crypto::ECPrivateKey> key_pair,
153 std::unique_ptr<crypto::SymmetricKey> secret_key,
154 std::vector<uint8_t> metadata_encryption_key,
155 std::vector<uint8_t> id,
156 nearbyshare::proto::EncryptedMetadata unencrypted_metadata,
157 std::set<std::vector<uint8_t>> consumed_salts)
158 : visibility_(visibility),
159 not_before_(not_before),
160 not_after_(not_after),
161 key_pair_(std::move(key_pair)),
162 secret_key_(std::move(secret_key)),
163 metadata_encryption_key_(std::move(metadata_encryption_key)),
164 id_(std::move(id)),
165 unencrypted_metadata_(std::move(unencrypted_metadata)),
166 consumed_salts_(std::move(consumed_salts)) {
167 DCHECK_NE(visibility, nearby_share::mojom::Visibility::kNoOne);
168 }
169
NearbySharePrivateCertificate(const NearbySharePrivateCertificate & other)170 NearbySharePrivateCertificate::NearbySharePrivateCertificate(
171 const NearbySharePrivateCertificate& other) {
172 *this = other;
173 }
174
operator =(const NearbySharePrivateCertificate & other)175 NearbySharePrivateCertificate& NearbySharePrivateCertificate::operator=(
176 const NearbySharePrivateCertificate& other) {
177 if (this == &other)
178 return *this;
179
180 visibility_ = other.visibility_;
181 not_before_ = other.not_before_;
182 not_after_ = other.not_after_;
183 key_pair_ = other.key_pair_->Copy();
184 secret_key_ = crypto::SymmetricKey::Import(
185 crypto::SymmetricKey::Algorithm::AES, other.secret_key_->key());
186 metadata_encryption_key_ = other.metadata_encryption_key_;
187 id_ = other.id_;
188 unencrypted_metadata_ = other.unencrypted_metadata_;
189 consumed_salts_ = other.consumed_salts_;
190 next_salts_for_testing_ = other.next_salts_for_testing_;
191 offset_for_testing_ = other.offset_for_testing_;
192 return *this;
193 }
194
195 NearbySharePrivateCertificate::NearbySharePrivateCertificate(
196 NearbySharePrivateCertificate&& other) = default;
197
198 NearbySharePrivateCertificate& NearbySharePrivateCertificate::operator=(
199 NearbySharePrivateCertificate&& other) = default;
200
201 NearbySharePrivateCertificate::~NearbySharePrivateCertificate() = default;
202
203 base::Optional<NearbyShareEncryptedMetadataKey>
EncryptMetadataKey()204 NearbySharePrivateCertificate::EncryptMetadataKey() {
205 base::Optional<std::vector<uint8_t>> salt = GenerateUnusedSalt();
206 if (!salt) {
207 NS_LOG(ERROR) << "Encryption failed: Salt generation unsuccessful.";
208 return base::nullopt;
209 }
210
211 std::unique_ptr<crypto::Encryptor> encryptor =
212 CreateNearbyShareCtrEncryptor(secret_key_.get(), *salt);
213 if (!encryptor) {
214 NS_LOG(ERROR) << "Encryption failed: Could not create CTR encryptor.";
215 return base::nullopt;
216 }
217
218 DCHECK_EQ(kNearbyShareNumBytesMetadataEncryptionKey,
219 metadata_encryption_key_.size());
220 std::vector<uint8_t> encrypted_metadata_key;
221 if (!encryptor->Encrypt(metadata_encryption_key_, &encrypted_metadata_key)) {
222 NS_LOG(ERROR) << "Encryption failed: Could not encrypt metadata key.";
223 return base::nullopt;
224 }
225
226 return NearbyShareEncryptedMetadataKey(*salt, encrypted_metadata_key);
227 }
228
Sign(base::span<const uint8_t> payload) const229 base::Optional<std::vector<uint8_t>> NearbySharePrivateCertificate::Sign(
230 base::span<const uint8_t> payload) const {
231 std::unique_ptr<crypto::ECSignatureCreator> signer(
232 crypto::ECSignatureCreator::Create(key_pair_.get()));
233
234 std::vector<uint8_t> signature;
235 if (!signer->Sign(payload.data(), payload.size(), &signature)) {
236 NS_LOG(ERROR) << "Signing failed.";
237 return base::nullopt;
238 }
239
240 return signature;
241 }
242
HashAuthenticationToken(base::span<const uint8_t> authentication_token) const243 std::vector<uint8_t> NearbySharePrivateCertificate::HashAuthenticationToken(
244 base::span<const uint8_t> authentication_token) const {
245 return ComputeAuthenticationTokenHash(
246 authentication_token,
247 base::as_bytes(base::make_span(secret_key_->key())));
248 }
249
250 base::Optional<nearbyshare::proto::PublicCertificate>
ToPublicCertificate() const251 NearbySharePrivateCertificate::ToPublicCertificate() const {
252 std::vector<uint8_t> public_key;
253 if (!key_pair_->ExportPublicKey(&public_key)) {
254 NS_LOG(ERROR) << "Failed to export public key.";
255 return base::nullopt;
256 }
257
258 base::Optional<std::vector<uint8_t>> encrypted_metadata_bytes =
259 EncryptMetadata();
260 if (!encrypted_metadata_bytes) {
261 NS_LOG(ERROR) << "Failed to encrypt metadata.";
262 return base::nullopt;
263 }
264
265 base::Optional<std::vector<uint8_t>> metadata_encryption_key_tag =
266 CreateMetadataEncryptionKeyTag(metadata_encryption_key_);
267 if (!metadata_encryption_key_tag) {
268 NS_LOG(ERROR) << "Failed to compute metadata encryption key tag.";
269 return base::nullopt;
270 }
271
272 base::TimeDelta not_before_offset =
273 offset_for_testing_.value_or(GenerateRandomOffset());
274 base::TimeDelta not_after_offset =
275 offset_for_testing_.value_or(GenerateRandomOffset());
276
277 nearbyshare::proto::PublicCertificate public_certificate;
278 public_certificate.set_secret_id(std::string(id_.begin(), id_.end()));
279 public_certificate.set_secret_key(secret_key_->key());
280 public_certificate.set_public_key(
281 std::string(public_key.begin(), public_key.end()));
282 public_certificate.mutable_start_time()->set_seconds(
283 (not_before_ - not_before_offset).ToJavaTime() / 1000);
284 public_certificate.mutable_end_time()->set_seconds(
285 (not_after_ + not_after_offset).ToJavaTime() / 1000);
286 public_certificate.set_for_selected_contacts(
287 visibility_ == nearby_share::mojom::Visibility::kSelectedContacts);
288 public_certificate.set_metadata_encryption_key(std::string(
289 metadata_encryption_key_.begin(), metadata_encryption_key_.end()));
290 public_certificate.set_encrypted_metadata_bytes(std::string(
291 encrypted_metadata_bytes->begin(), encrypted_metadata_bytes->end()));
292 public_certificate.set_metadata_encryption_key_tag(
293 std::string(metadata_encryption_key_tag->begin(),
294 metadata_encryption_key_tag->end()));
295
296 return public_certificate;
297 }
298
ToDictionary() const299 base::Value NearbySharePrivateCertificate::ToDictionary() const {
300 base::Value dict(base::Value::Type::DICTIONARY);
301
302 dict.SetIntKey(kVisibility, static_cast<int>(visibility_));
303 dict.SetKey(kNotBefore, util::TimeToValue(not_before_));
304 dict.SetKey(kNotAfter, util::TimeToValue(not_after_));
305
306 std::vector<uint8_t> key_pair;
307 key_pair_->ExportPrivateKey(&key_pair);
308 dict.SetStringKey(kKeyPair, BytesToEncodedString(key_pair));
309
310 dict.SetStringKey(kSecretKey, EncodeString(secret_key_->key()));
311 dict.SetStringKey(kMetadataEncryptionKey,
312 BytesToEncodedString(metadata_encryption_key_));
313 dict.SetStringKey(kId, BytesToEncodedString(id_));
314 dict.SetStringKey(kUnencryptedMetadata,
315 EncodeString(unencrypted_metadata_.SerializeAsString()));
316 dict.SetStringKey(kConsumedSalts, SaltsToString(consumed_salts_));
317
318 return dict;
319 }
320
321 base::Optional<NearbySharePrivateCertificate>
FromDictionary(const base::Value & dict)322 NearbySharePrivateCertificate::FromDictionary(const base::Value& dict) {
323 base::Optional<int> int_opt;
324 const std::string* str_ptr;
325 base::Optional<std::string> str_opt;
326 base::Optional<base::Time> time_opt;
327 base::Optional<std::vector<uint8_t>> bytes_opt;
328
329 int_opt = dict.FindIntPath(kVisibility);
330 if (!int_opt)
331 return base::nullopt;
332
333 nearby_share::mojom::Visibility visibility =
334 static_cast<nearby_share::mojom::Visibility>(*int_opt);
335
336 time_opt = util::ValueToTime(dict.FindPath(kNotBefore));
337 if (!time_opt)
338 return base::nullopt;
339
340 base::Time not_before = *time_opt;
341
342 time_opt = util::ValueToTime(dict.FindPath(kNotAfter));
343 if (!time_opt)
344 return base::nullopt;
345
346 base::Time not_after = *time_opt;
347
348 bytes_opt = EncodedStringToBytes(dict.FindStringPath(kKeyPair));
349 if (!bytes_opt)
350 return base::nullopt;
351
352 std::unique_ptr<crypto::ECPrivateKey> key_pair =
353 crypto::ECPrivateKey::CreateFromPrivateKeyInfo(*bytes_opt);
354
355 str_opt = DecodeString(dict.FindStringPath(kSecretKey));
356 if (!str_opt)
357 return base::nullopt;
358
359 std::unique_ptr<crypto::SymmetricKey> secret_key =
360 crypto::SymmetricKey::Import(crypto::SymmetricKey::Algorithm::AES,
361 *str_opt);
362
363 bytes_opt = EncodedStringToBytes(dict.FindStringPath(kMetadataEncryptionKey));
364 if (!bytes_opt)
365 return base::nullopt;
366
367 std::vector<uint8_t> metadata_encryption_key = *bytes_opt;
368
369 bytes_opt = EncodedStringToBytes(dict.FindStringPath(kId));
370 if (!bytes_opt)
371 return base::nullopt;
372
373 std::vector<uint8_t> id = *bytes_opt;
374
375 str_opt = DecodeString(dict.FindStringPath(kUnencryptedMetadata));
376 if (!str_opt)
377 return base::nullopt;
378
379 nearbyshare::proto::EncryptedMetadata unencrypted_metadata;
380 if (!unencrypted_metadata.ParseFromString(*str_opt))
381 return base::nullopt;
382
383 str_ptr = dict.FindStringPath(kConsumedSalts);
384 if (!str_ptr)
385 return base::nullopt;
386
387 std::set<std::vector<uint8_t>> consumed_salts = StringToSalts(*str_ptr);
388
389 return NearbySharePrivateCertificate(
390 visibility, not_before, not_after, std::move(key_pair),
391 std::move(secret_key), std::move(metadata_encryption_key), std::move(id),
392 std::move(unencrypted_metadata), std::move(consumed_salts));
393 }
394
395 base::Optional<std::vector<uint8_t>>
GenerateUnusedSalt()396 NearbySharePrivateCertificate::GenerateUnusedSalt() {
397 if (consumed_salts_.size() >= kNearbyShareMaxNumMetadataEncryptionKeySalts) {
398 NS_LOG(ERROR) << "All salts exhausted for certificate.";
399 return base::nullopt;
400 }
401
402 for (size_t attempt = 0;
403 attempt < kNearbyShareMaxNumMetadataEncryptionKeySaltGenerationRetries;
404 ++attempt) {
405 std::vector<uint8_t> salt;
406 if (next_salts_for_testing_.empty()) {
407 salt = GenerateRandomBytes(2u);
408 } else {
409 salt = next_salts_for_testing_.front();
410 next_salts_for_testing_.pop();
411 }
412 DCHECK_EQ(2u, salt.size());
413
414 if (!base::Contains(consumed_salts_, salt)) {
415 consumed_salts_.insert(salt);
416 return salt;
417 }
418 }
419
420 NS_LOG(ERROR) << "Salt generation exceeded max number of retries. This is "
421 "highly improbable.";
422 return base::nullopt;
423 }
424
425 base::Optional<std::vector<uint8_t>>
EncryptMetadata() const426 NearbySharePrivateCertificate::EncryptMetadata() const {
427 // Init() keeps a reference to the input key, so that reference must outlive
428 // the lifetime of |aead|.
429 std::vector<uint8_t> derived_key = DeriveNearbyShareKey(
430 metadata_encryption_key_, kNearbyShareNumBytesAesGcmKey);
431
432 crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM);
433 aead.Init(derived_key);
434
435 std::vector<uint8_t> metadata_array(unencrypted_metadata_.ByteSizeLong());
436 unencrypted_metadata_.SerializeToArray(metadata_array.data(),
437 metadata_array.size());
438
439 return aead.Seal(
440 metadata_array,
441 /*nonce=*/
442 DeriveNearbyShareKey(base::as_bytes(base::make_span(secret_key_->key())),
443 kNearbyShareNumBytesAesGcmIv),
444 /*additional_data=*/base::span<const uint8_t>());
445 }
446