// 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" namespace apdu { namespace { // APDU command data length is 2 bytes encoded in big endian order. uint16_t ParseMessageLength(base::span message, size_t offset) { DCHECK_GE(message.size(), offset + 2); return (message[offset] << 8) | message[offset + 1]; } } // namespace absl::optional ApduCommand::CreateFromMessage( base::span 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 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 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 ApduCommand::GetEncodedCommand() const { std::vector 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