Keyple Util C++ Library - 2.4.0
Component of the Keyple C++ middleware
BerTlvUtil.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/ *
3 * *
4 * See the NOTICE file(s) distributed with this work for additional *
5 * information regarding copyright ownership. *
6 * *
7 * This program and the accompanying materials are made available under the *
8 * terms of the Eclipse Public License 2.0 which is available at *
9 * http://www.eclipse.org/legal/epl-2.0 *
10 * *
11 * SPDX-License-Identifier: EPL-2.0 *
12 ******************************************************************************/
13
14#include "keyple/core/util/BerTlvUtil.hpp"
15
16#include <map>
17#include <vector>
18
19#include "keyple/core/util/cpp/Arrays.hpp"
20#include "keyple/core/util/cpp/exception/IllegalArgumentException.hpp"
21#include "keyple/core/util/cpp/exception/IndexOutOfBoundsException.hpp"
22
23namespace keyple {
24namespace core {
25namespace util {
26
27using keyple::core::util::cpp::Arrays;
28using keyple::core::util::cpp::exception::IllegalArgumentException;
29using keyple::core::util::cpp::exception::IndexOutOfBoundsException;
30
31BerTlvUtil::BerTlvUtil()
32{
33}
34
35const std::map<const int, const std::vector<uint8_t>>
36BerTlvUtil::parseSimple(
37 const std::vector<uint8_t>& tlvStructure, const bool primitiveOnly)
38{
39 try {
40 return parseBufferSimple(tlvStructure, primitiveOnly);
41 } catch (const IndexOutOfBoundsException& e) {
42 (void)e;
43 throw IllegalArgumentException("Invalid TLV structure.");
44 }
45}
46
47const std::map<const int, std::vector<std::vector<uint8_t>>>
48BerTlvUtil::parse(
49 const std::vector<uint8_t>& tlvStructure, const bool primitiveOnly)
50{
51 try {
52 return parseBuffer(tlvStructure, primitiveOnly);
53 } catch (const IndexOutOfBoundsException& e) {
54 (void)e;
55 throw IllegalArgumentException("Invalid TLV structure.");
56 }
57}
58
59bool
60BerTlvUtil::isConstructed(const int tagId)
61{
62 if (tagId < 0 || tagId > 0xFFFFFF) {
63 throw IllegalArgumentException("Tag Id out of range.");
64 }
65
66 if (tagId <= 0xFF) {
67 return (tagId & 0x20) != 0;
68 }
69
70 if (tagId <= 0xFFFF) {
71 return (tagId & 0x2000) != 0;
72 }
73
74 return (tagId & 0x200000) != 0;
75}
76
77const std::map<const int, const std::vector<uint8_t>>
78BerTlvUtil::parseBufferSimple(
79 const std::vector<uint8_t>& tlvStructure, const bool primitiveOnly)
80{
81 std::map<const int, const std::vector<uint8_t>> tlvs;
82 int offset = 0;
83
84 do {
85 const int tagSize = getTagSize(tlvStructure, offset);
86 const std::vector<uint8_t> tagBytes
87 = Arrays::copyOfRange(tlvStructure, offset, offset + tagSize);
88 const int tag = getTag(tlvStructure, offset, tagSize);
89 const int lengthSize = getLengthSize(tlvStructure, offset + tagSize);
90 const int valueSize
91 = getLength(tlvStructure, offset + tagSize, lengthSize);
92 const std::vector<uint8_t> value = Arrays::copyOfRange(
93 tlvStructure,
94 offset + tagSize + lengthSize,
95 offset + tagSize + lengthSize + valueSize);
96
97 offset += tagSize + lengthSize + valueSize;
98
99 if ((tagBytes[0] & 0x20) != 0) {
100 /* Tag is constructed */
101 if (!primitiveOnly) {
102 tlvs.insert({tag, value});
103 }
104
105 const std::map<const int, const std::vector<uint8_t>> parse
106 = parseSimple(value, primitiveOnly);
107 tlvs.insert(parse.begin(), parse.end());
108 } else {
109 /* Tag is primitive */
110 tlvs.insert({tag, value});
111 }
112 } while (offset < static_cast<int>(tlvStructure.size()));
113
114 return tlvs;
115}
116
117const std::map<const int, std::vector<std::vector<uint8_t>>>
118BerTlvUtil::parseBuffer(
119 const std::vector<uint8_t>& tlvStructure, const bool primitiveOnly)
120{
121 std::map<const int, std::vector<std::vector<uint8_t>>> tlvs;
122 int offset = 0;
123
124 do {
125 const int tagSize = getTagSize(tlvStructure, offset);
126 const std::vector<uint8_t> tagBytes
127 = Arrays::copyOfRange(tlvStructure, offset, offset + tagSize);
128 const int tag = getTag(tlvStructure, offset, tagSize);
129 const int lengthSize = getLengthSize(tlvStructure, offset + tagSize);
130 const int valueSize
131 = getLength(tlvStructure, offset + tagSize, lengthSize);
132 const std::vector<uint8_t> value = Arrays::copyOfRange(
133 tlvStructure,
134 offset + tagSize + lengthSize,
135 offset + tagSize + lengthSize + valueSize);
136
137 offset += tagSize + lengthSize + valueSize;
138
139 if ((tagBytes[0] & 0x20) != 0) {
140 /* Tag is constructed */
141 if (!primitiveOnly) {
142 std::vector<std::vector<uint8_t>>& values
143 = getOrInitTagValues(tlvs, tag);
144 values.push_back(value);
145 }
146
147 std::map<const int, std::vector<std::vector<uint8_t>>> tlvs2
148 = parse(value, primitiveOnly);
149
150 for (const auto& entry : tlvs2) {
151 std::vector<std::vector<uint8_t>>& values
152 = getOrInitTagValues(tlvs, entry.first);
153 Arrays::addAll(values, entry.second);
154 }
155 } else {
156 /* Tag is primitive */
157 std::vector<std::vector<uint8_t>>& values
158 = getOrInitTagValues(tlvs, tag);
159 values.push_back(value);
160 }
161 } while (offset < static_cast<int>(tlvStructure.size()));
162
163 return tlvs;
164}
165
166std::vector<std::vector<uint8_t>>&
167BerTlvUtil::getOrInitTagValues(
168 std::map<const int, std::vector<std::vector<uint8_t>>>& tlvs, const int tag)
169{
170 const auto it = tlvs.find(tag);
171
172 if (it == tlvs.end()) {
173 std::vector<std::vector<uint8_t>> values;
174 tlvs.insert({tag, values});
175 }
176
177 /* Look again and return reference */
178 return tlvs.find(tag)->second;
179}
180
181int
182BerTlvUtil::getTagSize(
183 const std::vector<uint8_t>& tlvStructure, const int offset)
184{
185 /* C++: prevent accessing unexisting values */
186 if (offset >= static_cast<int>(tlvStructure.size())) {
187 throw IndexOutOfBoundsException("Invalid index");
188 }
189
190 if ((tlvStructure[offset] & 0x1F) == 0x1F) {
191 if ((tlvStructure[offset + 1] & 0x80) == 0) {
192 return 2;
193 } else {
194 if ((tlvStructure[offset + 2] & 0x80) != 0) {
195 throw IllegalArgumentException("Invalid tag.");
196 }
197 }
198
199 return 3;
200 } else {
201 return 1;
202 }
203}
204
205int
206BerTlvUtil::getTag(
207 const std::vector<uint8_t>& tlvStructure, const int offset, const int size)
208{
209 switch (size) {
210 case 1:
211 return tlvStructure[offset] & 0xFF;
212 case 2:
213 return ((tlvStructure[offset] & 0xFF) << 8)
214 + (tlvStructure[offset + 1] & 0xFF);
215 case 3:
216 return ((tlvStructure[offset] & 0xFF) << 16)
217 + ((tlvStructure[offset + 1] & 0xFF) << 8)
218 + (tlvStructure[offset + 2] & 0xFF);
219 default:
220 throw IllegalArgumentException("Bad tag size.");
221 }
222}
223
224int
225BerTlvUtil::getLengthSize(
226 const std::vector<uint8_t>& tlvStructure, const int offset)
227{
228 int firstByteLength = tlvStructure[offset] & 0xff;
229
230 switch (firstByteLength) {
231 case 0x82:
232 return 3;
233 case 0x81:
234 return 2;
235 default:
236 if (firstByteLength >= 0x80) {
237 throw IllegalArgumentException("Invalid length.");
238 }
239
240 return 1;
241 }
242}
243
244int
245BerTlvUtil::getLength(
246 const std::vector<uint8_t>& tlvStructure, const int offset, const int size)
247{
248 switch (size) {
249 case 1:
250 return tlvStructure[offset] & 0x7F;
251 case 2:
252 return tlvStructure[offset + 1] & 0xFF;
253 case 3:
254 return ((tlvStructure[offset + 1] & 0xFF) << 8)
255 + (tlvStructure[offset + 2] & 0xFF);
256 default:
257 throw IllegalArgumentException("Bad length size.");
258 }
259}
260
261} /* namespace util */
262} /* namespace core */
263} /* namespace keyple */