# HG changeset patch # User František Kučera # Date 1625076699 -7200 # Node ID 9b6f86760384e5e549f2c8c3ffd96c013f4e7a86 # Parent 7050364456720d755934cfadb2ba5cfabf60cd25 slightly improved UTCTime and GeneralizedTime support diff -r 705036445672 -r 9b6f86760384 src/lib/ASN1ContentHandler.h --- a/src/lib/ASN1ContentHandler.h Mon Jun 28 22:48:04 2021 +0200 +++ b/src/lib/ASN1ContentHandler.h Wed Jun 30 20:11:39 2021 +0200 @@ -201,7 +201,9 @@ int8_t hour = 0; int8_t minute = 0; int8_t second = 0; - // TODO: ms/ns + int32_t nanosecond = 0; + int8_t timezoneHour = 0; + int8_t timezoneMinute = 0; virtual ~DateTime() { } @@ -215,7 +217,10 @@ result << "T" << std::setw(2) << (int) hour; result << ":" << std::setw(2) << (int) minute; result << ":" << std::setw(2) << (int) second; - result << "+00:00"; // TODO: timezone + if (precision == Precision::Nanosecond) result << "," << (int) nanosecond; + result << (timezoneHour < 0 ? "-" : "+"); + result << std::setw(2) << (int) std::abs(timezoneHour); + result << ":" << std::setw(2) << (int) timezoneMinute; return result.str(); } }; diff -r 705036445672 -r 9b6f86760384 src/lib/BasicASN1Reader.h --- a/src/lib/BasicASN1Reader.h Mon Jun 28 22:48:04 2021 +0200 +++ b/src/lib/BasicASN1Reader.h Wed Jun 30 20:11:39 2021 +0200 @@ -49,6 +49,46 @@ Constructed = 1 }; + enum UniversalType : uint64_t { + EndOfContent = 0x00, + Boolean = 0x01, + Integer = 0x02, + BitString = 0x03, + OctetString = 0x04, + Null = 0x05, + ObjectIdentifier = 0x06, + ObjectDescriptor = 0x07, + External = 0x08, + Real = 0x09, + Enumerated = 0xA, + Embedded = 0xB, + UTF8String = 0xC, + RelativeObjectIdentifier = 0xD, + Time = 0xE, + Reserved = 0xF, + Sequence = 0x10, + Set = 0x11, + NumericString = 0x12, + PrintableString = 0x13, + T61String = 0x14, + VideotexString = 0x15, + IA5String = 0x16, + UTCTime = 0x17, + GeneralizedTime = 0x18, + GraphicString = 0x19, + VisibleString = 0x1A, + GeneralString = 0x1B, + UniversalString = 0x1C, + CharacterString = 0x1D, + BMPString = 0x1E, + Date = 0x1F, + TimeOfDay = 0x20, + DateTime = 0x21, + Duration = 0x22, + ObjectIdentifierIRI = 0x23, + RelativeObjectIdentifierIRI = 0x24, + }; + class Header { public: TagClass tagClass; @@ -136,46 +176,46 @@ // TODO: check tagClass and pc // TODO: constants, more types - if (typeHeader.tag == 0 && typeHeader.tagClass == TagClass::Universal && typeHeader.pc == PC::Primitive) { + if (typeHeader.tag == UniversalType::EndOfContent && typeHeader.tagClass == TagClass::Universal && typeHeader.pc == PC::Primitive) { handlers.writeCollectionEnd(); - } else if (typeHeader.tag == 16) { + } else if (typeHeader.tag == UniversalType::Sequence) { level.push_back({typeHeader.definiteLength, typeHeader.length, getBytesRead()}); // TODO: transaction handlers.writeCollectionStart(ASN1ContentHandler::CollectionType::Sequence); - } else if (typeHeader.tag == 17) { + } else if (typeHeader.tag == UniversalType::Set) { level.push_back({typeHeader.definiteLength, typeHeader.length, getBytesRead()}); // TODO: transaction handlers.writeCollectionStart(ASN1ContentHandler::CollectionType::Set); } else if (typeHeader.pc == PC::Constructed) { level.push_back({typeHeader.definiteLength, typeHeader.length, getBytesRead()}); // TODO: transaction handlers.writeCollectionStart(ASN1ContentHandler::CollectionType::Constructed); - } else if (typeHeader.tag == 5 && typeHeader.length == 0) { + } else if (typeHeader.tag == UniversalType::Null && typeHeader.length == 0) { handlers.writeNull(); - } else if (typeHeader.tag == 1) { + } else if (typeHeader.tag == UniversalType::Boolean && typeHeader.definiteLength && typeHeader.length == 1) { bool value; read((uint8_t*) & value, 1); handlers.writeBoolean(value); - } else if (typeHeader.tag == 2 && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) { + } else if (typeHeader.tag == UniversalType::Integer && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) { // TODO: check available bytes before allocating buffer std::vector value(typeHeader.length, 0x00); read(value.data(), typeHeader.length); handlers.writeInteger(ASN1ContentHandler::Integer(value)); - } else if (typeHeader.tag == 6 && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) { + } else if (typeHeader.tag == UniversalType::ObjectIdentifier && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) { std::vector value(typeHeader.length, 0x00); read(value.data(), typeHeader.length); handlers.writeOID({value}); - } else if (typeHeader.tag == 12 && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) { + } else if (typeHeader.tag == UniversalType::UTF8String && 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); handlers.writeString(ASN1ContentHandler::StringType::UTF8String, s); - } else if (typeHeader.tag == 19 && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) { + } else if (typeHeader.tag == UniversalType::PrintableString && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) { // TODO: check encoding // TODO: check available bytes before allocating buffer std::string s; 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) { + } else if (typeHeader.tag == UniversalType::UTCTime && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) { // TODO: check available bytes before allocating buffer std::string s; s.resize(typeHeader.length); @@ -184,7 +224,14 @@ 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"))) { + 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|([+-][0-9]{2})'?([0-9]{2})'?)"))) { + // Supported UTCTime formats: + // YYMMDDhhmmZ + // YYMMDDhhmmssZ + // YYMMDDhhmm+hhmm + // YYMMDDhhmm-hhmm + // YYMMDDhhmmss+hhmm + // YYMMDDhhmmss-hhmm int i = 1; uint32_t year = std::stoi(match[i++]); dateTime.year = year < 50 ? 2000 + year : 1900 + year; @@ -192,17 +239,46 @@ dateTime.day = std::stoi(match[i++]); dateTime.hour = std::stoi(match[i++]); dateTime.minute = std::stoi(match[i++]); - dateTime.precision = ASN1ContentHandler::DateTime::Precision::Second; + dateTime.precision = match[i].length() ? ASN1ContentHandler::DateTime::Precision::Second : ASN1ContentHandler::DateTime::Precision::Minute; + dateTime.second = match[i].length() ? std::stoi(match[i++]) : 0; + if (match[i++] != "Z") { + dateTime.timezoneHour = std::stoi(match[i++]); + dateTime.timezoneMinute = std::stoi(match[i++]); + } 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); + throw std::invalid_argument("Unsupported UTCTime format: " + s); // TODO: better exception + } + + } else if (typeHeader.tag == UniversalType::GeneralizedTime && 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]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})(\\.([0-9]{1,3}))?(Z|([+-][0-9]{2})'?([0-9]{2})'?)"))) { + int i = 1; + dateTime.year = std::stoi(match[i++]); + 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.second = match[i].length() ? std::stoi(match[i++]) : 0; + dateTime.precision = match[i++].length() ? ASN1ContentHandler::DateTime::Precision::Nanosecond : ASN1ContentHandler::DateTime::Precision::Second; + if (match[i].length() == 1) dateTime.nanosecond = std::stoi(match[i++]) * 100 * 1000000; + else if (match[i].length() == 2) dateTime.nanosecond = std::stoi(match[i++]) * 10 * 1000000; + else if (match[i].length() == 3) dateTime.nanosecond = std::stoi(match[i++]) * 1000000; + else i++; + if (match[i++] != "Z") { + dateTime.timezoneHour = std::stoi(match[i++]); + dateTime.timezoneMinute = std::stoi(match[i++]); + } + handlers.writeDateTime(ASN1ContentHandler::DateTimeType::GeneralizedTime, dateTime); + } else { + throw std::invalid_argument("Unsupported GeneralizedTime format: " + s); // TODO: better exception } } else {