partial UTCTime support v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Mon, 28 Jun 2021 22:48:04 +0200
branchv_0
changeset 21 705036445672
parent 20 fac034e3e867
child 22 9b6f86760384
partial UTCTime support
src/lib/ASN1ContentHandler.h
src/lib/BasicASN1Reader.h
src/lib/GenericASN1ContentHandler.h
--- a/src/lib/ASN1ContentHandler.h	Sun Jun 27 19:22:23 2021 +0200
+++ b/src/lib/ASN1ContentHandler.h	Mon Jun 28 22:48:04 2021 +0200
@@ -52,6 +52,17 @@
 		BMPString,
 	};
 
+	enum class DateTimeType : uint64_t {
+		UTCTime,
+		GeneralizedTime,
+		Time,
+		Date,
+		TimeOfDay,
+		DateTime,
+		Duration,
+		// TODO: review date/time types
+	};
+
 	class Integer {
 	private:
 		// TODO: use std::string (of octets, not ASCII) instead of std::vector?
@@ -167,9 +178,52 @@
 
 	};
 
+	class DateTime {
+	public:
+
+		enum class Precision {
+			Year,
+			Month,
+			Day,
+			Hour,
+			Minute,
+			Second,
+			Milisecond,
+			Nanosecond
+		};
+
+		// TODO: timezone (in minutes or 1/4 hours)
+
+		Precision precision = Precision::Second;
+		int32_t year = 1970;
+		int8_t month = 1;
+		int8_t day = 1;
+		int8_t hour = 0;
+		int8_t minute = 0;
+		int8_t second = 0;
+		// TODO: ms/ns
+
+		virtual ~DateTime() {
+		}
+
+		const std::string toString() const {
+			std::stringstream result;
+			result << std::setfill('0');
+			result << std::setw(4) << (int) year;
+			result << "-" << std::setw(2) << (int) month;
+			result << "-" << std::setw(2) << (int) day;
+			result << "T" << std::setw(2) << (int) hour;
+			result << ":" << std::setw(2) << (int) minute;
+			result << ":" << std::setw(2) << (int) second;
+			result << "+00:00"; // TODO: timezone
+			return result.str();
+		}
+	};
+
 	virtual ~ASN1ContentHandler() = default;
 
 	// TODO: more metadata, support OID decoding and ASN.1 modules (schema), probably through a plug-in
+	// 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
 
 	virtual void writeStreamStart() = 0;
 	virtual void writeStreamEnd() = 0;
@@ -181,18 +235,12 @@
 	virtual void writeInteger(Integer value) = 0;
 	virtual void writeString(StringType type, std::string value) = 0;
 	virtual void writeOID(ObjectIdentifier value) = 0;
+	virtual void writeDateTime(DateTimeType type, DateTime value) = 0;
 	// Object descriptor
 	// virtual void writeReal(float value) = 0;
 	// Enumerated
 	// Embedded PVD
 	// Relative OID
-	// TIME
-	// UTC time
-	// Generalized time
-	// Date
-	// Time of day
-	// Date-time
-	// Duration
 	// OID-IRI
 	// Relative OID-IRI
 
@@ -245,6 +293,9 @@
 		handler->writeOID(value);
 	}
 
+	void writeDateTime(DateTimeType type, DateTime value) override {
+		handler->writeDateTime(type, value);
+	}
 
 #undef handler
 
--- a/src/lib/BasicASN1Reader.h	Sun Jun 27 19:22:23 2021 +0200
+++ b/src/lib/BasicASN1Reader.h	Mon Jun 28 22:48:04 2021 +0200
@@ -20,6 +20,7 @@
 #include <vector>
 #include <array>
 #include <sstream>
+#include <regex>
 
 #include "ASN1Reader.h"
 
@@ -174,6 +175,36 @@
 			s.resize(typeHeader.length);
 			read((uint8_t*) s.data(), typeHeader.length);
 			handlers.writeString(ASN1ContentHandler::StringType::PrintableString, s);
+		} else if (typeHeader.tag == 0x17 && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
+			// TODO: check available bytes before allocating buffer
+			std::string s;
+			s.resize(typeHeader.length);
+			read((uint8_t*) s.data(), typeHeader.length);
+
+			ASN1ContentHandler::DateTime dateTime;
+
+			std::smatch match;
+			if (std::regex_match(s, match, std::regex("([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})Z"))) {
+				int i = 1;
+				uint32_t year = std::stoi(match[i++]);
+				dateTime.year = year < 50 ? 2000 + year : 1900 + year;
+				dateTime.month = std::stoi(match[i++]);
+				dateTime.day = std::stoi(match[i++]);
+				dateTime.hour = std::stoi(match[i++]);
+				dateTime.minute = std::stoi(match[i++]);
+				dateTime.precision = ASN1ContentHandler::DateTime::Precision::Second;
+				handlers.writeDateTime(ASN1ContentHandler::DateTimeType::UTCTime, dateTime);
+			} else {
+				// FIXME: decode more UTCTime formats:
+				// YYMMDDhhmmZ
+				// YYMMDDhhmm+hh'mm'
+				// YYMMDDhhmm-hh'mm'
+				// YYMMDDhhmmssZ
+				// YYMMDDhhmmss+hh'mm'
+				// YYMMDDhhmmss-hh'mm'
+				handlers.writeString(ASN1ContentHandler::StringType::UTF8String, "FIXME: UTCTime format not yet supported: " + s);
+			}
+
 		} else {
 			// TODO: do not skip, parse
 			std::vector<uint8_t> temp(typeHeader.length, 0);
--- a/src/lib/GenericASN1ContentHandler.h	Sun Jun 27 19:22:23 2021 +0200
+++ b/src/lib/GenericASN1ContentHandler.h	Mon Jun 28 22:48:04 2021 +0200
@@ -89,6 +89,11 @@
 		handlers.writeEndElement();
 	}
 
+	void writeDateTime(DateTimeType type, DateTime value) override {
+		handlers.writeStartElement("date-time");
+		handlers.writeCharacters(value.toString());
+		handlers.writeEndElement();
+	}
 
 };