123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- // Copyright 2017 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 "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
- absl::optional<ApduCommand> ApduCommand::CreateFromMessage(
- base::span<const uint8_t> message) {
- if (message.size() < kApduMinHeader || message.size() > kApduMaxLength)
- return absl::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 absl::nullopt;
- // No data present; response expected.
- case kApduMinHeader + 3:
- // Fifth byte must be 0.
- if (message[4] != 0)
- return absl::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 absl::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 absl::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
|