lin
2025-07-30 fcd736bf35fd93b563e9bbf594f2aa7b62028cc9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
#ifndef ESED_PN81A_UTILS_H_
#define ESED_PN81A_UTILS_H_
 
#include <functional>
#include <string>
 
#include <hidl/Status.h>
 
#include <apdu/apdu.h>
 
#include "pn81a.h"
 
namespace android {
namespace esed {
namespace pn81a {
 
// HIDL
using ::android::hardware::Status;
 
 
// libapdu
using ::android::CommandApdu;
using ResponseApdu = ::android::ResponseApdu<const std::vector<uint8_t>>;
 
/**
 * Reads a 32-bit integer from an iterator.
 * @param first The first position to read from.
 * @return A tuple of the value read and an iterator to one after the last position read from.
 */
template<typename InputIt>
std::tuple<uint32_t, InputIt> readBigEndianInt32(InputIt first) {
    uint32_t ret;
    auto it = first;
    ret  = *it++ << 24;
    ret |= *it++ << 16;
    ret |= *it++ <<  8;
    ret |= *it++;
    return {ret, it};
}
 
/**
 * Writes a 32-bit integer to the iterator in big-endian format.
 * @param value The value to write.
 * @param first The first position in the iterator.
 * @return An iterator to one after the last position written to.
 */
template<typename OutputIt>
OutputIt writeBigEndian(const uint32_t value, OutputIt first) {
    auto it = first;
    *it++ = 0xff & (value >> 24);
    *it++ = 0xff & (value >> 16);
    *it++ = 0xff & (value >>  8);
    *it++ = 0xff & value;
    return it;
}
 
/**
 * Transceive a command with the eSE and perform common error checking. When the
 * handler is called, it has been checked that the transmission to and reception
 * from the eSE was successful and that the response is in a valid format.
 */
template<typename T, T OK, T FAILED>
T transceive(::android::esed::EseInterface& ese, const CommandApdu& command,
                  std::function<T(const ResponseApdu&)> handler = {}) {
    // +1 for max size of extended response, +2 for status bytes
    constexpr size_t MAX_RESPONSE_SIZE = std::numeric_limits<uint16_t>::max() + 1 + 2;
    std::vector<uint8_t> responseBuffer(MAX_RESPONSE_SIZE);
    const int ret = ese.transceive(command.vector(), responseBuffer);
 
    // Check eSE communication was successful
    if (ret < 0) {
        std::string errMsg = "Failed to transceive data between AP and eSE";
        if (ese.error()) {
            errMsg += " (" + std::to_string(ese.error_code()) + "): " + ese.error_message();
        } else {
            errMsg += ": reason unknown";
        }
        LOG(ERROR) << errMsg;
        return FAILED;
    }
    const size_t recvd = static_cast<size_t>(ret);
 
    // Need to recalculate the maximum response size if this fails
    if (recvd > MAX_RESPONSE_SIZE) {
        LOG(ERROR) << "eSE response was longer than the buffer, check the buffer size.";
        return FAILED;
    }
    responseBuffer.resize(recvd);
 
    // Check for ISO 7816-4 APDU response format errors
    ResponseApdu apdu{responseBuffer};
    if (!apdu.ok()) {
        LOG(ERROR) << "eSE response was invalid.";
        return FAILED;
    }
 
    // Call handler if one was provided
    if (handler) {
        return handler(apdu);
    }
 
    return OK;
}
 
/**
 * Checks that the amount of data in the response APDU matches the expected
 * value.
 */
template<typename T, T OK, T FAILED>
T checkLength(const ResponseApdu& apdu, const size_t size) {
    if (apdu.dataSize() != size) {
        LOG(ERROR) << "eSE response was the wrong length.";
        return FAILED;
    }
    return OK;
}
 
/**
 * Checks that the response APDU does no encode an error and that the amount of
 * data matches the expected value.
 */
template<typename T, T OK, T FAILED>
T checkNoErrorAndLength(const ResponseApdu& apdu, const size_t size) {
    if (apdu.isError()) {
        LOG(ERROR) << "eSE operation failed";
        return FAILED;
    }
    return checkLength<T, OK, FAILED>(apdu, size);
}
 
} // namespace android
} // namespace esed
} // namespace pn81a
 
#endif // ESED_PN81A_UTILS_H_