src/lib/ASN1ContentHandler.h
branchv_0
changeset 1 68a281aefa76
equal deleted inserted replaced
0:28294b895e5e 1:68a281aefa76
       
     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 }