1 // Copyright 2017 The Chromium 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 "components/apdu/apdu_command.h"
6
7 namespace apdu {
8
9 namespace {
10
11 // APDU command data length is 2 bytes encoded in big endian order.
ParseMessageLength(base::span<const uint8_t> message,size_t offset)12 uint16_t ParseMessageLength(base::span<const uint8_t> message, size_t offset) {
13 DCHECK_GE(message.size(), offset + 2);
14 return (message[offset] << 8) | message[offset + 1];
15 }
16
17 } // namespace
18
CreateFromMessage(base::span<const uint8_t> message)19 base::Optional<ApduCommand> ApduCommand::CreateFromMessage(
20 base::span<const uint8_t> message) {
21 if (message.size() < kApduMinHeader || message.size() > kApduMaxLength)
22 return base::nullopt;
23
24 uint8_t cla = message[0];
25 uint8_t ins = message[1];
26 uint8_t p1 = message[2];
27 uint8_t p2 = message[3];
28
29 size_t response_length = 0;
30 std::vector<uint8_t> data;
31
32 switch (message.size()) {
33 // No data present; no expected response.
34 case kApduMinHeader:
35 break;
36 // Invalid encoding sizes.
37 case kApduMinHeader + 1:
38 case kApduMinHeader + 2:
39 return base::nullopt;
40 // No data present; response expected.
41 case kApduMinHeader + 3:
42 // Fifth byte must be 0.
43 if (message[4] != 0)
44 return base::nullopt;
45 response_length = ParseMessageLength(message, kApduCommandLengthOffset);
46 // Special case where response length of 0x0000 corresponds to 65536
47 // as defined in ISO7816-4.
48 if (response_length == 0)
49 response_length = kApduMaxResponseLength;
50 break;
51 default:
52 // Fifth byte must be 0.
53 if (message[4] != 0)
54 return base::nullopt;
55 auto data_length = ParseMessageLength(message, kApduCommandLengthOffset);
56
57 if (message.size() == data_length + kApduCommandDataOffset) {
58 // No response expected.
59 data.insert(data.end(), message.begin() + kApduCommandDataOffset,
60 message.end());
61 } else if (message.size() == data_length + kApduCommandDataOffset + 2) {
62 // Maximum response size is stored in final 2 bytes.
63 data.insert(data.end(), message.begin() + kApduCommandDataOffset,
64 message.end() - 2);
65 auto response_length_offset = kApduCommandDataOffset + data_length;
66 response_length = ParseMessageLength(message, response_length_offset);
67 // Special case where response length of 0x0000 corresponds to 65536
68 // as defined in ISO7816-4.
69 if (response_length == 0)
70 response_length = kApduMaxResponseLength;
71 } else {
72 return base::nullopt;
73 }
74 break;
75 }
76
77 return ApduCommand(cla, ins, p1, p2, response_length, std::move(data));
78 }
79
80 ApduCommand::ApduCommand() = default;
81
ApduCommand(uint8_t cla,uint8_t ins,uint8_t p1,uint8_t p2,size_t response_length,std::vector<uint8_t> data)82 ApduCommand::ApduCommand(uint8_t cla,
83 uint8_t ins,
84 uint8_t p1,
85 uint8_t p2,
86 size_t response_length,
87 std::vector<uint8_t> data)
88 : cla_(cla),
89 ins_(ins),
90 p1_(p1),
91 p2_(p2),
92 response_length_(response_length),
93 data_(std::move(data)) {}
94
95 ApduCommand::ApduCommand(ApduCommand&& that) = default;
96
97 ApduCommand& ApduCommand::operator=(ApduCommand&& that) = default;
98
99 ApduCommand::~ApduCommand() = default;
100
GetEncodedCommand() const101 std::vector<uint8_t> ApduCommand::GetEncodedCommand() const {
102 std::vector<uint8_t> encoded = {cla_, ins_, p1_, p2_};
103
104 // If data exists, request size (Lc) is encoded in 3 bytes, with the first
105 // byte always being null, and the other two bytes being a big-endian
106 // representation of the request size. If data length is 0, response size (Le)
107 // will be prepended with a null byte.
108 if (!data_.empty()) {
109 size_t data_length = data_.size();
110
111 encoded.push_back(0x0);
112 if (data_length > kApduMaxDataLength)
113 data_length = kApduMaxDataLength;
114 encoded.push_back((data_length >> 8) & 0xff);
115 encoded.push_back(data_length & 0xff);
116 encoded.insert(encoded.end(), data_.begin(), data_.begin() + data_length);
117 } else if (response_length_ > 0) {
118 encoded.push_back(0x0);
119 }
120
121 if (response_length_ > 0) {
122 size_t response_length = response_length_;
123 if (response_length > kApduMaxResponseLength)
124 response_length = kApduMaxResponseLength;
125 // A zero value represents a response length of 65,536 bytes.
126 encoded.push_back((response_length >> 8) & 0xff);
127 encoded.push_back(response_length & 0xff);
128 }
129 return encoded;
130 }
131
132 } // namespace apdu
133