1// Copyright 2017 The Chromium Authors. All rights reserved.
2// Copyright (C) 2018 Apple Inc. All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "config.h"
31#include "ApduCommand.h"
32
33#include <wtf/Optional.h>
34
35#if ENABLE(WEB_AUTHN)
36
37namespace apdu {
38
39namespace {
40
41// APDU command data length is 2 bytes encoded in big endian order.
42uint16_t parseMessageLength(const Vector<uint8_t>& message, size_t offset)
43{
44 ASSERT(message.size() >= offset + 2);
45 return (message[offset] << 8) | message[offset + 1];
46}
47
48} // namespace
49
50Optional<ApduCommand> ApduCommand::createFromMessage(const Vector<uint8_t>& message)
51{
52 if (message.size() < kApduMinHeader || message.size() > kApduMaxLength)
53 return WTF::nullopt;
54
55 uint8_t cla = message[0];
56 uint8_t ins = message[1];
57 uint8_t p1 = message[2];
58 uint8_t p2 = message[3];
59
60 size_t responseLength = 0;
61 Vector<uint8_t> data;
62
63 switch (message.size()) {
64 // No data present; no expected response.
65 case kApduMinHeader:
66 break;
67 // Invalid encoding sizes.
68 case kApduMinHeader + 1:
69 case kApduMinHeader + 2:
70 return WTF::nullopt;
71 // No data present; response expected.
72 case kApduMinHeader + 3:
73 // Fifth byte must be 0.
74 if (message[4])
75 return WTF::nullopt;
76 responseLength = parseMessageLength(message, kApduCommandLengthOffset);
77 // Special case where response length of 0x0000 corresponds to 65536
78 // as defined in ISO7816-4.
79 if (!responseLength)
80 responseLength = kApduMaxResponseLength;
81 break;
82 default:
83 // Fifth byte must be 0.
84 if (message[4])
85 return WTF::nullopt;
86 auto dataLength = parseMessageLength(message, kApduCommandLengthOffset);
87
88 if (message.size() == dataLength + kApduCommandDataOffset) {
89 // No response expected.
90 data.appendRange(message.begin() + kApduCommandDataOffset, message.end());
91 } else if (message.size() == dataLength + kApduCommandDataOffset + 2) {
92 // Maximum response size is stored in final 2 bytes.
93 data.appendRange(message.begin() + kApduCommandDataOffset, message.end() - 2);
94 auto responseLengthOffset = kApduCommandDataOffset + dataLength;
95 responseLength = parseMessageLength(message, responseLengthOffset);
96 // Special case where response length of 0x0000 corresponds to 65536
97 // as defined in ISO7816-4.
98 if (!responseLength)
99 responseLength = kApduMaxResponseLength;
100 } else
101 return WTF::nullopt;
102 break;
103 }
104
105 return ApduCommand(cla, ins, p1, p2, responseLength, WTFMove(data));
106}
107
108ApduCommand::ApduCommand(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, size_t responseLength, Vector<uint8_t>&& data)
109 : m_cla(cla)
110 , m_ins(ins)
111 , m_p1(p1)
112 , m_p2(p2)
113 , m_responseLength(responseLength)
114 , m_data(WTFMove(data))
115{
116}
117
118Vector<uint8_t> ApduCommand::getEncodedCommand() const
119{
120 Vector<uint8_t> encoded = { m_cla, m_ins, m_p1, m_p2 };
121
122 // If data exists, request size (Lc) is encoded in 3 bytes, with the first
123 // byte always being null, and the other two bytes being a big-endian
124 // representation of the request size. If data length is 0, response size (Le)
125 // will be prepended with a null byte.
126 if (!m_data.isEmpty()) {
127 size_t dataLength = m_data.size();
128
129 encoded.append(0x0);
130 if (dataLength > kApduMaxDataLength)
131 dataLength = kApduMaxDataLength;
132 encoded.append((dataLength >> 8) & 0xff);
133 encoded.append(dataLength & 0xff);
134 encoded.appendRange(m_data.begin(), m_data.begin() + dataLength);
135 } else if (m_responseLength > 0)
136 encoded.append(0x0);
137
138 if (m_responseLength > 0) {
139 size_t responseLength = m_responseLength;
140 if (responseLength > kApduMaxResponseLength)
141 responseLength = kApduMaxResponseLength;
142 // A zero value represents a response length of 65,536 bytes.
143 encoded.append((responseLength >> 8) & 0xff);
144 encoded.append(responseLength & 0xff);
145 }
146 return encoded;
147}
148
149} // namespace apdu
150
151#endif // ENABLE(WEB_AUTHN)
152