// 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 <utility>
#include "chromeos/ash/services/device_sync/proto/cryptauth_logging.h"
#include "base/base64url.h"
#include "base/i18n/time_formatting.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
namespace cryptauthv2 {
namespace {
std::string Encode(const std::string& str) {
std::string encoded_string;
base::Base64UrlEncode(str, base::Base64UrlEncodePolicy::INCLUDE_PADDING,
&encoded_string);
return encoded_string;
}
} // namespace
std::string TruncateStringForLogs(const std::string& str) {
if (str.length() <= 10)
return str;
return str.substr(0, 5) + "..." + str.substr(str.length() - 5, str.length());
}
std::string TargetServiceToString(TargetService service) {
switch (service) {
case TargetService::TARGET_SERVICE_UNSPECIFIED:
return "[Unspecified]";
case TargetService::ENROLLMENT:
return "[Enrollment]";
case TargetService::DEVICE_SYNC:
return "[DeviceSync]";
default:
return "[Unknown TargetService value " + base::NumberToString(service) +
"]";
}
}
std::ostream& operator<<(std::ostream& stream, const TargetService& service) {
stream << TargetServiceToString(service);
return stream;
}
std::string InvocationReasonToString(ClientMetadata::InvocationReason reason) {
switch (reason) {
case ClientMetadata::INVOCATION_REASON_UNSPECIFIED:
return "[Unspecified]";
case ClientMetadata::INITIALIZATION:
return "[Initialization]";
case ClientMetadata::PERIODIC:
return "[Periodic]";
case ClientMetadata::SLOW_PERIODIC:
return "[Slow periodic]";
case ClientMetadata::FAST_PERIODIC:
return "[Fast periodic]";
case ClientMetadata::EXPIRATION:
return "[Expiration]";
case ClientMetadata::FAILURE_RECOVERY:
return "[Failure recovery]";
case ClientMetadata::NEW_ACCOUNT:
return "[New account]";
case ClientMetadata::CHANGED_ACCOUNT:
return "[Changed account]";
case ClientMetadata::FEATURE_TOGGLED:
return "[Feature toggled]";
case ClientMetadata::SERVER_INITIATED:
return "[Server initiated]";
case ClientMetadata::ADDRESS_CHANGE:
return "[Address change]";
case ClientMetadata::SOFTWARE_UPDATE:
return "[Software update]";
case ClientMetadata::MANUAL:
return "[Manual]";
case ClientMetadata::CUSTOM_KEY_INVALIDATION:
return "[Custom key invalidation]";
case ClientMetadata::PROXIMITY_PERIODIC:
return "[Proximity periodic]";
default:
return "[Unknown InvocationReason value " + base::NumberToString(reason) +
"]";
}
}
std::ostream& operator<<(std::ostream& stream,
const ClientMetadata::InvocationReason& reason) {
stream << InvocationReasonToString(reason);
return stream;
}
std::string ConnectivityStatusToString(ConnectivityStatus status) {
switch (status) {
case ConnectivityStatus::UNKNOWN_CONNECTIVITY:
return "[Unknown connectivity]";
case ConnectivityStatus::OFFLINE:
return "[Offline]";
case ConnectivityStatus::ONLINE:
return "[Online]";
default:
return "[Unknown ConnectivityStatus value " +
base::NumberToString(status) + "]";
}
}
std::ostream& operator<<(std::ostream& stream,
const ConnectivityStatus& status) {
stream << ConnectivityStatusToString(status);
return stream;
}
base::Value::Dict PolicyReferenceToReadableDictionary(
const PolicyReference& policy) {
return base::Value::Dict()
.Set("Name", policy.name())
.Set("Version", static_cast<int>(policy.version()));
}
std::ostream& operator<<(std::ostream& stream, const PolicyReference& policy) {
stream << PolicyReferenceToReadableDictionary(policy);
return stream;
}
base::Value::Dict InvokeNextToReadableDictionary(
const InvokeNext& invoke_next) {
return base::Value::Dict()
.Set("Target service", TargetServiceToString(invoke_next.service()))
.Set("Key name", invoke_next.key_name());
}
std::ostream& operator<<(std::ostream& stream, const InvokeNext& invoke_next) {
stream << InvokeNextToReadableDictionary(invoke_next);
return stream;
}
base::Value::Dict ClientDirectiveToReadableDictionary(
const ClientDirective& directive) {
base::Value::Dict dict;
dict.Set("Policy reference",
PolicyReferenceToReadableDictionary(directive.policy_reference()));
{
std::u16string checkin_delay;
bool success = base::TimeDurationFormatWithSeconds(
base::Milliseconds(directive.checkin_delay_millis()),
base::DurationFormatWidth::DURATION_WIDTH_NARROW, &checkin_delay);
if (success) {
dict.Set("Next enrollment", checkin_delay);
}
checkin_delay = base::UTF8ToUTF16(
"[Error formatting time " +
base::NumberToString(directive.checkin_delay_millis()) + "ms]");
}
dict.Set("Immediate failure retry attempts",
static_cast<int>(directive.retry_attempts()));
{
std::u16string retry_period;
bool success = base::TimeDurationFormatWithSeconds(
base::Milliseconds(directive.retry_period_millis()),
base::DurationFormatWidth::DURATION_WIDTH_NARROW, &retry_period);
if (!success) {
retry_period = base::UTF8ToUTF16(
"[Error formatting time " +
base::NumberToString(directive.retry_period_millis()) + "ms]");
}
dict.Set("Failure retry delay", retry_period);
}
dict.Set("Directive creation time",
base::TimeFormatShortDateAndTimeWithTimeZone(
base::Time::FromMillisecondsSinceUnixEpoch(
directive.create_time_millis())));
base::Value::List invoke_next_list;
for (const auto& invoke_next : directive.invoke_next()) {
invoke_next_list.Append(InvokeNextToReadableDictionary(invoke_next));
}
dict.Set("Invoke next list", std::move(invoke_next_list));
return dict;
}
std::ostream& operator<<(std::ostream& stream,
const ClientDirective& directive) {
stream << ClientDirectiveToReadableDictionary(directive);
return stream;
}
base::Value::Dict DeviceMetadataPacketToReadableDictionary(
const DeviceMetadataPacket& packet) {
base::Value::Dict dict;
dict.Set("Instance ID", packet.device_id());
dict.Set("Encrypted metadata",
(packet.encrypted_metadata().empty()
? "[Empty]"
: TruncateStringForLogs(Encode(packet.encrypted_metadata()))));
dict.Set("Needs group private key?", packet.need_group_private_key());
dict.Set("DeviceSync:BetterTogether device public key",
TruncateStringForLogs(Encode(packet.device_public_key())));
dict.Set("Device name", packet.device_name());
return dict;
}
std::ostream& operator<<(std::ostream& stream,
const DeviceMetadataPacket& packet) {
stream << DeviceMetadataPacketToReadableDictionary(packet);
return stream;
}
base::Value::Dict EncryptedGroupPrivateKeyToReadableDictionary(
const EncryptedGroupPrivateKey& key) {
return base::Value::Dict()
.Set("Recipient Instance ID", key.recipient_device_id())
.Set("Sender Instance ID", key.sender_device_id())
.Set("Encrypted group private key",
(key.encrypted_private_key().empty()
? "[Empty]"
: TruncateStringForLogs(Encode(key.encrypted_private_key()))))
.Set("Group public key hash",
base::NumberToString(key.group_public_key_hash()));
}
std::ostream& operator<<(std::ostream& stream,
const EncryptedGroupPrivateKey& key) {
stream << EncryptedGroupPrivateKeyToReadableDictionary(key);
return stream;
}
base::Value::Dict SyncMetadataResponseToReadableDictionary(
const SyncMetadataResponse& response) {
base::Value::List metadata_list;
for (const auto& metadata : response.encrypted_metadata()) {
metadata_list.Append(DeviceMetadataPacketToReadableDictionary(metadata));
}
auto dict =
base::Value::Dict()
.Set("Device metadata packets", std::move(metadata_list))
.Set("Group public key",
TruncateStringForLogs(Encode(response.group_public_key())))
.Set("Freshness token",
TruncateStringForLogs(Encode(response.freshness_token())));
if (response.has_encrypted_group_private_key()) {
dict.Set("Encrypted group private key",
EncryptedGroupPrivateKeyToReadableDictionary(
response.encrypted_group_private_key()));
} else {
dict.Set("Encrypted group private key", "[Not sent]");
}
if (response.has_client_directive()) {
dict.Set("Client directive",
ClientDirectiveToReadableDictionary(response.client_directive()));
} else {
dict.Set("Client directive", "[Not sent]");
}
return dict;
}
std::ostream& operator<<(std::ostream& stream,
const SyncMetadataResponse& response) {
stream << SyncMetadataResponseToReadableDictionary(response);
return stream;
}
base::Value::Dict FeatureStatusToReadableDictionary(
const DeviceFeatureStatus::FeatureStatus& status) {
return base::Value::Dict()
.Set("Feature type", status.feature_type())
.Set("Enabled?", status.enabled())
.Set("Last modified time (BatchGet* only)",
base::TimeFormatShortDateAndTimeWithTimeZone(
base::Time::FromMillisecondsSinceUnixEpoch(
status.last_modified_time_millis())))
.Set("Enable exclusively (BatchSet* only)?", status.enable_exclusively());
}
std::ostream& operator<<(std::ostream& stream,
const DeviceFeatureStatus::FeatureStatus& status) {
stream << FeatureStatusToReadableDictionary(status);
return stream;
}
base::Value::Dict DeviceFeatureStatusToReadableDictionary(
const DeviceFeatureStatus& status) {
base::Value::List feature_status_list;
for (const auto& feature_status : status.feature_statuses()) {
feature_status_list.Append(
FeatureStatusToReadableDictionary(feature_status));
}
return base::Value::Dict()
.Set("Instance ID", status.device_id())
.Set("Feature statuses", std::move(feature_status_list));
}
std::ostream& operator<<(std::ostream& stream,
const DeviceFeatureStatus& status) {
stream << DeviceFeatureStatusToReadableDictionary(status);
return stream;
}
base::Value::Dict BatchGetFeatureStatusesResponseToReadableDictionary(
const BatchGetFeatureStatusesResponse& response) {
base::Value::Dict dict;
base::Value::List device_statuses_list;
for (const auto& device_statuses : response.device_feature_statuses()) {
device_statuses_list.Append(
DeviceFeatureStatusToReadableDictionary(device_statuses));
}
dict.Set("Device feature statuses list", std::move(device_statuses_list));
return dict;
}
std::ostream& operator<<(std::ostream& stream,
const BatchGetFeatureStatusesResponse& response) {
stream << BatchGetFeatureStatusesResponseToReadableDictionary(response);
return stream;
}
base::Value::Dict DeviceActivityStatusToReadableDictionary(
const DeviceActivityStatus& status) {
return base::Value::Dict()
.Set("Instance ID", status.device_id())
.Set("Last activity time",
base::TimeFormatShortDateAndTimeWithTimeZone(
base::Time::FromTimeT(status.last_activity_time_sec())))
.Set("Connectivity status",
ConnectivityStatusToString(status.connectivity_status()))
.Set("Last update time",
base::TimeFormatShortDateAndTimeWithTimeZone(
base::Time::FromTimeT(status.last_update_time().seconds()) +
base::Nanoseconds(status.last_update_time().nanos())));
}
std::ostream& operator<<(std::ostream& stream,
const DeviceActivityStatus& status) {
stream << DeviceActivityStatusToReadableDictionary(status);
return stream;
}
base::Value::Dict GetDevicesActivityStatusResponseToReadableDictionary(
const GetDevicesActivityStatusResponse& response) {
base::Value::List status_list;
for (const auto& status : response.device_activity_statuses()) {
status_list.Append(DeviceActivityStatusToReadableDictionary(status));
}
return base::Value::Dict().Set("Device activity statuses",
std::move(status_list));
}
std::ostream& operator<<(std::ostream& stream,
const GetDevicesActivityStatusResponse& response) {
stream << GetDevicesActivityStatusResponseToReadableDictionary(response);
return stream;
}
base::Value::Dict BeaconSeedToReadableDictionary(const BeaconSeed& seed) {
return base::Value::Dict()
.Set("Data", TruncateStringForLogs(Encode(seed.data())))
.Set("Start time", base::TimeFormatShortDateAndTimeWithTimeZone(
base::Time::FromMillisecondsSinceUnixEpoch(
seed.start_time_millis())))
.Set("End time", base::TimeFormatShortDateAndTimeWithTimeZone(
base::Time::FromMillisecondsSinceUnixEpoch(
seed.end_time_millis())));
}
std::ostream& operator<<(std::ostream& stream, const BeaconSeed& seed) {
stream << BeaconSeedToReadableDictionary(seed);
return stream;
}
base::Value::Dict BetterTogetherDeviceMetadataToReadableDictionary(
const BetterTogetherDeviceMetadata& metadata) {
base::Value::List beacon_seed_list;
for (const auto& seed : metadata.beacon_seeds()) {
beacon_seed_list.Append(BeaconSeedToReadableDictionary(seed));
}
return base::Value::Dict()
.Set("Public key", TruncateStringForLogs(Encode(metadata.public_key())))
.Set("PII-free device name", metadata.no_pii_device_name())
.Set("Bluetooth MAC address", metadata.bluetooth_public_address())
.Set("Beacon seeds", std::move(beacon_seed_list));
}
std::ostream& operator<<(std::ostream& stream,
const BetterTogetherDeviceMetadata& metadata) {
stream << BetterTogetherDeviceMetadataToReadableDictionary(metadata);
return stream;
}
} // namespace cryptauthv2