// 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 "chrome/browser/chromeos/extensions/smart_card_provider_private/smart_card_provider_private_api.h"
#include <queue>
#include <variant>
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/timer/timer.h"
#include "chrome/common/extensions/api/smart_card_provider_private.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/event_listener_map.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/event_router_factory.h"
#include "extensions/browser/extension_event_histogram_value.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/device/public/mojom/smart_card.mojom.h"
namespace scard_api = extensions::api::smart_card_provider_private;
using device::mojom::SmartCardConnectResult;
using device::mojom::SmartCardCreateContextResult;
using device::mojom::SmartCardCreateContextResultPtr;
using device::mojom::SmartCardDataResult;
using device::mojom::SmartCardError;
using device::mojom::SmartCardListReadersResult;
using device::mojom::SmartCardResult;
using device::mojom::SmartCardResultPtr;
using device::mojom::SmartCardStatus;
using device::mojom::SmartCardStatusChangeResult;
using device::mojom::SmartCardStatusResult;
using device::mojom::SmartCardSuccess;
using device::mojom::SmartCardTransactionResult;
using device::mojom::SmartCardTransactionResultPtr;
namespace {
device::mojom::SmartCardResultPtr ProviderResultCodeToSmartCardResult(
scard_api::ResultCode code) {
switch (code) {
case scard_api::ResultCode::kNone:
return SmartCardResult::NewError(SmartCardError::kUnknown);
case scard_api::ResultCode::kSuccess:
return SmartCardResult::NewSuccess(SmartCardSuccess::kOk);
case scard_api::ResultCode::kRemovedCard:
return SmartCardResult::NewError(SmartCardError::kRemovedCard);
case scard_api::ResultCode::kResetCard:
return SmartCardResult::NewError(SmartCardError::kResetCard);
case scard_api::ResultCode::kUnpoweredCard:
return SmartCardResult::NewError(SmartCardError::kUnpoweredCard);
case scard_api::ResultCode::kUnresponsiveCard:
return SmartCardResult::NewError(SmartCardError::kUnresponsiveCard);
case scard_api::ResultCode::kUnsupportedCard:
return SmartCardResult::NewError(SmartCardError::kUnsupportedCard);
case scard_api::ResultCode::kReaderUnavailable:
return SmartCardResult::NewError(SmartCardError::kReaderUnavailable);
case scard_api::ResultCode::kSharingViolation:
return SmartCardResult::NewError(SmartCardError::kSharingViolation);
case scard_api::ResultCode::kNotTransacted:
return SmartCardResult::NewError(SmartCardError::kNotTransacted);
case scard_api::ResultCode::kNoSmartcard:
return SmartCardResult::NewError(SmartCardError::kNoSmartcard);
case scard_api::ResultCode::kProtoMismatch:
return SmartCardResult::NewError(SmartCardError::kProtoMismatch);
case scard_api::ResultCode::kSystemCancelled:
return SmartCardResult::NewError(SmartCardError::kSystemCancelled);
case scard_api::ResultCode::kNotReady:
return SmartCardResult::NewError(SmartCardError::kNotReady);
case scard_api::ResultCode::kCancelled:
return SmartCardResult::NewError(SmartCardError::kCancelled);
case scard_api::ResultCode::kInsufficientBuffer:
return SmartCardResult::NewError(SmartCardError::kInsufficientBuffer);
case scard_api::ResultCode::kInvalidHandle:
return SmartCardResult::NewError(SmartCardError::kInvalidHandle);
case scard_api::ResultCode::kInvalidParameter:
return SmartCardResult::NewError(SmartCardError::kInvalidParameter);
case scard_api::ResultCode::kInvalidValue:
return SmartCardResult::NewError(SmartCardError::kInvalidValue);
case scard_api::ResultCode::kNoMemory:
return SmartCardResult::NewError(SmartCardError::kNoMemory);
case scard_api::ResultCode::kTimeout:
return SmartCardResult::NewError(SmartCardError::kTimeout);
case scard_api::ResultCode::kUnknownReader:
return SmartCardResult::NewError(SmartCardError::kUnknownReader);
case scard_api::ResultCode::kUnsupportedFeature:
return SmartCardResult::NewError(SmartCardError::kUnsupportedFeature);
case scard_api::ResultCode::kNoReadersAvailable:
return SmartCardResult::NewError(SmartCardError::kNoReadersAvailable);
case scard_api::ResultCode::kServiceStopped:
return SmartCardResult::NewError(SmartCardError::kServiceStopped);
case scard_api::ResultCode::kNoService:
return SmartCardResult::NewError(SmartCardError::kNoService);
case scard_api::ResultCode::kCommError:
return SmartCardResult::NewError(SmartCardError::kCommError);
case scard_api::ResultCode::kInternalError:
return SmartCardResult::NewError(SmartCardError::kInternalError);
case scard_api::ResultCode::kUnknownError:
return SmartCardResult::NewError(SmartCardError::kUnknownError);
case scard_api::ResultCode::kServerTooBusy:
return SmartCardResult::NewError(SmartCardError::kServerTooBusy);
case scard_api::ResultCode::kUnexpected:
return SmartCardResult::NewError(SmartCardError::kUnexpected);
case scard_api::ResultCode::kShutdown:
return SmartCardResult::NewError(SmartCardError::kShutdown);
case scard_api::ResultCode::kUnknown:
return SmartCardResult::NewError(SmartCardError::kUnknown);
}
}
device::mojom::SmartCardReaderStateFlagsPtr ToSmartCardProviderReaderStateFlags(
const scard_api::ReaderStateFlags& flags) {
auto result = device::mojom::SmartCardReaderStateFlags::New();
#define CONVERT_FLAG(flag) \
if (flags.flag.has_value()) { \
result->flag = flags.flag.value(); \
}
CONVERT_FLAG(unaware)
CONVERT_FLAG(ignore)
CONVERT_FLAG(changed)
CONVERT_FLAG(unknown)
CONVERT_FLAG(unavailable)
CONVERT_FLAG(empty)
CONVERT_FLAG(present)
CONVERT_FLAG(exclusive)
CONVERT_FLAG(inuse)
CONVERT_FLAG(mute)
CONVERT_FLAG(unpowered)
#undef CONVERT_FLAG
return result;
}
std::vector<device::mojom::SmartCardReaderStateOutPtr>
ToSmartCardProviderReaderStateOutVector(
std::vector<scard_api::ReaderStateOut>& reader_states) {
std::vector<device::mojom::SmartCardReaderStateOutPtr> result_vector;
result_vector.reserve(reader_states.size());
for (auto& reader_state : reader_states) {
auto mojom_reader_state = device::mojom::SmartCardReaderStateOut::New();
mojom_reader_state->reader = std::move(reader_state.reader);
mojom_reader_state->event_state =
ToSmartCardProviderReaderStateFlags(reader_state.event_state);
mojom_reader_state->event_count = reader_state.event_count;
mojom_reader_state->answer_to_reset = std::move(reader_state.atr);
result_vector.push_back(std::move(mojom_reader_state));
}
return result_vector;
}
base::Value::Dict ToValue(
const device::mojom::SmartCardReaderStateFlags& state_flags) {
scard_api::ReaderStateFlags result;
#define CONVERT_FLAG(flag) result.flag = state_flags.flag;
CONVERT_FLAG(unaware)
CONVERT_FLAG(ignore)
CONVERT_FLAG(changed)
CONVERT_FLAG(unknown)
CONVERT_FLAG(unavailable)
CONVERT_FLAG(empty)
CONVERT_FLAG(present)
CONVERT_FLAG(exclusive)
CONVERT_FLAG(inuse)
CONVERT_FLAG(mute)
CONVERT_FLAG(unpowered)
#undef CONVERT_FLAG
return result.ToValue();
}
base::Value::Dict ToValue(
const device::mojom::SmartCardReaderStateIn& state_in) {
return base::Value::Dict()
.Set("reader", state_in.reader)
.Set("currentState", ToValue(*state_in.current_state.get()))
.Set("currentCount", state_in.current_count);
}
scard_api::ShareMode ToApiShareMode(
device::mojom::SmartCardShareMode share_mode) {
switch (share_mode) {
case device::mojom::SmartCardShareMode::kShared:
return scard_api::ShareMode::kShared;
case device::mojom::SmartCardShareMode::kExclusive:
return scard_api::ShareMode::kExclusive;
case device::mojom::SmartCardShareMode::kDirect:
return scard_api::ShareMode::kDirect;
}
}
base::Value ToValue(device::mojom::SmartCardShareMode share_mode) {
return base::Value(scard_api::ToString(ToApiShareMode(share_mode)));
}
base::Value::Dict ToValue(const device::mojom::SmartCardProtocols& protocols) {
scard_api::Protocols result;
result.t0 = protocols.t0;
result.t1 = protocols.t1;
result.raw = protocols.raw;
return result.ToValue();
}
scard_api::Disposition ToApiDisposition(
device::mojom::SmartCardDisposition disposition) {
switch (disposition) {
case device::mojom::SmartCardDisposition::kLeave:
return scard_api::Disposition::kLeaveCard;
case device::mojom::SmartCardDisposition::kReset:
return scard_api::Disposition::kResetCard;
case device::mojom::SmartCardDisposition::kUnpower:
return scard_api::Disposition::kUnpowerCard;
case device::mojom::SmartCardDisposition::kEject:
return scard_api::Disposition::kEjectCard;
}
}
base::Value ToValue(device::mojom::SmartCardDisposition disposition) {
return base::Value(scard_api::ToString(ToApiDisposition(disposition)));
}
device::mojom::SmartCardProtocol ToDeviceMojomSmartCardProtocol(
scard_api::Protocol protocol) {
switch (protocol) {
case scard_api::Protocol::kNone:
case scard_api::Protocol::kUndefined:
return device::mojom::SmartCardProtocol::kUndefined;
case scard_api::Protocol::kT0:
return device::mojom::SmartCardProtocol::kT0;
case scard_api::Protocol::kT1:
return device::mojom::SmartCardProtocol::kT1;
case scard_api::Protocol::kRaw:
return device::mojom::SmartCardProtocol::kRaw;
}
}
device::mojom::SmartCardConnectionState ToDeviceMojomSmartCardConnectionState(
scard_api::ConnectionState state) {
switch (state) {
case scard_api::ConnectionState::kNone:
case scard_api::ConnectionState::kAbsent:
return device::mojom::SmartCardConnectionState::kAbsent;
case scard_api::ConnectionState::kPresent:
return device::mojom::SmartCardConnectionState::kPresent;
case scard_api::ConnectionState::kSwallowed:
return device::mojom::SmartCardConnectionState::kSwallowed;
case scard_api::ConnectionState::kPowered:
return device::mojom::SmartCardConnectionState::kPowered;
case scard_api::ConnectionState::kNegotiable:
return device::mojom::SmartCardConnectionState::kNegotiable;
case scard_api::ConnectionState::kSpecific:
return device::mojom::SmartCardConnectionState::kSpecific;
}
}
scard_api::Protocol ToApiProtocol(device::mojom::SmartCardProtocol protocol) {
switch (protocol) {
case device::mojom::SmartCardProtocol::kUndefined:
return scard_api::Protocol::kUndefined;
case device::mojom::SmartCardProtocol::kT0:
return scard_api::Protocol::kT0;
case device::mojom::SmartCardProtocol::kT1:
return scard_api::Protocol::kT1;
case device::mojom::SmartCardProtocol::kRaw:
return scard_api::Protocol::kRaw;
}
}
base::Value ToValue(device::mojom::SmartCardProtocol protocol) {
return base::Value(scard_api::ToString(ToApiProtocol(protocol)));
}
template <class PendingType>
std::unique_ptr<PendingType> Extract(
std::map<extensions::SmartCardProviderPrivateAPI::RequestId,
std::unique_ptr<PendingType>>& pending_map,
extensions::SmartCardProviderPrivateAPI::RequestId request_id) {
auto it = pending_map.find(request_id);
if (it == pending_map.end()) {
return nullptr;
}
std::unique_ptr<PendingType> pending = std::move(it->second);
CHECK(pending);
pending_map.erase(it);
return pending;
}
} // namespace
namespace extensions {
struct SmartCardProviderPrivateAPI::PendingResult {
PendingResult() = default;
~PendingResult() = default;
base::OneShotTimer timer;
ContextId scard_context; // Can be invalid (null).
ProcessResultCallback process_result;
SmartCardCallback callback;
};
// Information about an established scard_context
struct SmartCardProviderPrivateAPI::ContextData {
ContextData() = default;
ContextData(ContextData&&) = default;
ContextData& operator=(ContextData&&) = default;
ContextData(const ContextData&) = delete;
ContextData& operator=(const ContextData&) = delete;
bool HasActiveTransaction(Handle handle) const {
auto it = handles_map.find(handle);
return it != handles_map.end() ? it->second : false;
}
// A PC/SC context can only handle one request at a time (exception being
// SCardCancel()).
// Thus we have to make sure not to send a request on a ContextId which has an
// ongoing call (ie, a request was sent to the provider but its result was not
// yet received).
//
// This queue contains requests from device::mojom::SmartCardContext or
// device::mojom::SmartCardConnection for this context that have arrived
// while it was waiting for the result of a previous request.
std::queue<base::OnceClosure> task_queue;
// All device::mojom::SmartCardConnection receivers created on this context.
std::set<mojo::ReceiverId> connection_receiver_ids;
// Maps a valid PC/SC Handle to whether it has an active transaction. Ie,
// transactions begun by the browser and that, therefore, the browser should
// also end.
std::map<Handle, bool> handles_map;
};
// static
BrowserContextKeyedAPIFactory<SmartCardProviderPrivateAPI>*
SmartCardProviderPrivateAPI::GetFactoryInstance() {
static base::NoDestructor<
BrowserContextKeyedAPIFactory<SmartCardProviderPrivateAPI>>
instance;
return instance.get();
}
// static
SmartCardProviderPrivateAPI& SmartCardProviderPrivateAPI::Get(
content::BrowserContext& context) {
return *GetFactoryInstance()->Get(&context);
}
SmartCardProviderPrivateAPI::SmartCardProviderPrivateAPI(
content::BrowserContext* context)
: browser_context_(raw_ref<content::BrowserContext>::from_ptr(context)),
event_router_(raw_ref<EventRouter>::from_ptr(
EventRouterFactory::GetForBrowserContext(context))) {
context_receivers_.set_disconnect_handler(base::BindRepeating(
&SmartCardProviderPrivateAPI::OnMojoContextDisconnected,
weak_ptr_factory_.GetWeakPtr()));
connection_receivers_.set_disconnect_handler(base::BindRepeating(
&SmartCardProviderPrivateAPI::OnMojoConnectionDisconnected,
weak_ptr_factory_.GetWeakPtr()));
transaction_receivers_.set_disconnect_handler(base::BindRepeating(
&SmartCardProviderPrivateAPI::OnMojoTransactionDisconnected,
weak_ptr_factory_.GetWeakPtr()));
}
SmartCardProviderPrivateAPI::~SmartCardProviderPrivateAPI() = default;
mojo::PendingRemote<device::mojom::SmartCardContextFactory>
SmartCardProviderPrivateAPI::GetSmartCardContextFactory() {
mojo::PendingRemote<device::mojom::SmartCardContextFactory> pending_remote;
context_factory_receivers_.Add(
this, pending_remote.InitWithNewPipeAndPassReceiver());
return pending_remote;
}
void SmartCardProviderPrivateAPI::CreateContext(
CreateContextCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DispatchEventWithTimeout(
ContextId(), // This call is requesting a new context, not passing an
// existing one.
scard_api::OnEstablishContextRequested::kEventName,
extensions::events::
SMART_CARD_PROVIDER_PRIVATE_ON_ESTABLISH_CONTEXT_REQUESTED,
ProcessResultCallback(), // It has its own ReportEstablishContextResult
// method.
std::move(callback),
&SmartCardProviderPrivateAPI::OnEstablishContextTimeout);
}
void SmartCardProviderPrivateAPI::OnMojoContextDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disconnect_observer_) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, disconnect_observer_);
}
const ContextId scard_context = context_receivers_.current_context();
CHECK(scard_context);
ContextData& context_data = GetContextData(scard_context);
// Disconnect all mojom::SmartCardConnection receivers created on this context
// as their handles will all become invalid at PC/SC level once the context
// is released.
for (mojo::ReceiverId connection_receiver_id :
context_data.connection_receiver_ids) {
connection_receivers_.Remove(connection_receiver_id);
}
RunOrQueueRequest(
scard_context,
base::BindOnce(&SmartCardProviderPrivateAPI::SendReleaseContext,
weak_ptr_factory_.GetWeakPtr(), scard_context));
}
void SmartCardProviderPrivateAPI::OnMojoConnectionDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (disconnect_observer_) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, disconnect_observer_);
}
auto callback =
base::BindOnce(&SmartCardProviderPrivateAPI::OnScardHandleDisconnected,
weak_ptr_factory_.GetWeakPtr());
const auto& [context_id, handle] = connection_receivers_.current_context();
CHECK(context_id);
CHECK(handle);
if (!context_data_map_.contains(context_id)) {
return;
}
ContextData& context_data = GetContextData(context_id);
const auto handles_it = context_data.handles_map.find(handle);
if (handles_it == context_data.handles_map.end()) {
// Already disconnected.
return;
}
// If there's an active transaction, end it before disconnecting.
if (handles_it->second) {
EndTransactionInternal(
context_id, handle, context_data,
device::mojom::SmartCardDisposition::kLeave,
base::BindOnce(&SmartCardProviderPrivateAPI::OnEndTransactionDone,
weak_ptr_factory_.GetWeakPtr()));
}
RunOrQueueRequest(
context_id,
base::BindOnce(&SmartCardProviderPrivateAPI::SendDisconnect,
weak_ptr_factory_.GetWeakPtr(), context_id, handle,
device::mojom::SmartCardDisposition::kLeave,
std::move(callback)));
}
void SmartCardProviderPrivateAPI::OnMojoTransactionDisconnected() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& [scard_context, handle] =
transaction_receivers_.current_context();
if (!context_data_map_.contains(scard_context)) {
return;
}
ContextData& context_data = GetContextData(scard_context);
if (!context_data.HasActiveTransaction(handle)) {
return;
}
EndTransactionInternal(
scard_context, handle, context_data,
device::mojom::SmartCardDisposition::kLeave,
base::BindOnce(&SmartCardProviderPrivateAPI::OnEndTransactionDone,
weak_ptr_factory_.GetWeakPtr()));
}
void SmartCardProviderPrivateAPI::OnEndTransactionDone(
device::mojom::SmartCardResultPtr result) {
if (result->is_error()) {
LOG(WARNING) << "EndTransaction call failed: " << result->get_error();
}
}
void SmartCardProviderPrivateAPI::OnScardHandleDisconnected(
device::mojom::SmartCardResultPtr result) {
if (result->is_error()) {
LOG(WARNING) << "Failed to disconnect SCard Handle: "
<< result->get_error();
}
}
void SmartCardProviderPrivateAPI::RunOrQueueRequest(ContextId scard_context,
base::OnceClosure request) {
if (IsContextBusy(scard_context)) {
GetContextData(scard_context).task_queue.push(std::move(request));
return;
}
std::move(request).Run();
}
void SmartCardProviderPrivateAPI::SendReleaseContext(ContextId scard_context) {
RequestId request_id = request_id_generator_.GenerateNextId();
auto event = std::make_unique<extensions::Event>(
extensions::events::
SMART_CARD_PROVIDER_PRIVATE_ON_RELEASE_CONTEXT_REQUESTED,
scard_api::OnReleaseContextRequested::kEventName,
base::Value::List()
.Append(request_id.GetUnsafeValue())
.Append(scard_context.GetUnsafeValue()),
&*browser_context_);
const std::string provider_extension_id = GetListenerExtensionId(*event);
if (provider_extension_id.empty()) {
return;
}
auto pending = std::make_unique<PendingResult>();
pending->scard_context = scard_context;
pending->timer.Start(
FROM_HERE, response_time_limit_,
base::BindOnce(&SmartCardProviderPrivateAPI::OnReleaseContextTimeout,
weak_ptr_factory_.GetWeakPtr(), provider_extension_id,
request_id));
pending_results_[request_id] = std::move(pending);
event_router_->DispatchEventToExtension(provider_extension_id,
std::move(event));
}
void SmartCardProviderPrivateAPI::SendDisconnect(
ContextId scard_context,
Handle handle,
device::mojom::SmartCardDisposition disposition,
DisconnectCallback callback) {
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessPlainResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnDisconnectRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_DISCONNECT_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnDisconnectTimeout,
/*event_arguments=*/
base::Value::List()
.Append(handle.GetUnsafeValue())
.Append(ToValue(disposition)));
}
void SmartCardProviderPrivateAPI::SendTransmit(
ContextId scard_context,
Handle handle,
device::mojom::SmartCardProtocol protocol,
const std::vector<uint8_t>& data,
TransmitCallback callback) {
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessDataResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnTransmitRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_TRANSMIT_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnTransmitTimeout,
/*event_arguments=*/
base::Value::List()
.Append(handle.GetUnsafeValue())
.Append(ToValue(protocol))
.Append(base::Value(std::move(data))));
}
void SmartCardProviderPrivateAPI::SendControl(ContextId scard_context,
Handle handle,
uint32_t control_code,
const std::vector<uint8_t>& data,
ControlCallback callback) {
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessDataResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnControlRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_CONTROL_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnControlTimeout,
/*event_arguments=*/
base::Value::List()
.Append(handle.GetUnsafeValue())
.Append(int(control_code))
.Append(base::Value(std::move(data))));
}
void SmartCardProviderPrivateAPI::SendGetAttrib(ContextId scard_context,
Handle handle,
uint32_t id,
GetAttribCallback callback) {
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessDataResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnGetAttribRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_GET_ATTRIB_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnGetAttribTimeout,
/*event_arguments=*/
base::Value::List().Append(handle.GetUnsafeValue()).Append(int(id)));
}
void SmartCardProviderPrivateAPI::SendSetAttrib(
ContextId scard_context,
Handle handle,
uint32_t id,
const std::vector<uint8_t>& data,
SetAttribCallback callback) {
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessPlainResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnSetAttribRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_SET_ATTRIB_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnSetAttribTimeout,
/*event_arguments=*/
base::Value::List()
.Append(handle.GetUnsafeValue())
.Append(int(id))
.Append(base::Value(data)));
}
void SmartCardProviderPrivateAPI::SendStatus(ContextId scard_context,
Handle handle,
StatusCallback callback) {
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessStatusResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnStatusRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_STATUS_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnStatusTimeout,
/*event_arguments=*/
base::Value::List().Append(handle.GetUnsafeValue()));
}
void SmartCardProviderPrivateAPI::SendBeginTransaction(
ContextId scard_context,
Handle handle,
BeginTransactionCallback callback) {
auto process_result = base::BindOnce(
&SmartCardProviderPrivateAPI::ProcessBeginTransactionResult,
weak_ptr_factory_.GetWeakPtr(), scard_context, handle);
DispatchEventWithTimeout(
scard_context, scard_api::OnBeginTransactionRequested::kEventName,
extensions::events::
SMART_CARD_PROVIDER_PRIVATE_ON_BEGIN_TRANSACTION_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnBeginTransactionTimeout,
/*event_arguments=*/
base::Value::List().Append(handle.GetUnsafeValue()));
}
void SmartCardProviderPrivateAPI::SendEndTransaction(
ContextId scard_context,
Handle handle,
device::mojom::SmartCardDisposition disposition,
EndTransactionCallback callback) {
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessPlainResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnEndTransactionRequested::kEventName,
extensions::events::
SMART_CARD_PROVIDER_PRIVATE_ON_END_TRANSACTION_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnEndTransactionTimeout,
/*event_arguments=*/
base::Value::List()
.Append(handle.GetUnsafeValue())
.Append(ToValue(disposition)));
}
void SmartCardProviderPrivateAPI::ReportResult(
RequestId request_id,
ResultArgs result_args,
device::mojom::SmartCardResultPtr result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<PendingResult> pending =
Extract(pending_results_, request_id);
if (!pending) {
return;
}
std::move(pending->process_result)
.Run(std::move(result_args), std::move(result),
std::move(pending->callback));
// 'Cancel' does not affect the task queue for its context.
// Thus it won't set an scard_context on its `PendingResult`.
if (pending->scard_context) {
RunNextRequestForContext(pending->scard_context);
}
}
void SmartCardProviderPrivateAPI::ReportEstablishContextResult(
RequestId request_id,
ContextId scard_context,
SmartCardResultPtr result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<PendingResult> pending =
Extract(pending_results_, request_id);
if (!pending) {
if (result->is_success() && scard_context) {
LOG(WARNING) << "Releasing scard_context from an unknown "
"EstablishContext request.";
SendReleaseContext(scard_context);
}
return;
}
SmartCardCreateContextResultPtr context_result;
if (result->is_success()) {
if (scard_context) {
mojo::PendingRemote<device::mojom::SmartCardContext> context_remote;
context_receivers_.Add(
this, context_remote.InitWithNewPipeAndPassReceiver(), scard_context);
context_result =
SmartCardCreateContextResult::NewContext(std::move(context_remote));
// It's neither expected nor supported for the provider to recycle a
// scard_context value so soon.
CHECK(!context_data_map_.contains(scard_context));
context_data_map_[scard_context] = ContextData();
} else {
LOG(ERROR) << "Provider reported an invalid scard_context value: "
<< scard_context.GetUnsafeValue();
// Just ignore this result.
context_result = SmartCardCreateContextResult::NewError(
SmartCardError::kInternalError);
}
} else {
context_result =
SmartCardCreateContextResult::NewError(result->get_error());
}
CHECK(std::holds_alternative<CreateContextCallback>(pending->callback));
std::move(std::get<CreateContextCallback>(pending->callback))
.Run(std::move(context_result));
}
void SmartCardProviderPrivateAPI::ReportReleaseContextResult(
RequestId request_id,
SmartCardResultPtr result) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<PendingResult> pending =
Extract(pending_results_, request_id);
if (!pending) {
return;
}
if (result->is_error()) {
// There's nothing really to be done about it.
LOG(WARNING) << "Failed to release context: " << result->get_error();
}
auto it = context_data_map_.find(pending->scard_context);
CHECK(it != context_data_map_.end());
context_data_map_.erase(it);
}
void SmartCardProviderPrivateAPI::ProcessListReadersResult(
ResultArgs result_args,
SmartCardResultPtr result,
SmartCardCallback callback) {
CHECK(std::holds_alternative<std::vector<std::string>>(result_args));
auto readers = std::move(std::get<std::vector<std::string>>(result_args));
CHECK(std::holds_alternative<ListReadersCallback>(callback));
std::move(std::get<ListReadersCallback>(callback))
.Run(result->is_success()
? SmartCardListReadersResult::NewReaders(std::move(readers))
: SmartCardListReadersResult::NewError(result->get_error()));
}
void SmartCardProviderPrivateAPI::ProcessGetStatusChangeResult(
ResultArgs result_args,
SmartCardResultPtr result,
SmartCardCallback callback) {
device::mojom::SmartCardStatusChangeResultPtr status_change_result;
CHECK(std::holds_alternative<
std::vector<device::mojom::SmartCardReaderStateOutPtr>>(result_args));
auto reader_states = std::move(
std::get<std::vector<device::mojom::SmartCardReaderStateOutPtr>>(
result_args));
if (result->is_success()) {
status_change_result =
SmartCardStatusChangeResult::NewReaderStates(std::move(reader_states));
} else {
status_change_result =
SmartCardStatusChangeResult::NewError(result->get_error());
}
CHECK(std::holds_alternative<GetStatusChangeCallback>(callback));
std::move(std::get<GetStatusChangeCallback>(callback))
.Run(std::move(status_change_result));
}
void SmartCardProviderPrivateAPI::ProcessPlainResult(
ResultArgs result_args,
SmartCardResultPtr result,
SmartCardCallback callback) {
CHECK(std::holds_alternative<std::monostate>(result_args));
CHECK(std::holds_alternative<PlainCallback>(callback));
std::move(std::get<PlainCallback>(callback)).Run(std::move(result));
}
device::mojom::SmartCardConnectResultPtr
SmartCardProviderPrivateAPI::CreateSmartCardConnection(
ContextId scard_context,
Handle handle,
device::mojom::SmartCardProtocol active_protocol) {
if (handle.is_null()) {
LOG(ERROR) << "Provider reported an invalid handle value: "
<< handle.GetUnsafeValue();
// Just ignore this result.
return SmartCardConnectResult::NewError(SmartCardError::kInternalError);
}
mojo::PendingRemote<device::mojom::SmartCardConnection> connection_remote;
mojo::ReceiverId connection_receiver_id = connection_receivers_.Add(
this, connection_remote.InitWithNewPipeAndPassReceiver(),
std::make_tuple(scard_context, handle));
GetContextData(scard_context)
.connection_receiver_ids.insert(connection_receiver_id);
return SmartCardConnectResult::NewSuccess(
device::mojom::SmartCardConnectSuccess::New(std::move(connection_remote),
active_protocol));
}
void SmartCardProviderPrivateAPI::ReportConnectResult(
RequestId request_id,
Handle handle,
device::mojom::SmartCardProtocol active_protocol,
device::mojom::SmartCardResultPtr result) {
if (!pending_results_.contains(request_id)) {
// TODO(crbug.com/40247152): send disconnect request to PC/SC provider if
// the handle is valid and the result is success to avoid leaking this
// seemingly unrequested connection.
return;
}
ReportResult(request_id, std::make_tuple(handle, active_protocol),
std::move(result));
}
void SmartCardProviderPrivateAPI::ProcessConnectResult(
ContextId scard_context,
ResultArgs result_args,
device::mojom::SmartCardResultPtr result,
SmartCardCallback callback) {
device::mojom::SmartCardConnectResultPtr connect_result;
auto handle_and_protocol =
std::get<std::tuple<Handle, device::mojom::SmartCardProtocol>>(
result_args);
if (result->is_success()) {
Handle handle = std::get<Handle>(handle_and_protocol);
connect_result = CreateSmartCardConnection(
scard_context, handle,
std::get<device::mojom::SmartCardProtocol>(handle_and_protocol));
auto& context_data = GetContextData(scard_context);
CHECK(!context_data.handles_map.contains(handle));
// Handle exists but it has no active transaction.
context_data.handles_map[handle] = false;
} else {
connect_result = SmartCardConnectResult::NewError(result->get_error());
}
CHECK(std::holds_alternative<ConnectCallback>(callback));
std::move(std::get<ConnectCallback>(callback)).Run(std::move(connect_result));
}
void SmartCardProviderPrivateAPI::RunNextRequestForContext(
ContextId scard_context) {
auto it = context_data_map_.find(scard_context);
CHECK(it != context_data_map_.end());
ContextData& context_data = it->second;
// Context must be free, since this method is called after a pending request
// finishes processing the received result.
CHECK(!IsContextBusy(scard_context));
if (context_data.task_queue.empty()) {
return;
}
auto task = std::move(context_data.task_queue.front());
context_data.task_queue.pop();
std::move(task).Run();
}
void SmartCardProviderPrivateAPI::ProcessDataResult(
ResultArgs result_args,
device::mojom::SmartCardResultPtr result,
SmartCardCallback callback) {
CHECK(std::holds_alternative<std::vector<uint8_t>>(result_args));
auto data = std::move(std::get<std::vector<uint8_t>>(result_args));
CHECK(std::holds_alternative<DataCallback>(callback));
std::move(std::get<DataCallback>(callback))
.Run(result->is_success()
? SmartCardDataResult::NewData(std::move(data))
: SmartCardDataResult::NewError(result->get_error()));
}
void SmartCardProviderPrivateAPI::ProcessStatusResult(
ResultArgs result_args,
device::mojom::SmartCardResultPtr result,
SmartCardCallback callback) {
CHECK(std::holds_alternative<StatusResultArgs>(result_args));
auto [reader_name, state, protocol, answer_to_reset] =
std::get<StatusResultArgs>(std::move(result_args));
std::move(std::get<StatusCallback>(callback))
.Run(result->is_success()
? SmartCardStatusResult::NewStatus(SmartCardStatus::New(
reader_name, state, protocol, std::move(answer_to_reset)))
: SmartCardStatusResult::NewError(result->get_error()));
}
device::mojom::SmartCardTransactionResultPtr
SmartCardProviderPrivateAPI::CreateSmartCardTransaction(ContextId scard_context,
Handle handle) {
mojo::PendingAssociatedRemote<device::mojom::SmartCardTransaction>
transaction_remote;
transaction_receivers_.Add(
this, transaction_remote.InitWithNewEndpointAndPassReceiver(),
{scard_context, handle});
return SmartCardTransactionResult::NewTransaction(
std::move(transaction_remote));
}
void SmartCardProviderPrivateAPI::ProcessBeginTransactionResult(
ContextId scard_context,
Handle handle,
ResultArgs result_args,
device::mojom::SmartCardResultPtr result,
SmartCardCallback callback) {
CHECK(std::holds_alternative<std::monostate>(result_args));
CHECK(std::holds_alternative<BeginTransactionCallback>(callback));
SmartCardTransactionResultPtr transaction_result;
if (result->is_success()) {
auto& context_data = GetContextData(scard_context);
auto handles_it = context_data.handles_map.find(handle);
// Entry must have been created already by SmartCardConnection
CHECK(handles_it != context_data.handles_map.end());
// Only register an active transaction once the BeginTransaction
// PC/SC call is known to have succeeded.
handles_it->second = true;
transaction_result = CreateSmartCardTransaction(scard_context, handle);
} else {
transaction_result =
SmartCardTransactionResult::NewError(result->get_error());
}
std::move(std::get<BeginTransactionCallback>(callback))
.Run(std::move(transaction_result));
}
void SmartCardProviderPrivateAPI::SetResponseTimeLimitForTesting(
base::TimeDelta value) {
response_time_limit_ = value;
}
void SmartCardProviderPrivateAPI::SetDisconnectObserverForTesting(
DisconnectObserver observer) {
disconnect_observer_ = observer;
}
// TODO(crbug.com/40247152): Consider if we need to wait for a known
// SmartCard provider Extension to load or finish installation
// before querying for listeners.
// Use case is if the Web API is used immediately after a user logs
// in.
std::string SmartCardProviderPrivateAPI::GetListenerExtensionId(
const extensions::Event& event) {
std::set<const extensions::EventListener*> listener_set =
event_router_->listeners().GetEventListeners(event);
if (listener_set.empty()) {
LOG(ERROR) << "No extension listening to " << event.event_name << ".";
return std::string();
}
// Allow list on the extension API permission enforces that there can't
// be multiple extensions with access to it. Thus don't bother
// iterating through the set.
return (*listener_set.cbegin())->extension_id();
}
template <typename ResultPtr>
void SmartCardProviderPrivateAPI::DispatchEventWithTimeout(
ContextId scard_context,
const std::string& event_name,
extensions::events::HistogramValue histogram_value,
ProcessResultCallback process_result,
base::OnceCallback<void(ResultPtr)> callback,
void (SmartCardProviderPrivateAPI::*OnTimeout)(const std::string&,
RequestId),
base::Value::List event_arguments,
std::optional<base::TimeDelta> timeout) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
RequestId request_id = request_id_generator_.GenerateNextId();
event_arguments.Insert(event_arguments.begin(),
base::Value(request_id.GetUnsafeValue()));
auto event = std::make_unique<extensions::Event>(histogram_value, event_name,
std::move(event_arguments),
&*browser_context_);
const std::string provider_extension_id = GetListenerExtensionId(*event);
if (provider_extension_id.empty()) {
ResultPtr error(std::in_place);
error->set_error(SmartCardError::kNoService);
std::move(callback).Run(std::move(error));
return;
}
auto pending = std::make_unique<PendingResult>();
pending->scard_context = scard_context;
pending->callback = std::move(callback);
pending->process_result = std::move(process_result);
pending->timer.Start(FROM_HERE,
timeout ? timeout.value() : response_time_limit_,
base::BindOnce(OnTimeout, weak_ptr_factory_.GetWeakPtr(),
provider_extension_id, request_id));
pending_results_[request_id] = std::move(pending);
event_router_->DispatchEventToExtension(provider_extension_id,
std::move(event));
}
void SmartCardProviderPrivateAPI::ListReaders(ListReadersCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const ContextId scard_context = context_receivers_.current_context();
CHECK(scard_context);
RunOrQueueRequest(
scard_context,
base::BindOnce(&SmartCardProviderPrivateAPI::SendListReaders,
weak_ptr_factory_.GetWeakPtr(), scard_context,
std::move(callback)));
}
void SmartCardProviderPrivateAPI::SendListReaders(
ContextId scard_context,
ListReadersCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(scard_context);
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessListReadersResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnListReadersRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_LIST_READERS_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnListReadersTimeout,
base::Value::List().Append(scard_context.GetUnsafeValue()));
}
void SmartCardProviderPrivateAPI::GetStatusChange(
base::TimeDelta time_delta,
std::vector<device::mojom::SmartCardReaderStateInPtr> reader_states,
GetStatusChangeCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const ContextId scard_context = context_receivers_.current_context();
DCHECK(!scard_context.is_null());
RunOrQueueRequest(
scard_context,
base::BindOnce(&SmartCardProviderPrivateAPI::SendGetStatusChange,
weak_ptr_factory_.GetWeakPtr(), scard_context, time_delta,
std::move(reader_states), std::move(callback)));
}
void SmartCardProviderPrivateAPI::SendGetStatusChange(
ContextId scard_context,
base::TimeDelta time_delta,
std::vector<device::mojom::SmartCardReaderStateInPtr> reader_states,
GetStatusChangeCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(scard_context);
const bool finite_timeout =
!time_delta.is_inf() &&
time_delta.InMilliseconds() < int64_t(std::numeric_limits<int>::max());
scard_api::Timeout timeout;
if (finite_timeout) {
timeout.milliseconds = int(time_delta.InMilliseconds());
}
base::Value::List reader_states_list;
for (const auto& reader_state : reader_states) {
reader_states_list.Append(ToValue(*reader_state.get()));
}
auto event_args = base::Value::List()
.Append(scard_context.GetUnsafeValue())
.Append(timeout.ToValue())
.Append(std::move(reader_states_list));
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessGetStatusChangeResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
scard_context, scard_api::OnGetStatusChangeRequested::kEventName,
extensions::events::
SMART_CARD_PROVIDER_PRIVATE_ON_GET_STATUS_CHANGE_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnGetStatusChangeTimeout,
std::move(event_args), std::max(base::Milliseconds(500), time_delta * 2));
}
void SmartCardProviderPrivateAPI::Cancel(CancelCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const ContextId scard_context = context_receivers_.current_context();
CHECK(!scard_context.is_null());
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessPlainResult,
weak_ptr_factory_.GetWeakPtr());
DispatchEventWithTimeout(
ContextId(), // Passing an invalid context as Cancel can be called in
// parallel to other PC/SC calls and therefore never gets
// queued or is affected by the context being in busy state.
scard_api::OnCancelRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_CANCEL_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnCancelTimeout,
/*event_arguments=*/
base::Value::List().Append(scard_context.GetUnsafeValue()));
}
void SmartCardProviderPrivateAPI::Connect(
const std::string& reader,
device::mojom::SmartCardShareMode share_mode,
device::mojom::SmartCardProtocolsPtr preferred_protocols,
ConnectCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const ContextId scard_context = context_receivers_.current_context();
CHECK(!scard_context.is_null());
RunOrQueueRequest(
scard_context,
base::BindOnce(&SmartCardProviderPrivateAPI::SendConnect,
weak_ptr_factory_.GetWeakPtr(), scard_context, reader,
share_mode, std::move(preferred_protocols),
std::move(callback)));
}
void SmartCardProviderPrivateAPI::SendConnect(
ContextId scard_context,
const std::string& reader,
device::mojom::SmartCardShareMode share_mode,
device::mojom::SmartCardProtocolsPtr preferred_protocols,
ConnectCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(scard_context);
auto event_args = base::Value::List()
.Append(scard_context.GetUnsafeValue())
.Append(reader)
.Append(ToValue(share_mode))
.Append(ToValue(*preferred_protocols.get()));
auto process_result =
base::BindOnce(&SmartCardProviderPrivateAPI::ProcessConnectResult,
weak_ptr_factory_.GetWeakPtr(), scard_context);
DispatchEventWithTimeout(
scard_context, scard_api::OnConnectRequested::kEventName,
extensions::events::SMART_CARD_PROVIDER_PRIVATE_ON_CONNECT_REQUESTED,
std::move(process_result), std::move(callback),
&SmartCardProviderPrivateAPI::OnConnectTimeout, std::move(event_args));
}
void SmartCardProviderPrivateAPI::Disconnect(
device::mojom::SmartCardDisposition disposition,
DisconnectCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& [context_id, handle] = connection_receivers_.current_context();
CHECK(context_id);
CHECK(handle);
// Consider the handle no longer valid irrespective of whether the Disconnect
// PC/SC call actually succeeds in the end as any PC/SC failure of this call
// is non-recoverable (eg "no service" or "invalid handle").
//
// Also note that the handles_map might no longer exist if this method has
// already been called once. Nothing stops the user from calling this multiple
// times.
//
// We also don't care whether there's an active transaction for this
// connection. If the user decides to disconnect without ending the
// transaction first, let it be so.
GetContextData(context_id).handles_map.erase(handle);
RunOrQueueRequest(context_id,
base::BindOnce(&SmartCardProviderPrivateAPI::SendDisconnect,
weak_ptr_factory_.GetWeakPtr(), context_id,
handle, disposition, std::move(callback)));
}
void SmartCardProviderPrivateAPI::Transmit(
device::mojom::SmartCardProtocol protocol,
const std::vector<uint8_t>& data,
TransmitCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& [context_id, handle] = connection_receivers_.current_context();
CHECK(context_id);
CHECK(handle);
RunOrQueueRequest(
context_id,
base::BindOnce(&SmartCardProviderPrivateAPI::SendTransmit,
weak_ptr_factory_.GetWeakPtr(), context_id, handle,
protocol, std::move(data), std::move(callback)));
}
void SmartCardProviderPrivateAPI::Control(uint32_t control_code,
const std::vector<uint8_t>& data,
ControlCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& [context_id, handle] = connection_receivers_.current_context();
CHECK(context_id);
CHECK(handle);
RunOrQueueRequest(
context_id,
base::BindOnce(&SmartCardProviderPrivateAPI::SendControl,
weak_ptr_factory_.GetWeakPtr(), context_id, handle,
control_code, std::move(data), std::move(callback)));
}
void SmartCardProviderPrivateAPI::GetAttrib(uint32_t id,
GetAttribCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& [context_id, handle] = connection_receivers_.current_context();
CHECK(context_id);
CHECK(handle);
RunOrQueueRequest(context_id,
base::BindOnce(&SmartCardProviderPrivateAPI::SendGetAttrib,
weak_ptr_factory_.GetWeakPtr(), context_id,
handle, id, std::move(callback)));
}
SmartCardProviderPrivateAPI::ContextData&
SmartCardProviderPrivateAPI::GetContextData(ContextId scard_context) {
auto it = context_data_map_.find(scard_context);
CHECK(it != context_data_map_.end());
return it->second;
}
void SmartCardProviderPrivateAPI::SetAttrib(uint32_t id,
const std::vector<uint8_t>& data,
SetAttribCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& [context_id, handle] = connection_receivers_.current_context();
CHECK(context_id);
CHECK(handle);
RunOrQueueRequest(context_id,
base::BindOnce(&SmartCardProviderPrivateAPI::SendSetAttrib,
weak_ptr_factory_.GetWeakPtr(), context_id,
handle, id, data, std::move(callback)));
}
void SmartCardProviderPrivateAPI::Status(StatusCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& [context_id, handle] = connection_receivers_.current_context();
CHECK(context_id);
CHECK(handle);
RunOrQueueRequest(context_id,
base::BindOnce(&SmartCardProviderPrivateAPI::SendStatus,
weak_ptr_factory_.GetWeakPtr(), context_id,
handle, std::move(callback)));
}
void SmartCardProviderPrivateAPI::BeginTransaction(
BeginTransactionCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto& [context_id, handle] = connection_receivers_.current_context();
CHECK(context_id);
CHECK(handle);
RunOrQueueRequest(
context_id,
base::BindOnce(&SmartCardProviderPrivateAPI::SendBeginTransaction,
weak_ptr_factory_.GetWeakPtr(), context_id, handle,
std::move(callback)));
}
void SmartCardProviderPrivateAPI::EndTransaction(
device::mojom::SmartCardDisposition disposition,
EndTransactionCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Note that nothing stops the mojo remote from calling EndTransaction
// multiple times. If there's no active transaction the PC/SC stack will
// answer with an error, which we will forward to the mojo remote caller.
const auto& [scard_context, handle] =
transaction_receivers_.current_context();
EndTransactionInternal(scard_context, handle, GetContextData(scard_context),
disposition, std::move(callback));
}
void SmartCardProviderPrivateAPI::EndTransactionInternal(
ContextId scard_context,
Handle handle,
ContextData& context_data,
device::mojom::SmartCardDisposition disposition,
EndTransactionCallback callback) {
auto it = context_data.handles_map.find(handle);
// Entry must have been created already by SmartCardConnection
CHECK(it != context_data.handles_map.end());
// BeginTransaction must have set it to true.
CHECK_EQ(it->second, true);
// Consider it no longer active irrespective of whether the EndTransaction
// PC/SC call actually succeeds in the end as there's nothing the browser can
// do if it fails.
it->second = false;
RunOrQueueRequest(
scard_context,
base::BindOnce(&SmartCardProviderPrivateAPI::SendEndTransaction,
weak_ptr_factory_.GetWeakPtr(), scard_context, handle,
disposition, std::move(callback)));
}
bool SmartCardProviderPrivateAPI::IsContextBusy(ContextId scard_context) const {
// I expect no more than a dozen contexts in a profile at any given time (in
// most cases much less than that). Thus the cost traversing the map is
// negligible.
for (const auto& [request_id, pending_result] : pending_results_) {
if (pending_result->scard_context == scard_context) {
return true;
}
}
return false;
}
#define ON_TIMEOUT_IMPL(FunctionName, ReportResultName, ...) \
void SmartCardProviderPrivateAPI::On##FunctionName##Timeout( \
const std::string& provider_extension_id, RequestId request_id) { \
LOG(ERROR) << "Provider extension " << provider_extension_id \
<< " did not report the result of " << #FunctionName \
<< " (request id " << request_id.GetUnsafeValue() << ")."; \
ReportResultName(request_id, __VA_ARGS__); \
}
ON_TIMEOUT_IMPL(ReleaseContext,
ReportReleaseContextResult,
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(EstablishContext,
ReportEstablishContextResult,
ContextId(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(ListReaders,
ReportResult,
std::vector<std::string>(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(GetStatusChange,
ReportResult,
std::vector<device::mojom::SmartCardReaderStateOutPtr>(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(Cancel,
ReportResult,
std::monostate(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(Connect,
ReportResult,
std::make_tuple(Handle(),
device::mojom::SmartCardProtocol::kUndefined),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(Disconnect,
ReportResult,
std::monostate(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(Transmit,
ReportResult,
std::vector<uint8_t>(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(Control,
ReportResult,
std::vector<uint8_t>(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(GetAttrib,
ReportResult,
std::vector<uint8_t>(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(SetAttrib,
ReportResult,
std::monostate(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(
Status,
ReportResult,
std::make_tuple(std::string(),
device::mojom::SmartCardConnectionState::kAbsent,
device::mojom::SmartCardProtocol::kUndefined,
std::vector<uint8_t>()),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(BeginTransaction,
ReportResult,
std::monostate(),
SmartCardResult::NewError(SmartCardError::kNoService))
ON_TIMEOUT_IMPL(EndTransaction,
ReportResult,
std::monostate(),
SmartCardResult::NewError(SmartCardError::kNoService))
#undef ON_TIMEOUT_IMPL
#define REPORT_RESULT_FUNCTION_IMPL(FunctionName, ReportResultName, ...) \
SmartCardProviderPrivateReport##FunctionName##ResultFunction:: \
~SmartCardProviderPrivateReport##FunctionName##ResultFunction() = \
default; \
ExtensionFunction::ResponseAction \
SmartCardProviderPrivateReport##FunctionName##ResultFunction::Run() { \
std::optional<scard_api::Report##FunctionName##Result::Params> params( \
scard_api::Report##FunctionName##Result::Params::Create(args())); \
EXTENSION_FUNCTION_VALIDATE(params); \
if (!params) { \
return RespondNow(NoArguments()); \
} \
\
SmartCardProviderPrivateAPI::RequestId request_id(params->request_id); \
\
auto& scard_provider = \
SmartCardProviderPrivateAPI::Get(*browser_context()); \
scard_provider.ReportResultName(request_id, __VA_ARGS__); \
return RespondNow(NoArguments()); \
}
REPORT_RESULT_FUNCTION_IMPL(
ReleaseContext,
ReportReleaseContextResult,
ProviderResultCodeToSmartCardResult(params->result_code))
REPORT_RESULT_FUNCTION_IMPL(
EstablishContext,
ReportEstablishContextResult,
SmartCardProviderPrivateAPI::ContextId(params->scard_context),
ProviderResultCodeToSmartCardResult(params->result_code))
REPORT_RESULT_FUNCTION_IMPL(
Connect,
ReportConnectResult,
SmartCardProviderPrivateAPI::Handle(params->scard_handle),
ToDeviceMojomSmartCardProtocol(params->active_protocol),
ProviderResultCodeToSmartCardResult(params->result_code))
REPORT_RESULT_FUNCTION_IMPL(
ListReaders,
ReportResult,
std::move(params->readers),
ProviderResultCodeToSmartCardResult(params->result_code))
REPORT_RESULT_FUNCTION_IMPL(
GetStatusChange,
ReportResult,
ToSmartCardProviderReaderStateOutVector(params->reader_states),
ProviderResultCodeToSmartCardResult(params->result_code))
REPORT_RESULT_FUNCTION_IMPL(
Plain,
ReportResult,
std::monostate(),
ProviderResultCodeToSmartCardResult(params->result_code))
REPORT_RESULT_FUNCTION_IMPL(
Data,
ReportResult,
std::move(params->data),
ProviderResultCodeToSmartCardResult(params->result_code))
REPORT_RESULT_FUNCTION_IMPL(
Status,
ReportResult,
std::make_tuple(std::move(params->reader_name),
ToDeviceMojomSmartCardConnectionState(params->state),
ToDeviceMojomSmartCardProtocol(params->protocol),
std::move(params->atr)),
ProviderResultCodeToSmartCardResult(params->result_code))
#undef REPORT_RESULT_FUNCTION_IMPL
} // namespace extensions