|
1 /** |
|
2 * Relational pipes |
|
3 * Copyright © 2021 František Kučera (Frantovo.cz, GlobalCode.info) |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU General Public License as published by |
|
7 * the Free Software Foundation, version 3 of the License. |
|
8 * |
|
9 * This program is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 * GNU General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU General Public License |
|
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 */ |
|
17 #pragma once |
|
18 |
|
19 #include <memory> |
|
20 #include <vector> |
|
21 #include <sstream> |
|
22 #include <iomanip> |
|
23 #include <cmath> |
|
24 |
|
25 #include "ProxyVector.h" |
|
26 |
|
27 namespace relpipe { |
|
28 namespace in { |
|
29 namespace asn1 { |
|
30 namespace lib { |
|
31 |
|
32 namespace UniversalType { |
|
33 static const uint64_t EndOfContent = 0x00; |
|
34 static const uint64_t Boolean = 0x01; |
|
35 static const uint64_t Integer = 0x02; |
|
36 static const uint64_t BitString = 0x03; |
|
37 static const uint64_t OctetString = 0x04; |
|
38 static const uint64_t Null = 0x05; |
|
39 static const uint64_t ObjectIdentifier = 0x06; |
|
40 static const uint64_t ObjectDescriptor = 0x07; |
|
41 static const uint64_t External = 0x08; |
|
42 static const uint64_t Real = 0x09; |
|
43 static const uint64_t Enumerated = 0xA; |
|
44 static const uint64_t Embedded = 0xB; |
|
45 static const uint64_t UTF8String = 0xC; |
|
46 static const uint64_t RelativeObjectIdentifier = 0xD; |
|
47 static const uint64_t Time = 0xE; |
|
48 static const uint64_t Reserved = 0xF; |
|
49 static const uint64_t Sequence = 0x10; |
|
50 static const uint64_t Set = 0x11; |
|
51 static const uint64_t NumericString = 0x12; |
|
52 static const uint64_t PrintableString = 0x13; |
|
53 static const uint64_t T61String = 0x14; |
|
54 static const uint64_t VideotexString = 0x15; |
|
55 static const uint64_t IA5String = 0x16; |
|
56 static const uint64_t UTCTime = 0x17; |
|
57 static const uint64_t GeneralizedTime = 0x18; |
|
58 static const uint64_t GraphicString = 0x19; |
|
59 static const uint64_t VisibleString = 0x1A; |
|
60 static const uint64_t GeneralString = 0x1B; |
|
61 static const uint64_t UniversalString = 0x1C; |
|
62 static const uint64_t CharacterString = 0x1D; |
|
63 static const uint64_t BMPString = 0x1E; |
|
64 static const uint64_t Date = 0x1F; |
|
65 static const uint64_t TimeOfDay = 0x20; |
|
66 static const uint64_t DateTime = 0x21; |
|
67 static const uint64_t Duration = 0x22; |
|
68 static const uint64_t ObjectIdentifierIRI = 0x23; |
|
69 static const uint64_t RelativeObjectIdentifierIRI = 0x24; |
|
70 } |
|
71 |
|
72 class ASN1ContentHandler { |
|
73 public: |
|
74 |
|
75 enum class TagClass : uint8_t { |
|
76 Universal = 0, |
|
77 Application = 1, |
|
78 ContextSpecific = 2, |
|
79 Private = 3 |
|
80 }; |
|
81 |
|
82 enum class PC : uint8_t { |
|
83 Primitive = 0, |
|
84 Constructed = 1 |
|
85 }; |
|
86 |
|
87 class Header { |
|
88 public: |
|
89 TagClass tagClass; |
|
90 PC pc; |
|
91 uint64_t tag; |
|
92 }; |
|
93 |
|
94 // TODO: separate implementation of particular types from the interface, separate SPI from API? |
|
95 |
|
96 class Integer { |
|
97 private: |
|
98 // TODO: use std::string (of octets, not ASCII) instead of std::vector? |
|
99 // TODO: use this class as BigInteger across Relational pipes? |
|
100 std::vector<uint8_t> data; |
|
101 public: |
|
102 |
|
103 /** |
|
104 * @param data integer octets as in BER encoding |
|
105 */ |
|
106 Integer(std::vector<uint8_t> data) : data(data) { |
|
107 } |
|
108 |
|
109 virtual ~Integer() { |
|
110 } |
|
111 |
|
112 size_t size() const { |
|
113 return data.size(); |
|
114 } |
|
115 |
|
116 const uint8_t& operator[](std::size_t index) const { |
|
117 return data[index]; |
|
118 } |
|
119 |
|
120 const std::string toHex() const { |
|
121 std::stringstream hex; |
|
122 hex << std::hex << std::setfill('0'); |
|
123 for (uint8_t b : data) hex << std::setw(2) << (int) b; |
|
124 return hex.str(); |
|
125 } |
|
126 |
|
127 const std::string toString() const { |
|
128 try { |
|
129 return std::to_string(toInt64()); |
|
130 } catch (...) { |
|
131 // integer has more than 64 bits → only HEX form value will be available |
|
132 // TODO: support longer values than 64 bits |
|
133 // TODO: do not ignore zero-length error? |
|
134 return ""; |
|
135 } |
|
136 } |
|
137 |
|
138 const int64_t toInt64() const { |
|
139 int64_t value = 0; |
|
140 |
|
141 if (data.size() > sizeof (value)) throw std::invalid_argument("Integer is too long"); |
|
142 else if (data.size() == 0) throw std::invalid_argument("Integer has zero length"); |
|
143 |
|
144 value = data[0]; |
|
145 bool negative = data[0] & 0x80; |
|
146 |
|
147 for (size_t i = 1, limit = data.size(); i < limit; i++) value = (value << 8) | data[i]; |
|
148 |
|
149 if (negative) value -= std::pow(256, data.size()); |
|
150 |
|
151 return value; |
|
152 } |
|
153 |
|
154 }; |
|
155 |
|
156 class ObjectIdentifier { |
|
157 private: |
|
158 // TODO: use std::string (of octets, not ASCII) instead of std::vector? |
|
159 // TODO: use this class across Relational pipes as one of basic types? |
|
160 std::vector<uint8_t> data; |
|
161 |
|
162 public: |
|
163 |
|
164 /** |
|
165 * @param data integer octets as in BER encoding |
|
166 */ |
|
167 ObjectIdentifier(std::vector<uint8_t> data) : data(data) { |
|
168 // TODO: cache size and element values? |
|
169 } |
|
170 |
|
171 virtual ~ObjectIdentifier() { |
|
172 } |
|
173 |
|
174 /** |
|
175 * @return number of elements, not octets |
|
176 */ |
|
177 size_t size() const { |
|
178 return 0; // FIXME: correct OID size |
|
179 } |
|
180 |
|
181 /** |
|
182 * @param index 0 = root element |
|
183 * @return value of the element at given position |
|
184 */ |
|
185 const uint8_t& operator[](std::size_t index) const { |
|
186 return data[index]; // FIXME: correct OID value |
|
187 } |
|
188 |
|
189 const std::string toString() const { |
|
190 if (data.size() == 0) return ""; |
|
191 |
|
192 std::stringstream result; |
|
193 |
|
194 result << (data[0] / 40) << "." << (data[0] % 40); // first two elements are encoded in the first octet |
|
195 |
|
196 for (size_t i = 1, limit = data.size(), octet = 0, element = 0; i < limit; i++) { |
|
197 octet = data[i]; |
|
198 element = element << 7 | (octet & 0xFF >> 1); |
|
199 // TODO: throw exception if the element value overflows? (should not happen) or format even longer values |
|
200 if ((octet & 1 << 7) == 0) { |
|
201 result << "." << element; |
|
202 element = 0; |
|
203 } |
|
204 } |
|
205 |
|
206 return result.str(); |
|
207 } |
|
208 |
|
209 }; |
|
210 |
|
211 class DateTime { |
|
212 public: |
|
213 |
|
214 enum class Precision { |
|
215 Year, |
|
216 Month, |
|
217 Day, |
|
218 Hour, |
|
219 Minute, |
|
220 Second, |
|
221 Milisecond, |
|
222 Nanosecond |
|
223 }; |
|
224 |
|
225 // TODO: timezone (in minutes or 1/4 hours) |
|
226 |
|
227 Precision precision = Precision::Second; |
|
228 int32_t year = 1970; |
|
229 int8_t month = 1; |
|
230 int8_t day = 1; |
|
231 int8_t hour = 0; |
|
232 int8_t minute = 0; |
|
233 int8_t second = 0; |
|
234 int32_t nanosecond = 0; |
|
235 int8_t timezoneHour = 0; |
|
236 int8_t timezoneMinute = 0; |
|
237 |
|
238 virtual ~DateTime() { |
|
239 } |
|
240 |
|
241 const std::string toString() const { |
|
242 std::stringstream result; |
|
243 result << std::setfill('0'); |
|
244 result << std::setw(4) << (int) year; |
|
245 result << "-" << std::setw(2) << (int) month; |
|
246 result << "-" << std::setw(2) << (int) day; |
|
247 result << "T" << std::setw(2) << (int) hour; |
|
248 result << ":" << std::setw(2) << (int) minute; |
|
249 result << ":" << std::setw(2) << (int) second; |
|
250 if (precision == Precision::Nanosecond) result << "," << (int) nanosecond; |
|
251 result << (timezoneHour < 0 ? "-" : "+"); |
|
252 result << std::setw(2) << (int) std::abs(timezoneHour); |
|
253 result << ":" << std::setw(2) << (int) timezoneMinute; |
|
254 return result.str(); |
|
255 } |
|
256 }; |
|
257 |
|
258 virtual ~ASN1ContentHandler() = default; |
|
259 |
|
260 // TODO: more metadata, support OID decoding and ASN.1 modules (schema), probably through a plug-in |
|
261 // TODO: support also extension extractor plug-ins? (could decode some opaque structures like octet strings and replace them with nested elements) e.g. subjectAltName in https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6 |
|
262 |
|
263 /** |
|
264 * @param uri identifier of the option |
|
265 * @param value value of the option |
|
266 * @return whether this option is supported and was applied here |
|
267 */ |
|
268 virtual bool setOption(const std::string& uri, const std::string& value) { |
|
269 return false; |
|
270 } |
|
271 |
|
272 virtual void writeStreamStart() = 0; |
|
273 virtual void writeStreamEnd() = 0; |
|
274 |
|
275 virtual void writeCollectionStart(const Header& header) = 0; |
|
276 virtual void writeCollectionEnd() = 0; |
|
277 virtual void writeBoolean(const Header& header, bool value) = 0; |
|
278 virtual void writeNull(const Header& header) = 0; |
|
279 virtual void writeInteger(const Header& header, Integer value) = 0; |
|
280 /** |
|
281 * @param type original type in ASN.1 |
|
282 * @param value original text converted to UTF-8 |
|
283 */ |
|
284 virtual void writeTextString(const Header& header, std::string value) = 0; |
|
285 /** |
|
286 * @param value arbitrary sequence of octets (bytes), usually not a human-readable text |
|
287 */ |
|
288 virtual void writeOctetString(const Header& header, std::string value) = 0; |
|
289 /** |
|
290 * @param value arbitrary sequence of bits (booleans), usually not a human-readable text |
|
291 */ |
|
292 virtual void writeBitString(const Header& header, std::vector<bool> value) = 0; |
|
293 virtual void writeOID(const Header& header, ObjectIdentifier value) = 0; |
|
294 virtual void writeDateTime(const Header& header, DateTime value) = 0; |
|
295 // Object descriptor |
|
296 // virtual void writeReal(float value) = 0; |
|
297 // Enumerated |
|
298 // Embedded PVD |
|
299 // Relative OID |
|
300 // OID-IRI |
|
301 // Relative OID-IRI |
|
302 |
|
303 /** |
|
304 * Specific value that was not parsed. |
|
305 * May be processed in a generic way (as binary data or ASCII or UTF-8 string, when possible) |
|
306 * or according to given application, context or private specification. |
|
307 * |
|
308 * @param value original raw data |
|
309 */ |
|
310 virtual void writeSpecific(const Header& header, std::string value) = 0; |
|
311 |
|
312 }; |
|
313 |
|
314 class ASN1ContentHandlerProxy : public ASN1ContentHandler { |
|
315 private: |
|
316 ProxyVector<ASN1ContentHandler> handlers; |
|
317 public: |
|
318 |
|
319 void addHandler(std::shared_ptr<ASN1ContentHandler> handler) { |
|
320 handlers.push_back(handler); |
|
321 } |
|
322 |
|
323 void writeStreamStart() override { |
|
324 handlers.forward(&ASN1ContentHandler::writeStreamStart); |
|
325 } |
|
326 |
|
327 void writeStreamEnd() override { |
|
328 handlers.forward(&ASN1ContentHandler::writeStreamEnd); |
|
329 } |
|
330 |
|
331 void writeCollectionStart(const Header& header) override { |
|
332 handlers.forward(&ASN1ContentHandler::writeCollectionStart, header); |
|
333 } |
|
334 |
|
335 void writeCollectionEnd() override { |
|
336 handlers.forward(&ASN1ContentHandler::writeCollectionEnd); |
|
337 } |
|
338 |
|
339 void writeBoolean(const Header& header, bool value) override { |
|
340 handlers.forward(&ASN1ContentHandler::writeBoolean, header, value); |
|
341 } |
|
342 |
|
343 void writeNull(const Header& header) override { |
|
344 handlers.forward(&ASN1ContentHandler::writeNull, header); |
|
345 } |
|
346 |
|
347 void writeInteger(const Header& header, Integer value) override { |
|
348 handlers.forward(&ASN1ContentHandler::writeInteger, header, value); |
|
349 } |
|
350 |
|
351 void writeTextString(const Header& header, std::string value) override { |
|
352 handlers.forward(&ASN1ContentHandler::writeTextString, header, value); |
|
353 } |
|
354 |
|
355 void writeOctetString(const Header& header, std::string value) override { |
|
356 handlers.forward(&ASN1ContentHandler::writeOctetString, header, value); |
|
357 } |
|
358 |
|
359 void writeBitString(const Header& header, std::vector<bool> value) override { |
|
360 handlers.forward(&ASN1ContentHandler::writeBitString, header, value); |
|
361 } |
|
362 |
|
363 void writeOID(const Header& header, ObjectIdentifier value) override { |
|
364 handlers.forward(&ASN1ContentHandler::writeOID, header, value); |
|
365 } |
|
366 |
|
367 void writeDateTime(const Header& header, DateTime value) override { |
|
368 handlers.forward(&ASN1ContentHandler::writeDateTime, header, value); |
|
369 } |
|
370 |
|
371 void writeSpecific(const Header& header, std::string value) override { |
|
372 handlers.forward(&ASN1ContentHandler::writeSpecific, header, value); |
|
373 } |
|
374 |
|
375 }; |
|
376 |
|
377 |
|
378 } |
|
379 } |
|
380 } |
|
381 } |