1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef APDU_H_
18#define APDU_H_
19
20#include <cstddef>
21#include <cstdint>
22#include <iterator>
23#include <vector>
24
25namespace android {
26
27/**
28 * Helper to build an APDU command. If a data section is needed, it is left empty with dataBegin
29 * and dataEnd able to return iterators to where the data should be filled in.
30 *
31 * The command bytes are stored sequentially in the same manner as std::vector.
32 */
33class CommandApdu {
34public:
35    CommandApdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2)
36            : CommandApdu(cla, ins, p1, p2, 0, 0) {}
37    CommandApdu(uint8_t cla, uint8_t ins, uint8_t p1, uint8_t p2, size_t lc, size_t le);
38
39    using iterator = std::vector<uint8_t>::iterator;
40    using const_iterator = std::vector<uint8_t>::const_iterator;
41
42    iterator begin() { return mCommand.begin(); }
43    iterator end() { return mCommand.end(); }
44    const_iterator begin() const { return mCommand.begin(); }
45    const_iterator end() const { return mCommand.end(); }
46
47    iterator dataBegin() { return mDataBegin; }
48    iterator dataEnd() { return mDataEnd; }
49    const_iterator dataBegin() const { return mDataBegin; }
50    const_iterator dataEnd() const { return mDataEnd; }
51
52    size_t size() const { return mCommand.size(); }
53    size_t dataSize() const { return std::distance(mDataBegin, mDataEnd); }
54
55    const std::vector<uint8_t>& vector() const { return mCommand; }
56
57private:
58    std::vector<uint8_t> mCommand;
59    std::vector<uint8_t>::iterator mDataBegin;
60    std::vector<uint8_t>::iterator mDataEnd;
61};
62
63/**
64 * Helper to deconstruct a response APDU. This wraps a reference to an iterable byte container.
65 */
66template<typename T>
67class ResponseApdu {
68    static constexpr size_t STATUS_SIZE = 2;
69    static constexpr uint8_t BYTES_AVAILABLE = 0x61;
70    static constexpr uint8_t SW1_WARNING_NON_VOLATILE_MEMORY_UNCHANGED = 0x62;
71    static constexpr uint8_t SW1_WARNING_NON_VOLATILE_MEMORY_CHANGED = 0x63;
72    static constexpr uint8_t SW1_FIRST_EXECUTION_ERROR = 0x64;
73    static constexpr uint8_t SW1_LAST_EXECUTION_ERROR = 0x66;
74    static constexpr uint8_t SW1_FIRST_CHECKING_ERROR = 0x67;
75    static constexpr uint8_t SW1_LAST_CHECKING_ERROR = 0x6f;
76
77public:
78    ResponseApdu(const T& data) : mData(data) {}
79
80    bool ok() const {
81        return static_cast<size_t>(
82                std::distance(std::begin(mData), std::end(mData))) >= STATUS_SIZE;
83    }
84
85    uint8_t sw1() const { return *(std::end(mData) - 2); }
86    uint8_t sw2() const { return *(std::end(mData) - 1); }
87    uint16_t status() const { return (static_cast<uint16_t>(sw1()) << 8) | sw2(); }
88
89    int8_t remainingBytes() const { return sw1() == BYTES_AVAILABLE ? sw2() : 0; }
90
91    bool isWarning() const {
92        const uint8_t sw1 = this->sw1();
93        return sw1 == SW1_WARNING_NON_VOLATILE_MEMORY_UNCHANGED
94            || sw1 == SW1_WARNING_NON_VOLATILE_MEMORY_CHANGED;
95    }
96    bool isExecutionError() const {
97        const uint8_t sw1 = this->sw1();
98        return sw1 >= SW1_FIRST_EXECUTION_ERROR && sw1 <= SW1_LAST_EXECUTION_ERROR;
99    }
100    bool isCheckingError() const {
101        const uint8_t sw1 = this->sw1();
102        return sw1 >= SW1_FIRST_CHECKING_ERROR && sw1 <= SW1_LAST_CHECKING_ERROR;
103    }
104    bool isError() const { return isExecutionError() || isCheckingError(); }
105
106    auto dataBegin() const { return std::begin(mData); }
107    auto dataEnd() const { return std::end(mData) - STATUS_SIZE; }
108
109    size_t dataSize() const { return std::distance(dataBegin(), dataEnd()); }
110
111private:
112    const T& mData;
113};
114
115} // namespace android
116
117#endif // APDU_H_
118