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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
// 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.
#include "chromeos/ash/components/quick_start/quick_start_requests.h"
#include "base/base64.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "chromeos/ash/components/quick_start/quick_start_message.h"
#include "chromeos/ash/components/quick_start/quick_start_message_type.h"
#include "chromeos/ash/components/quick_start/types.h"
#include "chromeos/constants/devicetype.h"
#include "components/cbor/values.h"
#include "components/cbor/writer.h"
#include "crypto/sha2.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace ash::quick_start::requests {
namespace {
// bootstrapOptions key telling the phone how to handle
// challenge UI in case of fallback.
constexpr char kFlowTypeKey[] = "flowType";
// bootstrapOptions key telling the phone the number of
// accounts are expected to transfer account to the target device.
constexpr char kAccountRequirementKey[] = "accountRequirement";
// bootstrapOptions key telling the deviceName of target device.
constexpr char kDeviceNameKey[] = "deviceName";
// bootstrapOptions key containing object with phone actions after transfer.
constexpr char kPostTransferActionKey[] = "PostTransferAction";
// bootstrapOptions URI key inside the PostTransferAction object.
constexpr char kPostTransferActionURIKey[] = "uri";
// bootstrapOptions PostTransferAction uri value.
constexpr char kPostTransferActionURIValue[] =
"intent:#Intent;action=com.google.android.gms.quickstart.LANDING_SCREEN;"
"package=com.google.android.gms;end";
// Device names
constexpr char kChromebook[] = "Chromebook";
constexpr char kChromebase[] = "Chromebase";
constexpr char kChromebox[] = "Chromebox";
// Base64 encoded CBOR bytes containing the Fido command. This will be used
// for GetInfo and GetAssertion.
static constexpr char FIDO_MESSAGE_KEY[] = "fidoMessage";
// Maps to AccountRequirementSingle enum value for Account Requirement field
// meaning that at least one account is required on the phone. The user will
// select the specified account to transfer.
// Enum Source: go/bootstrap-options-account-requirement-single.
constexpr int kAccountRequirementSingle = 2;
// Maps to FlowTypeTargetChallenge enum value for Flow Type field meaning that
// the fallback challenge will happen on the target device.
// Enum Source: go/bootstrap-options-flow-type-target-challenge.
constexpr int kFlowTypeTargetChallenge = 2;
const char kRelyingPartyId[] = "google.com";
const char kOrigin[] = "https://accounts.google.com";
// Maps to CBOR byte labelling FIDO request as GetAssertion.
const uint8_t kAuthenticatorGetAssertionCommand = 0x02;
const char kUserPresenceMapKey[] = "up";
const char kUserVerificationMapKey[] = "uv";
// Maps to CBOR byte labelling FIDO request as GetInfo.
const uint8_t kAuthenticatorGetInfoCommand = 0x04;
// Boolean in WifiCredentialsRequest indicating we should request WiFi
// Credentials
constexpr char kRequestWifiKey[] = "request_wifi";
// Key in WifiCredentialsRequest and NotifySourceOfUpdateMessage including the
// shared secret to resume the connection if a reboot occurs.
constexpr char kSharedSecretKey[] = "shared_secret";
// Key in WifiCredentialsRequest and NotifySourceOfUpdateMessage for the session
// ID
constexpr char kSessionIdKey[] = "SESSION_ID";
// Boolean in NotifySourceOfUpdateMessage indicating target device requires an
// update.
constexpr char kNotifySourceOfUpdateMessageKey[] = "forced_update_required";
// bootstrapOptions key to inform source of the target device type.
constexpr char kDeviceTypeKey[] = "deviceType";
// Device type should map to ChromeOS device type. See:
// http://google3/java/com/google/android/gmscore/integ/client/smartdevice/src/com/google/android/gms/smartdevice/d2d/DeviceType.java;l=57
constexpr int kDeviceTypeChrome = 7;
// BootstrapState maps to values set here:
// http://google3/java/com/google/android/gmscore/integ/modules/smartdevice/src/com/google/android/gms/smartdevice/d2d/data/BootstrapState.java
constexpr int kBootstrapStateCancel = 1;
constexpr int kBootstrapStateComplete = 2;
// Key in BootstrapState message.
constexpr char kBootstrapStateKey[] = "bootstrapState";
std::string GetDeviceName() {
switch (chromeos::GetDeviceType()) {
case chromeos::DeviceType::kChromebook:
return kChromebook;
case chromeos::DeviceType::kChromebase:
return kChromebase;
case chromeos::DeviceType::kChromebox:
return kChromebox;
default:
return kChromebook;
}
}
} // namespace
std::unique_ptr<QuickStartMessage> BuildBootstrapOptionsRequest() {
std::unique_ptr<QuickStartMessage> message =
std::make_unique<QuickStartMessage>(
QuickStartMessageType::kBootstrapOptions);
message->GetPayload()->Set(kAccountRequirementKey, kAccountRequirementSingle);
message->GetPayload()->Set(kFlowTypeKey, kFlowTypeTargetChallenge);
message->GetPayload()->Set(kDeviceTypeKey, kDeviceTypeChrome);
message->GetPayload()->Set(kDeviceNameKey, GetDeviceName());
// TODO(b/332603236): Remove postTransferAction payload when new device info
// exchange is implemented.
base::Value::Dict post_transfer_action;
post_transfer_action.Set(kPostTransferActionURIKey,
kPostTransferActionURIValue);
message->GetPayload()->Set(kPostTransferActionKey,
std::move(post_transfer_action));
return message;
}
std::unique_ptr<QuickStartMessage> BuildAssertionRequestMessage(
base::span<const uint8_t, crypto::hash::kSha256Size> client_data_hash) {
cbor::Value request = GenerateGetAssertionRequest(client_data_hash);
std::vector<uint8_t> ctap_request_command =
CBOREncodeGetAssertionRequest(std::move(request));
auto message = std::make_unique<QuickStartMessage>(
QuickStartMessageType::kSecondDeviceAuthPayload);
message->GetPayload()->Set(FIDO_MESSAGE_KEY,
base::Base64Encode(ctap_request_command));
return message;
}
std::unique_ptr<QuickStartMessage> BuildGetInfoRequestMessage() {
std::vector<uint8_t> ctap_request_command({kAuthenticatorGetInfoCommand});
auto message = std::make_unique<QuickStartMessage>(
QuickStartMessageType::kSecondDeviceAuthPayload);
message->GetPayload()->Set(FIDO_MESSAGE_KEY,
base::Base64Encode(ctap_request_command));
return message;
}
std::unique_ptr<QuickStartMessage> BuildRequestWifiCredentialsMessage(
uint64_t session_id,
std::string& shared_secret) {
auto message = std::make_unique<QuickStartMessage>(
QuickStartMessageType::kQuickStartPayload);
message->GetPayload()->Set(kRequestWifiKey, true);
std::string shared_secret_str(shared_secret.begin(), shared_secret.end());
std::string shared_secret_base64 = base::Base64Encode(shared_secret_str);
message->GetPayload()->Set(kSharedSecretKey, shared_secret_base64);
message->GetPayload()->Set(kSessionIdKey, base::NumberToString(session_id));
return message;
}
cbor::Value GenerateGetAssertionRequest(
base::span<const uint8_t, crypto::hash::kSha256Size> client_data_hash) {
url::Origin origin = url::Origin::Create(GURL(kOrigin));
cbor::Value::MapValue cbor_map;
cbor_map.insert_or_assign(cbor::Value(0x01), cbor::Value(kRelyingPartyId));
cbor_map.insert_or_assign(cbor::Value(0x02), cbor::Value(client_data_hash));
cbor::Value::MapValue option_map;
option_map.insert_or_assign(cbor::Value(kUserPresenceMapKey),
cbor::Value(true));
option_map.insert_or_assign(cbor::Value(kUserVerificationMapKey),
cbor::Value(true));
cbor_map.insert_or_assign(cbor::Value(0x05),
cbor::Value(std::move(option_map)));
return cbor::Value(std::move(cbor_map));
}
std::vector<uint8_t> CBOREncodeGetAssertionRequest(const cbor::Value& request) {
// Encode the CtapGetAssertionRequest into cbor bytes vector.
std::optional<std::vector<uint8_t>> cbor_bytes = cbor::Writer::Write(request);
CHECK(cbor_bytes);
std::vector<uint8_t> request_bytes = std::move(*cbor_bytes);
// Add the command byte to the beginning of this now fully encoded cbor bytes
// vector.
request_bytes.insert(request_bytes.begin(),
kAuthenticatorGetAssertionCommand);
return request_bytes;
}
std::unique_ptr<QuickStartMessage> BuildNotifySourceOfUpdateMessage(
uint64_t session_id,
base::span<const uint8_t, 32> shared_secret) {
auto message = std::make_unique<QuickStartMessage>(
QuickStartMessageType::kQuickStartPayload);
message->GetPayload()->Set(kNotifySourceOfUpdateMessageKey, true);
message->GetPayload()->Set(kSharedSecretKey,
base::Base64Encode(shared_secret));
message->GetPayload()->Set(kSessionIdKey, base::NumberToString(session_id));
return message;
}
std::unique_ptr<QuickStartMessage> BuildBootstrapStateCancelMessage() {
auto message = std::make_unique<QuickStartMessage>(
QuickStartMessageType::kBootstrapState);
message->GetPayload()->Set(kBootstrapStateKey, kBootstrapStateCancel);
return message;
}
std::unique_ptr<QuickStartMessage> BuildBootstrapStateCompleteMessage() {
auto message = std::make_unique<QuickStartMessage>(
QuickStartMessageType::kBootstrapState);
message->GetPayload()->Set(kBootstrapStateKey, kBootstrapStateComplete);
// TODO(b/332603236): Remove postTransferAction payload when new device info
// exchange is implemented.
base::Value::Dict post_transfer_action;
post_transfer_action.Set(kPostTransferActionURIKey,
kPostTransferActionURIValue);
message->GetPayload()->Set(kPostTransferActionKey,
std::move(post_transfer_action));
return message;
}
} // namespace ash::quick_start::requests
|