1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_WEBAUTHN_ANDROID_CABLE_REGISTRATION_STATE_H_
#define CHROME_BROWSER_WEBAUTHN_ANDROID_CABLE_REGISTRATION_STATE_H_
#include <array>
#include <string>
#include <vector>
#include "base/functional/callback_forward.h"
#include "device/fido/cable/v2_registration.h"
#include "third_party/boringssl/src/include/openssl/base.h"
namespace webauthn::authenticator {
// RegistrationState is a singleton object that holds two FCM registrations when
// required. One registration, the "linking" registration, is used when the user
// links with another device by scanning a QR code. The second is advertised via
// Sync for other devices signed into the same account. The reason for having
// two registrations is that the linking registration can be rotated if the user
// wishes to unlink all QR-linked devices. But we don't want to break synced
// peers when that happens. Instead, for synced peers we require that they have
// received a recent sync status from this device, i.e. we rotate them
// automatically.
class RegistrationState {
public:
// SystemInterface abstracts the rest of the system. This is mocked out for
// tests.
class SystemInterface {
public:
virtual ~SystemInterface();
// See v2_registration.h to understand this function.
virtual std::unique_ptr<device::cablev2::authenticator::Registration>
NewRegistration(
device::cablev2::authenticator::Registration::Type type,
base::OnceCallback<void()> on_ready,
base::RepeatingCallback<
void(std::unique_ptr<
device::cablev2::authenticator::Registration::Event>)>
event_callback) = 0;
// Test whether the current device is suitable for prelinking.
virtual void CanDeviceSupportCable(
base::OnceCallback<void(bool)> callback) = 0;
// Test whether the current process is an in Android work profile.
virtual void AmInWorkProfile(base::OnceCallback<void(bool)> callback) = 0;
// Fetch prelinking information from Play Services, if any.
virtual void GetPrelinkFromPlayServices(
base::OnceCallback<void(std::optional<std::vector<uint8_t>>)>
callback) = 0;
// Request that Sync refresh the DeviceInfo entity for this device.
virtual void RefreshLocalDeviceInfo() = 0;
};
explicit RegistrationState(std::unique_ptr<SystemInterface> interface);
~RegistrationState();
void Register();
bool is_registered_for_linking() const {
return linking_registration_ != nullptr;
}
bool is_registered_for_sync() const { return sync_registration_ != nullptr; }
device::cablev2::authenticator::Registration* linking_registration() const {
return linking_registration_.get();
}
device::cablev2::authenticator::Registration* sync_registration() const {
return sync_registration_.get();
}
bool device_supports_cable() const { return *device_supports_cable_; }
bool am_in_work_profile() const { return *am_in_work_profile_; }
const std::optional<std::vector<uint8_t>>& link_data_from_play_services()
const {
DCHECK(have_link_data_from_play_services_);
return link_data_from_play_services_;
}
// have_data_for_sync returns true if this object has loaded enough state to
// put information into sync's DeviceInfo.
bool have_data_for_sync() const;
// Request that this object trigger a DeviceInfo refresh when
// `have_data_for_sync` is true.
void SignalSyncWhenReady();
private:
bool have_play_services_data() const;
void QueryPlayServices();
void OnHavePlayServicesLinkingInformation(
std::optional<std::vector<uint8_t>> cbor);
void OnLinkingRegistrationReady();
void OnSyncRegistrationReady();
// OnEvent is called when a GCM message is received.
void OnEvent(
std::unique_ptr<device::cablev2::authenticator::Registration::Event>
event);
void MaybeFlushPendingEvent();
// MaybeSignalSync prompts the Sync system to refresh local-device data if
// the Sync data is now ready and |signal_sync_when_ready_| has been set to
// indicate that the Sync data was not available last time Sync queried it.
void MaybeSignalSync();
// OnCanDeviceSupportCable is run with the result of `TestDeviceSupport`.
void OnDeviceSupportResult(bool result);
// OnWorkProfileResult is run with the result of `AmInWorkProfile`.
void OnWorkProfileResult(bool result);
const std::unique_ptr<SystemInterface> interface_;
std::unique_ptr<device::cablev2::authenticator::Registration>
linking_registration_;
std::unique_ptr<device::cablev2::authenticator::Registration>
sync_registration_;
std::unique_ptr<device::cablev2::authenticator::Registration::Event>
pending_event_;
// device_supports_cable_ caches the result of a Java function that checks
// some prerequisites: that the device has Bluetooth and a screenlock. If
// this value is |nullopt| then its value has not yet been determined.
//
// The presence of a screen lock could change but, because of this caching,
// Clank won't notice in this context until the process restarts. Users can
// always use a QR code if pre-linking hasn't worked by the time they need
// it.
std::optional<bool> device_supports_cable_;
// am_in_work_profile_ stores whether the current process is in an Android
// work profile.
std::optional<bool> am_in_work_profile_;
// link_data_from_play_services_ contains the response from Play Services, as
// CBOR-encoded linking information, or `nullopt` if the call was
// unsuccessful. This field is only meaningful if
// `have_link_data_from_play_services_` is true.
std::optional<std::vector<uint8_t>> link_data_from_play_services_;
// have_link_data_from_play_services_ is true if any call to Play Services has
// ever completed, successful or not.
bool have_link_data_from_play_services_ = false;
// link_data_from_play_services_timeticks_ contains the timestamp when
// `link_data_from_play_services_` was set.
base::TimeTicks link_data_from_play_services_timeticks_;
// play_services_query_pending_ is true if a request to Play Services is
// currently outstanding.
bool play_services_query_pending_ = false;
bool signal_sync_when_ready_ = false;
};
} // namespace webauthn::authenticator
#endif // CHROME_BROWSER_WEBAUTHN_ANDROID_CABLE_REGISTRATION_STATE_H_
|