Fixes: https://bugs.chromium.org/p/chromium/issues/detail?id=451248

Uses /dev/fido via libfido2 bundled with the system. Due to an
abstraction level mismatch only the discovery phase is done via
libfido2.

The port was verified to work with demo.yubico.com, google.com,
github.com on amd64 6.6-current. 4 different devices from Yubico
and a couple other manufacturers work fine.

Known limitations:
  * the u2f device needs to be plugged into the USB port before
    starting authentication as the devices aren't discovered dynamically;
  * kernel crashes are possible without patching a problem showing up
    in filt_uhidrdetach https://marc.info/?l=openbsd-tech&m=157812352913919

The code started from FreeBSD port and then adapted for OpenBSD.
---
 www/chromium/Makefile                         |   4 +-
 www/chromium/files/hid_connection_fido.cc     | 196 +++++++++++
 www/chromium/files/hid_connection_fido.h      |  56 +++
 www/chromium/files/hid_service_fido.cc        | 321 ++++++++++++++++++
 www/chromium/files/hid_service_fido.h         |  40 +++
 www/chromium/files/unveil.main                |   3 +
 .../patch-services_device_hid_BUILD_gn        |  10 +-
 .../patch-services_device_hid_hid_service_cc  |  23 ++
 8 files changed, 650 insertions(+), 3 deletions(-)
 create mode 100644 www/chromium/files/hid_connection_fido.cc
 create mode 100644 www/chromium/files/hid_connection_fido.h
 create mode 100644 www/chromium/files/hid_service_fido.cc
 create mode 100644 www/chromium/files/hid_service_fido.h
 create mode 100644
www/chromium/patches/patch-services_device_hid_hid_service_cc

diff --git a/www/chromium/Makefile b/www/chromium/Makefile
index 0345e7dddf4..7104534624c 100644
--- a/www/chromium/Makefile
+++ b/www/chromium/Makefile
@@ -36,7 +36,7 @@ FLAVOR?=
 .if ${FLAVOR:Melectron}
 REVISION= 3
 .else
-REVISION= 0
+REVISION= 1
 .endif

 # BSD-like
@@ -79,6 +79,7 @@ WANTLIB += xml2 z
 WANTLIB += harfbuzz
 WANTLIB += ffi png
 WANTLIB += iconv lzma
+WANTLIB += fido2 cbor usbhid crypto

 RUN_DEPENDS= devel/xdg-utils \
  devel/desktop-file-utils \
@@ -251,6 +252,7 @@ pre-configure:
 .  endfor
 .endfor
  @mkdir -p ${WRKSRC}/media/audio/sndio ${WRKSRC}/media/audio/openbsd
+ @cp ${FILESDIR}/hid_{service,connection}_fido.{cc,h}
${WRKSRC}/services/device/hid
  @cp ${FILESDIR}/sndio_{out,in}put.{cc,h} ${WRKSRC}/media/audio/sndio
  @cp ${FILESDIR}/audio_manager_openbsd.{cc,h} ${WRKSRC}/media/audio/openbsd
  @mkdir -p ${WRKSRC}/third_party/node/openbsd/node-openbsd/bin
diff --git a/www/chromium/files/hid_connection_fido.cc
b/www/chromium/files/hid_connection_fido.cc
new file mode 100644
index 00000000000..90575659610
--- /dev/null
+++ b/www/chromium/files/hid_connection_fido.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/device/hid/hid_connection_fido.h"
+
+#include "base/bind.h"
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/location.h"
+#include "base/numerics/safe_math.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/single_thread_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/device_event_log/device_event_log.h"
+#include "services/device/hid/hid_service.h"
+
+namespace device {
+
+class HidConnectionFido::BlockingTaskHelper {
+public:
+  BlockingTaskHelper(base::ScopedFD fd,
+                     scoped_refptr<HidDeviceInfo> device_info,
+                     base::WeakPtr<HidConnectionFido> connection)
+      : fd_(std::move(fd)),
+        // Report buffers must always have room for the report ID.
+        report_buffer_size_(device_info->max_input_report_size() + 1),
+        has_report_id_(device_info->has_report_id()),
connection_(connection),
+        origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+    DETACH_FROM_SEQUENCE(sequence_checker_);
+  }
+
+  ~BlockingTaskHelper() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
+
+  // Starts the FileDescriptorWatcher that reads input events from the
device.
+  // Must be called on a thread that has a base::MessageLoopForIO.
+  void Start() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    base::internal::AssertBlockingAllowed();
+
+    file_watcher_ = base::FileDescriptorWatcher::WatchReadable(
+        fd_.get(),
base::Bind(&BlockingTaskHelper::OnFileCanReadWithoutBlocking,
+                              base::Unretained(this)));
+  }
+
+  void Write(scoped_refptr<base::RefCountedBytes> buffer,
+             WriteCallback callback) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+
+    auto data = buffer->front();
+    size_t size = buffer->size();
+    // if report id is 0, it shouldn't be included
+    if (data[0] == 0) {
+      data++;
+      size--;
+    }
+
+    ssize_t result = HANDLE_EINTR(write(fd_.get(), data, size));
+    bool success = static_cast<size_t>(result) == size;
+    if (!success) {
+      HID_LOG(EVENT) << "HID write failed: " << result << " != " << size;
+    }
+    origin_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(callback),
success));
+  }
+
+  void GetFeatureReport(uint8_t report_id,
+                        scoped_refptr<base::RefCountedBytes> buffer,
+                        ReadCallback callback) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+    HID_PLOG(EVENT) << "GendFeatureReport not implemented on OpenBSD";
+    origin_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(std::move(callback), false, nullptr, 0));
+  }
+
+  void SendFeatureReport(scoped_refptr<base::RefCountedBytes> buffer,
+                         WriteCallback callback) {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    HID_PLOG(EVENT) << "SendFeatureReport not implemented on OpenBSD";
+    origin_task_runner_->PostTask(FROM_HERE,
+                                  base::BindOnce(std::move(callback),
false));
+  }
+
+private:
+  void OnFileCanReadWithoutBlocking() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+    scoped_refptr<base::RefCountedBytes> buffer(
+        new base::RefCountedBytes(report_buffer_size_));
+    unsigned char *data = buffer->front();
+    size_t length = report_buffer_size_;
+    if (!has_report_id_) {
+      // OpenBSD will not prefix the buffer with a report ID if report IDs
are
+      // not used by the device. Prefix the buffer with 0.
+      *data++ = 0;
+      length--;
+    }
+
+    ssize_t bytes_read = HANDLE_EINTR(read(fd_.get(), data, length));
+    if (bytes_read < 0) {
+      if (errno != EAGAIN) {
+        HID_PLOG(EVENT) << "Read failed";
+        // This assumes that the error is unrecoverable and disables
reading
+        // from the device until it has been re-opened.
+        // TODO(reillyg): Investigate starting and stopping the file
descriptor
+        // watcher in response to pending read requests so that per-request
+        // errors can be returned to the client.
+        file_watcher_.reset();
+      }
+      return;
+    }
+    if (!has_report_id_) {
+      // Behave as if the byte prefixed above as the the report ID was
read.
+      bytes_read++;
+    }
+
+    origin_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&HidConnectionFido::ProcessInputReport,
+                                  connection_, buffer, bytes_read));
+  }
+
+  SEQUENCE_CHECKER(sequence_checker_);
+  base::ScopedFD fd_;
+  const size_t report_buffer_size_;
+  const bool has_report_id_;
+  base::WeakPtr<HidConnectionFido> connection_;
+  const scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
+  std::unique_ptr<base::FileDescriptorWatcher::Controller> file_watcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper);
+};
+
+HidConnectionFido::HidConnectionFido(
+    scoped_refptr<HidDeviceInfo> device_info, base::ScopedFD fd,
+    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
+    : HidConnection(device_info),
+      blocking_task_runner_(std::move(blocking_task_runner)),
+      weak_factory_(this),
+      helper_(std::make_unique<BlockingTaskHelper>(
+          std::move(fd), device_info, weak_factory_.GetWeakPtr())) {
+  blocking_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&BlockingTaskHelper::Start,
+                                base::Unretained(helper_.get())));
+}
+
+HidConnectionFido::~HidConnectionFido() = default;
+
+void HidConnectionFido::PlatformClose() {
+  // By closing the device on the blocking task runner 1) the requirement
that
+  // base::ScopedFD is destroyed on a thread where I/O is allowed is
satisfied
+  // and 2) any tasks posted to this task runner that refer to this file
will
+  // complete before it is closed.
+  blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
+}
+
+void HidConnectionFido::PlatformWrite(
+    scoped_refptr<base::RefCountedBytes> buffer, WriteCallback callback) {
+  blocking_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&BlockingTaskHelper::Write,
+                                base::Unretained(helper_.get()), buffer,
+                                std::move(callback)));
+}
+
+void HidConnectionFido::PlatformGetFeatureReport(uint8_t report_id,
+                                                 ReadCallback callback) {
+  // The first byte of the destination buffer is the report ID being
requested
+  // and is overwritten by the feature report.
+  DCHECK_GT(device_info()->max_feature_report_size(), 0u);
+  scoped_refptr<base::RefCountedBytes> buffer(
+      new base::RefCountedBytes(device_info()->max_feature_report_size() +
1));
+  if (report_id != 0)
+    buffer->data()[0] = report_id;
+
+  blocking_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&BlockingTaskHelper::GetFeatureReport,
+                                base::Unretained(helper_.get()), report_id,
+                                buffer, std::move(callback)));
+}
+
+void HidConnectionFido::PlatformSendFeatureReport(
+    scoped_refptr<base::RefCountedBytes> buffer, WriteCallback callback) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+
 base::BlockingType::MAY_BLOCK);
+  blocking_task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&BlockingTaskHelper::SendFeatureReport,
+                                base::Unretained(helper_.get()), buffer,
+                                std::move(callback)));
+}
+
+} // namespace device
diff --git a/www/chromium/files/hid_connection_fido.h
b/www/chromium/files/hid_connection_fido.h
new file mode 100644
index 00000000000..32e61bc3a48
--- /dev/null
+++ b/www/chromium/files/hid_connection_fido.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICE_EVICE_HID_HID_CONNECTION_FIDO_H_
+#define SERVICE_DEVICE_HID_HID_CONNECTION_FIDO_H_
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequence_checker.h"
+#include "services/device/hid/hid_connection.h"
+
+namespace device {
+
+class HidConnectionFido : public HidConnection {
+public:
+  HidConnectionFido(
+      scoped_refptr<HidDeviceInfo> device_info, base::ScopedFD fd,
+      scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
+
+private:
+  friend class base::RefCountedThreadSafe<HidConnectionFido>;
+  class BlockingTaskHelper;
+
+  ~HidConnectionFido() override;
+
+  // HidConnection implementation.
+  void PlatformClose() override;
+  void PlatformWrite(scoped_refptr<base::RefCountedBytes> buffer,
+                     WriteCallback callback) override;
+  void PlatformGetFeatureReport(uint8_t report_id,
+                                ReadCallback callback) override;
+  void PlatformSendFeatureReport(scoped_refptr<base::RefCountedBytes>
buffer,
+                                 WriteCallback callback) override;
+
+  const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<HidConnectionFido> weak_factory_;
+
+  // |helper_| lives on the sequence to which |blocking_task_runner_| posts
+  // tasks so all calls must be posted there including this object's
+  // destruction.
+  std::unique_ptr<BlockingTaskHelper> helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(HidConnectionFido);
+};
+
+} // namespace device
+
+#endif // SERVICE_DEVICE_HID_HID_CONNECTION_FIDO_H_
diff --git a/www/chromium/files/hid_service_fido.cc
b/www/chromium/files/hid_service_fido.cc
new file mode 100644
index 00000000000..6c3a0a87fa9
--- /dev/null
+++ b/www/chromium/files/hid_service_fido.cc
@@ -0,0 +1,321 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "services/device/hid/hid_service_fido.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+// TODO: remove once the missing guard in fido.h is fixed upstream.
+extern "C" {
+#include <fido.h>
+}
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file.h"
+#include "base/files/file_descriptor_watcher_posix.h"
+#include "base/files/file_enumerator.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/pattern.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/task/post_task.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/device_event_log/device_event_log.h"
+#include "services/device/hid/hid_connection_fido.h"
+
+namespace device {
+
+namespace {
+
+struct ConnectParams {
+  ConnectParams(scoped_refptr<HidDeviceInfo> device_info,
+                const HidService::ConnectCallback &callback)
+      : device_info(std::move(device_info)), callback(callback),
+        task_runner(base::ThreadTaskRunnerHandle::Get()),
+        blocking_task_runner(
+
 base::CreateSequencedTaskRunner(HidService::kBlockingTaskTraits)) {}
+  ~ConnectParams() {}
+
+  scoped_refptr<HidDeviceInfo> device_info;
+  HidService::ConnectCallback callback;
+  scoped_refptr<base::SequencedTaskRunner> task_runner;
+  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
+  base::ScopedFD fd;
+};
+
+void CreateConnection(std::unique_ptr<ConnectParams> params) {
+  DCHECK(params->fd.is_valid());
+  params->callback.Run(base::MakeRefCounted<HidConnectionFido>(
+      std::move(params->device_info), std::move(params->fd),
+      std::move(params->blocking_task_runner)));
+}
+
+void FinishOpen(std::unique_ptr<ConnectParams> params) {
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
params->task_runner;
+
+  task_runner->PostTask(FROM_HERE,
+                        base::Bind(&CreateConnection,
base::Passed(&params)));
+}
+
+bool terrible_ping_kludge(int fd, const std::string &path) {
+  u_char data[256];
+  int i, n;
+  struct pollfd pfd;
+
+  for (i = 0; i < 4; i++) {
+    memset(data, 0, sizeof(data));
+    /* broadcast channel ID */
+    data[1] = 0xff;
+    data[2] = 0xff;
+    data[3] = 0xff;
+    data[4] = 0xff;
+    /* Ping command */
+    data[5] = 0x81;
+    /* One byte ping only, Vasili */
+    data[6] = 0;
+    data[7] = 1;
+    HID_LOG(EVENT) << "send ping " << i << " " << path;
+    if (write(fd, data, 64) == -1) {
+      HID_PLOG(ERROR) << "write " << path;
+      return false;
+    }
+    HID_LOG(EVENT) << "wait reply " << path;
+    memset(&pfd, 0, sizeof(pfd));
+    pfd.fd = fd;
+    pfd.events = POLLIN;
+    if ((n = poll(&pfd, 1, 100)) == -1) {
+      HID_PLOG(EVENT) << "poll " << path;
+      return false;
+    } else if (n == 0) {
+      HID_LOG(EVENT) << "timed out " << path;
+      continue;
+    }
+    if (read(fd, data, 64) == -1) {
+      HID_PLOG(ERROR) << "read " << path;
+      return false;
+    }
+    /*
+     * Ping isn't always supported on the broadcast channel,
+     * so we might get an error, but we don't care - we're
+     * synched now.
+     */
+    HID_LOG(EVENT) << "got reply " << path;
+    return true;
+  }
+  HID_LOG(ERROR) << "no response " << path;
+  return false;
+}
+
+void OpenOnBlockingThread(std::unique_ptr<ConnectParams> params) {
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+
 base::BlockingType::MAY_BLOCK);
+  scoped_refptr<base::SequencedTaskRunner> task_runner =
params->task_runner;
+
+  const auto &device_node = params->device_info->device_node();
+  base::FilePath device_path(device_node);
+  int flags =
+      base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WRITE;
+  base::File device_file(device_path, flags);
+  if (!device_file.IsValid()) {
+    HID_LOG(EVENT) << "Failed to open '" << device_node << "': "
+                   <<
base::File::ErrorToString(device_file.error_details());
+    task_runner->PostTask(FROM_HERE, base::Bind(params->callback,
nullptr));
+    return;
+  }
+  if (!terrible_ping_kludge(device_file.GetPlatformFile(), device_node)) {
+    HID_LOG(EVENT) << "Failed to ping " << device_node;
+    task_runner->PostTask(FROM_HERE, base::Bind(params->callback,
nullptr));
+    return;
+  }
+  params->fd.reset(device_file.TakePlatformFile());
+  FinishOpen(std::move(params));
+}
+
+// HID report descriptor for U2F interface. Copied from:
+//
https://chromium.googlesource.com/chromiumos/platform2/+/c6c7e4e54fce11932fedaa3ea10236bf75d85a2b%5E%21/u2fd/u2fhid.cc
+// Apparently Chromium wants to see these bytes, but OpenBSD fido(4)
+// devices prohibit USB_GET_REPORT_DESC ioctl that could be used to
+// get the bytes from the USB device.
+constexpr uint8_t kU2fReportDesc[] = {
+    0x06, 0xD0, 0xF1, /* Usage Page (FIDO Alliance), FIDO_USAGE_PAGE */
+    0x09, 0x01,       /* Usage (U2F HID Auth. Device) FIDO_USAGE_U2FHID */
+    0xA1, 0x01,       /* Collection (Application), HID_APPLICATION */
+    0x09, 0x20,       /*  Usage (Input Report Data), FIDO_USAGE_DATA_IN */
+    0x15, 0x00,       /*  Logical Minimum (0) */
+    0x26, 0xFF, 0x00, /*  Logical Maximum (255) */
+    0x75, 0x08,       /*  Report Size (8) */
+    0x95, 0x40,       /*  Report Count (64), HID_INPUT_REPORT_BYTES */
+    0x81, 0x02,       /*  Input (Data, Var, Abs), Usage */
+    0x09, 0x21,       /*  Usage (Output Report Data), FIDO_USAGE_DATA_OUT
*/
+    0x15, 0x00,       /*  Logical Minimum (0) */
+    0x26, 0xFF, 0x00, /*  Logical Maximum (255) */
+    0x75, 0x08,       /*  Report Size (8) */
+    0x95, 0x40,       /*  Report Count (64), HID_OUTPUT_REPORT_BYTES */
+    0x91, 0x02,       /*  Output (Data, Var, Abs), Usage */
+    0xC0              /* End Collection */
+};
+
+} // namespace
+
+class HidServiceFido::BlockingTaskHelper {
+public:
+  BlockingTaskHelper(base::WeakPtr<HidServiceFido> service)
+      : service_(std::move(service)),
+        task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+    DETACH_FROM_SEQUENCE(sequence_checker_);
+  }
+
+  ~BlockingTaskHelper() = default;
+
+  void Start() {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+
+    fido_dev_info_t *devlist = NULL;
+    fido_dev_t *dev = NULL;
+    size_t devlist_len = 0, i;
+    const char *path;
+    int r;
+    const int MAX_FIDO_DEVICES = 256;
+
+    if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
+      HID_LOG(ERROR) << "fido_dev_info_new failed";
+      goto out;
+    }
+    if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
&devlist_len)) !=
+        FIDO_OK) {
+      HID_LOG(ERROR) << "fido_dev_info_manifest: " << fido_strerr(r);
+      goto out;
+    }
+
+    HID_LOG(EVENT) << "fido_dev_info_manifest found " << devlist_len
+                   << " device(s)";
+
+    for (i = 0; i < devlist_len; i++) {
+      const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
+      if (di == NULL) {
+        HID_LOG(ERROR) << "fido_dev_info_ptr " << i << " failed";
+        continue;
+      }
+      if ((path = fido_dev_info_path(di)) == NULL) {
+        HID_LOG(ERROR) << "fido_dev_info_path " << i << " failed";
+        continue;
+      }
+      HID_LOG(EVENT) << "trying device " << i << ": " << path;
+      if ((dev = fido_dev_new()) == NULL) {
+        HID_LOG(ERROR) << "fido_dev_new failed";
+        continue;
+      }
+      if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
+        HID_LOG(ERROR) << "fido_dev_open failed " << path;
+        fido_dev_free(&dev);
+        continue;
+      }
+      OnDeviceAdded(di);
+      fido_dev_close(dev);
+      fido_dev_free(&dev);
+    }
+
+  out:
+    if (devlist != NULL)
+      fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
+
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&HidServiceFido::FirstEnumerationComplete, service_));
+  }
+
+  void OnDeviceAdded(const fido_dev_info_t *di) {
+    auto null_as_empty = [](const char *r) -> std::string {
+      return (r != nullptr) ? r : "";
+    };
+    std::string device_node(null_as_empty(fido_dev_info_path(di)));
+    std::vector<uint8_t> report_descriptor(
+        kU2fReportDesc, kU2fReportDesc + sizeof(kU2fReportDesc));
+    scoped_refptr<HidDeviceInfo> device_info(new HidDeviceInfo(
+        device_node, fido_dev_info_vendor(di), fido_dev_info_product(di),
+        null_as_empty(fido_dev_info_product_string(di)),
+        null_as_empty(fido_dev_info_manufacturer_string(di)),
+        device::mojom::HidBusType::kHIDBusTypeUSB, report_descriptor,
+        device_node));
+
+    task_runner_->PostTask(FROM_HERE,
base::Bind(&HidServiceFido::AddDevice,
+                                                 service_, device_info));
+  }
+
+  void OnDeviceRemoved(std::string device_node) {
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+    task_runner_->PostTask(FROM_HERE,
base::Bind(&HidServiceFido::RemoveDevice,
+                                                 service_, device_node));
+  }
+
+private:
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  // This weak pointer is only valid when checked on this task runner.
+  base::WeakPtr<HidServiceFido> service_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlockingTaskHelper);
+};
+
+HidServiceFido::HidServiceFido()
+    : task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      blocking_task_runner_(
+          base::CreateSequencedTaskRunner(kBlockingTaskTraits)),
+      weak_factory_(this), helper_(std::make_unique<BlockingTaskHelper>(
+                               weak_factory_.GetWeakPtr())) {
+  blocking_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&BlockingTaskHelper::Start,
base::Unretained(helper_.get())));
+}
+
+HidServiceFido::~HidServiceFido() {
+  blocking_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
+}
+
+base::WeakPtr<HidService> HidServiceFido::GetWeakPtr() {
+  return weak_factory_.GetWeakPtr();
+}
+
+void HidServiceFido::Connect(const std::string &device_guid,
+                             const ConnectCallback &callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  const auto &map_entry = devices().find(device_guid);
+  if (map_entry == devices().end()) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(callback, nullptr));
+    return;
+  }
+
+  scoped_refptr<HidDeviceInfo> device_info = map_entry->second;
+
+  auto params = std::make_unique<ConnectParams>(device_info, callback);
+
+  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
+      params->blocking_task_runner;
+  blocking_task_runner->PostTask(
+      FROM_HERE, base::Bind(&OpenOnBlockingThread, base::Passed(&params)));
+}
+
+} // namespace device
diff --git a/www/chromium/files/hid_service_fido.h
b/www/chromium/files/hid_service_fido.h
new file mode 100644
index 00000000000..a116043d224
--- /dev/null
+++ b/www/chromium/files/hid_service_fido.h
@@ -0,0 +1,40 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICE_DEVICE_HID_HID_SERVICE_FIDO_H_
+#define SERVICE_DEVICE_HID_HID_SERVICE_FIDO_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "services/device/hid/hid_service.h"
+
+namespace device {
+
+class HidServiceFido : public HidService {
+public:
+  HidServiceFido();
+  ~HidServiceFido() override;
+
+  void Connect(const std::string &device_guid,
+               const ConnectCallback &connect) override;
+  base::WeakPtr<HidService> GetWeakPtr() override;
+
+private:
+  class BlockingTaskHelper;
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
+  base::WeakPtrFactory<HidServiceFido> weak_factory_;
+  // |helper_| lives on the sequence |blocking_task_runner_| posts to and
holds
+  // a weak reference back to the service that owns it.
+  std::unique_ptr<BlockingTaskHelper> helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(HidServiceFido);
+};
+
+} // namespace device
+
+#endif // SERVICE_DEVICE_HID_HID_SERVICE_FIDO_H_
diff --git a/www/chromium/files/unveil.main b/www/chromium/files/unveil.main
index 6cda8ca819b..8d456b8dea1 100644
--- a/www/chromium/files/unveil.main
+++ b/www/chromium/files/unveil.main
@@ -3,6 +3,9 @@
 # needed for video(4)
 /dev/video rw

+# needed for FIDO authentication
+/dev/fido rw
+
 # needed for chromium
 /etc/chromium r

diff --git a/www/chromium/patches/patch-services_device_hid_BUILD_gn
b/www/chromium/patches/patch-services_device_hid_BUILD_gn
index 9ee086068f1..80cc09bd6c2 100644
--- a/www/chromium/patches/patch-services_device_hid_BUILD_gn
+++ b/www/chromium/patches/patch-services_device_hid_BUILD_gn
@@ -3,7 +3,7 @@ $OpenBSD: patch-services_device_hid_BUILD_gn,v 1.3
2018/04/23 15:00:16 robert Ex
 Index: services/device/hid/BUILD.gn
 --- services/device/hid/BUILD.gn.orig
 +++ services/device/hid/BUILD.gn
-@@ -41,6 +41,13 @@ source_set("hid") {
+@@ -41,6 +41,19 @@ source_set("hid") {
      "//services/device/public/mojom",
    ]

@@ -12,8 +12,14 @@ Index: services/device/hid/BUILD.gn
 +      "hid_connection_linux.cc",
 +      "hid_connection_linux.h",
 +    ]
++    sources += [
++      "hid_connection_fido.cc",
++      "hid_connection_fido.h",
++      "hid_service_fido.cc",
++      "hid_service_fido.h",
++    ]
++    libs = ["fido2", "cbor", "usbhid", "crypto", "util"]
 +  }
-+
    if (is_linux && use_udev) {
      sources += [
        "hid_service_linux.cc",
diff --git a/www/chromium/patches/patch-services_device_hid_hid_service_cc
b/www/chromium/patches/patch-services_device_hid_hid_service_cc
new file mode 100644
index 00000000000..9bfb270e094
--- /dev/null
+++ b/www/chromium/patches/patch-services_device_hid_hid_service_cc
@@ -0,0 +1,23 @@
+$OpenBSD$
+
+Index: services/device/hid/hid_service.cc
+--- services/device/hid/hid_service.cc.orig
++++ services/device/hid/hid_service.cc
+@@ -16,6 +16,8 @@
+
+ #if defined(OS_LINUX) && defined(USE_UDEV)
+ #include "services/device/hid/hid_service_linux.h"
++#elif defined(OS_BSD)
++#include "services/device/hid/hid_service_fido.h"
+ #elif defined(OS_MACOSX)
+ #include "services/device/hid/hid_service_mac.h"
+ #elif defined(OS_WIN)
+@@ -36,6 +38,8 @@ constexpr base::TaskTraits HidService::kBlockingTaskTr
+ std::unique_ptr<HidService> HidService::Create() {
+ #if defined(OS_LINUX) && defined(USE_UDEV)
+   return base::WrapUnique(new HidServiceLinux());
++#elif defined(OS_BSD)
++  return base::WrapUnique(new HidServiceFido());
+ #elif defined(OS_MACOSX)
+   return base::WrapUnique(new HidServiceMac());
+ #elif defined(OS_WIN)
-- 
2.24.1



-- 
nest.cx is Gmail hosted, use PGP:
https://pgp.key-server.io/0x0B1542BD8DF5A1B0
Fingerprint: 5E2B 2D0E 1E03 2046 BEC3  4D50 0B15 42BD 8DF5 A1B0

Reply via email to