Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 1 | // Copyright 2017 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include <algorithm> |
| 6 | #include <memory> |
| 7 | #include <utility> |
| 8 | |
| 9 | #include <base/bind.h> |
| 10 | #include <base/callback.h> |
Louis Collard | 10ac9e9 | 2019-02-23 18:34:45 +0800 | [diff] [blame] | 11 | #include <base/optional.h> |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 12 | #include <base/strings/string_number_conversions.h> |
| 13 | #include <base/sys_byteorder.h> |
| 14 | #include <base/timer/timer.h> |
Louis Collard | 10ac9e9 | 2019-02-23 18:34:45 +0800 | [diff] [blame] | 15 | #include <openssl/bn.h> |
| 16 | #include <openssl/ecdsa.h> |
| 17 | #include <trunks/cr50_headers/u2f.h> |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 18 | |
Louis Collard | 10ac9e9 | 2019-02-23 18:34:45 +0800 | [diff] [blame] | 19 | #include "u2fd/u2f_adpu.h" |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 20 | #include "u2fd/u2fhid.h" |
Louis Collard | f59aa94 | 2019-02-25 17:50:14 +0800 | [diff] [blame] | 21 | #include "u2fd/user_state.h" |
Louis Collard | 10ac9e9 | 2019-02-23 18:34:45 +0800 | [diff] [blame] | 22 | #include "u2fd/util.h" |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 23 | |
| 24 | namespace { |
| 25 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 26 | // Size of the payload for an INIT U2F HID report. |
| 27 | constexpr size_t kInitReportPayloadSize = 57; |
| 28 | // Size of the payload for a Continuation U2F HID report. |
| 29 | constexpr size_t kContReportPayloadSize = 59; |
| 30 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 31 | constexpr uint8_t kInterfaceVersion = 2; |
| 32 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 33 | constexpr int kU2fHidTimeoutMs = 500; |
| 34 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 35 | // Maximum duration one can keep the channel lock as specified by the U2FHID |
| 36 | // specification |
| 37 | constexpr int kMaxLockDurationSeconds = 10; |
| 38 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 39 | // HID report descriptor for U2F interface. |
| 40 | constexpr uint8_t kU2fReportDesc[] = { |
| 41 | 0x06, 0xD0, 0xF1, /* Usage Page (FIDO Alliance), FIDO_USAGE_PAGE */ |
| 42 | 0x09, 0x01, /* Usage (U2F HID Auth. Device) FIDO_USAGE_U2FHID */ |
| 43 | 0xA1, 0x01, /* Collection (Application), HID_APPLICATION */ |
| 44 | 0x09, 0x20, /* Usage (Input Report Data), FIDO_USAGE_DATA_IN */ |
| 45 | 0x15, 0x00, /* Logical Minimum (0) */ |
| 46 | 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ |
| 47 | 0x75, 0x08, /* Report Size (8) */ |
| 48 | 0x95, 0x40, /* Report Count (64), HID_INPUT_REPORT_BYTES */ |
| 49 | 0x81, 0x02, /* Input (Data, Var, Abs), Usage */ |
| 50 | 0x09, 0x21, /* Usage (Output Report Data), FIDO_USAGE_DATA_OUT */ |
| 51 | 0x15, 0x00, /* Logical Minimum (0) */ |
| 52 | 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ |
| 53 | 0x75, 0x08, /* Report Size (8) */ |
| 54 | 0x95, 0x40, /* Report Count (64), HID_OUTPUT_REPORT_BYTES */ |
| 55 | 0x91, 0x02, /* Output (Data, Var, Abs), Usage */ |
| 56 | 0xC0 /* End Collection */ |
| 57 | }; |
| 58 | |
| 59 | } // namespace |
| 60 | |
| 61 | namespace u2f { |
| 62 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 63 | class U2fHid::HidPacket { |
| 64 | public: |
| 65 | explicit HidPacket(const std::string& report); |
| 66 | |
| 67 | bool IsValidFrame() const { return valid_; } |
| 68 | |
| 69 | bool IsInitFrame() const { return (tcs_ & kFrameTypeMask) == kFrameTypeInit; } |
| 70 | |
| 71 | uint32_t ChannelId() const { return cid_; } |
| 72 | |
| 73 | U2fHid::U2fHidCommand Command() const { |
| 74 | return static_cast<U2fHidCommand>(tcs_ & ~kFrameTypeMask); |
| 75 | } |
| 76 | |
| 77 | uint8_t SeqNumber() const { return tcs_ & ~kFrameTypeMask; } |
| 78 | |
| 79 | int PayloadIndex() const { return IsInitFrame() ? 8 : 6; } |
| 80 | |
| 81 | size_t MessagePayloadSize() const { return bcnt_; } |
| 82 | |
| 83 | private: |
| 84 | bool valid_; |
| 85 | uint32_t cid_; // Channel Identifier |
| 86 | uint8_t tcs_; // type and command or sequence number |
| 87 | uint16_t bcnt_; // payload length as defined by U2fHID specification |
| 88 | }; |
| 89 | |
| 90 | U2fHid::HidPacket::HidPacket(const std::string& report) |
| 91 | : valid_(false), cid_(0), tcs_(0), bcnt_(0) { |
| 92 | // the report is prefixed by the report ID (we skip it below). |
| 93 | if (report.size() != kU2fReportSize + 1) /* Invalid U2FHID report */ |
| 94 | return; |
| 95 | |
| 96 | // U2FHID frame bytes parsing. |
| 97 | // As defined in the "FIDO U2F HID Protocol Specification": |
| 98 | // An initialization packet is defined as |
| 99 | // |
| 100 | // Offset Length Mnemonic Description |
| 101 | // 0 4 CID Channel identifier |
| 102 | // 4 1 CMD Command identifier (bit 7 always set) |
| 103 | // 5 1 BCNTH High part of payload length |
| 104 | // 6 1 BCNTL Low part of payload length |
| 105 | // 7 (s - 7) DATA Payload data (s is the fixed packet size) |
| 106 | // The command byte has always the highest bit set to distinguish it |
| 107 | // from a continuation packet, which is described below. |
| 108 | // |
| 109 | // A continuation packet is defined as |
| 110 | // |
| 111 | // Offset Length Mnemonic Description |
| 112 | // 0 4 CID Channel identifier |
| 113 | // 4 1 SEQ Packet sequence 0x00..0x7f (bit 7 always cleared) |
| 114 | // 5 (s - 5) DATA Payload data (s is the fixed packet size) |
| 115 | // With this approach, a message with a payload less or equal to (s - 7) |
| 116 | // may be sent as one packet. A larger message is then divided into one or |
| 117 | // more continuation packets, starting with sequence number 0 which then |
| 118 | // increments by one to a maximum of 127. |
| 119 | |
| 120 | // the CID word is not aligned |
| 121 | memcpy(&cid_, &report[1], sizeof(cid_)); |
| 122 | tcs_ = report[5]; |
| 123 | |
| 124 | uint16_t raw_count; |
| 125 | memcpy(&raw_count, &report[6], sizeof(raw_count)); |
| 126 | bcnt_ = base::NetToHost16(raw_count); |
| 127 | |
| 128 | valid_ = true; |
| 129 | } |
| 130 | |
| 131 | class U2fHid::HidMessage { |
| 132 | public: |
| 133 | HidMessage(U2fHidCommand cmd, uint32_t cid) : cid_(cid), cmd_(cmd) {} |
| 134 | |
| 135 | // Appends |bytes| to the message payload. |
| 136 | void AddPayload(const std::string& bytes); |
| 137 | |
| 138 | // Appends the single |byte| to the message payload. |
| 139 | void AddByte(uint8_t byte); |
| 140 | |
| 141 | // Fills an HID report with the part of the message starting at |offset|. |
| 142 | // Returns the offset of the remaining unused content in the message. |
| 143 | int BuildReport(int offset, std::string* report_out); |
| 144 | |
| 145 | private: |
| 146 | uint32_t cid_; |
| 147 | U2fHidCommand cmd_; |
| 148 | std::string payload_; |
| 149 | }; |
| 150 | |
| 151 | void U2fHid::HidMessage::AddPayload(const std::string& bytes) { |
| 152 | payload_ += bytes; |
| 153 | } |
| 154 | |
| 155 | void U2fHid::HidMessage::AddByte(uint8_t byte) { |
| 156 | payload_.push_back(byte); |
| 157 | } |
| 158 | |
| 159 | int U2fHid::HidMessage::BuildReport(int offset, std::string* report_out) { |
| 160 | size_t data_size; |
| 161 | |
| 162 | // Serialize one chunk of the message in a 64-byte HID report |
| 163 | // (see the HID report structure in HidPacket constructor) |
| 164 | report_out->assign( |
| 165 | std::string(reinterpret_cast<char*>(&cid_), sizeof(uint32_t))); |
| 166 | if (offset == 0) { // INIT message |
| 167 | uint16_t bcnt = payload_.size(); |
| 168 | report_out->push_back(static_cast<uint8_t>(cmd_) | kFrameTypeInit); |
| 169 | report_out->push_back(bcnt >> 8); |
| 170 | report_out->push_back(bcnt & 0xff); |
| 171 | data_size = kInitReportPayloadSize; |
| 172 | } else { // CONT message |
| 173 | // Insert sequence number. |
| 174 | report_out->push_back((offset - kInitReportPayloadSize) / |
| 175 | kContReportPayloadSize); |
| 176 | data_size = kContReportPayloadSize; |
| 177 | } |
| 178 | data_size = std::min(data_size, payload_.size() - offset); |
| 179 | *report_out += payload_.substr(offset, data_size); |
| 180 | // Ensure the report is 64-B long |
| 181 | report_out->insert(report_out->end(), kU2fReportSize - report_out->size(), 0); |
| 182 | offset += data_size; |
| 183 | |
| 184 | VLOG(2) << "TX RPT [" |
| 185 | << base::HexEncode(report_out->data(), report_out->size()) << "]"; |
| 186 | |
| 187 | return offset != payload_.size() ? offset : 0; |
| 188 | } |
| 189 | |
| 190 | struct U2fHid::Transaction { |
Vincent Palatin | 167f0fb | 2017-08-11 22:28:12 +0200 | [diff] [blame] | 191 | uint32_t cid = 0; |
| 192 | U2fHidCommand cmd = U2fHidCommand::kError; |
| 193 | size_t total_size = 0; |
| 194 | int seq = 0; |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 195 | std::string payload; |
| 196 | base::OneShotTimer timeout; |
| 197 | }; |
| 198 | |
| 199 | U2fHid::U2fHid(std::unique_ptr<HidInterface> hid, |
Louis Collard | ca8d271 | 2019-08-26 17:57:58 +0800 | [diff] [blame] | 200 | U2fMessageHandler* msg_handler) |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 201 | : hid_(std::move(hid)), |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 202 | free_cid_(1), |
Vincent Palatin | 828bfe9 | 2018-04-05 15:14:44 +0200 | [diff] [blame] | 203 | locked_cid_(0), |
Louis Collard | ca8d271 | 2019-08-26 17:57:58 +0800 | [diff] [blame] | 204 | msg_handler_(msg_handler) { |
Ben Chan | 921e975 | 2017-09-29 00:27:10 -0700 | [diff] [blame] | 205 | transaction_ = std::make_unique<Transaction>(); |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 206 | hid_->SetOutputReportHandler( |
| 207 | base::Bind(&U2fHid::ProcessReport, base::Unretained(this))); |
| 208 | } |
| 209 | |
| 210 | U2fHid::~U2fHid() = default; |
| 211 | |
| 212 | bool U2fHid::Init() { |
| 213 | return hid_->Init(kInterfaceVersion, |
| 214 | std::string(reinterpret_cast<const char*>(kU2fReportDesc), |
| 215 | sizeof(kU2fReportDesc))); |
| 216 | } |
| 217 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 218 | void U2fHid::ReturnError(U2fHidError errcode, uint32_t cid, bool clear) { |
| 219 | HidMessage msg(U2fHidCommand::kError, cid); |
| 220 | |
| 221 | msg.AddByte(static_cast<uint8_t>(errcode)); |
| 222 | VLOG(1) << "ERROR/" << std::hex << static_cast<int>(errcode) |
| 223 | << " CID:" << std::hex << cid; |
| 224 | if (clear) |
Ben Chan | 921e975 | 2017-09-29 00:27:10 -0700 | [diff] [blame] | 225 | transaction_ = std::make_unique<Transaction>(); |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 226 | |
| 227 | std::string report; |
| 228 | msg.BuildReport(0, &report); |
| 229 | hid_->SendReport(report); |
| 230 | } |
| 231 | |
| 232 | void U2fHid::TransactionTimeout() { |
| 233 | ReturnError(U2fHidError::kMsgTimeout, transaction_->cid, true); |
| 234 | } |
| 235 | |
| 236 | void U2fHid::LockTimeout() { |
| 237 | if (locked_cid_) |
| 238 | LOG(WARNING) << "Cancelled lock CID:" << std::hex << locked_cid_; |
| 239 | locked_cid_ = 0; |
| 240 | } |
| 241 | |
| 242 | void U2fHid::ReturnResponse(const std::string& resp) { |
| 243 | HidMessage msg(transaction_->cmd, transaction_->cid); |
| 244 | int offset = 0; |
| 245 | |
| 246 | msg.AddPayload(resp); |
| 247 | // Send all the chunks of the message (split in 64-B HID reports) |
| 248 | do { |
| 249 | std::string report; |
| 250 | offset = msg.BuildReport(offset, &report); |
| 251 | hid_->SendReport(report); |
| 252 | } while (offset); |
| 253 | } |
| 254 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 255 | void U2fHid::CmdInit(uint32_t cid, const std::string& payload) { |
| 256 | HidMessage msg(U2fHidCommand::kInit, cid); |
| 257 | |
| 258 | if (payload.size() != kInitNonceSize) { |
| 259 | VLOG(1) << "Payload size " << payload.size(); |
| 260 | ReturnError(U2fHidError::kInvalidLen, cid, false); |
| 261 | return; |
| 262 | } |
| 263 | |
| 264 | VLOG(1) << "INIT CID:" << std::hex << cid << " NONCE " |
| 265 | << base::HexEncode(payload.data(), payload.size()); |
| 266 | |
| 267 | if (cid == kCidBroadcast) { // Allocate Channel ID |
| 268 | cid = free_cid_++; |
| 269 | // Roll-over if needed |
| 270 | if (free_cid_ == kCidBroadcast) |
| 271 | free_cid_ = 1; |
| 272 | } |
| 273 | |
| 274 | // Keep the nonce in the first 8 bytes |
| 275 | msg.AddPayload(payload); |
| 276 | |
| 277 | std::string serial_cid(reinterpret_cast<char*>(&cid), sizeof(uint32_t)); |
| 278 | msg.AddPayload(serial_cid); |
| 279 | |
| 280 | // Append the versions : interface / major / minor / build |
| 281 | msg.AddByte(kInterfaceVersion); |
| 282 | msg.AddByte(0); |
| 283 | msg.AddByte(0); |
| 284 | msg.AddByte(0); |
| 285 | // Append Capability flags |
Louis Collard | e41caa5 | 2020-02-12 13:54:04 +0800 | [diff] [blame] | 286 | msg.AddByte(kCapFlagLock); |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 287 | |
| 288 | std::string report; |
| 289 | msg.BuildReport(0, &report); |
| 290 | hid_->SendReport(report); |
| 291 | } |
| 292 | |
| 293 | int U2fHid::CmdPing(std::string* resp) { |
| 294 | VLOG(1) << "PING len " << transaction_->total_size; |
| 295 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 296 | // send back the same content |
| 297 | *resp = transaction_->payload.substr(0, transaction_->total_size); |
| 298 | return transaction_->total_size; |
| 299 | } |
| 300 | |
| 301 | int U2fHid::CmdLock(std::string* resp) { |
| 302 | int duration = transaction_->payload[0]; |
| 303 | |
| 304 | VLOG(1) << "LOCK " << duration << "s CID:" << std::hex << transaction_->cid; |
| 305 | |
| 306 | if (duration > kMaxLockDurationSeconds) { |
| 307 | ReturnError(U2fHidError::kInvalidPar, transaction_->cid, true); |
| 308 | return -EINVAL; |
| 309 | } |
| 310 | |
| 311 | if (!duration) { |
| 312 | lock_timeout_.Stop(); |
| 313 | locked_cid_ = 0; |
| 314 | } else { |
| 315 | locked_cid_ = transaction_->cid; |
| 316 | lock_timeout_.Start( |
Tom Hughes | 0f7203b | 2020-08-24 18:29:15 -0700 | [diff] [blame] | 317 | FROM_HERE, base::TimeDelta::FromSeconds(duration), |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 318 | base::Bind(&U2fHid::LockTimeout, base::Unretained(this))); |
| 319 | } |
| 320 | return 0; |
| 321 | } |
| 322 | |
Vincent Palatin | 828bfe9 | 2018-04-05 15:14:44 +0200 | [diff] [blame] | 323 | int U2fHid::CmdSysInfo(std::string* resp) { |
Louis Collard | b451477 | 2019-08-15 16:13:30 +0800 | [diff] [blame] | 324 | LOG(WARNING) << "Received unsupported SysInfo command"; |
| 325 | ReturnError(U2fHidError::kInvalidCmd, transaction_->cid, true); |
| 326 | return -EINVAL; |
Vincent Palatin | 828bfe9 | 2018-04-05 15:14:44 +0200 | [diff] [blame] | 327 | } |
| 328 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 329 | int U2fHid::CmdMsg(std::string* resp) { |
Louis Collard | ca8d271 | 2019-08-26 17:57:58 +0800 | [diff] [blame] | 330 | U2fResponseAdpu r = msg_handler_->ProcessMsg(transaction_->payload); |
Louis Collard | 10ac9e9 | 2019-02-23 18:34:45 +0800 | [diff] [blame] | 331 | |
Louis Collard | ca8d271 | 2019-08-26 17:57:58 +0800 | [diff] [blame] | 332 | r.ToString(resp); |
Louis Collard | 10ac9e9 | 2019-02-23 18:34:45 +0800 | [diff] [blame] | 333 | |
| 334 | return 0; |
| 335 | } |
| 336 | |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 337 | void U2fHid::ExecuteCmd() { |
| 338 | int rc; |
| 339 | std::string resp; |
| 340 | |
| 341 | transaction_->timeout.Stop(); |
| 342 | switch (transaction_->cmd) { |
| 343 | case U2fHidCommand::kMsg: |
| 344 | rc = CmdMsg(&resp); |
| 345 | break; |
| 346 | case U2fHidCommand::kPing: |
| 347 | rc = CmdPing(&resp); |
| 348 | break; |
| 349 | case U2fHidCommand::kLock: |
| 350 | rc = CmdLock(&resp); |
| 351 | break; |
Vincent Palatin | 828bfe9 | 2018-04-05 15:14:44 +0200 | [diff] [blame] | 352 | case U2fHidCommand::kVendorSysInfo: |
| 353 | rc = CmdSysInfo(&resp); |
| 354 | break; |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 355 | default: |
| 356 | LOG(WARNING) << "Unknown command " << std::hex |
| 357 | << static_cast<int>(transaction_->cmd); |
| 358 | ReturnError(U2fHidError::kInvalidCmd, transaction_->cid, true); |
| 359 | return; |
| 360 | } |
| 361 | |
| 362 | if (rc >= 0) |
| 363 | ReturnResponse(resp); |
| 364 | |
| 365 | // we are done with this transaction |
Ben Chan | 921e975 | 2017-09-29 00:27:10 -0700 | [diff] [blame] | 366 | transaction_ = std::make_unique<Transaction>(); |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 367 | } |
| 368 | |
| 369 | void U2fHid::ProcessReport(const std::string& report) { |
| 370 | HidPacket pkt(report); |
| 371 | |
| 372 | VLOG(2) << "RX RPT/" << report.size() << " [" |
| 373 | << base::HexEncode(report.data(), report.size()) << "]"; |
| 374 | if (!pkt.IsValidFrame()) |
| 375 | return; // Invalid report |
| 376 | |
| 377 | // Check frame validity |
| 378 | if (pkt.ChannelId() == 0) { |
| 379 | VLOG(1) << "No frame should use channel 0"; |
Tom Hughes | 0f7203b | 2020-08-24 18:29:15 -0700 | [diff] [blame] | 380 | ReturnError(U2fHidError::kInvalidCid, pkt.ChannelId(), |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 381 | pkt.ChannelId() == transaction_->cid); |
| 382 | return; |
| 383 | } |
| 384 | |
| 385 | if (pkt.IsInitFrame() && pkt.Command() == U2fHidCommand::kInit) { |
| 386 | if (pkt.ChannelId() == transaction_->cid) { |
| 387 | // Abort an ongoing multi-packet transaction |
| 388 | VLOG(1) << "Transaction cancelled on CID:" << std::hex << pkt.ChannelId(); |
Ben Chan | 921e975 | 2017-09-29 00:27:10 -0700 | [diff] [blame] | 389 | transaction_ = std::make_unique<Transaction>(); |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 390 | } |
| 391 | // special case: INIT should not interrupt other commands |
| 392 | CmdInit(pkt.ChannelId(), report.substr(pkt.PayloadIndex(), kInitNonceSize)); |
| 393 | return; |
| 394 | } |
| 395 | // not an INIT command from here |
| 396 | |
| 397 | if (pkt.IsInitFrame()) { // INIT frame type (not the INIT command) |
| 398 | if (pkt.ChannelId() == kCidBroadcast) { |
| 399 | VLOG(1) << "INIT command not on broadcast CID:" << std::hex |
| 400 | << pkt.ChannelId(); |
| 401 | ReturnError(U2fHidError::kInvalidCid, pkt.ChannelId(), false); |
| 402 | return; |
| 403 | } |
| 404 | if (locked_cid_ && pkt.ChannelId() != locked_cid_) { |
| 405 | // somebody else has the lock |
| 406 | VLOG(1) << "channel locked by CID:" << std::hex << locked_cid_; |
| 407 | ReturnError(U2fHidError::kChannelBusy, pkt.ChannelId(), false); |
| 408 | return; |
| 409 | } |
| 410 | if (transaction_->cid && (pkt.ChannelId() != transaction_->cid)) { |
| 411 | VLOG(1) << "channel used by CID:" << std::hex << transaction_->cid; |
| 412 | ReturnError(U2fHidError::kChannelBusy, pkt.ChannelId(), false); |
| 413 | return; |
| 414 | } |
| 415 | if (transaction_->cid) { |
| 416 | VLOG(1) << "CONT frame expected"; |
| 417 | ReturnError(U2fHidError::kInvalidSeq, pkt.ChannelId(), true); |
| 418 | return; |
| 419 | } |
| 420 | if (pkt.MessagePayloadSize() > kMaxPayloadSize) { |
| 421 | VLOG(1) << "Invalid length " << pkt.MessagePayloadSize(); |
| 422 | ReturnError(U2fHidError::kInvalidLen, pkt.ChannelId(), true); |
| 423 | return; |
| 424 | } |
| 425 | |
| 426 | transaction_->timeout.Start( |
Tom Hughes | 0f7203b | 2020-08-24 18:29:15 -0700 | [diff] [blame] | 427 | FROM_HERE, base::TimeDelta::FromMilliseconds(kU2fHidTimeoutMs), |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 428 | base::Bind(&U2fHid::TransactionTimeout, base::Unretained(this))); |
| 429 | |
| 430 | // record transaction parameters |
| 431 | transaction_->cid = pkt.ChannelId(); |
| 432 | transaction_->total_size = pkt.MessagePayloadSize(); |
| 433 | transaction_->cmd = pkt.Command(); |
| 434 | transaction_->seq = 0; |
| 435 | transaction_->payload = |
| 436 | report.substr(pkt.PayloadIndex(), transaction_->total_size); |
| 437 | } else { // CONT Frame |
Vincent Palatin | c03a6eb | 2017-12-01 15:05:59 +0100 | [diff] [blame] | 438 | if (transaction_->cid == 0 || transaction_->cid != pkt.ChannelId()) { |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 439 | VLOG(1) << "invalid CONT"; |
| 440 | return; // just ignore |
| 441 | } |
| 442 | if (transaction_->seq != pkt.SeqNumber()) { |
| 443 | VLOG(1) << "invalid sequence " << static_cast<int>(pkt.SeqNumber()) |
| 444 | << " != " << transaction_->seq; |
Tom Hughes | 0f7203b | 2020-08-24 18:29:15 -0700 | [diff] [blame] | 445 | ReturnError(U2fHidError::kInvalidSeq, pkt.ChannelId(), |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 446 | pkt.ChannelId() == transaction_->cid); |
| 447 | return; |
| 448 | } |
| 449 | // reload timeout |
| 450 | transaction_->timeout.Start( |
Tom Hughes | 0f7203b | 2020-08-24 18:29:15 -0700 | [diff] [blame] | 451 | FROM_HERE, base::TimeDelta::FromMilliseconds(kU2fHidTimeoutMs), |
Vincent Palatin | c6c7e4e | 2017-06-15 15:45:05 +0200 | [diff] [blame] | 452 | base::Bind(&U2fHid::TransactionTimeout, base::Unretained(this))); |
| 453 | // record the payload |
| 454 | transaction_->payload += report.substr(pkt.PayloadIndex()); |
| 455 | transaction_->seq++; |
| 456 | } |
| 457 | // Are we done with this transaction ? |
| 458 | if (transaction_->payload.size() >= transaction_->total_size) |
| 459 | ExecuteCmd(); |
| 460 | } |
| 461 | |
| 462 | } // namespace u2f |