Commit b773b788 authored by Josh Nohle's avatar Josh Nohle Committed by Commit Bot

[Nearby] Add certificate methods to hash authentication token

The Nearby Share protocol uses HKDF with an authentication token, using
a certificate's secret key as the salt and a trivial info, to generate a
hash. Both parties--the receiver and sender--confirm this hash. This,
along with a standard signing/verification flow, is used to establish
an authenticated connection between devices. See go/nearby-chrome-cert.

This CL implements a hashing method for both the private and
decrypted-public certificate classes. We add tests using test vectors
that are also used in GmsCore unit tests (cl/326170193) to ensure
consistency across platforms.

Fixed: 1115320
Change-Id: I5aa9f7de5340a537a57c323a5afb97f6a83a4b1b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2351709Reviewed-by: default avatarAlex Chau <alexchau@chromium.org>
Commit-Queue: Josh Nohle <nohle@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797270}
parent ce89e654
...@@ -45,6 +45,14 @@ std::vector<uint8_t> DeriveNearbyShareKey(base::span<const uint8_t> key, ...@@ -45,6 +45,14 @@ std::vector<uint8_t> DeriveNearbyShareKey(base::span<const uint8_t> key,
new_num_bytes); 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::unique_ptr<crypto::Encryptor> CreateNearbyShareCtrEncryptor( std::unique_ptr<crypto::Encryptor> CreateNearbyShareCtrEncryptor(
const crypto::SymmetricKey* secret_key, const crypto::SymmetricKey* secret_key,
base::span<const uint8_t> salt) { base::span<const uint8_t> salt) {
......
...@@ -31,6 +31,14 @@ bool IsNearbyShareCertificateWithinValidityPeriod( ...@@ -31,6 +31,14 @@ bool IsNearbyShareCertificateWithinValidityPeriod(
base::Time not_after, base::Time not_after,
bool use_public_certificate_tolerance); bool use_public_certificate_tolerance);
// Uses HKDF to create a hash of the |authentication_token|, using the
// |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);
// Uses HKDF to generate a new key of length |new_num_bytes| from |key|. To // Uses HKDF to generate a new key of length |new_num_bytes| from |key|. To
// conform with the GmsCore implementation, trivial salt and info are used. // conform with the GmsCore implementation, trivial salt and info are used.
std::vector<uint8_t> DeriveNearbyShareKey(base::span<const uint8_t> key, std::vector<uint8_t> DeriveNearbyShareKey(base::span<const uint8_t> key,
......
...@@ -3,12 +3,22 @@ ...@@ -3,12 +3,22 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/nearby_sharing/certificates/common.h" #include "chrome/browser/nearby_sharing/certificates/common.h"
#include "base/containers/span.h"
#include "chrome/browser/nearby_sharing/certificates/constants.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_decrypted_public_certificate.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h" #include "chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h"
#include "chrome/browser/nearby_sharing/certificates/test_util.h" #include "chrome/browser/nearby_sharing/certificates/test_util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
TEST(NearbyShareCertificatesCommonTest, AuthenticationTokenHash) {
EXPECT_EQ(GetNearbyShareTestPayloadHashUsingSecretKey(),
ComputeAuthenticationTokenHash(
GetNearbyShareTestPayloadToSign(),
base::as_bytes(
base::make_span(GetNearbyShareTestSecretKey()->key()))));
}
TEST(NearbyShareCertificatesCommonTest, ValidityPeriod_PrivateCertificate) { TEST(NearbyShareCertificatesCommonTest, ValidityPeriod_PrivateCertificate) {
NearbySharePrivateCertificate cert = NearbySharePrivateCertificate cert =
GetNearbyShareTestPrivateCertificate(NearbyShareVisibility::kAllContacts); GetNearbyShareTestPrivateCertificate(NearbyShareVisibility::kAllContacts);
......
...@@ -25,9 +25,8 @@ extern const base::TimeDelta ...@@ -25,9 +25,8 @@ extern const base::TimeDelta
// rotated on the local device. // rotated on the local device.
extern const size_t kNearbyShareNumPrivateCertificates; extern const size_t kNearbyShareNumPrivateCertificates;
// The number of bytes that // The number of bytes comprising the hash of the authentication token using the
// HMAC(/*key=*/|secret_key|, /*message=*/|authentication_token|) // secret key.
// is converted to via HKDF before advertising.
extern const size_t kNearbyShareNumBytesAuthenticationTokenHash; extern const size_t kNearbyShareNumBytesAuthenticationTokenHash;
// Length of key in bytes required by AES-GCM encryption. // Length of key in bytes required by AES-GCM encryption.
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include "crypto/encryptor.h" #include "crypto/encryptor.h"
#include "crypto/hmac.h" #include "crypto/hmac.h"
#include "crypto/signature_verifier.h" #include "crypto/signature_verifier.h"
#include "crypto/symmetric_key.h"
namespace { namespace {
...@@ -161,30 +160,48 @@ NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate( ...@@ -161,30 +160,48 @@ NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate(
} }
return NearbyShareDecryptedPublicCertificate( return NearbyShareDecryptedPublicCertificate(
not_before, not_after, std::move(public_key), std::move(id), not_before, not_after, std::move(secret_key), std::move(public_key),
std::move(unencrypted_metadata)); std::move(id), std::move(unencrypted_metadata));
} }
NearbyShareDecryptedPublicCertificate::NearbyShareDecryptedPublicCertificate( NearbyShareDecryptedPublicCertificate::NearbyShareDecryptedPublicCertificate(
base::Time not_before, base::Time not_before,
base::Time not_after, base::Time not_after,
std::unique_ptr<crypto::SymmetricKey> secret_key,
std::vector<uint8_t> public_key, std::vector<uint8_t> public_key,
std::vector<uint8_t> id, std::vector<uint8_t> id,
nearbyshare::proto::EncryptedMetadata unencrypted_metadata) nearbyshare::proto::EncryptedMetadata unencrypted_metadata)
: not_before_(not_before), : not_before_(not_before),
not_after_(not_after), not_after_(not_after),
secret_key_(std::move(secret_key)),
public_key_(std::move(public_key)), public_key_(std::move(public_key)),
id_(std::move(id)), id_(std::move(id)),
unencrypted_metadata_(std::move(unencrypted_metadata)) {} unencrypted_metadata_(std::move(unencrypted_metadata)) {}
NearbyShareDecryptedPublicCertificate::NearbyShareDecryptedPublicCertificate( NearbyShareDecryptedPublicCertificate::NearbyShareDecryptedPublicCertificate(
const NearbyShareDecryptedPublicCertificate&) = default; const NearbyShareDecryptedPublicCertificate& other) {
*this = other;
}
NearbyShareDecryptedPublicCertificate& NearbyShareDecryptedPublicCertificate&
NearbyShareDecryptedPublicCertificate::operator=( NearbyShareDecryptedPublicCertificate::operator=(
const NearbyShareDecryptedPublicCertificate&) = default; const NearbyShareDecryptedPublicCertificate& other) {
if (this == &other)
return *this;
not_before_ = other.not_before_;
not_after_ = other.not_after_;
secret_key_ = crypto::SymmetricKey::Import(
crypto::SymmetricKey::Algorithm::AES, other.secret_key_->key());
public_key_ = other.public_key_;
id_ = other.id_;
unencrypted_metadata_ = other.unencrypted_metadata_;
return *this;
}
NearbyShareDecryptedPublicCertificate::NearbyShareDecryptedPublicCertificate( NearbyShareDecryptedPublicCertificate::NearbyShareDecryptedPublicCertificate(
NearbyShareDecryptedPublicCertificate&&) = default; NearbyShareDecryptedPublicCertificate&&) = default;
NearbyShareDecryptedPublicCertificate& NearbyShareDecryptedPublicCertificate&
NearbyShareDecryptedPublicCertificate::operator=( NearbyShareDecryptedPublicCertificate::operator=(
NearbyShareDecryptedPublicCertificate&&) = default; NearbyShareDecryptedPublicCertificate&&) = default;
...@@ -206,3 +223,11 @@ bool NearbyShareDecryptedPublicCertificate::VerifySignature( ...@@ -206,3 +223,11 @@ bool NearbyShareDecryptedPublicCertificate::VerifySignature(
return verifier.VerifyFinal(); return verifier.VerifyFinal();
} }
std::vector<uint8_t>
NearbyShareDecryptedPublicCertificate::HashAuthenticationToken(
base::span<const uint8_t> authentication_token) const {
return ComputeAuthenticationTokenHash(
authentication_token,
base::as_bytes(base::make_span(secret_key_->key())));
}
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h" #include "chrome/browser/nearby_sharing/certificates/nearby_share_encrypted_metadata_key.h"
#include "chrome/browser/nearby_sharing/proto/encrypted_metadata.pb.h" #include "chrome/browser/nearby_sharing/proto/encrypted_metadata.pb.h"
#include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h" #include "chrome/browser/nearby_sharing/proto/rpc_resources.pb.h"
#include "crypto/symmetric_key.h"
// Stores decrypted metadata and crypto keys for the remote device that uploaded // Stores decrypted metadata and crypto keys for the remote device that uploaded
// this certificate to the Nearby Share server. Use DecryptPublicCertificate() // this certificate to the Nearby Share server. Use DecryptPublicCertificate()
...@@ -32,9 +33,9 @@ class NearbyShareDecryptedPublicCertificate { ...@@ -32,9 +33,9 @@ class NearbyShareDecryptedPublicCertificate {
const NearbyShareEncryptedMetadataKey& encrypted_metadata_key); const NearbyShareEncryptedMetadataKey& encrypted_metadata_key);
NearbyShareDecryptedPublicCertificate( NearbyShareDecryptedPublicCertificate(
const NearbyShareDecryptedPublicCertificate&); const NearbyShareDecryptedPublicCertificate& other);
NearbyShareDecryptedPublicCertificate& operator=( NearbyShareDecryptedPublicCertificate& operator=(
const NearbyShareDecryptedPublicCertificate&); const NearbyShareDecryptedPublicCertificate& other);
NearbyShareDecryptedPublicCertificate( NearbyShareDecryptedPublicCertificate(
NearbyShareDecryptedPublicCertificate&&); NearbyShareDecryptedPublicCertificate&&);
NearbyShareDecryptedPublicCertificate& operator=( NearbyShareDecryptedPublicCertificate& operator=(
...@@ -54,10 +55,17 @@ class NearbyShareDecryptedPublicCertificate { ...@@ -54,10 +55,17 @@ class NearbyShareDecryptedPublicCertificate {
bool VerifySignature(base::span<const uint8_t> payload, bool VerifySignature(base::span<const uint8_t> payload,
base::span<const uint8_t> signature) const; base::span<const uint8_t> signature) const;
// 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;
private: private:
NearbyShareDecryptedPublicCertificate( NearbyShareDecryptedPublicCertificate(
base::Time not_before, base::Time not_before,
base::Time not_after, base::Time not_after,
std::unique_ptr<crypto::SymmetricKey> secret_key,
std::vector<uint8_t> public_key, std::vector<uint8_t> public_key,
std::vector<uint8_t> id, std::vector<uint8_t> id,
nearbyshare::proto::EncryptedMetadata unencrypted_metadata); nearbyshare::proto::EncryptedMetadata unencrypted_metadata);
...@@ -68,6 +76,10 @@ class NearbyShareDecryptedPublicCertificate { ...@@ -68,6 +76,10 @@ class NearbyShareDecryptedPublicCertificate {
base::Time not_before_; base::Time not_before_;
base::Time not_after_; base::Time not_after_;
// A 32-byte AES key that was used for metadata key and metadata decryption.
// Also, used to generate an authentication token hash.
std::unique_ptr<crypto::SymmetricKey> secret_key_;
// A P-256 public key used for verification. The bytes comprise a DER-encoded // A P-256 public key used for verification. The bytes comprise a DER-encoded
// ASN.1 SubjectPublicKeyInfo from the X.509 specification (RFC 5280). // ASN.1 SubjectPublicKeyInfo from the X.509 specification (RFC 5280).
std::vector<uint8_t> public_key_; std::vector<uint8_t> public_key_;
......
...@@ -95,3 +95,12 @@ TEST(NearbyShareDecryptedPublicCertificateTest, Verify_WrongSignature) { ...@@ -95,3 +95,12 @@ TEST(NearbyShareDecryptedPublicCertificateTest, Verify_WrongSignature) {
cert->VerifySignature(GetNearbyShareTestPayloadToSign(), cert->VerifySignature(GetNearbyShareTestPayloadToSign(),
/*signature=*/base::span<const uint8_t>())); /*signature=*/base::span<const uint8_t>()));
} }
TEST(NearbyShareDecryptedPublicCertificateTest, HashAuthenticationToken) {
base::Optional<NearbyShareDecryptedPublicCertificate> cert =
NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate(
GetNearbyShareTestPublicCertificate(),
GetNearbyShareTestEncryptedMetadataKey());
EXPECT_EQ(GetNearbyShareTestPayloadHashUsingSecretKey(),
cert->HashAuthenticationToken(GetNearbyShareTestPayloadToSign()));
}
...@@ -248,6 +248,13 @@ base::Optional<std::vector<uint8_t>> NearbySharePrivateCertificate::Sign( ...@@ -248,6 +248,13 @@ base::Optional<std::vector<uint8_t>> NearbySharePrivateCertificate::Sign(
return signature; return signature;
} }
std::vector<uint8_t> NearbySharePrivateCertificate::HashAuthenticationToken(
base::span<const uint8_t> authentication_token) const {
return ComputeAuthenticationTokenHash(
authentication_token,
base::as_bytes(base::make_span(secret_key_->key())));
}
base::Optional<nearbyshare::proto::PublicCertificate> base::Optional<nearbyshare::proto::PublicCertificate>
NearbySharePrivateCertificate::ToPublicCertificate() { NearbySharePrivateCertificate::ToPublicCertificate() {
std::vector<uint8_t> public_key; std::vector<uint8_t> public_key;
......
...@@ -87,6 +87,12 @@ class NearbySharePrivateCertificate { ...@@ -87,6 +87,12 @@ class NearbySharePrivateCertificate {
base::Optional<std::vector<uint8_t>> Sign( base::Optional<std::vector<uint8_t>> Sign(
base::span<const uint8_t> payload) const; base::span<const uint8_t> payload) const;
// 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;
// Converts this private certificate to a public certificate proto that can be // Converts this private certificate to a public certificate proto that can be
// shared with select contacts. Returns base::nullopt if the conversion was // shared with select contacts. Returns base::nullopt if the conversion was
// unsuccessful. // unsuccessful.
...@@ -132,8 +138,9 @@ class NearbySharePrivateCertificate { ...@@ -132,8 +138,9 @@ class NearbySharePrivateCertificate {
std::unique_ptr<crypto::ECPrivateKey> key_pair_; std::unique_ptr<crypto::ECPrivateKey> key_pair_;
// A 32-byte AES key used, along with a salt, to encrypt the // A 32-byte AES key used, along with a salt, to encrypt the
// |metadata_encryption_key_|, after which it can be safely advertised. // |metadata_encryption_key_|, after which it can be safely advertised. Also,
// Included in the public certificate. // used to generate an authentication token hash. Included in the public
// certificate.
std::unique_ptr<crypto::SymmetricKey> secret_key_; std::unique_ptr<crypto::SymmetricKey> secret_key_;
// A 14-byte symmetric key used to encrypt |unencrypted_metadata_|. Not // A 14-byte symmetric key used to encrypt |unencrypted_metadata_|. Not
......
...@@ -143,3 +143,11 @@ TEST(NearbySharePrivateCertificateTest, SignVerifyRoundtrip) { ...@@ -143,3 +143,11 @@ TEST(NearbySharePrivateCertificateTest, SignVerifyRoundtrip) {
EXPECT_TRUE(decrypted_public_certificate->VerifySignature( EXPECT_TRUE(decrypted_public_certificate->VerifySignature(
GetNearbyShareTestPayloadToSign(), *signature)); GetNearbyShareTestPayloadToSign(), *signature));
} }
TEST(NearbySharePrivateCertificateTest, HashAuthenticationToken) {
NearbySharePrivateCertificate private_certificate =
GetNearbyShareTestPrivateCertificate(NearbyShareVisibility::kAllContacts);
EXPECT_EQ(GetNearbyShareTestPayloadHashUsingSecretKey(),
private_certificate.HashAuthenticationToken(
GetNearbyShareTestPayloadToSign()));
}
...@@ -110,6 +110,12 @@ const uint8_t kTestSampleSignature[] = { ...@@ -110,6 +110,12 @@ const uint8_t kTestSampleSignature[] = {
// end s // 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 kTestNotBeforeMillis = 1881702000000;
const int64_t kTestValidityOffsetMillis = 1800000; // 30 minutes const int64_t kTestValidityOffsetMillis = 1800000; // 30 minutes
...@@ -215,6 +221,13 @@ const std::vector<uint8_t>& GetNearbyShareTestSampleSignature() { ...@@ -215,6 +221,13 @@ const std::vector<uint8_t>& GetNearbyShareTestSampleSignature() {
return *signature; return *signature;
} }
const std::vector<uint8_t>& GetNearbyShareTestPayloadHashUsingSecretKey() {
static const base::NoDestructor<std::vector<uint8_t>> hash(
std::begin(kTestPayloadHashUsingSecretKey),
std::end(kTestPayloadHashUsingSecretKey));
return *hash;
}
NearbySharePrivateCertificate GetNearbyShareTestPrivateCertificate( NearbySharePrivateCertificate GetNearbyShareTestPrivateCertificate(
NearbyShareVisibility visibility) { NearbyShareVisibility visibility) {
NearbySharePrivateCertificate cert( NearbySharePrivateCertificate cert(
......
...@@ -38,6 +38,7 @@ const std::vector<uint8_t>& GetNearbyShareTestEncryptedMetadata(); ...@@ -38,6 +38,7 @@ const std::vector<uint8_t>& GetNearbyShareTestEncryptedMetadata();
const std::vector<uint8_t>& GetNearbyShareTestPayloadToSign(); const std::vector<uint8_t>& GetNearbyShareTestPayloadToSign();
const std::vector<uint8_t>& GetNearbyShareTestSampleSignature(); const std::vector<uint8_t>& GetNearbyShareTestSampleSignature();
const std::vector<uint8_t>& GetNearbyShareTestPayloadHashUsingSecretKey();
NearbySharePrivateCertificate GetNearbyShareTestPrivateCertificate( NearbySharePrivateCertificate GetNearbyShareTestPrivateCertificate(
NearbyShareVisibility visibility); NearbyShareVisibility visibility);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment