/* * Copyright (C) 2018 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "HidConnection.h" #if ENABLE(WEB_AUTHN) #import <WebCore/FidoConstants.h> #import <wtf/BlockPtr.h> #import <wtf/RunLoop.h> namespace WebKit { using namespace fido; // FIXME(191518) static void reportReceived(void* context, IOReturn status, void*, IOHIDReportType type, uint32_t reportID, uint8_t* report, CFIndex reportLength) { ASSERT(RunLoop::isMain()); auto* connection = static_cast<HidConnection*>(context); if (status) { LOG_ERROR("Couldn't receive report from the authenticator: %d", status); connection->receiveReport({ }); return; } ASSERT(type == kIOHIDReportTypeInput); // FIXME(191519): Determine report id and max report size dynamically. ASSERT(reportID == kHidReportId); ASSERT(reportLength == kHidMaxPacketSize); Vector<uint8_t> reportData; reportData.append(report, reportLength); connection->receiveReport(WTFMove(reportData)); } HidConnection::HidConnection(IOHIDDeviceRef device) : m_device(device) { } HidConnection::~HidConnection() { ASSERT(!m_isInitialized); } void HidConnection::initialize() { IOHIDDeviceOpen(m_device.get(), kIOHIDOptionsTypeNone); IOHIDDeviceScheduleWithRunLoop(m_device.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); m_inputBuffer.resize(kHidMaxPacketSize); IOHIDDeviceRegisterInputReportCallback(m_device.get(), m_inputBuffer.data(), m_inputBuffer.size(), &reportReceived, this); m_isInitialized = true; } void HidConnection::terminate() { IOHIDDeviceRegisterInputReportCallback(m_device.get(), m_inputBuffer.data(), m_inputBuffer.size(), nullptr, nullptr); IOHIDDeviceUnscheduleFromRunLoop(m_device.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); IOHIDDeviceClose(m_device.get(), kIOHIDOptionsTypeNone); m_isInitialized = false; } auto HidConnection::sendSync(const Vector<uint8_t>& data) -> DataSent { ASSERT(m_isInitialized); auto status = IOHIDDeviceSetReport(m_device.get(), kIOHIDReportTypeOutput, kHidReportId, data.data(), data.size()); if (status) { LOG_ERROR("Couldn't send report to the authenticator: %d", status); return DataSent::No; } return DataSent::Yes; } void HidConnection::send(Vector<uint8_t>&& data, DataSentCallback&& callback) { ASSERT(m_isInitialized); auto task = makeBlockPtr([device = m_device, data = WTFMove(data), callback = WTFMove(callback)]() mutable { ASSERT(!RunLoop::isMain()); DataSent sent = DataSent::Yes; auto status = IOHIDDeviceSetReport(device.get(), kIOHIDReportTypeOutput, kHidReportId, data.data(), data.size()); if (status) { LOG_ERROR("Couldn't send report to the authenticator: %d", status); sent = DataSent::No; } RunLoop::main().dispatch([callback = WTFMove(callback), sent]() mutable { callback(sent); }); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), task.get()); } void HidConnection::registerDataReceivedCallback(DataReceivedCallback&& callback) { ASSERT(m_isInitialized); ASSERT(!m_inputCallback); m_inputCallback = WTFMove(callback); consumeReports(); registerDataReceivedCallbackInternal(); } void HidConnection::unregisterDataReceivedCallback() { m_inputCallback = nullptr; } void HidConnection::receiveReport(Vector<uint8_t>&& report) { m_inputReports.append(WTFMove(report)); consumeReports(); } void HidConnection::consumeReports() { while (!m_inputReports.isEmpty() && m_inputCallback) m_inputCallback(m_inputReports.takeFirst()); } void HidConnection::registerDataReceivedCallbackInternal() { } } // namespace WebKit #endif // ENABLE(WEB_AUTHN)