0

Switch to compile-time sizes for various crypto methods/types.

This started with changing HkdfSha256() to a compile-time size, and
the rest is the transitive closure of what I needed to touch.

Compile-time sizes shift errors left: certain kinds of misuse become
impossible.

Bug: none
Change-Id: I72ae216e90a898a092a342a907d8b9578bde7ed8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6098327
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Jood Hajeer <jood@google.com>
Reviewed-by: Will Harris <wfh@chromium.org>
Reviewed-by: Adam Langley <agl@chromium.org>
Reviewed-by: Ryan Hansberry <hansberry@chromium.org>
Commit-Queue: Peter Kasting <pkasting@chromium.org>
Reviewed-by: Fabio Tirelo <ftirelo@chromium.org>
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1398728}
This commit is contained in:
Peter Kasting
2024-12-19 11:00:09 -08:00
committed by Chromium LUCI CQ
parent 20b3b308ad
commit 2dd6f6078d
41 changed files with 483 additions and 433 deletions

@ -8,7 +8,6 @@ source_set("certificates") {
sources = [
"common.cc",
"common.h",
"constants.cc",
"constants.h",
"nearby_share_certificate_manager.cc",
"nearby_share_certificate_manager.h",

@ -4,12 +4,13 @@
#include "chrome/browser/nearby_sharing/certificates/common.h"
#include <array>
#include "base/logging.h"
#include "base/rand_util.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
#include "crypto/encryptor.h"
#include "crypto/hkdf.h"
#include "crypto/random.h"
#include "crypto/sha2.h"
#include "crypto/symmetric_key.h"
@ -39,22 +40,10 @@ bool IsNearbyShareCertificateWithinValidityPeriod(
use_public_certificate_tolerance);
}
std::vector<uint8_t> DeriveNearbyShareKey(base::span<const uint8_t> key,
size_t new_num_bytes) {
return crypto::HkdfSha256(key,
/*salt=*/base::span<const uint8_t>(),
/*info=*/base::span<const uint8_t>(),
new_num_bytes);
}
std::vector<uint8_t> ComputeAuthenticationTokenHash(
base::span<const uint8_t> authentication_token,
base::span<const uint8_t> secret_key) {
return crypto::HkdfSha256(authentication_token, secret_key,
/*info=*/base::span<const uint8_t>(),
kNearbyShareNumBytesAuthenticationTokenHash);
}
std::vector<uint8_t> GenerateRandomBytes(size_t num_bytes) {
return crypto::RandBytesAsVector(num_bytes);
std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>
ComputeAuthenticationTokenHash(base::span<const uint8_t> authentication_token,
base::span<const uint8_t> secret_key) {
return crypto::HkdfSha256<kNearbyShareNumBytesAuthenticationTokenHash>(
authentication_token, secret_key,
/*info=*/base::span<const uint8_t>());
}

@ -5,11 +5,15 @@
#ifndef CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_COMMON_H_
#define CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_COMMON_H_
#include <array>
#include <memory>
#include <vector>
#include "base/containers/span.h"
#include "base/time/time.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
#include "crypto/hkdf.h"
#include "crypto/random.h"
// Returns true if the |current_time| exceeds |not_after| by more than the
// public certificate clock-skew tolerance if applicable.
@ -30,16 +34,24 @@ bool IsNearbyShareCertificateWithinValidityPeriod(
// |secret_key|. A trivial info parameter is used, and the output length is
// fixed to be kNearbyShareNumBytesAuthenticationTokenHash to conform with the
// GmsCore implementation.
std::vector<uint8_t> ComputeAuthenticationTokenHash(
base::span<const uint8_t> authentication_token,
base::span<const uint8_t> secret_key);
std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>
ComputeAuthenticationTokenHash(base::span<const uint8_t> authentication_token,
base::span<const uint8_t> secret_key);
// Uses HKDF to generate a new key of length |new_num_bytes| from |key|. To
// Uses HKDF to generate a new key of length |NewNumBytes| from |key|. To
// conform with the GmsCore implementation, trivial salt and info are used.
std::vector<uint8_t> DeriveNearbyShareKey(base::span<const uint8_t> key,
size_t new_num_bytes);
template <size_t NewNumBytes>
std::array<uint8_t, NewNumBytes> DeriveNearbyShareKey(
base::span<const uint8_t> key) {
return crypto::HkdfSha256<NewNumBytes>(key, /*salt=*/{}, /*info=*/{});
}
// Generates a random byte array with size |num_bytes|.
std::vector<uint8_t> GenerateRandomBytes(size_t num_bytes);
template <size_t NumBytes>
std::array<uint8_t, NumBytes> GenerateRandomBytes() {
std::array<uint8_t, NumBytes> bytes;
crypto::RandBytes(bytes);
return bytes;
}
#endif // CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_COMMON_H_

@ -1,29 +0,0 @@
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/nearby_sharing/certificates/constants.h"
const base::TimeDelta kNearbyShareCertificateValidityPeriod = base::Days(3);
const base::TimeDelta kNearbyShareMaxPrivateCertificateValidityBoundOffset =
base::Hours(2);
const base::TimeDelta
kNearbySharePublicCertificateValidityBoundOffsetTolerance =
base::Minutes(30);
const size_t kNearbyShareNumPrivateCertificates = 3;
const size_t kNearbyShareNumBytesAuthenticationTokenHash = 6;
const size_t kNearbyShareNumBytesAesGcmKey = 32;
const size_t kNearbyShareNumBytesAesGcmIv = 12;
const size_t kNearbyShareNumBytesAesCtrIv = 16;
const size_t kNearbyShareNumBytesSecretKey = 32;
const size_t kNearbyShareNumBytesMetadataEncryptionKey = 14;
const size_t kNearbyShareNumBytesMetadataEncryptionKeySalt = 2;
const size_t kNearbyShareNumBytesMetadataEncryptionKeyTag = 32;
const size_t kNearbyShareNumBytesCertificateId = 32;
const size_t kNearbyShareMaxNumMetadataEncryptionKeySalts = 32768;
const size_t kNearbyShareMaxNumMetadataEncryptionKeySaltGenerationRetries = 128;
const char kNearbyShareSenderVerificationPrefix = 0x01;
const char kNearbyShareReceiverVerificationPrefix = 0x02;
const size_t kNearbyShareCertificateStorageMaxNumInitializeAttempts = 3;
const base::TimeDelta kNearbySharePublicCertificateDownloadPeriod =
base::Hours(12);

@ -8,55 +8,62 @@
#include "base/time/time.h"
// The number of days a certificate is valid.
extern const base::TimeDelta kNearbyShareCertificateValidityPeriod;
inline constexpr base::TimeDelta kNearbyShareCertificateValidityPeriod =
base::Days(3);
// The maximum offset for obfuscating a private certificate's not before/after
// timestamps when converting to a public certificate.
extern const base::TimeDelta
kNearbyShareMaxPrivateCertificateValidityBoundOffset;
inline constexpr base::TimeDelta
kNearbyShareMaxPrivateCertificateValidityBoundOffset = base::Hours(2);
// To account for clock skew between the local device and remote devices, public
// certificates will be considered valid if the current time is within the
// bounds [not-before - tolerance, not-after + tolerance).
extern const base::TimeDelta
kNearbySharePublicCertificateValidityBoundOffsetTolerance;
inline constexpr base::TimeDelta
kNearbySharePublicCertificateValidityBoundOffsetTolerance =
base::Minutes(30);
// The number of private certificates for a given visibility to be stored and
// rotated on the local device.
extern const size_t kNearbyShareNumPrivateCertificates;
inline constexpr size_t kNearbyShareNumPrivateCertificates = 3;
// The number of bytes comprising the hash of the authentication token using the
// secret key.
extern const size_t kNearbyShareNumBytesAuthenticationTokenHash;
inline constexpr size_t kNearbyShareNumBytesAuthenticationTokenHash = 6;
// Length of key in bytes required by AES-GCM encryption.
extern const size_t kNearbyShareNumBytesAesGcmKey;
inline constexpr size_t kNearbyShareNumBytesAesGcmKey = 32;
// Length of salt in bytes required by AES-GCM encryption.
extern const size_t kNearbyShareNumBytesAesGcmIv;
inline constexpr size_t kNearbyShareNumBytesAesGcmIv = 12;
// Length of salt in bytes required by AES-CTR encryption.
extern const size_t kNearbyShareNumBytesAesCtrIv;
inline constexpr size_t kNearbyShareNumBytesAesCtrIv = 16;
// The number of bytes of the AES secret key used to encrypt/decrypt the
// metadata encryption key.
extern const size_t kNearbyShareNumBytesSecretKey;
inline constexpr size_t kNearbyShareNumBytesSecretKey = 32;
// The number of the bytes of the AES key used to encryption personal info
// metadata, for example, name and picture data. These bytes are broadcast in an
// advertisement to other devices, thus the smaller byte size.
extern const size_t kNearbyShareNumBytesMetadataEncryptionKey;
inline constexpr size_t kNearbyShareNumBytesMetadataEncryptionKey = 14;
// The number of bytes for the salt used for encryption of the metadata
// encryption key. These bytes are broadcast in the advertisement to other
// devices.
extern const size_t kNearbyShareNumBytesMetadataEncryptionKeySalt;
inline constexpr size_t kNearbyShareNumBytesMetadataEncryptionKeySalt = 2;
// The number of bytes used for the hash of the metadata encryption key.
extern const size_t kNearbyShareNumBytesMetadataEncryptionKeyTag;
inline constexpr size_t kNearbyShareNumBytesMetadataEncryptionKeyTag = 32;
// The number of bytes in a certificate's identifier.
extern const size_t kNearbyShareNumBytesCertificateId;
inline constexpr size_t kNearbyShareNumBytesCertificateId = 32;
// The size of the random byte array used for the encryption frame's signed data
// if a valid signature cannot be generated. This size is consistent with the
// GmsCore implementation.
inline constexpr size_t kNearbyShareNumBytesRandomSignature = 72;
// Half of the possible 2-byte salt values.
//
@ -72,25 +79,27 @@ extern const size_t kNearbyShareNumBytesCertificateId;
// device that avoids salts that youve seen in the past is statistically likely
// to be the device youre tracking. Therefore, we only use half of the
// available 2-byte salts.
extern const size_t kNearbyShareMaxNumMetadataEncryptionKeySalts;
inline constexpr size_t kNearbyShareMaxNumMetadataEncryptionKeySalts = 32768;
// The max number of retries allowed to generate a salt. This is a sanity check
// that will never be hit.
extern const size_t
kNearbyShareMaxNumMetadataEncryptionKeySaltGenerationRetries;
inline constexpr size_t
kNearbyShareMaxNumMetadataEncryptionKeySaltGenerationRetries = 128;
// The prefix prepended to the UKEY2 authentication token by the sender before
// signing.
extern const char kNearbyShareSenderVerificationPrefix;
inline constexpr char kNearbyShareSenderVerificationPrefix = 0x01;
// The prefix prepended to the UKEY2 authentication token by the receiver before
// signing.
extern const char kNearbyShareReceiverVerificationPrefix;
inline constexpr char kNearbyShareReceiverVerificationPrefix = 0x02;
// The maximum number of attempts to initialize LevelDB in Certificate Storage.
extern const size_t kNearbyShareCertificateStorageMaxNumInitializeAttempts;
inline constexpr size_t kNearbyShareCertificateStorageMaxNumInitializeAttempts =
3;
// The frequency with which to download public certificates.
extern const base::TimeDelta kNearbySharePublicCertificateDownloadPeriod;
inline constexpr base::TimeDelta kNearbySharePublicCertificateDownloadPeriod =
base::Hours(12);
#endif // CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_CONSTANTS_H_

@ -77,7 +77,7 @@ std::optional<NearbySharePrivateCertificate>
FakeNearbyShareCertificateManager::GetValidPrivateCertificate(
nearby_share::mojom::Visibility visibility) const {
auto cert = GetNearbyShareTestPrivateCertificate(visibility);
cert.next_salts_for_testing() = base::queue<std::vector<uint8_t>>();
cert.next_salts_for_testing() = {};
cert.next_salts_for_testing().push(next_salt_);
return cert;
}

@ -8,8 +8,10 @@
#include <memory>
#include <vector>
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/time/clock.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate.h"
@ -89,7 +91,11 @@ class FakeNearbyShareCertificateManager : public NearbyShareCertificateManager {
using NearbyShareCertificateManager::NotifyPrivateCertificatesChanged;
using NearbyShareCertificateManager::NotifyPublicCertificatesDownloaded;
void set_next_salt(const std::vector<uint8_t>& salt) { next_salt_ = salt; }
void set_next_salt(
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>
salt) {
base::span(next_salt_).copy_from(salt);
}
size_t num_get_private_certificates_as_public_certificates_calls() {
return num_get_private_certificates_as_public_certificates_calls_;
@ -117,7 +123,7 @@ class FakeNearbyShareCertificateManager : public NearbyShareCertificateManager {
size_t num_download_public_certificates_calls_ = 0;
std::vector<GetDecryptedPublicCertificateCall>
get_decrypted_public_certificate_calls_;
std::vector<uint8_t> next_salt_;
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt> next_salt_;
};
#endif // CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_FAKE_NEARBY_SHARE_CERTIFICATE_MANAGER_H_

@ -4,6 +4,10 @@
#include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager.h"
#include <array>
#include "chrome/browser/nearby_sharing/certificates/constants.h"
NearbyShareCertificateManager::NearbyShareCertificateManager() = default;
NearbyShareCertificateManager::~NearbyShareCertificateManager() = default;
@ -63,7 +67,7 @@ NearbyShareCertificateManager::SignWithPrivateCertificate(
return cert->Sign(payload);
}
std::optional<std::vector<uint8_t>>
std::optional<std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>>
NearbyShareCertificateManager::HashAuthenticationTokenWithPrivateCertificate(
nearby_share::mojom::Visibility visibility,
base::span<const uint8_t> authentication_token) const {

@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_CERTIFICATE_MANAGER_H_
#define CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_CERTIFICATE_MANAGER_H_
#include <array>
#include <optional>
#include <vector>
@ -12,6 +13,7 @@
#include "base/functional/callback.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
@ -75,7 +77,8 @@ class NearbyShareCertificateManager {
// Creates a hash of the |authentication_token| using the currently valid
// private certificate. Returns std::nullopt if there is no valid private
// certificate with |visibility|.
std::optional<std::vector<uint8_t>>
std::optional<
std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>>
HashAuthenticationTokenWithPrivateCertificate(
nearby_share::mojom::Visibility visibility,
base::span<const uint8_t> authentication_token) const;

@ -4,6 +4,7 @@
#include "chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include <array>
#include <utility>
#include "chrome/browser/nearby_sharing/certificates/common.h"
@ -38,12 +39,11 @@ std::optional<std::vector<uint8_t>> DecryptMetadataKey(
const NearbyShareEncryptedMetadataKey& encrypted_metadata_key,
const crypto::SymmetricKey* secret_key) {
auto key = base::as_byte_span(secret_key->key());
auto counter = DeriveNearbyShareKey(encrypted_metadata_key.salt(),
crypto::aes_ctr::kCounterSize);
auto counter = DeriveNearbyShareKey<crypto::aes_ctr::kCounterSize>(
encrypted_metadata_key.salt());
return crypto::aes_ctr::Decrypt(
key, base::span<const uint8_t, crypto::aes_ctr::kCounterSize>(counter),
base::as_byte_span(encrypted_metadata_key.encrypted_key()));
key, counter, base::as_byte_span(encrypted_metadata_key.encrypted_key()));
}
// Attempts to decrypt |encrypted_metadata| with |metadata_encryption_key|,
@ -55,16 +55,16 @@ std::optional<std::vector<uint8_t>> DecryptMetadataPayload(
const crypto::SymmetricKey* secret_key) {
// Init() keeps a reference to the input key, so that reference must outlive
// the lifetime of |aead|.
std::vector<uint8_t> derived_key = DeriveNearbyShareKey(
metadata_encryption_key, kNearbyShareNumBytesAesGcmKey);
auto derived_key = DeriveNearbyShareKey<kNearbyShareNumBytesAesGcmKey>(
metadata_encryption_key);
crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM);
aead.Init(derived_key);
return aead.Open(encrypted_metadata,
/*nonce=*/
DeriveNearbyShareKey(base::as_byte_span(secret_key->key()),
kNearbyShareNumBytesAesGcmIv),
DeriveNearbyShareKey<kNearbyShareNumBytesAesGcmIv>(
base::as_byte_span(secret_key->key())),
/*additional_data=*/base::span<const uint8_t>());
}
@ -221,7 +221,7 @@ bool NearbyShareDecryptedPublicCertificate::VerifySignature(
return verifier.VerifyFinal();
}
std::vector<uint8_t>
std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>
NearbyShareDecryptedPublicCertificate::HashAuthenticationToken(
base::span<const uint8_t> authentication_token) const {
return ComputeAuthenticationTokenHash(authentication_token,

@ -5,12 +5,14 @@
#ifndef CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_DECRYPTED_PUBLIC_CERTIFICATE_H_
#define CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_DECRYPTED_PUBLIC_CERTIFICATE_H_
#include <array>
#include <memory>
#include <optional>
#include <vector>
#include "base/containers/span.h"
#include "base/time/time.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h"
#include "crypto/symmetric_key.h"
#include "third_party/nearby/sharing/proto/encrypted_metadata.pb.h"
@ -60,8 +62,8 @@ class NearbyShareDecryptedPublicCertificate {
// Creates a hash of the |authentication_token|, using |secret_key_|. The use
// of HKDF and the output vector size is part of the Nearby Share protocol and
// conforms with the GmsCore implementation.
std::vector<uint8_t> HashAuthenticationToken(
base::span<const uint8_t> authentication_token) const;
std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>
HashAuthenticationToken(base::span<const uint8_t> authentication_token) const;
private:
NearbyShareDecryptedPublicCertificate(

@ -4,6 +4,7 @@
#include "chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include <array>
#include <optional>
#include "chrome/browser/nearby_sharing/certificates/constants.h"
@ -51,10 +52,8 @@ TEST(NearbyShareDecryptedPublicCertificateTest, Decrypt_IncorrectKeyFailure) {
EXPECT_FALSE(NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate(
GetNearbyShareTestPublicCertificate(kTestPublicCertificateVisibility),
NearbyShareEncryptedMetadataKey(
std::vector<uint8_t>(kNearbyShareNumBytesMetadataEncryptionKeySalt,
0x00),
std::vector<uint8_t>(kNearbyShareNumBytesMetadataEncryptionKey,
0x00))));
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>(),
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>())));
}
TEST(NearbyShareDecryptedPublicCertificateTest,

@ -4,17 +4,22 @@
#include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h"
#include <stdint.h>
#include <array>
#include <utility>
#include "base/check.h"
#include "base/containers/span.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
NearbyShareEncryptedMetadataKey::NearbyShareEncryptedMetadataKey(
std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_key)
: salt_(std::move(salt)), encrypted_key_(std::move(encrypted_key)) {
DCHECK_EQ(kNearbyShareNumBytesMetadataEncryptionKeySalt, salt_.size());
DCHECK_EQ(kNearbyShareNumBytesMetadataEncryptionKey, encrypted_key_.size());
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>
salt,
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>
encrypted_key) {
base::span(salt_).copy_from(salt);
base::span(encrypted_key_).copy_from(encrypted_key);
}
NearbyShareEncryptedMetadataKey::NearbyShareEncryptedMetadataKey(

@ -5,15 +5,22 @@
#ifndef CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_ENCRYPTED_METADATA_KEY_H_
#define CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_ENCRYPTED_METADATA_KEY_H_
#include <cstdint>
#include <vector>
#include <stdint.h>
#include <array>
#include "base/containers/span.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
// Holds the encrypted symmetric key--the key used to encrypt user/device
// metatdata--as well as the salt used to encrypt the key.
struct NearbyShareEncryptedMetadataKey {
public:
NearbyShareEncryptedMetadataKey(std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_key);
NearbyShareEncryptedMetadataKey(
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>
salt,
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>
encrypted_key);
NearbyShareEncryptedMetadataKey(const NearbyShareEncryptedMetadataKey&);
NearbyShareEncryptedMetadataKey& operator=(
const NearbyShareEncryptedMetadataKey&);
@ -21,12 +28,18 @@ struct NearbyShareEncryptedMetadataKey {
NearbyShareEncryptedMetadataKey& operator=(NearbyShareEncryptedMetadataKey&&);
~NearbyShareEncryptedMetadataKey();
const std::vector<uint8_t>& salt() const { return salt_; }
const std::vector<uint8_t>& encrypted_key() const { return encrypted_key_; }
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>
salt() const {
return salt_;
}
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>
encrypted_key() const {
return encrypted_key_;
}
private:
std::vector<uint8_t> salt_;
std::vector<uint8_t> encrypted_key_;
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt> salt_;
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKey> encrypted_key_;
};
#endif // CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_ENCRYPTED_METADATA_KEY_H_

@ -4,12 +4,18 @@
#include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
#include <stdint.h>
#include <array>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/base64url.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/json/values_util.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
@ -71,7 +77,7 @@ std::optional<std::vector<uint8_t>> CreateMetadataEncryptionKeyTag(
return result;
}
std::string EncodeString(const std::string& unencoded_string) {
std::string EncodeString(std::string_view unencoded_string) {
std::string encoded_string;
base::Base64UrlEncode(unencoded_string,
base::Base64UrlEncodePolicy::INCLUDE_PADDING,
@ -94,8 +100,8 @@ std::optional<std::string> DecodeString(const std::string* encoded_string) {
return decoded_string;
}
std::string BytesToEncodedString(const std::vector<uint8_t>& bytes) {
return EncodeString(std::string(bytes.begin(), bytes.end()));
std::string BytesToEncodedString(base::span<const uint8_t> bytes) {
return EncodeString(base::as_string_view(bytes));
}
std::optional<std::vector<uint8_t>> EncodedStringToBytes(
@ -106,23 +112,29 @@ std::optional<std::vector<uint8_t>> EncodedStringToBytes(
: std::nullopt;
}
std::string SaltsToString(const std::set<std::vector<uint8_t>>& salts) {
std::string SaltsToString(
const std::set<
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>&
salts) {
std::string str;
str.reserve(salts.size() * 2 * kNearbyShareNumBytesMetadataEncryptionKeySalt);
for (const std::vector<uint8_t>& salt : salts) {
for (const auto& salt : salts) {
str += base::HexEncode(salt);
}
return str;
}
std::set<std::vector<uint8_t>> StringToSalts(const std::string& str) {
const size_t chars_per_salt =
2 * kNearbyShareNumBytesMetadataEncryptionKeySalt;
DCHECK(str.size() % chars_per_salt == 0);
std::set<std::vector<uint8_t>> salts;
for (size_t i = 0; i < str.size(); i += chars_per_salt) {
std::vector<uint8_t> salt;
base::HexStringToBytes(std::string_view(&str[i], chars_per_salt), &salt);
std::set<std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
StringToSalts(const std::string& str) {
DCHECK(str.size() % (2 * kNearbyShareNumBytesMetadataEncryptionKeySalt) == 0);
std::vector<uint8_t> salt_bytes;
base::HexStringToBytes(str, &salt_bytes);
std::set<std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
salts;
for (base::span salt_span(salt_bytes); !salt_span.empty();) {
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt> salt;
base::span(salt).copy_from(
salt_span.take_first<kNearbyShareNumBytesMetadataEncryptionKeySalt>());
salts.insert(std::move(salt));
}
return salts;
@ -167,7 +179,7 @@ NearbySharePrivateCertificate::NearbySharePrivateCertificate(
crypto::SymmetricKey::Algorithm::AES,
/*key_size_in_bits=*/8 * kNearbyShareNumBytesSecretKey)),
metadata_encryption_key_(
GenerateRandomBytes(kNearbyShareNumBytesMetadataEncryptionKey)),
GenerateRandomBytes<kNearbyShareNumBytesMetadataEncryptionKey>()),
id_(CreateCertificateIdFromSecretKey(*secret_key_)),
unencrypted_metadata_(std::move(unencrypted_metadata)) {
DCHECK_NE(visibility, nearby_share::mojom::Visibility::kNoOne);
@ -179,20 +191,22 @@ NearbySharePrivateCertificate::NearbySharePrivateCertificate(
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,
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>
metadata_encryption_key,
std::vector<uint8_t> id,
nearby::sharing::proto::EncryptedMetadata unencrypted_metadata,
std::set<std::vector<uint8_t>> consumed_salts)
std::set<std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
consumed_salts)
: visibility_(visibility),
not_before_(not_before),
not_after_(not_after),
key_pair_(std::move(key_pair)),
secret_key_(std::move(secret_key)),
metadata_encryption_key_(std::move(metadata_encryption_key)),
id_(std::move(id)),
unencrypted_metadata_(std::move(unencrypted_metadata)),
consumed_salts_(std::move(consumed_salts)) {
DCHECK_NE(visibility, nearby_share::mojom::Visibility::kNoOne);
base::span(metadata_encryption_key_).copy_from(metadata_encryption_key);
}
NearbySharePrivateCertificate::NearbySharePrivateCertificate(
@ -230,7 +244,9 @@ NearbySharePrivateCertificate::~NearbySharePrivateCertificate() = default;
std::optional<NearbyShareEncryptedMetadataKey>
NearbySharePrivateCertificate::EncryptMetadataKey() {
std::optional<std::vector<uint8_t>> salt = GenerateUnusedSalt();
std::optional<
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
salt = GenerateUnusedSalt();
if (!salt) {
CD_LOG(ERROR, Feature::NS)
<< "Encryption failed: Salt generation unsuccessful.";
@ -238,17 +254,14 @@ NearbySharePrivateCertificate::EncryptMetadataKey() {
}
auto key = base::as_byte_span(secret_key_->key());
auto counter = DeriveNearbyShareKey(*salt, crypto::aes_ctr::kCounterSize);
DCHECK_EQ(kNearbyShareNumBytesMetadataEncryptionKey,
metadata_encryption_key_.size());
return NearbyShareEncryptedMetadataKey(
*salt,
crypto::aes_ctr::Encrypt(
key,
base::span<const uint8_t, crypto::aes_ctr::kCounterSize>(counter),
metadata_encryption_key_));
auto counter = DeriveNearbyShareKey<crypto::aes_ctr::kCounterSize>(*salt);
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>
metadata_encryption_key;
base::span(metadata_encryption_key)
.copy_from(
crypto::aes_ctr::Encrypt(key, counter, metadata_encryption_key_));
return NearbyShareEncryptedMetadataKey(std::move(*salt),
std::move(metadata_encryption_key));
}
std::optional<std::vector<uint8_t>> NearbySharePrivateCertificate::Sign(
@ -265,7 +278,8 @@ std::optional<std::vector<uint8_t>> NearbySharePrivateCertificate::Sign(
return signature;
}
std::vector<uint8_t> NearbySharePrivateCertificate::HashAuthenticationToken(
std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>
NearbySharePrivateCertificate::HashAuthenticationToken(
base::span<const uint8_t> authentication_token) const {
return ComputeAuthenticationTokenHash(authentication_token,
base::as_byte_span(secret_key_->key()));
@ -401,7 +415,9 @@ NearbySharePrivateCertificate::FromDictionary(const base::Value::Dict& dict) {
if (!bytes_opt)
return std::nullopt;
std::vector<uint8_t> metadata_encryption_key = *bytes_opt;
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>
metadata_encryption_key;
base::span(metadata_encryption_key).copy_from(*bytes_opt);
bytes_opt = EncodedStringToBytes(dict.FindString(kId));
if (!bytes_opt)
@ -421,7 +437,8 @@ NearbySharePrivateCertificate::FromDictionary(const base::Value::Dict& dict) {
if (!str_ptr)
return std::nullopt;
std::set<std::vector<uint8_t>> consumed_salts = StringToSalts(*str_ptr);
std::set<std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
consumed_salts = StringToSalts(*str_ptr);
return NearbySharePrivateCertificate(
visibility, not_before, not_after, std::move(key_pair),
@ -429,7 +446,8 @@ NearbySharePrivateCertificate::FromDictionary(const base::Value::Dict& dict) {
std::move(unencrypted_metadata), std::move(consumed_salts));
}
std::optional<std::vector<uint8_t>>
std::optional<
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
NearbySharePrivateCertificate::GenerateUnusedSalt() {
if (consumed_salts_.size() >= kNearbyShareMaxNumMetadataEncryptionKeySalts) {
CD_LOG(ERROR, Feature::NS) << "All salts exhausted for certificate.";
@ -439,14 +457,14 @@ NearbySharePrivateCertificate::GenerateUnusedSalt() {
for (size_t attempt = 0;
attempt < kNearbyShareMaxNumMetadataEncryptionKeySaltGenerationRetries;
++attempt) {
std::vector<uint8_t> salt;
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt> salt;
if (next_salts_for_testing_.empty()) {
salt = GenerateRandomBytes(2u);
salt =
GenerateRandomBytes<kNearbyShareNumBytesMetadataEncryptionKeySalt>();
} else {
salt = next_salts_for_testing_.front();
next_salts_for_testing_.pop();
}
DCHECK_EQ(2u, salt.size());
if (!base::Contains(consumed_salts_, salt)) {
consumed_salts_.insert(salt);
@ -464,8 +482,8 @@ std::optional<std::vector<uint8_t>>
NearbySharePrivateCertificate::EncryptMetadata() const {
// Init() keeps a reference to the input key, so that reference must outlive
// the lifetime of |aead|.
std::vector<uint8_t> derived_key = DeriveNearbyShareKey(
metadata_encryption_key_, kNearbyShareNumBytesAesGcmKey);
auto derived_key = DeriveNearbyShareKey<kNearbyShareNumBytesAesGcmKey>(
metadata_encryption_key_);
crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM);
aead.Init(derived_key);
@ -476,7 +494,7 @@ NearbySharePrivateCertificate::EncryptMetadata() const {
return aead.Seal(metadata_array,
/*nonce=*/
DeriveNearbyShareKey(base::as_byte_span(secret_key_->key()),
kNearbyShareNumBytesAesGcmIv),
DeriveNearbyShareKey<kNearbyShareNumBytesAesGcmIv>(
base::as_byte_span(secret_key_->key())),
/*additional_data=*/base::span<const uint8_t>());
}

@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_PRIVATE_CERTIFICATE_H_
#define CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_NEARBY_SHARE_PRIVATE_CERTIFICATE_H_
#include <array>
#include <memory>
#include <optional>
#include <set>
@ -15,6 +16,7 @@
#include "base/gtest_prod_util.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
#include "third_party/nearby/sharing/proto/encrypted_metadata.pb.h"
@ -53,10 +55,13 @@ class NearbySharePrivateCertificate {
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,
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>
metadata_encryption_key,
std::vector<uint8_t> id,
nearby::sharing::proto::EncryptedMetadata unencrypted_metadata,
std::set<std::vector<uint8_t>> consumed_salts);
std::set<
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
consumed_salts);
NearbySharePrivateCertificate(const NearbySharePrivateCertificate& other);
NearbySharePrivateCertificate& operator=(
@ -91,8 +96,8 @@ class NearbySharePrivateCertificate {
// Creates a hash of the |authentication_token|, using |secret_key_|. The use
// of HKDF and the output vector size is part of the Nearby Share protocol and
// conforms with the GmsCore implementation.
std::vector<uint8_t> HashAuthenticationToken(
base::span<const uint8_t> authentication_token) const;
std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>
HashAuthenticationToken(base::span<const uint8_t> authentication_token) const;
// Converts this private certificate to a public certificate proto that can be
// shared with select contacts. Returns std::nullopt if the conversion was
@ -105,7 +110,9 @@ class NearbySharePrivateCertificate {
base::Value::Dict ToDictionary() const;
// For testing only.
base::queue<std::vector<uint8_t>>& next_salts_for_testing() {
base::queue<
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>&
next_salts_for_testing() {
return next_salts_for_testing_;
}
std::optional<base::TimeDelta>& offset_for_testing() {
@ -118,7 +125,9 @@ class NearbySharePrivateCertificate {
// maximum number of salts have been exhausted or if an unconsumed salt cannot
// be found in a fixed number of attempts, though this is highly improbably.
// Note: This function is not thread safe.
std::optional<std::vector<uint8_t>> GenerateUnusedSalt();
std::optional<
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
GenerateUnusedSalt();
// Encrypts |unencrypted_metadata_| with the |metadata_encryption_key_|, using
// the |secret_key_| as salt.
@ -147,7 +156,8 @@ class NearbySharePrivateCertificate {
// A 14-byte symmetric key used to encrypt |unencrypted_metadata_|. Not
// included in public certificate.
std::vector<uint8_t> metadata_encryption_key_;
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>
metadata_encryption_key_;
// An ID for the certificate, generated from the secret key.
std::vector<uint8_t> id_;
@ -157,10 +167,13 @@ class NearbySharePrivateCertificate {
nearby::sharing::proto::EncryptedMetadata unencrypted_metadata_;
// The set of 2-byte salts already used to encrypt the metadata key.
std::set<std::vector<uint8_t>> consumed_salts_;
std::set<std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
consumed_salts_;
// For testing only.
base::queue<std::vector<uint8_t>> next_salts_for_testing_;
base::queue<
std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>>
next_salts_for_testing_;
std::optional<base::TimeDelta> offset_for_testing_;
FRIEND_TEST_ALL_PREFIXES(NearbySharePrivateCertificateTest, ToFromDictionary);

@ -9,6 +9,7 @@
#include "chrome/browser/nearby_sharing/certificates/test_util.h"
#include <array>
#include <set>
#include "base/no_destructor.h"
@ -67,21 +68,11 @@ const uint8_t kTestCertificateId[] = {
0x05, 0xf8, 0x0d, 0x63, 0x59, 0x18, 0xf9, 0x1b, 0xc2, 0x2b, 0x14,
0xd7, 0xed, 0x05, 0x71, 0x4d, 0x58, 0xf9, 0x67, 0x02, 0xdd};
const uint8_t kTestMetadataEncryptionKey[] = {0x60, 0x1e, 0xc3, 0x13, 0x77,
0x57, 0x89, 0xa5, 0xb7, 0xa7,
0xf5, 0x04, 0xbb, 0xf3};
const uint8_t kTestMetadataEncryptionKeyTag[] = {
0x51, 0x9b, 0x16, 0xd8, 0x91, 0xb4, 0x0d, 0x81, 0x11, 0x21, 0xe3,
0x70, 0x42, 0x80, 0x8f, 0x87, 0x23, 0x6a, 0x84, 0x9b, 0xcd, 0xac,
0xbc, 0xe3, 0x54, 0xd7, 0xff, 0x53, 0xdf, 0x5d, 0x8a, 0xda};
const uint8_t kTestSalt[] = {0xf0, 0xf1};
const uint8_t kTestEncryptedMetadataKey[] = {0x52, 0x0e, 0x7e, 0x6b, 0x8e,
0xb5, 0x40, 0xe8, 0xe2, 0xbd,
0xa0, 0xee, 0x9d, 0x7b};
// TODO fix
const uint8_t kTestEncryptedMetadata[] = {
0x4d, 0x59, 0x5d, 0xb6, 0xac, 0x70, 0x00, 0x8f, 0x32, 0x9d, 0x0d,
@ -95,32 +86,6 @@ const uint8_t kTestEncryptedMetadata[] = {
// Plaintext "sample" (from RFC 6979 A.2.5).
const uint8_t kTestPayloadToSign[] = {0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65};
// One possible signature (from RFC 6979 A.2.5).
const uint8_t kTestSampleSignature[] = {
0x30,
0x46, // length of remaining data
0x02,
0x21, // length of r
// begin r (note 0x00 padding since leading bit of 0xEF is 1)
0x00, 0xEF, 0xD4, 0x8B, 0x2A, 0xAC, 0xB6, 0xA8, 0xFD, 0x11, 0x40, 0xDD,
0x9C, 0xD4, 0x5E, 0x81, 0xD6, 0x9D, 0x2C, 0x87, 0x7B, 0x56, 0xAA, 0xF9,
0x91, 0xC3, 0x4D, 0x0E, 0xA8, 0x4E, 0xAF, 0x37, 0x16,
// end r
0x02,
0x21, // length of s
// begin s (note 0x00 padding since leading bit of 0xF7 is 1)
0x00, 0xF7, 0xCB, 0x1C, 0x94, 0x2D, 0x65, 0x7C, 0x41, 0xD4, 0x36, 0xC7,
0xA1, 0xB6, 0xE2, 0x9F, 0x65, 0xF3, 0xE9, 0x00, 0xDB, 0xB9, 0xAF, 0xF4,
0x06, 0x4D, 0xC4, 0xAB, 0x2F, 0x84, 0x3A, 0xCD, 0xA8
// end s
};
// The result of HKDF of kTestPayloadToSign, using kTestSecretKey as salt. A
// trivial info parameter is used, and the output length is fixed to be
// kNearbyShareNumBytesAuthenticationTokenHash.
const uint8_t kTestPayloadHashUsingSecretKey[] = {0xE2, 0xCB, 0x90,
0x58, 0xDE, 0x3A};
const int64_t kTestNotBeforeMillis = 1881702000000;
const int64_t kTestValidityOffsetMillis = 1800000; // 30 minutes
@ -157,11 +122,13 @@ const std::vector<uint8_t>& GetNearbyShareTestCertificateId() {
return *id;
}
const std::vector<uint8_t>& GetNearbyShareTestMetadataEncryptionKey() {
static const base::NoDestructor<std::vector<uint8_t>> metadata_encryption_key(
kTestMetadataEncryptionKey,
kTestMetadataEncryptionKey + kNearbyShareNumBytesMetadataEncryptionKey);
return *metadata_encryption_key;
const std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>&
GetNearbyShareTestMetadataEncryptionKey() {
static constexpr std::array<uint8_t,
kNearbyShareNumBytesMetadataEncryptionKey>
kMetadataEncryptionKey = {0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89,
0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3};
return kMetadataEncryptionKey;
}
const std::vector<uint8_t>& GetNearbyShareTestMetadataEncryptionKeyTag() {
@ -171,10 +138,12 @@ const std::vector<uint8_t>& GetNearbyShareTestMetadataEncryptionKeyTag() {
return *tag;
}
const std::vector<uint8_t>& GetNearbyShareTestSalt() {
static const base::NoDestructor<std::vector<uint8_t>> salt(
std::begin(kTestSalt), std::end(kTestSalt));
return *salt;
const std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>&
GetNearbyShareTestSalt() {
static constexpr std::array<uint8_t,
kNearbyShareNumBytesMetadataEncryptionKeySalt>
kTestSalt = {0xf0, 0xf1};
return kTestSalt;
}
const NearbyShareEncryptedMetadataKey&
@ -182,8 +151,8 @@ GetNearbyShareTestEncryptedMetadataKey() {
static const base::NoDestructor<NearbyShareEncryptedMetadataKey>
encrypted_metadata_key(
GetNearbyShareTestSalt(),
std::vector<uint8_t>(std::begin(kTestEncryptedMetadataKey),
std::end(kTestEncryptedMetadataKey)));
std::to_array<uint8_t>({0x52, 0x0e, 0x7e, 0x6b, 0x8e, 0xb5, 0x40,
0xe8, 0xe2, 0xbd, 0xa0, 0xee, 0x9d, 0x7b}));
return *encrypted_metadata_key;
}
@ -228,17 +197,40 @@ const std::vector<uint8_t>& GetNearbyShareTestPayloadToSign() {
return *payload;
}
const std::vector<uint8_t>& GetNearbyShareTestSampleSignature() {
static const base::NoDestructor<std::vector<uint8_t>> signature(
std::begin(kTestSampleSignature), std::end(kTestSampleSignature));
return *signature;
const std::array<uint8_t, kNearbyShareNumBytesRandomSignature>&
GetNearbyShareTestSampleSignature() {
// One possible signature (from RFC 6979 A.2.5).
static constexpr std::array<uint8_t, kNearbyShareNumBytesRandomSignature>
kSignature = {
0x30,
0x46, // length of remaining data
0x02,
0x21, // length of r
// begin r (note 0x00 padding since leading bit of 0xEF is 1)
0x00, 0xEF, 0xD4, 0x8B, 0x2A, 0xAC, 0xB6, 0xA8, 0xFD, 0x11, 0x40,
0xDD, 0x9C, 0xD4, 0x5E, 0x81, 0xD6, 0x9D, 0x2C, 0x87, 0x7B, 0x56,
0xAA, 0xF9, 0x91, 0xC3, 0x4D, 0x0E, 0xA8, 0x4E, 0xAF, 0x37, 0x16,
// end r
0x02,
0x21, // length of s
// begin s (note 0x00 padding since leading bit of 0xF7 is 1)
0x00, 0xF7, 0xCB, 0x1C, 0x94, 0x2D, 0x65, 0x7C, 0x41, 0xD4, 0x36,
0xC7, 0xA1, 0xB6, 0xE2, 0x9F, 0x65, 0xF3, 0xE9, 0x00, 0xDB, 0xB9,
0xAF, 0xF4, 0x06, 0x4D, 0xC4, 0xAB, 0x2F, 0x84, 0x3A, 0xCD, 0xA8
// end s
};
return kSignature;
}
const std::vector<uint8_t>& GetNearbyShareTestPayloadHashUsingSecretKey() {
static const base::NoDestructor<std::vector<uint8_t>> hash(
std::begin(kTestPayloadHashUsingSecretKey),
std::end(kTestPayloadHashUsingSecretKey));
return *hash;
const std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>&
GetNearbyShareTestPayloadHashUsingSecretKey() {
// The result of HKDF of kTestPayloadToSign, using kTestSecretKey as salt. A
// trivial info parameter is used, and the output length is fixed to be
// kNearbyShareNumBytesAuthenticationTokenHash.
static constexpr std::array<uint8_t,
kNearbyShareNumBytesAuthenticationTokenHash>
kHash = {0xE2, 0xCB, 0x90, 0x58, 0xDE, 0x3A};
return kHash;
}
NearbySharePrivateCertificate GetNearbyShareTestPrivateCertificate(
@ -250,7 +242,7 @@ NearbySharePrivateCertificate GetNearbyShareTestPrivateCertificate(
GetNearbyShareTestP256KeyPair(), GetNearbyShareTestSecretKey(),
GetNearbyShareTestMetadataEncryptionKey(),
GetNearbyShareTestCertificateId(), GetNearbyShareTestMetadata(),
/*consumed_salts=*/std::set<std::vector<uint8_t>>());
/*consumed_salts=*/{});
cert.next_salts_for_testing().push(GetNearbyShareTestSalt());
return cert;
}

@ -5,10 +5,12 @@
#ifndef CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_TEST_UTIL_H_
#define CHROME_BROWSER_NEARBY_SHARING_CERTIFICATES_TEST_UTIL_H_
#include <array>
#include <memory>
#include <vector>
#include "base/time/time.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_decrypted_public_certificate.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
@ -30,9 +32,11 @@ const std::vector<uint8_t>& GetNearbyShareTestP256PublicKey();
std::unique_ptr<crypto::SymmetricKey> GetNearbyShareTestSecretKey();
const std::vector<uint8_t>& GetNearbyShareTestCertificateId();
const std::vector<uint8_t>& GetNearbyShareTestMetadataEncryptionKey();
const std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>&
GetNearbyShareTestMetadataEncryptionKey();
const std::vector<uint8_t>& GetNearbyShareTestMetadataEncryptionKeyTag();
const std::vector<uint8_t>& GetNearbyShareTestSalt();
const std::array<uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>&
GetNearbyShareTestSalt();
const NearbyShareEncryptedMetadataKey& GetNearbyShareTestEncryptedMetadataKey();
base::Time GetNearbyShareTestNotBefore();
@ -42,8 +46,10 @@ const nearby::sharing::proto::EncryptedMetadata& GetNearbyShareTestMetadata();
const std::vector<uint8_t>& GetNearbyShareTestEncryptedMetadata();
const std::vector<uint8_t>& GetNearbyShareTestPayloadToSign();
const std::vector<uint8_t>& GetNearbyShareTestSampleSignature();
const std::vector<uint8_t>& GetNearbyShareTestPayloadHashUsingSecretKey();
const std::array<uint8_t, kNearbyShareNumBytesRandomSignature>&
GetNearbyShareTestSampleSignature();
const std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>&
GetNearbyShareTestPayloadHashUsingSecretKey();
NearbySharePrivateCertificate GetNearbyShareTestPrivateCertificate(
nearby_share::mojom::Visibility visibility,

@ -4,6 +4,7 @@
#include "chrome/browser/nearby_sharing/nearby_sharing_service_impl.h"
#include <array>
#include <utility>
#include "ash/public/cpp/new_window_delegate.h"
@ -296,6 +297,15 @@ bool isVisibleForAdvertising(nearby_share::mojom::Visibility visibility) {
visibility == nearby_share::mojom::Visibility::kYourDevices;
}
NearbyShareEncryptedMetadataKey AdvertisementToKey(
const sharing::mojom::AdvertisementPtr& advertisement) {
return NearbyShareEncryptedMetadataKey(
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKeySalt>(
advertisement->salt),
base::span<const uint8_t, kNearbyShareNumBytesMetadataEncryptionKey>(
advertisement->encrypted_metadata_key));
}
} // namespace
NearbySharingServiceImpl::NearbySharingServiceImpl(
@ -1584,16 +1594,22 @@ bool NearbySharingServiceImpl::IsVisibleInBackground(
const std::optional<std::vector<uint8_t>>
NearbySharingServiceImpl::CreateEndpointInfo(
const std::optional<std::string>& device_name) {
std::vector<uint8_t> salt;
std::vector<uint8_t> encrypted_key;
std::array<uint8_t, sharing::Advertisement::kSaltSize> salt;
salt = GenerateRandomBytes<sharing::Advertisement::kSaltSize>();
std::array<uint8_t,
sharing::Advertisement::kMetadataEncryptionKeyHashByteSize>
encrypted_key;
encrypted_key = GenerateRandomBytes<
sharing::Advertisement::kMetadataEncryptionKeyHashByteSize>();
nearby_share::mojom::Visibility visibility = settings_.GetVisibility();
if (isVisibleForAdvertising(visibility)) {
std::optional<NearbyShareEncryptedMetadataKey> encrypted_metadata_key =
certificate_manager_->EncryptPrivateCertificateMetadataKey(visibility);
if (encrypted_metadata_key) {
salt = encrypted_metadata_key->salt();
encrypted_key = encrypted_metadata_key->encrypted_key();
base::span(salt).copy_from(encrypted_metadata_key->salt());
base::span(encrypted_key)
.copy_from(encrypted_metadata_key->encrypted_key());
} else {
CD_LOG(WARNING, Feature::NS)
<< __func__ << ": Failed to encrypt private certificate metadata key "
@ -1601,24 +1617,14 @@ NearbySharingServiceImpl::CreateEndpointInfo(
}
}
if (salt.empty() || encrypted_key.empty()) {
// Generate random metadata key.
salt = GenerateRandomBytes(sharing::Advertisement::kSaltSize);
encrypted_key = GenerateRandomBytes(
sharing::Advertisement::kMetadataEncryptionKeyHashByteSize);
}
nearby_share::mojom::ShareTargetType device_type =
nearby_share::mojom::ShareTargetType::kLaptop;
std::unique_ptr<sharing::Advertisement> advertisement =
sharing::Advertisement::NewInstance(
std::move(salt), std::move(encrypted_key), device_type, device_name);
if (advertisement) {
return advertisement->ToEndpointInfo();
} else {
return std::nullopt;
}
sharing::Advertisement::NewInstance(salt, encrypted_key, device_type,
device_name);
return advertisement ? std::make_optional(advertisement->ToEndpointInfo())
: std::nullopt;
}
void NearbySharingServiceImpl::GetBluetoothAdapter() {
@ -1822,8 +1828,8 @@ void NearbySharingServiceImpl::OnOutgoingAdvertisementDecoded(
// Once we get the advertisement, the first thing to do is decrypt the
// certificate.
NearbyShareEncryptedMetadataKey encrypted_metadata_key(
advertisement->salt, advertisement->encrypted_metadata_key);
NearbyShareEncryptedMetadataKey encrypted_metadata_key =
AdvertisementToKey(advertisement);
GetCertificateManager()->GetDecryptedPublicCertificate(
std::move(encrypted_metadata_key),
base::BindOnce(&NearbySharingServiceImpl::OnOutgoingDecryptedCertificate,
@ -3324,8 +3330,8 @@ void NearbySharingServiceImpl::OnIncomingAdvertisementDecoded(
transfer_profiler_->OnIncomingEndpointDecoded(endpoint_id,
IsInHighVisibility());
NearbyShareEncryptedMetadataKey encrypted_metadata_key(
advertisement->salt, advertisement->encrypted_metadata_key);
NearbyShareEncryptedMetadataKey encrypted_metadata_key =
AdvertisementToKey(advertisement);
GetCertificateManager()->GetDecryptedPublicCertificate(
std::move(encrypted_metadata_key),
base::BindOnce(&NearbySharingServiceImpl::OnIncomingDecryptedCertificate,

@ -4,9 +4,11 @@
#include "chrome/browser/nearby_sharing/nearby_sharing_service_impl.h"
#include <array>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/containers/span.h"
@ -848,10 +850,16 @@ class NearbySharingServiceImplTestBase : public testing::Test {
device_name = kDeviceName;
}
auto encrypted_metadata_key =
GetNearbyShareTestEncryptedMetadataKey();
sharing::mojom::AdvertisementPtr advertisement =
sharing::mojom::Advertisement::New(
GetNearbyShareTestEncryptedMetadataKey().salt(),
GetNearbyShareTestEncryptedMetadataKey().encrypted_key(),
std::vector<uint8_t>(
encrypted_metadata_key.salt().begin(),
encrypted_metadata_key.salt().end()),
std::vector<uint8_t>(
encrypted_metadata_key.encrypted_key().begin(),
encrypted_metadata_key.encrypted_key().end()),
kDeviceType, device_name);
std::move(callback).Run(std::move(advertisement));
}));
@ -4674,7 +4682,7 @@ TEST_P(NearbySharingServiceImplTest, ShutdownCallsObserversWithArcCallback) {
}
TEST_P(NearbySharingServiceImplTest, RotateBackgroundAdvertisement_Periodic) {
certificate_manager()->set_next_salt({0x00, 0x01});
certificate_manager()->set_next_salt(std::to_array<uint8_t>({0x00, 0x01}));
SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
NiceMock<MockTransferUpdateCallback> callback;
NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@ -4684,7 +4692,7 @@ TEST_P(NearbySharingServiceImplTest, RotateBackgroundAdvertisement_Periodic) {
auto endpoint_info_initial =
fake_nearby_connections_manager_->advertising_endpoint_info();
certificate_manager()->set_next_salt({0x00, 0x02});
certificate_manager()->set_next_salt(std::to_array<uint8_t>({0x00, 0x02}));
task_environment_.FastForwardBy(base::Seconds(870));
EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
auto endpoint_info_rotated =
@ -4694,7 +4702,7 @@ TEST_P(NearbySharingServiceImplTest, RotateBackgroundAdvertisement_Periodic) {
TEST_P(NearbySharingServiceImplTest,
RotateBackgroundAdvertisement_PrivateCertificatesChange) {
certificate_manager()->set_next_salt({0x00, 0x01});
certificate_manager()->set_next_salt(std::to_array<uint8_t>({0x00, 0x01}));
SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
NiceMock<MockTransferUpdateCallback> callback;
NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
@ -4704,7 +4712,7 @@ TEST_P(NearbySharingServiceImplTest,
auto endpoint_info_initial =
fake_nearby_connections_manager_->advertising_endpoint_info();
certificate_manager()->set_next_salt({0x00, 0x02});
certificate_manager()->set_next_salt(std::to_array<uint8_t>({0x00, 0x02}));
certificate_manager()->NotifyPrivateCertificatesChanged();
EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
auto endpoint_info_rotated =
@ -5265,11 +5273,14 @@ TEST_P(NearbySharingServiceImplTest, VisibilityReminderTimerIsTriggered) {
}
TEST_P(NearbySharingServiceImplTest, CreateShareTarget) {
auto encrypted_metadata_key = GetNearbyShareTestEncryptedMetadataKey();
sharing::mojom::AdvertisementPtr advertisement =
sharing::mojom::Advertisement::New(
GetNearbyShareTestEncryptedMetadataKey().salt(),
GetNearbyShareTestEncryptedMetadataKey().encrypted_key(), kDeviceType,
kDeviceName);
std::vector<uint8_t>(encrypted_metadata_key.salt().begin(),
encrypted_metadata_key.salt().end()),
std::vector<uint8_t>(encrypted_metadata_key.encrypted_key().begin(),
encrypted_metadata_key.encrypted_key().end()),
kDeviceType, kDeviceName);
// Flip |for_self_share| to true to ensure the resulting ShareTarget picks
// this up.
@ -5280,7 +5291,7 @@ TEST_P(NearbySharingServiceImplTest, CreateShareTarget) {
std::optional<NearbyShareDecryptedPublicCertificate> certificate =
NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate(
certificate_proto, GetNearbyShareTestEncryptedMetadataKey());
certificate_proto, encrypted_metadata_key);
ASSERT_TRUE(certificate.has_value());
ASSERT_EQ(certificate_proto.for_self_share(), certificate->for_self_share());

@ -17,11 +17,6 @@
namespace {
// The size of the random byte array used for the encryption frame's signed data
// if a valid signature cannot be generated. This size is consistent with the
// GmsCore implementation.
const size_t kNearbyShareNumBytesRandomSignature = 72;
std::ostream& operator<<(
std::ostream& out,
const PairedKeyVerificationRunner::PairedKeyVerificationResult& obj) {
@ -255,22 +250,6 @@ void PairedKeyVerificationRunner::SendCertificateInfo() {
}
void PairedKeyVerificationRunner::SendPairedKeyEncryptionFrame() {
std::optional<std::vector<uint8_t>> signature =
certificate_manager_->SignWithPrivateCertificate(
visibility_, PadPrefix(local_prefix_, raw_token_));
if (!signature || signature->empty()) {
signature = GenerateRandomBytes(kNearbyShareNumBytesRandomSignature);
}
std::vector<uint8_t> certificate_id_hash;
if (certificate_) {
certificate_id_hash = certificate_->HashAuthenticationToken(raw_token_);
}
if (certificate_id_hash.empty()) {
certificate_id_hash =
GenerateRandomBytes(kNearbyShareNumBytesAuthenticationTokenHash);
}
nearby::sharing::service::proto::Frame frame;
frame.set_version(nearby::sharing::service::proto::Frame::V1);
nearby::sharing::service::proto::V1Frame* v1_frame = frame.mutable_v1();
@ -278,9 +257,25 @@ void PairedKeyVerificationRunner::SendPairedKeyEncryptionFrame() {
nearby::sharing::service::proto::V1Frame::PAIRED_KEY_ENCRYPTION);
nearby::sharing::service::proto::PairedKeyEncryptionFrame* encryption_frame =
v1_frame->mutable_paired_key_encryption();
encryption_frame->set_signed_data(signature->data(), signature->size());
encryption_frame->set_secret_id_hash(certificate_id_hash.data(),
certificate_id_hash.size());
if (std::optional<std::vector<uint8_t>> signature =
certificate_manager_->SignWithPrivateCertificate(
visibility_, PadPrefix(local_prefix_, raw_token_));
signature && !signature->empty()) {
encryption_frame->set_signed_data(signature->data(), signature->size());
} else {
auto random_bytes =
GenerateRandomBytes<kNearbyShareNumBytesRandomSignature>();
encryption_frame->set_signed_data(random_bytes.data(), random_bytes.size());
}
{
std::array<uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>
certificate_id_hash =
certificate_ ? certificate_->HashAuthenticationToken(raw_token_)
: GenerateRandomBytes<
kNearbyShareNumBytesAuthenticationTokenHash>();
encryption_frame->set_secret_id_hash(certificate_id_hash.data(),
certificate_id_hash.size());
}
std::vector<uint8_t> data(frame.ByteSize());
frame.SerializeToArray(data.data(), frame.ByteSize());
@ -290,10 +285,12 @@ void PairedKeyVerificationRunner::SendPairedKeyEncryptionFrame() {
PairedKeyVerificationRunner::PairedKeyVerificationResult
PairedKeyVerificationRunner::VerifyRemotePublicCertificate(
const sharing::mojom::V1FramePtr& frame) {
std::optional<std::vector<uint8_t>> hash =
certificate_manager_->HashAuthenticationTokenWithPrivateCertificate(
visibility_, raw_token_);
if (hash && *hash == frame->get_paired_key_encryption()->secret_id_hash) {
if (std::optional<std::array<
uint8_t, kNearbyShareNumBytesAuthenticationTokenHash>> hash =
certificate_manager_->HashAuthenticationTokenWithPrivateCertificate(
visibility_, raw_token_);
hash && std::ranges::equal(
*hash, frame->get_paired_key_encryption()->secret_id_hash)) {
CD_LOG(VERBOSE, Feature::NS)
<< __func__ << ": Successfully verified remote public certificate.";
return PairedKeyVerificationResult::kSuccess;

@ -1005,9 +1005,9 @@ std::vector<uint8_t> EncryptWrappedPIN(
0x3a, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x3a, 0x47, 0x50, 0x4d,
0x20, 0x50, 0x49, 0x4e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x77,
0x72, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x6b, 0x65, 0x79};
const std::vector<uint8_t> derived_key = crypto::HkdfSha256(
const std::array<uint8_t, 32> derived_key = crypto::HkdfSha256<32>(
security_domain_secret, /*salt=*/base::span<const uint8_t>(),
kKeyPurposePinDataKey, 32);
kKeyPurposePinDataKey);
crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM);
aead.Init(derived_key);
uint8_t nonce[12];

@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <utility>
#include "chrome/services/sharing/nearby/decoder/advertisement_decoder.h"
#include <array>
#include <utility>
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_util.h"
@ -67,8 +69,7 @@ std::unique_ptr<sharing::Advertisement> AdvertisementDecoder::FromEndpointInfo(
return nullptr;
}
auto iter = endpoint_info.begin();
uint8_t first_byte = *iter++;
uint8_t first_byte = endpoint_info.take_first_elem();
int version = ParseVersion(first_byte);
if (version < 0 || version > kMaxSupportedAdvertisementParsedVersionNumber) {
@ -81,19 +82,24 @@ std::unique_ptr<sharing::Advertisement> AdvertisementDecoder::FromEndpointInfo(
nearby_share::mojom::ShareTargetType device_type =
ParseDeviceType(first_byte);
std::vector<uint8_t> salt(iter, iter + sharing::Advertisement::kSaltSize);
iter += sharing::Advertisement::kSaltSize;
std::array<uint8_t, sharing::Advertisement::kSaltSize> salt;
base::span(salt).copy_from(
endpoint_info.take_first<sharing::Advertisement::kSaltSize>());
std::vector<uint8_t> encrypted_metadata_key(
iter, iter + sharing::Advertisement::kMetadataEncryptionKeyHashByteSize);
iter += sharing::Advertisement::kMetadataEncryptionKeyHashByteSize;
std::array<uint8_t,
sharing::Advertisement::kMetadataEncryptionKeyHashByteSize>
encrypted_metadata_key;
base::span(encrypted_metadata_key)
.copy_from(endpoint_info.take_first<
sharing::Advertisement::kMetadataEncryptionKeyHashByteSize>());
int device_name_length = 0;
if (iter != endpoint_info.end())
device_name_length = *iter++ & 0xff;
size_t device_name_length = 0;
if (!endpoint_info.empty()) {
device_name_length = endpoint_info.take_first_elem();
}
if (endpoint_info.end() - iter < device_name_length ||
(device_name_length == 0 && has_device_name)) {
if ((has_device_name && !device_name_length) ||
endpoint_info.size() < device_name_length) {
LOG(ERROR) << "Failed to parse advertisement because the device name did "
"not match the expected length "
<< device_name_length;
@ -101,9 +107,9 @@ std::unique_ptr<sharing::Advertisement> AdvertisementDecoder::FromEndpointInfo(
}
std::optional<std::string> optional_device_name;
if (device_name_length > 0) {
optional_device_name = std::string(iter, iter + device_name_length);
iter += device_name_length;
if (device_name_length) {
optional_device_name = std::string(
base::as_string_view(endpoint_info.first(device_name_length)));
if (!base::IsStringUTF8(*optional_device_name)) {
LOG(ERROR) << "Failed to parse advertisement because the device name was "

@ -19,14 +19,13 @@ namespace sharing {
namespace {
const char kDeviceName[] = "deviceName";
constexpr char kDeviceName[] = "deviceName";
// Salt for advertisement.
const std::vector<uint8_t> kSalt(Advertisement::kSaltSize, 0);
constexpr std::array<uint8_t, Advertisement::kSaltSize> kSalt = {};
// Key for encrypting personal info metadata.
static const std::vector<uint8_t> kEncryptedMetadataKey(
Advertisement::kMetadataEncryptionKeyHashByteSize,
0);
const nearby_share::mojom::ShareTargetType kDeviceType =
constexpr std::array<uint8_t, Advertisement::kMetadataEncryptionKeyHashByteSize>
kEncryptedMetadataKey = {};
constexpr nearby_share::mojom::ShareTargetType kDeviceType =
nearby_share::mojom::ShareTargetType::kPhone;
class AdvertisementDecoderTest : public testing::Test {

@ -8,6 +8,7 @@
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/functional/callback.h"
#include "chrome/services/sharing/nearby/decoder/advertisement_decoder.h"
@ -238,7 +239,10 @@ void NearbySharingDecoder::DecodeAdvertisement(
}
std::move(callback).Run(mojom::Advertisement::New(
advertisement->salt(), advertisement->encrypted_metadata_key(),
std::vector<uint8_t>(advertisement->salt().begin(),
advertisement->salt().end()),
std::vector<uint8_t>(advertisement->encrypted_metadata_key().begin(),
advertisement->encrypted_metadata_key().end()),
advertisement->device_type(), advertisement->device_name()));
}

@ -24,22 +24,22 @@ namespace sharing {
namespace {
const char kDeviceName[] = "deviceName";
constexpr char kDeviceName[] = "deviceName";
// Salt for advertisement.
const std::vector<uint8_t> kSalt(Advertisement::kSaltSize, 0);
constexpr std::array<uint8_t, Advertisement::kSaltSize> kSalt = {};
// Key for encrypting personal info metadata.
static const std::vector<uint8_t> kEncryptedMetadataKey(
Advertisement::kMetadataEncryptionKeyHashByteSize,
0);
const nearby_share::mojom::ShareTargetType kDeviceType =
constexpr std::array<uint8_t, Advertisement::kMetadataEncryptionKeyHashByteSize>
kEncryptedMetadataKey = {};
constexpr nearby_share::mojom::ShareTargetType kDeviceType =
nearby_share::mojom::ShareTargetType::kPhone;
void ExpectEquals(const Advertisement& self,
const mojom::AdvertisementPtr& other) {
EXPECT_EQ(self.device_type(), other->device_type);
EXPECT_EQ(self.device_name(), other->device_name);
EXPECT_EQ(self.salt(), other->salt);
EXPECT_EQ(self.encrypted_metadata_key(), other->encrypted_metadata_key);
EXPECT_EQ(self.salt(), base::span(other->salt));
EXPECT_EQ(self.encrypted_metadata_key(),
base::span(other->encrypted_metadata_key));
}
void ExpectFrameContainsIntroduction(

@ -43,26 +43,11 @@ namespace sharing {
// static
std::unique_ptr<Advertisement> Advertisement::NewInstance(
std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_metadata_key,
base::span<const uint8_t, kSaltSize> salt,
base::span<const uint8_t, kMetadataEncryptionKeyHashByteSize>
encrypted_metadata_key,
nearby_share::mojom::ShareTargetType device_type,
std::optional<std::string> device_name) {
if (salt.size() != sharing::Advertisement::kSaltSize) {
LOG(ERROR) << "Failed to create advertisement because the salt did "
"not match the expected length "
<< salt.size();
return nullptr;
}
if (encrypted_metadata_key.size() !=
sharing::Advertisement::kMetadataEncryptionKeyHashByteSize) {
LOG(ERROR) << "Failed to create advertisement because the encrypted "
"metadata key did "
"not match the expected length "
<< encrypted_metadata_key.size();
return nullptr;
}
if (device_name && device_name->size() > UINT8_MAX) {
LOG(ERROR) << "Failed to create advertisement because device name "
"was over UINT8_MAX: "
@ -102,15 +87,18 @@ std::vector<uint8_t> Advertisement::ToEndpointInfo() {
}
// private
Advertisement::Advertisement(int version,
std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_metadata_key,
nearby_share::mojom::ShareTargetType device_type,
std::optional<std::string> device_name)
Advertisement::Advertisement(
int version,
base::span<const uint8_t, kSaltSize> salt,
base::span<const uint8_t, kMetadataEncryptionKeyHashByteSize>
encrypted_metadata_key,
nearby_share::mojom::ShareTargetType device_type,
std::optional<std::string> device_name)
: version_(version),
salt_(std::move(salt)),
encrypted_metadata_key_(std::move(encrypted_metadata_key)),
device_type_(device_type),
device_name_(std::move(device_name)) {}
device_name_(std::move(device_name)) {
base::span(salt_).copy_from(salt);
base::span(encrypted_metadata_key_).copy_from(encrypted_metadata_key);
}
} // namespace sharing

@ -7,11 +7,13 @@
#include <stdint.h>
#include <array>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "base/containers/span.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_share_target_types.mojom.h"
namespace sharing {
@ -22,9 +24,13 @@ namespace sharing {
// a missing device name indicates the advertisement is contacts-only.
class Advertisement {
public:
static constexpr uint8_t kSaltSize = 2;
static constexpr uint8_t kMetadataEncryptionKeyHashByteSize = 14;
static std::unique_ptr<Advertisement> NewInstance(
std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_metadata_key,
base::span<const uint8_t, kSaltSize> salt,
base::span<const uint8_t, kMetadataEncryptionKeyHashByteSize>
encrypted_metadata_key,
nearby_share::mojom::ShareTargetType device_type,
std::optional<std::string> device_name);
@ -36,8 +42,9 @@ class Advertisement {
std::vector<uint8_t> ToEndpointInfo();
int version() const { return version_; }
const std::vector<uint8_t>& salt() const { return salt_; }
const std::vector<uint8_t>& encrypted_metadata_key() const {
base::span<const uint8_t, kSaltSize> salt() const { return salt_; }
base::span<const uint8_t, kMetadataEncryptionKeyHashByteSize>
encrypted_metadata_key() const {
return encrypted_metadata_key_;
}
nearby_share::mojom::ShareTargetType device_type() const {
@ -46,13 +53,11 @@ class Advertisement {
const std::optional<std::string>& device_name() const { return device_name_; }
bool HasDeviceName() const { return device_name_.has_value(); }
static const uint8_t kSaltSize = 2;
static const uint8_t kMetadataEncryptionKeyHashByteSize = 14;
private:
Advertisement(int version,
std::vector<uint8_t> salt,
std::vector<uint8_t> encrypted_metadata_key,
base::span<const uint8_t, kSaltSize> salt,
base::span<const uint8_t, kMetadataEncryptionKeyHashByteSize>
encrypted_metadata_key,
nearby_share::mojom::ShareTargetType device_type,
std::optional<std::string> device_name);
@ -62,13 +67,14 @@ class Advertisement {
// Random bytes that were used as salt during encryption of public certificate
// metadata.
std::vector<uint8_t> salt_;
std::array<uint8_t, kSaltSize> salt_;
// An encrypted symmetric key that was used to encrypt public certificate
// metadata, including an account identifier signifying the remote device.
// The key can be decrypted using |salt| and the corresponding public
// certificate's secret/authenticity key.
std::vector<uint8_t> encrypted_metadata_key_;
std::array<uint8_t, kMetadataEncryptionKeyHashByteSize>
encrypted_metadata_key_;
// The type of device that the advertisement identifies.
nearby_share::mojom::ShareTargetType device_type_;
@ -79,4 +85,4 @@ class Advertisement {
} // namespace sharing
#endif // CHROME_SERVICES_SHARING_PUBLIC_CPP_ADVERTISEMENT_H_
#endif // CHROME_SERVICES_SHARING_PUBLIC_CPP_ADVERTISEMENT_H_

@ -2,12 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/services/sharing/public/cpp/advertisement.h"
#include <stdint.h>
#include <array>
#include <memory>
#include <string>
#include <vector>
#include "chrome/services/sharing/public/cpp/advertisement.h"
#include "base/strings/strcat.h"
#include "base/test/task_environment.h"
@ -18,14 +19,13 @@ namespace sharing {
namespace {
const char kDeviceName[] = "deviceName";
constexpr char kDeviceName[] = "deviceName";
// Salt for advertisement.
const std::vector<uint8_t> kSalt(Advertisement::kSaltSize, 0);
constexpr std::array<uint8_t, Advertisement::kSaltSize> kSalt = {};
// Key for encrypting personal info metadata.
static const std::vector<uint8_t> kEncryptedMetadataKey(
Advertisement::kMetadataEncryptionKeyHashByteSize,
0);
const nearby_share::mojom::ShareTargetType kDeviceType =
constexpr std::array<uint8_t, Advertisement::kMetadataEncryptionKeyHashByteSize>
kEncryptedMetadataKey = {};
constexpr nearby_share::mojom::ShareTargetType kDeviceType =
nearby_share::mojom::ShareTargetType::kPhone;
} // namespace
@ -53,16 +53,4 @@ TEST(AdvertisementTest, CreateNewInstance) {
EXPECT_EQ(kSalt, advertisement->salt());
}
TEST(AdvertisementTest, CreateNewInstanceWithWrongSaltSize) {
EXPECT_FALSE(sharing::Advertisement::NewInstance(
/* salt= */ std::vector<uint8_t>(5, 5), kEncryptedMetadataKey,
kDeviceType, kDeviceName));
}
TEST(AdvertisementTest, CreateNewInstanceWithWrongAccountIdentifierSize) {
EXPECT_FALSE(sharing::Advertisement::NewInstance(
kSalt, /* encrypted_metadata_key= */ std::vector<uint8_t>(2, 1),
kDeviceType, kDeviceName));
}
} // namespace sharing

@ -90,7 +90,7 @@ class COMPONENT_EXPORT(ENTERPRISE_OBFUSCATION) DownloadObfuscator {
private:
// Shared members.
std::vector<uint8_t> nonce_prefix_;
std::vector<uint8_t> derived_key_;
std::array<uint8_t, kKeySize> derived_key_;
uint32_t chunk_counter_ = 0;
int64_t total_overhead_ = 0;
std::unique_ptr<crypto::SecureHash> unobfuscated_hash_;

@ -4,6 +4,7 @@
#include "components/enterprise/obfuscation/core/utils.h"
#include <array>
#include <utility>
#include "base/containers/span.h"
@ -20,8 +21,11 @@
namespace enterprise_obfuscation {
HeaderData::HeaderData() = default;
HeaderData::HeaderData(std::vector<uint8_t> key, std::vector<uint8_t> prefix)
: derived_key(std::move(key)), nonce_prefix(std::move(prefix)) {}
HeaderData::HeaderData(base::span<const uint8_t, kKeySize> key,
std::vector<uint8_t> prefix)
: nonce_prefix(std::move(prefix)) {
base::span(derived_key).copy_from(key);
}
HeaderData::HeaderData(const HeaderData& other) = default;
HeaderData& HeaderData::operator=(const HeaderData& other) = default;
@ -70,7 +74,7 @@ bool IsFileObfuscationEnabled() {
}
base::expected<std::vector<uint8_t>, Error> CreateHeader(
std::vector<uint8_t>* derived_key,
std::array<uint8_t, kKeySize>* derived_key,
std::vector<uint8_t>* nonce_prefix) {
if (!IsFileObfuscationEnabled()) {
return base::unexpected(Error::kDisabled);
@ -90,8 +94,8 @@ base::expected<std::vector<uint8_t>, Error> CreateHeader(
header.insert(header.end(), salt.begin(), salt.end());
// Generate file-specific key.
*derived_key = crypto::HkdfSha256(GetSymmetricKey(), salt,
base::span<uint8_t>(), kKeySize);
*derived_key = crypto::HkdfSha256<kKeySize>(GetSymmetricKey(), salt,
base::span<uint8_t>());
// Generate nonce prefix.
*nonce_prefix = crypto::RandBytesAsVector(kNoncePrefixSize);
@ -170,8 +174,8 @@ base::expected<HeaderData, Error> GetHeaderData(
const auto& [salt, nonce_prefix] = header.split_at<kSaltSize>();
// Generate file-specific key.
std::vector<uint8_t> derived_key =
crypto::HkdfSha256(GetSymmetricKey(), salt, {}, kKeySize);
std::array<uint8_t, kKeySize> derived_key =
crypto::HkdfSha256<kKeySize>(GetSymmetricKey(), salt, {});
return base::ok(
HeaderData(std::move(derived_key), base::ToVector(nonce_prefix)));

@ -5,6 +5,7 @@
#ifndef COMPONENTS_ENTERPRISE_OBFUSCATION_CORE_UTILS_H_
#define COMPONENTS_ENTERPRISE_OBFUSCATION_CORE_UTILS_H_
#include <array>
#include <vector>
#include "base/component_export.h"
@ -62,7 +63,7 @@ enum class Error {
// The header structure is: size of header (1 byte) | salt | noncePrefix.
COMPONENT_EXPORT(ENTERPRISE_OBFUSCATION)
base::expected<std::vector<uint8_t>, Error> CreateHeader(
std::vector<uint8_t>* derived_key,
std::array<uint8_t, kKeySize>* derived_key,
std::vector<uint8_t>* nonce_prefix);
// Obfuscate data chunk using crypto::Aead
@ -88,7 +89,8 @@ base::expected<size_t, Error> GetObfuscatedChunkSize(
// header.
struct COMPONENT_EXPORT(ENTERPRISE_OBFUSCATION) HeaderData {
HeaderData();
HeaderData(std::vector<uint8_t> key, std::vector<uint8_t> prefix);
HeaderData(base::span<const uint8_t, kKeySize> key,
std::vector<uint8_t> prefix);
HeaderData(const HeaderData& other);
HeaderData& operator=(const HeaderData& other);
@ -98,7 +100,7 @@ struct COMPONENT_EXPORT(ENTERPRISE_OBFUSCATION) HeaderData {
~HeaderData();
std::vector<uint8_t> derived_key;
std::array<uint8_t, kKeySize> derived_key;
std::vector<uint8_t> nonce_prefix;
};

@ -19,7 +19,7 @@ namespace {
// Helper function to divide data in chunks of random sizes.
void ObfuscateTestDataInChunks(base::span<const uint8_t> test_data,
std::vector<uint8_t>& obfuscated_content) {
std::vector<uint8_t> derived_key;
std::array<uint8_t, kKeySize> derived_key;
std::vector<uint8_t> nonce_prefix;
auto header = CreateHeader(&derived_key, &nonce_prefix);
ASSERT_TRUE(header.has_value());
@ -81,7 +81,7 @@ TEST_P(ObfuscationUtilsTest, ObfuscateAndDeobfuscateSingleDataChunk) {
// Obfuscate the data chunk.
std::vector<uint8_t> test_data = base::RandBytesAsVector(test_data_size());
std::vector<uint8_t> derived_key;
std::array<uint8_t, kKeySize> derived_key;
std::vector<uint8_t> nonce_prefix;
auto header = CreateHeader(&derived_key, &nonce_prefix);
constexpr uint32_t kInitialChunkCounter = 0;

@ -215,9 +215,9 @@ void SecretPortalKeyProvider::ReceivedSecret() {
return Finalize(InitStatus::kEmptySecret);
}
auto hashed = crypto::HkdfSha256(
auto hashed = crypto::HkdfSha256<Encryptor::Key::kAES256GCMKeySize>(
base::span(secret_), base::as_byte_span(kSaltForHkdf),
base::as_byte_span(kInfoForHkdf), Encryptor::Key::kAES256GCMKeySize);
base::as_byte_span(kInfoForHkdf));
secret_.clear();
Encryptor::Key derived_key(hashed, mojom::Algorithm::kAES256GCM);

@ -5,6 +5,7 @@
#include "components/trusted_vault/securebox.h"
#include <algorithm>
#include <array>
#include <cstdint>
#include <memory>
#include <string>
@ -115,7 +116,7 @@ bssl::UniquePtr<EC_KEY> GenerateECKey(
// computes the EC-DH secret. Appends the |shared_secret|, and computes HKDF of
// that. |public_key| and |private_key| might be null, but if either of them is
// not null, other must be not null as well. |shared_secret| may be empty.
std::vector<uint8_t> SecureBoxComputeSecret(
std::array<uint8_t, kAES128KeyLength> SecureBoxComputeSecret(
const EC_KEY* private_key,
const EC_POINT* public_key,
base::span<const uint8_t> shared_secret,
@ -135,8 +136,8 @@ std::vector<uint8_t> SecureBoxComputeSecret(
}
std::vector<uint8_t> key_material = ConcatBytes({dh_secret, shared_secret});
return crypto::HkdfSha256(key_material, kHkdfSalt, StringToBytes(hkdf_info),
kAES128KeyLength);
return crypto::HkdfSha256<kAES128KeyLength>(key_material, kHkdfSalt,
StringToBytes(hkdf_info));
}
// This function implements AES-GCM, using AES-128, a 96-bit nonce, and 128-bit
@ -248,7 +249,7 @@ std::vector<uint8_t> SecureBoxEncryptImpl(
base::span<const uint8_t> payload,
const crypto::OpenSSLErrStackTracer& err_tracer) {
DCHECK_EQ(!!our_key_pair, !!their_public_key);
std::vector<uint8_t> secret = SecureBoxComputeSecret(
std::array<uint8_t, kAES128KeyLength> secret = SecureBoxComputeSecret(
our_key_pair, their_public_key, shared_secret, err_tracer);
std::vector<uint8_t> nonce = crypto::RandBytesAsVector(kNonceLength);
@ -298,7 +299,7 @@ std::optional<std::vector<uint8_t>> SecureBoxDecryptImpl(
offset += kECPointLength;
}
std::vector<uint8_t> secret_key = SecureBoxComputeSecret(
std::array<uint8_t, kAES128KeyLength> secret_key = SecureBoxComputeSecret(
our_private_key, their_ec_public_key_point, shared_secret, err_tracer);
base::span<const uint8_t> nonce =

@ -61,6 +61,8 @@ constexpr std::string_view kAadWebauthnCredentialSpecificsPrivateKey = "";
// https://www.w3.org/TR/webauthn-2/#signature-counter
constexpr uint8_t kSignatureCounter[4] = {0};
constexpr size_t kEncryptionSecretSize = 32;
struct PasskeyComparator {
bool operator()(const sync_pb::WebauthnCredentialSpecifics& a,
const sync_pb::WebauthnCredentialSpecifics& b) const {
@ -88,15 +90,14 @@ bool EncryptAes256Gcm(base::span<const uint8_t> key,
return aead.Seal(plaintext, nonce, aad, ciphertext);
}
std::vector<uint8_t> DerivePasskeyEncryptionSecret(
std::array<uint8_t, kEncryptionSecretSize> DerivePasskeyEncryptionSecret(
base::span<const uint8_t> trusted_vault_key) {
constexpr std::string_view kHkdfInfo =
"KeychainApplicationKey:gmscore_module:com.google.android.gms.fido";
constexpr size_t kEncryptionSecretSize = 32u;
return crypto::HkdfSha256(trusted_vault_key,
/*salt=*/base::span<const uint8_t>(),
base::as_bytes(base::span(kHkdfInfo)),
kEncryptionSecretSize);
return crypto::HkdfSha256<kEncryptionSecretSize>(
trusted_vault_key,
/*salt=*/base::span<const uint8_t>(),
base::as_bytes(base::span(kHkdfInfo)));
}
} // namespace

@ -7,10 +7,9 @@
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "base/check.h"
#include "crypto/hmac.h"
#include "third_party/boringssl/src/include/openssl/digest.h"
#include "third_party/boringssl/src/include/openssl/hkdf.h"
@ -31,17 +30,4 @@ std::string HkdfSha256(std::string_view secret,
return key;
}
std::vector<uint8_t> HkdfSha256(base::span<const uint8_t> secret,
base::span<const uint8_t> salt,
base::span<const uint8_t> info,
size_t derived_key_size) {
std::vector<uint8_t> ret;
ret.resize(derived_key_size);
int result =
::HKDF(ret.data(), derived_key_size, EVP_sha256(), secret.data(),
secret.size(), salt.data(), salt.size(), info.data(), info.size());
DCHECK(result);
return ret;
}
} // namespace crypto

@ -6,13 +6,17 @@
#define CRYPTO_HKDF_H_
#include <stddef.h>
#include <stdint.h>
#include <array>
#include <string>
#include <string_view>
#include <vector>
#include "base/check.h"
#include "base/containers/span.h"
#include "crypto/crypto_export.h"
#include "third_party/boringssl/src/include/openssl/digest.h"
#include "third_party/boringssl/src/include/openssl/hkdf.h"
namespace crypto {
@ -22,11 +26,17 @@ std::string HkdfSha256(std::string_view secret,
std::string_view info,
size_t derived_key_size);
CRYPTO_EXPORT
std::vector<uint8_t> HkdfSha256(base::span<const uint8_t> secret,
base::span<const uint8_t> salt,
base::span<const uint8_t> info,
size_t derived_key_size);
template <size_t KeySize>
std::array<uint8_t, KeySize> HkdfSha256(base::span<const uint8_t> secret,
base::span<const uint8_t> salt,
base::span<const uint8_t> info) {
std::array<uint8_t, KeySize> ret;
int result =
::HKDF(ret.data(), KeySize, EVP_sha256(), secret.data(), secret.size(),
salt.data(), salt.size(), info.data(), info.size());
DCHECK(result);
return ret;
}
} // namespace crypto

@ -4,6 +4,7 @@
#include "device/fido/cable/fido_cable_handshake_handler.h"
#include <array>
#include <string_view>
#include <tuple>
#include <utility>
@ -163,24 +164,23 @@ bool FidoCableV1HandshakeHandler::ValidateAuthenticatorHandshakeMessage(
}
cable_device_->SetV1EncryptionData(
*base::as_byte_span(
GetEncryptionKeyAfterSuccessfulHandshake(*sized_nonce_span))
.to_fixed_extent<32>(),
base::as_byte_span(
GetEncryptionKeyAfterSuccessfulHandshake(*sized_nonce_span)),
nonce_);
return true;
}
std::vector<uint8_t>
std::array<uint8_t, 32>
FidoCableV1HandshakeHandler::GetEncryptionKeyAfterSuccessfulHandshake(
base::span<const uint8_t, 16> authenticator_random_nonce) const {
std::vector<uint8_t> nonce_message;
fido_parsing_utils::Append(&nonce_message, nonce_);
fido_parsing_utils::Append(&nonce_message, client_session_random_);
fido_parsing_utils::Append(&nonce_message, authenticator_random_nonce);
return crypto::HkdfSha256(session_pre_key_, crypto::SHA256Hash(nonce_message),
kCableDeviceEncryptionKeyInfo,
/*derived_key_length=*/32);
return crypto::HkdfSha256<32>(session_pre_key_,
crypto::SHA256Hash(nonce_message),
kCableDeviceEncryptionKeyInfo);
}
} // namespace device

@ -64,7 +64,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableV1HandshakeHandler
FRIEND_TEST_ALL_PREFIXES(FidoCableHandshakeHandlerTest,
HandshakeFailWithIncorrectAuthenticatorResponse);
std::vector<uint8_t> GetEncryptionKeyAfterSuccessfulHandshake(
std::array<uint8_t, 32> GetEncryptionKeyAfterSuccessfulHandshake(
base::span<const uint8_t, 16> authenticator_random_nonce) const;
const raw_ptr<FidoCableDevice> cable_device_;

@ -125,15 +125,15 @@ constexpr char kIncorrectHandshakeKey[] = "INCORRECT_HANDSHAKE_KEY_12345678";
// factors (i.e. authenticator session random, session pre key, and nonce) are
// |kAuthenticatorSessionRandom|, |kTestSessionPreKey|, and |kTestNonce|,
// respectively.
std::vector<uint8_t> GetExpectedEncryptionKey(
std::array<uint8_t, 32> GetExpectedEncryptionKey(
base::span<const uint8_t> client_random_nonce) {
std::vector<uint8_t> nonce_message =
fido_parsing_utils::Materialize(kTestNonce);
fido_parsing_utils::Append(&nonce_message, client_random_nonce);
fido_parsing_utils::Append(&nonce_message, kAuthenticatorSessionRandom);
return crypto::HkdfSha256(kTestSessionPreKey,
crypto::SHA256Hash(nonce_message),
kCableDeviceEncryptionKeyInfo, 32);
return crypto::HkdfSha256<32>(kTestSessionPreKey,
crypto::SHA256Hash(nonce_message),
kCableDeviceEncryptionKeyInfo);
}
// Given a hello message and handshake key from the authenticator, construct