Commit 4d4269c3 authored by Adam Langley's avatar Adam Langley Committed by Commit Bot

device/fido: add caBLE v2 handshake infrastructure.

This change isn't intended to have any semantic effect but adds
infrastructure for a caBLE v2 handshake based around Noise NNpsk0. The
implementation has been run against the Noise Explorer implementation[1]
to confirm that it matches the spec.

[1] https://noiseexplorer.com/patterns/NNpsk0/

Change-Id: Iff92bcddfb5b4d07280ef4df5331e471d18bdd18
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1752936
Commit-Queue: Adam Langley <agl@chromium.org>
Reviewed-by: default avatarMartin Kreichgauer <martinkr@google.com>
Cr-Commit-Position: refs/heads/master@{#688339}
parent dae91f52
...@@ -305,6 +305,21 @@ fuzzer_test("fido_cable_handshake_handler_fuzzer") { ...@@ -305,6 +305,21 @@ fuzzer_test("fido_cable_handshake_handler_fuzzer") {
libfuzzer_options = [ "max_len=2048" ] libfuzzer_options = [ "max_len=2048" ]
} }
fuzzer_test("fido_cable_handshake_handler_v2_fuzzer") {
sources = [
"cable/fido_cable_handshake_handler_v2_fuzzer.cc",
]
deps = [
":fido",
"//base",
"//base/test:test_support",
"//device/bluetooth:mocks",
"//testing/gmock",
"//testing/gtest",
]
libfuzzer_options = [ "max_len=2048" ]
}
is_linux_without_udev = is_linux && !use_udev is_linux_without_udev = is_linux && !use_udev
source_set("test_support") { source_set("test_support") {
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "device/fido/ble/fido_ble_uuids.h" #include "device/fido/ble/fido_ble_uuids.h"
#include "device/fido/cable/fido_cable_device.h" #include "device/fido/cable/fido_cable_device.h"
#include "device/fido/cable/fido_cable_handshake_handler.h" #include "device/fido/cable/fido_cable_handshake_handler.h"
#include "device/fido/features.h"
#include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_parsing_utils.h"
#include "third_party/boringssl/src/include/openssl/digest.h" #include "third_party/boringssl/src/include/openssl/digest.h"
#include "third_party/boringssl/src/include/openssl/hkdf.h" #include "third_party/boringssl/src/include/openssl/hkdf.h"
...@@ -189,13 +190,39 @@ FidoCableDiscovery::~FidoCableDiscovery() { ...@@ -189,13 +190,39 @@ FidoCableDiscovery::~FidoCableDiscovery() {
advertisement.second->Unregister(base::DoNothing(), base::DoNothing()); advertisement.second->Unregister(base::DoNothing(), base::DoNothing());
} }
std::unique_ptr<FidoCableHandshakeHandler> base::Optional<std::unique_ptr<FidoCableHandshakeHandler>>
FidoCableDiscovery::CreateHandshakeHandler( FidoCableDiscovery::CreateHandshakeHandler(
FidoCableDevice* device, FidoCableDevice* device,
base::span<const uint8_t, kCableSessionPreKeySize> session_pre_key, const CableDiscoveryData* discovery_data) {
base::span<const uint8_t, 8> nonce) { std::unique_ptr<FidoCableHandshakeHandler> handler;
return std::make_unique<FidoCableHandshakeHandler>(device, nonce, switch (discovery_data->version) {
session_pre_key); case 1: {
// Nonce is embedded as first 8 bytes of client EID.
std::array<uint8_t, 8> nonce;
const bool ok = fido_parsing_utils::ExtractArray(
discovery_data->client_eid, 0, &nonce);
DCHECK(ok);
handler.reset(new FidoCableV1HandshakeHandler(
device, nonce, discovery_data->session_pre_key));
break;
}
case 2:
if (!base::FeatureList::IsEnabled(device::kWebAuthPhoneSupport)) {
return base::nullopt;
}
handler.reset(new FidoCableV2HandshakeHandler(
device, discovery_data->session_pre_key));
break;
default:
FIDO_LOG(DEBUG) << "Dropping caBLE handshake request for unknown version "
<< discovery_data->version;
return base::nullopt;
}
return handler;
} }
void FidoCableDiscovery::DeviceAdded(BluetoothAdapter* adapter, void FidoCableDiscovery::DeviceAdded(BluetoothAdapter* adapter,
...@@ -353,17 +380,19 @@ void FidoCableDiscovery::CableDeviceFound(BluetoothAdapter* adapter, ...@@ -353,17 +380,19 @@ void FidoCableDiscovery::CableDeviceFound(BluetoothAdapter* adapter,
StopAdvertisements( StopAdvertisements(
base::BindOnce(&FidoCableDiscovery::ConductEncryptionHandshake, base::BindOnce(&FidoCableDiscovery::ConductEncryptionHandshake,
weak_factory_.GetWeakPtr(), std::move(cable_device), weak_factory_.GetWeakPtr(), std::move(cable_device),
found_cable_device_data->session_pre_key, nonce)); *found_cable_device_data));
} }
void FidoCableDiscovery::ConductEncryptionHandshake( void FidoCableDiscovery::ConductEncryptionHandshake(
std::unique_ptr<FidoCableDevice> cable_device, std::unique_ptr<FidoCableDevice> cable_device,
base::span<const uint8_t, kCableSessionPreKeySize> session_pre_key, CableDiscoveryData discovery_data) {
base::span<const uint8_t, 8> nonce) { base::Optional<std::unique_ptr<FidoCableHandshakeHandler>> handshake_handler =
auto handshake_handler = CreateHandshakeHandler(cable_device.get(), &discovery_data);
CreateHandshakeHandler(cable_device.get(), session_pre_key, nonce); if (!handshake_handler) {
auto* const handshake_handler_ptr = handshake_handler.get(); return;
cable_handshake_handlers_.emplace_back(std::move(handshake_handler)); }
auto* const handshake_handler_ptr = handshake_handler->get();
cable_handshake_handlers_.emplace_back(std::move(*handshake_handler));
handshake_handler_ptr->InitiateCableHandshake( handshake_handler_ptr->InitiateCableHandshake(
base::BindOnce(&FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage, base::BindOnce(&FidoCableDiscovery::ValidateAuthenticatorHandshakeMessage,
......
...@@ -35,10 +35,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery ...@@ -35,10 +35,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
~FidoCableDiscovery() override; ~FidoCableDiscovery() override;
protected: protected:
virtual std::unique_ptr<FidoCableHandshakeHandler> CreateHandshakeHandler( virtual base::Optional<std::unique_ptr<FidoCableHandshakeHandler>>
FidoCableDevice* device, CreateHandshakeHandler(FidoCableDevice* device,
base::span<const uint8_t, kCableSessionPreKeySize> session_pre_key, const CableDiscoveryData* discovery_data);
base::span<const uint8_t, 8> nonce);
private: private:
FRIEND_TEST_ALL_PREFIXES(FidoCableDiscoveryTest, FRIEND_TEST_ALL_PREFIXES(FidoCableDiscoveryTest,
...@@ -76,10 +75,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery ...@@ -76,10 +75,8 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableDiscovery
// |callback|. // |callback|.
void StopAdvertisements(base::OnceClosure callback); void StopAdvertisements(base::OnceClosure callback);
void CableDeviceFound(BluetoothAdapter* adapter, BluetoothDevice* device); void CableDeviceFound(BluetoothAdapter* adapter, BluetoothDevice* device);
void ConductEncryptionHandshake( void ConductEncryptionHandshake(std::unique_ptr<FidoCableDevice> cable_device,
std::unique_ptr<FidoCableDevice> device, CableDiscoveryData discovery_data);
base::span<const uint8_t, kCableSessionPreKeySize> session_pre_key,
base::span<const uint8_t, 8> nonce);
void ValidateAuthenticatorHandshakeMessage( void ValidateAuthenticatorHandshakeMessage(
std::unique_ptr<FidoCableDevice> cable_device, std::unique_ptr<FidoCableDevice> cable_device,
FidoCableHandshakeHandler* handshake_handler, FidoCableHandshakeHandler* handshake_handler,
......
...@@ -266,12 +266,12 @@ class CableMockAdapter : public MockBluetoothAdapter { ...@@ -266,12 +266,12 @@ class CableMockAdapter : public MockBluetoothAdapter {
~CableMockAdapter() override = default; ~CableMockAdapter() override = default;
}; };
class FakeHandshakeHandler : public FidoCableHandshakeHandler { class FakeHandshakeHandler : public FidoCableV1HandshakeHandler {
public: public:
FakeHandshakeHandler(FidoCableDevice* device, FakeHandshakeHandler(FidoCableDevice* device,
base::span<const uint8_t, 8> nonce, base::span<const uint8_t, 8> nonce,
base::span<const uint8_t, 32> session_pre_key) base::span<const uint8_t, 32> session_pre_key)
: FidoCableHandshakeHandler(device, nonce, session_pre_key) {} : FidoCableV1HandshakeHandler(device, nonce, session_pre_key) {}
~FakeHandshakeHandler() override = default; ~FakeHandshakeHandler() override = default;
void InitiateCableHandshake(FidoDevice::DeviceCallback callback) override { void InitiateCableHandshake(FidoDevice::DeviceCallback callback) override {
...@@ -295,12 +295,16 @@ class FakeFidoCableDiscovery : public FidoCableDiscovery { ...@@ -295,12 +295,16 @@ class FakeFidoCableDiscovery : public FidoCableDiscovery {
~FakeFidoCableDiscovery() override = default; ~FakeFidoCableDiscovery() override = default;
private: private:
std::unique_ptr<FidoCableHandshakeHandler> CreateHandshakeHandler( base::Optional<std::unique_ptr<FidoCableHandshakeHandler>>
FidoCableDevice* device, CreateHandshakeHandler(FidoCableDevice* device,
base::span<const uint8_t, kCableSessionPreKeySize> session_pre_key, const CableDiscoveryData* discovery_data) override {
base::span<const uint8_t, 8> nonce) override { // Nonce is embedded as first 8 bytes of client EID.
return std::make_unique<FakeHandshakeHandler>(device, nonce, std::array<uint8_t, 8> nonce;
session_pre_key); const bool ok =
fido_parsing_utils::ExtractArray(discovery_data->client_eid, 0, &nonce);
DCHECK(ok);
return std::make_unique<FakeHandshakeHandler>(
device, nonce, discovery_data->session_pre_key);
} }
}; };
......
...@@ -5,21 +5,30 @@ ...@@ -5,21 +5,30 @@
#include "device/fido/cable/fido_cable_handshake_handler.h" #include "device/fido/cable/fido_cable_handshake_handler.h"
#include <algorithm> #include <algorithm>
#include <tuple>
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/containers/span.h" #include "base/containers/span.h"
#include "base/strings/string_number_conversions.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "components/cbor/reader.h" #include "components/cbor/reader.h"
#include "components/cbor/values.h" #include "components/cbor/values.h"
#include "components/cbor/writer.h" #include "components/cbor/writer.h"
#include "components/device_event_log/device_event_log.h" #include "components/device_event_log/device_event_log.h"
#include "crypto/aead.h"
#include "crypto/hkdf.h" #include "crypto/hkdf.h"
#include "crypto/hmac.h" #include "crypto/hmac.h"
#include "crypto/random.h" #include "crypto/random.h"
#include "device/fido/cable/fido_cable_device.h" #include "device/fido/cable/fido_cable_device.h"
#include "device/fido/fido_constants.h" #include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_parsing_utils.h"
#include "third_party/boringssl/src/include/openssl/digest.h"
#include "third_party/boringssl/src/include/openssl/ec_key.h"
#include "third_party/boringssl/src/include/openssl/ecdh.h"
#include "third_party/boringssl/src/include/openssl/hkdf.h"
#include "third_party/boringssl/src/include/openssl/obj.h"
#include "third_party/boringssl/src/include/openssl/sha.h"
namespace device { namespace device {
...@@ -76,7 +85,9 @@ ConstructHandshakeMessage(base::StringPiece handshake_key, ...@@ -76,7 +85,9 @@ ConstructHandshakeMessage(base::StringPiece handshake_key,
} // namespace } // namespace
FidoCableHandshakeHandler::FidoCableHandshakeHandler( FidoCableHandshakeHandler::~FidoCableHandshakeHandler() {}
FidoCableV1HandshakeHandler::FidoCableV1HandshakeHandler(
FidoCableDevice* cable_device, FidoCableDevice* cable_device,
base::span<const uint8_t, 8> nonce, base::span<const uint8_t, 8> nonce,
base::span<const uint8_t, 32> session_pre_key) base::span<const uint8_t, 32> session_pre_key)
...@@ -91,9 +102,9 @@ FidoCableHandshakeHandler::FidoCableHandshakeHandler( ...@@ -91,9 +102,9 @@ FidoCableHandshakeHandler::FidoCableHandshakeHandler(
client_session_random_.size()); client_session_random_.size());
} }
FidoCableHandshakeHandler::~FidoCableHandshakeHandler() = default; FidoCableV1HandshakeHandler::~FidoCableV1HandshakeHandler() = default;
void FidoCableHandshakeHandler::InitiateCableHandshake( void FidoCableV1HandshakeHandler::InitiateCableHandshake(
FidoDevice::DeviceCallback callback) { FidoDevice::DeviceCallback callback) {
auto handshake_message = auto handshake_message =
ConstructHandshakeMessage(handshake_key_, client_session_random_); ConstructHandshakeMessage(handshake_key_, client_session_random_);
...@@ -108,7 +119,7 @@ void FidoCableHandshakeHandler::InitiateCableHandshake( ...@@ -108,7 +119,7 @@ void FidoCableHandshakeHandler::InitiateCableHandshake(
fido_parsing_utils::Materialize(*handshake_message), std::move(callback)); fido_parsing_utils::Materialize(*handshake_message), std::move(callback));
} }
bool FidoCableHandshakeHandler::ValidateAuthenticatorHandshakeMessage( bool FidoCableV1HandshakeHandler::ValidateAuthenticatorHandshakeMessage(
base::span<const uint8_t> response) { base::span<const uint8_t> response) {
crypto::HMAC hmac(crypto::HMAC::SHA256); crypto::HMAC hmac(crypto::HMAC::SHA256);
if (!hmac.Init(handshake_key_)) if (!hmac.Init(handshake_key_))
...@@ -158,7 +169,8 @@ bool FidoCableHandshakeHandler::ValidateAuthenticatorHandshakeMessage( ...@@ -158,7 +169,8 @@ bool FidoCableHandshakeHandler::ValidateAuthenticatorHandshakeMessage(
return true; return true;
} }
std::string FidoCableHandshakeHandler::GetEncryptionKeyAfterSuccessfulHandshake( std::string
FidoCableV1HandshakeHandler::GetEncryptionKeyAfterSuccessfulHandshake(
base::span<const uint8_t, 16> authenticator_random_nonce) const { base::span<const uint8_t, 16> authenticator_random_nonce) const {
std::vector<uint8_t> nonce_message; std::vector<uint8_t> nonce_message;
fido_parsing_utils::Append(&nonce_message, nonce_); fido_parsing_utils::Append(&nonce_message, nonce_);
...@@ -172,4 +184,206 @@ std::string FidoCableHandshakeHandler::GetEncryptionKeyAfterSuccessfulHandshake( ...@@ -172,4 +184,206 @@ std::string FidoCableHandshakeHandler::GetEncryptionKeyAfterSuccessfulHandshake(
kCableDeviceEncryptionKeyInfo); kCableDeviceEncryptionKeyInfo);
} }
FidoCableV2HandshakeHandler::FidoCableV2HandshakeHandler(
FidoCableDevice* cable_device,
base::span<const uint8_t, 32> session_pre_key)
: cable_device_(cable_device),
session_pre_key_(fido_parsing_utils::Materialize(session_pre_key)) {}
FidoCableV2HandshakeHandler::~FidoCableV2HandshakeHandler() {}
namespace {
// P256PointSize is the number of bytes in an X9.62 encoding of a P-256 point.
constexpr size_t P256PointSize = 65;
// HKDF2 implements the functions with the same name from Noise[1], specialized
// to the case where |num_outputs| is two.
//
// [1] https://www.noiseprotocol.org/noise.html#hash-functions
std::tuple<std::array<uint8_t, 32>, std::array<uint8_t, 32>> HKDF2(
base::span<const uint8_t, 32> ck,
base::span<const uint8_t> ikm) {
uint8_t output[32 * 2];
HKDF(output, sizeof(output), EVP_sha256(), ikm.data(), ikm.size(), ck.data(),
ck.size(), /*salt=*/nullptr, 0);
std::array<uint8_t, 32> a, b;
memcpy(a.data(), &output[0], 32);
memcpy(b.data(), &output[32], 32);
return std::make_tuple(a, b);
}
// HKDF3 implements the functions with the same name from Noise[1], specialized
// to the case where |num_outputs| is three.
//
// [1] https://www.noiseprotocol.org/noise.html#hash-functions
std::tuple<std::array<uint8_t, 32>,
std::array<uint8_t, 32>,
std::array<uint8_t, 32>>
HKDF3(base::span<const uint8_t, 32> ck, base::span<const uint8_t> ikm) {
uint8_t output[32 * 3];
HKDF(output, sizeof(output), EVP_sha256(), ikm.data(), ikm.size(), ck.data(),
ck.size(), /*salt=*/nullptr, 0);
std::array<uint8_t, 32> a, b, c;
memcpy(a.data(), &output[0], 32);
memcpy(b.data(), &output[32], 32);
memcpy(c.data(), &output[64], 32);
return std::make_tuple(a, b, c);
}
} // namespace
void FidoCableV2HandshakeHandler::InitiateCableHandshake(
FidoDevice::DeviceCallback callback) {
// See https://www.noiseprotocol.org/noise.html#the-handshakestate-object
static const char kProtocolName[] = "Noise_NNpsk0_P256_AESGCM_SHA256";
static_assert(sizeof(kProtocolName) == crypto::kSHA256Length,
"name may need padding if not HASHLEN bytes long");
static_assert(
std::tuple_size<decltype(chaining_key_)>::value == crypto::kSHA256Length,
"chaining_key_ is wrong size");
static_assert(std::tuple_size<decltype(h_)>::value == crypto::kSHA256Length,
"h_ is wrong size");
memcpy(chaining_key_.data(), kProtocolName, sizeof(kProtocolName));
h_ = chaining_key_;
static const uint8_t kPrologue[] = "caBLE QR code handshake";
MixHash(kPrologue);
MixKeyAndHash(session_pre_key_);
ephemeral_key_.reset(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
CHECK(EC_KEY_generate_key(ephemeral_key_.get()));
uint8_t ephemeral_key_public_bytes[P256PointSize];
CHECK_EQ(sizeof(ephemeral_key_public_bytes),
EC_POINT_point2oct(
EC_KEY_get0_group(ephemeral_key_.get()),
EC_KEY_get0_public_key(ephemeral_key_.get()),
POINT_CONVERSION_UNCOMPRESSED, ephemeral_key_public_bytes,
sizeof(ephemeral_key_public_bytes), /*ctx=*/nullptr));
MixHash(ephemeral_key_public_bytes);
MixKey(ephemeral_key_public_bytes);
std::vector<uint8_t> ciphertext = Encrypt(base::span<const uint8_t>());
MixHash(ciphertext);
std::vector<uint8_t> handshake_message;
handshake_message.reserve(sizeof(ephemeral_key_public_bytes) +
ciphertext.size());
handshake_message.insert(
handshake_message.end(), ephemeral_key_public_bytes,
ephemeral_key_public_bytes + sizeof(ephemeral_key_public_bytes));
handshake_message.insert(handshake_message.end(), ciphertext.begin(),
ciphertext.end());
cable_device_->SendHandshakeMessage(std::move(handshake_message),
std::move(callback));
}
bool FidoCableV2HandshakeHandler::ValidateAuthenticatorHandshakeMessage(
base::span<const uint8_t> response) {
if (response.size() < P256PointSize) {
return false;
}
auto peer_point_bytes = response.subspan(0, P256PointSize);
auto ciphertext = response.subspan(P256PointSize);
bssl::UniquePtr<EC_POINT> peer_point(
EC_POINT_new(EC_KEY_get0_group(ephemeral_key_.get())));
uint8_t shared_key[32];
if (!EC_POINT_oct2point(EC_KEY_get0_group(ephemeral_key_.get()),
peer_point.get(), peer_point_bytes.data(),
peer_point_bytes.size(), /*ctx=*/nullptr) ||
!ECDH_compute_key(shared_key, sizeof(shared_key), peer_point.get(),
ephemeral_key_.get(), /*kdf=*/nullptr)) {
return false;
}
MixHash(peer_point_bytes);
MixKey(peer_point_bytes);
MixKey(shared_key);
auto maybe_plaintext = Decrypt(ciphertext);
if (!maybe_plaintext || !maybe_plaintext->empty()) {
return false;
}
// Here the spec says to do MixHash(ciphertext), but there are no more
// handshake messages so that's moot.
// MixHash(ciphertext);
std::array<uint8_t, 32> key1, unused_key2;
std::tie(key1, unused_key2) =
HKDF2(chaining_key_, base::span<const uint8_t>());
uint8_t zero_nonce[8] = {0};
cable_device_->SetEncryptionData(
std::string(reinterpret_cast<const char*>(key1.data()), key1.size()),
zero_nonce);
return true;
}
void FidoCableV2HandshakeHandler::MixHash(base::span<const uint8_t> in) {
// See https://www.noiseprotocol.org/noise.html#the-symmetricstate-object
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, h_.data(), h_.size());
SHA256_Update(&ctx, in.data(), in.size());
SHA256_Final(h_.data(), &ctx);
}
void FidoCableV2HandshakeHandler::MixKey(base::span<const uint8_t> ikm) {
// See https://www.noiseprotocol.org/noise.html#the-symmetricstate-object
std::array<uint8_t, 32> temp_k;
std::tie(chaining_key_, temp_k) = HKDF2(chaining_key_, ikm);
InitializeKey(temp_k);
}
void FidoCableV2HandshakeHandler::MixKeyAndHash(base::span<const uint8_t> ikm) {
// See https://www.noiseprotocol.org/noise.html#the-symmetricstate-object
std::array<uint8_t, 32> temp_h, temp_k;
std::tie(chaining_key_, temp_h, temp_k) = HKDF3(chaining_key_, ikm);
MixHash(temp_h);
InitializeKey(temp_k);
}
void FidoCableV2HandshakeHandler::InitializeKey(
base::span<const uint8_t, 32> key) {
// See https://www.noiseprotocol.org/noise.html#the-cipherstate-object
DCHECK_EQ(symmetric_key_.size(), key.size());
memcpy(symmetric_key_.data(), key.data(), symmetric_key_.size());
symmetric_nonce_ = 0;
}
std::vector<uint8_t> FidoCableV2HandshakeHandler::Encrypt(
base::span<const uint8_t> plaintext) {
uint8_t nonce[12] = {0};
nonce[8] = symmetric_nonce_ >> 24;
nonce[9] = symmetric_nonce_ >> 16;
nonce[10] = symmetric_nonce_ >> 8;
nonce[11] = symmetric_nonce_;
symmetric_nonce_++;
crypto::Aead aead(crypto::Aead::AES_256_GCM);
aead.Init(symmetric_key_);
return aead.Seal(base::span<const uint8_t>(), nonce, h_);
}
base::Optional<std::vector<uint8_t>> FidoCableV2HandshakeHandler::Decrypt(
base::span<const uint8_t> ciphertext) {
uint8_t nonce[12] = {0};
nonce[8] = symmetric_nonce_ >> 24;
nonce[9] = symmetric_nonce_ >> 16;
nonce[10] = symmetric_nonce_ >> 8;
nonce[11] = symmetric_nonce_;
symmetric_nonce_++;
crypto::Aead aead(crypto::Aead::AES_256_GCM);
aead.Init(symmetric_key_);
return aead.Open(ciphertext, nonce, h_);
}
} // namespace device } // namespace device
...@@ -16,27 +16,41 @@ ...@@ -16,27 +16,41 @@
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "device/fido/fido_device.h" #include "device/fido/fido_device.h"
#include "third_party/boringssl/src/include/openssl/base.h"
namespace device { namespace device {
class FidoCableDevice; class FidoCableDevice;
// FidoCableHandshakeHandler abstracts over the different versions of caBLE
// handshakes.
class FidoCableHandshakeHandler {
public:
virtual ~FidoCableHandshakeHandler() = 0;
virtual void InitiateCableHandshake(FidoDevice::DeviceCallback callback) = 0;
virtual bool ValidateAuthenticatorHandshakeMessage(
base::span<const uint8_t> response) = 0;
};
// Handles exchanging handshake messages with external authenticator and // Handles exchanging handshake messages with external authenticator and
// validating the handshake messages to derive a shared session key to be used // validating the handshake messages to derive a shared session key to be used
// for message encryption. // for message encryption.
// See: fido-client-to-authenticator-protocol.html#cable-encryption-handshake of // See: fido-client-to-authenticator-protocol.html#cable-encryption-handshake of
// the most up-to-date spec. // the most up-to-date spec.
class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableHandshakeHandler { class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableV1HandshakeHandler
: public FidoCableHandshakeHandler {
public: public:
FidoCableHandshakeHandler(FidoCableDevice* device, FidoCableV1HandshakeHandler(FidoCableDevice* device,
base::span<const uint8_t, 8> nonce, base::span<const uint8_t, 8> nonce,
base::span<const uint8_t, 32> session_pre_key); base::span<const uint8_t, 32> session_pre_key);
virtual ~FidoCableHandshakeHandler(); ~FidoCableV1HandshakeHandler() override;
virtual void InitiateCableHandshake(FidoDevice::DeviceCallback callback); // FidoCableHandshakeHandler:
virtual bool ValidateAuthenticatorHandshakeMessage( void InitiateCableHandshake(FidoDevice::DeviceCallback callback) override;
base::span<const uint8_t> response); bool ValidateAuthenticatorHandshakeMessage(
base::span<const uint8_t> response) override;
private: private:
FRIEND_TEST_ALL_PREFIXES(FidoCableHandshakeHandlerTest, HandShakeSuccess); FRIEND_TEST_ALL_PREFIXES(FidoCableHandshakeHandlerTest, HandShakeSuccess);
...@@ -52,9 +66,43 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableHandshakeHandler { ...@@ -52,9 +66,43 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableHandshakeHandler {
std::array<uint8_t, 16> client_session_random_; std::array<uint8_t, 16> client_session_random_;
std::string handshake_key_; std::string handshake_key_;
base::WeakPtrFactory<FidoCableHandshakeHandler> weak_factory_{this}; base::WeakPtrFactory<FidoCableV1HandshakeHandler> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(FidoCableV1HandshakeHandler);
};
// FidoCableV2HandshakeHandler implements an NNpsk0[1] handshake that provides
// forward secrecy.
//
// [1] https://noiseexplorer.com/patterns/NNpsk0/
class COMPONENT_EXPORT(DEVICE_FIDO) FidoCableV2HandshakeHandler
: public FidoCableHandshakeHandler {
public:
FidoCableV2HandshakeHandler(FidoCableDevice* device,
base::span<const uint8_t, 32> session_pre_key);
~FidoCableV2HandshakeHandler() override;
// FidoCableHandshakeHandler:
void InitiateCableHandshake(FidoDevice::DeviceCallback callback) override;
bool ValidateAuthenticatorHandshakeMessage(
base::span<const uint8_t> response) override;
DISALLOW_COPY_AND_ASSIGN(FidoCableHandshakeHandler); private:
void MixHash(base::span<const uint8_t> in);
void MixKey(base::span<const uint8_t> ikm);
void MixKeyAndHash(base::span<const uint8_t> ikm);
void InitializeKey(base::span<const uint8_t, 32> key);
std::vector<uint8_t> Encrypt(base::span<const uint8_t> plaintext);
base::Optional<std::vector<uint8_t>> Decrypt(
base::span<const uint8_t> ciphertext);
FidoCableDevice* const cable_device_;
std::array<uint8_t, 32> session_pre_key_;
std::array<uint8_t, 32> chaining_key_;
std::array<uint8_t, 32> h_;
std::array<uint8_t, 32> symmetric_key_;
uint32_t symmetric_nonce_;
bssl::UniquePtr<EC_KEY> ephemeral_key_;
}; };
} // namespace device } // namespace device
......
...@@ -37,8 +37,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* raw_data, size_t size) { ...@@ -37,8 +37,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* raw_data, size_t size) {
auto adapter = auto adapter =
base::MakeRefCounted<::testing::NiceMock<device::MockBluetoothAdapter>>(); base::MakeRefCounted<::testing::NiceMock<device::MockBluetoothAdapter>>();
device::FidoCableDevice test_cable_device(adapter.get(), kTestDeviceAddress); device::FidoCableDevice test_cable_device(adapter.get(), kTestDeviceAddress);
device::FidoCableHandshakeHandler handshake_handler(
device::FidoCableV1HandshakeHandler handshake_handler_v1(
&test_cable_device, kTestNonce, kTestSessionPreKey); &test_cable_device, kTestNonce, kTestSessionPreKey);
handshake_handler.ValidateAuthenticatorHandshakeMessage(data_span); handshake_handler_v1.ValidateAuthenticatorHandshakeMessage(data_span);
return 0; return 0;
} }
...@@ -264,11 +264,11 @@ class FidoCableHandshakeHandlerTest : public Test { ...@@ -264,11 +264,11 @@ class FidoCableHandshakeHandlerTest : public Test {
connection_->read_callback() = device_->GetReadCallbackForTesting(); connection_->read_callback() = device_->GetReadCallbackForTesting();
} }
std::unique_ptr<FidoCableHandshakeHandler> CreateHandshakeHandler( std::unique_ptr<FidoCableV1HandshakeHandler> CreateHandshakeHandler(
std::array<uint8_t, 8> nonce, std::array<uint8_t, 8> nonce,
std::array<uint8_t, 32> session_pre_key) { std::array<uint8_t, 32> session_pre_key) {
return std::make_unique<FidoCableHandshakeHandler>(device_.get(), nonce, return std::make_unique<FidoCableV1HandshakeHandler>(device_.get(), nonce,
session_pre_key); session_pre_key);
} }
void ConnectWithLength(uint16_t length) { void ConnectWithLength(uint16_t length) {
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <array>
#include "base/containers/span.h"
#include "base/memory/ref_counted.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/fido/cable/fido_cable_device.h"
#include "device/fido/cable/fido_cable_handshake_handler.h"
#include "device/fido/fido_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr std::array<uint8_t, 32> kTestSessionPreKey = {{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}};
constexpr char kTestDeviceAddress[] = "Fake_Address";
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* raw_data, size_t size) {
auto data_span = base::make_span(raw_data, size);
auto adapter =
base::MakeRefCounted<::testing::NiceMock<device::MockBluetoothAdapter>>();
device::FidoCableDevice test_cable_device(adapter.get(), kTestDeviceAddress);
test_cable_device.SetStateForTesting(
device::FidoCableDevice::State::kDeviceError);
device::FidoCableV2HandshakeHandler handshake_handler_v2(&test_cable_device,
kTestSessionPreKey);
handshake_handler_v2.InitiateCableHandshake(base::DoNothing());
handshake_handler_v2.ValidateAuthenticatorHandshakeMessage(data_span);
return 0;
}
...@@ -25,4 +25,7 @@ extern const base::Feature kWebAuthBiometricEnrollment{ ...@@ -25,4 +25,7 @@ extern const base::Feature kWebAuthBiometricEnrollment{
extern const base::Feature kWebAuthCredentialManagement{ extern const base::Feature kWebAuthCredentialManagement{
"WebAuthenticationCredentialManagement", base::FEATURE_ENABLED_BY_DEFAULT}; "WebAuthenticationCredentialManagement", base::FEATURE_ENABLED_BY_DEFAULT};
extern const base::Feature kWebAuthPhoneSupport{
"WebAuthenticationPhoneSupport", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace device } // namespace device
...@@ -28,6 +28,10 @@ extern const base::Feature kWebAuthBiometricEnrollment; ...@@ -28,6 +28,10 @@ extern const base::Feature kWebAuthBiometricEnrollment;
COMPONENT_EXPORT(DEVICE_FIDO) COMPONENT_EXPORT(DEVICE_FIDO)
extern const base::Feature kWebAuthCredentialManagement; extern const base::Feature kWebAuthCredentialManagement;
// Enable using a phone as a generic security key.
COMPONENT_EXPORT(DEVICE_FIDO)
extern const base::Feature kWebAuthPhoneSupport;
} // namespace device } // namespace device
#endif // DEVICE_FIDO_FEATURES_H_ #endif // DEVICE_FIDO_FEATURES_H_
...@@ -115,6 +115,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice { ...@@ -115,6 +115,7 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDevice {
} }
State state_for_testing() const { return state_; } State state_for_testing() const { return state_; }
void SetStateForTesting(State state) { state_ = state; }
protected: protected:
void OnDeviceInfoReceived(base::OnceClosure done, void OnDeviceInfoReceived(base::OnceClosure done,
......
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