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