910e62b5创建于 1月15日历史提交
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/apdu/apdu_command.h"

#include "base/check_op.h"

namespace apdu {

namespace {

// APDU command data length is 2 bytes encoded in big endian order.
uint16_t ParseMessageLength(base::span<const uint8_t> message, size_t offset) {
  DCHECK_GE(message.size(), offset + 2);
  return (message[offset] << 8) | message[offset + 1];
}

}  // namespace

std::optional<ApduCommand> ApduCommand::CreateFromMessage(
    base::span<const uint8_t> message) {
  if (message.size() < kApduMinHeader || message.size() > kApduMaxLength)
    return std::nullopt;

  uint8_t cla = message[0];
  uint8_t ins = message[1];
  uint8_t p1 = message[2];
  uint8_t p2 = message[3];

  size_t response_length = 0;
  std::vector<uint8_t> data;

  switch (message.size()) {
    // No data present; no expected response.
    case kApduMinHeader:
      break;
    // Invalid encoding sizes.
    case kApduMinHeader + 1:
    case kApduMinHeader + 2:
      return std::nullopt;
    // No data present; response expected.
    case kApduMinHeader + 3:
      // Fifth byte must be 0.
      if (message[4] != 0) {
        return std::nullopt;
      }
      response_length = ParseMessageLength(message, kApduCommandLengthOffset);
      // Special case where response length of 0x0000 corresponds to 65536
      // as defined in ISO7816-4.
      if (response_length == 0) {
        response_length = kApduMaxResponseLength;
      }
      break;
    default:
      // Fifth byte must be 0.
      if (message[4] != 0) {
        return std::nullopt;
      }
      auto data_length = ParseMessageLength(message, kApduCommandLengthOffset);

      if (message.size() == data_length + kApduCommandDataOffset) {
        // No response expected.
        data.insert(data.end(), message.begin() + kApduCommandDataOffset,
                    message.end());
      } else if (message.size() == data_length + kApduCommandDataOffset + 2) {
        // Maximum response size is stored in final 2 bytes.
        data.insert(data.end(), message.begin() + kApduCommandDataOffset,
                    message.end() - 2);
        auto response_length_offset = kApduCommandDataOffset + data_length;
        response_length = ParseMessageLength(message, response_length_offset);
        // Special case where response length of 0x0000 corresponds to 65536
        // as defined in ISO7816-4.
        if (response_length == 0) {
          response_length = kApduMaxResponseLength;
        }
      } else {
        return std::nullopt;
      }
      break;
  }

  return ApduCommand(cla, ins, p1, p2, response_length, std::move(data));
}

ApduCommand::ApduCommand() = default;

ApduCommand::ApduCommand(uint8_t cla,
                         uint8_t ins,
                         uint8_t p1,
                         uint8_t p2,
                         size_t response_length,
                         std::vector<uint8_t> data)
    : cla_(cla),
      ins_(ins),
      p1_(p1),
      p2_(p2),
      response_length_(response_length),
      data_(std::move(data)) {}

ApduCommand::ApduCommand(ApduCommand&& that) = default;

ApduCommand& ApduCommand::operator=(ApduCommand&& that) = default;

ApduCommand::~ApduCommand() = default;

std::vector<uint8_t> ApduCommand::GetEncodedCommand() const {
  std::vector<uint8_t> encoded = {cla_, ins_, p1_, p2_};

  // If data exists, request size (Lc) is encoded in 3 bytes, with the first
  // byte always being null, and the other two bytes being a big-endian
  // representation of the request size. If data length is 0, response size (Le)
  // will be prepended with a null byte.
  if (!data_.empty()) {
    size_t data_length = data_.size();

    encoded.push_back(0x0);
    if (data_length > kApduMaxDataLength) {
      data_length = kApduMaxDataLength;
    }
    encoded.push_back((data_length >> 8) & 0xff);
    encoded.push_back(data_length & 0xff);
    encoded.insert(encoded.end(), data_.begin(), data_.begin() + data_length);
  } else if (response_length_ > 0) {
    encoded.push_back(0x0);
  }

  if (response_length_ > 0) {
    size_t response_length = response_length_;
    if (response_length > kApduMaxResponseLength) {
      response_length = kApduMaxResponseLength;
    }
    // A zero value represents a response length of 65,536 bytes.
    encoded.push_back((response_length >> 8) & 0xff);
    encoded.push_back(response_length & 0xff);
  }
  return encoded;
}

}  // namespace apdu