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