parse encapsulated structures (octet string or bit string containing valid ASN.1): reusable readString() method + let exception do its job on invalid input instead of attempt to allocate too much memory v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sun, 18 Jul 2021 20:08:29 +0200
branchv_0
changeset 36 caed9146f72f
parent 35 bf2c546f49d1
child 37 0845ca3636b6
parse encapsulated structures (octet string or bit string containing valid ASN.1): reusable readString() method + let exception do its job on invalid input instead of attempt to allocate too much memory (typeHeader.length may contain insane numbers if input in isValidBER() is not valid ASN.1/BER)
src/lib/BasicASN1Reader.h
--- a/src/lib/BasicASN1Reader.h	Sun Jul 18 12:37:40 2021 +0200
+++ b/src/lib/BasicASN1Reader.h	Sun Jul 18 20:08:29 2021 +0200
@@ -112,7 +112,28 @@
 		return h;
 	}
 
-	void readNext() {
+	const std::string readString(size_t length) {
+		std::string result;
+
+		for (size_t remaining = length; remaining;) {
+			size_t current = std::min(remaining, (size_t) 3);
+			result.resize(result.size() + current);
+			read((uint8_t*) result.data() + result.size() - current, current);
+			remaining -= current;
+		}
+
+		return result;
+	}
+
+	const std::vector<uint8_t> readVector(size_t length) {
+		std::vector<uint8_t> result;
+		std::string s = readString(length); // TODO: read directly to the vector
+		result.resize(length);
+		for (size_t i = 0; i < length; i++) result[i] = (uint8_t) s[i];
+		return result;
+	}
+
+	void processNext() {
 		using TagClass = ASN1ContentHandler::TagClass;
 		using PC = ASN1ContentHandler::PC;
 
@@ -146,38 +167,24 @@
 			read((uint8_t*) & value, 1);
 			handlers->writeBoolean(typeHeader, value);
 		} else if (typeHeader.tag == UniversalType::Integer && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
-			// TODO: check available bytes before allocating buffer
-			std::vector<uint8_t> value(typeHeader.length, 0x00);
-			read(value.data(), typeHeader.length);
+			std::vector<uint8_t> value = readVector(typeHeader.length);
 			handlers->writeInteger(typeHeader, ASN1ContentHandler::Integer(value));
 		} else if (typeHeader.tag == UniversalType::ObjectIdentifier && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
 			std::vector<uint8_t> value(typeHeader.length, 0x00);
 			read(value.data(), typeHeader.length);
 			handlers->writeOID(typeHeader,{value});
 		} 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);
+			std::string s = readString(typeHeader.length);
 			handlers->writeTextString(typeHeader, s);
 		} 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);
+			std::string s = readString(typeHeader.length);
 			handlers->writeTextString(typeHeader, s);
 		} else if (typeHeader.tag == UniversalType::OctetString && 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);
+			std::string s = readString(typeHeader.length);
 			if (processEncapsulatedContent(typeHeader, s) == false) handlers->writeOctetString(typeHeader, s);
 		} else if (typeHeader.tag == UniversalType::BitString && 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);
+			std::string s = readString(typeHeader.length);
 			if (processEncapsulatedContent(typeHeader, s) == false) {
 				std::vector<bool> bits;
 				// TODO: throw exception on wrong padding or insufficient length?
@@ -189,11 +196,8 @@
 				handlers->writeBitString(typeHeader, bits);
 			}
 		} else if (typeHeader.tag == UniversalType::UTCTime && typeHeader.tagClass == TagClass::Universal && typeHeader.definiteLength) {
-			// TODO: check available bytes before allocating buffer
 			// TODO: check encoding
-			std::string s;
-			s.resize(typeHeader.length);
-			read((uint8_t*) s.data(), typeHeader.length);
+			std::string s = readString(typeHeader.length);
 
 			ASN1ContentHandler::DateTime dateTime;
 
@@ -219,10 +223,7 @@
 			}
 
 		} 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);
+			std::string s = readString(typeHeader.length);
 
 			ASN1ContentHandler::DateTime dateTime;
 
@@ -252,10 +253,7 @@
 
 		} else {
 			// TODO: do not skip, parse
-			// TODO: check available bytes before allocating buffer
-			std::string s;
-			s.resize(typeHeader.length);
-			read((uint8_t*) s.data(), typeHeader.length);
+			std::string s = readString(typeHeader.length);
 			handlers->writeSpecific(typeHeader, s);
 		}
 
@@ -327,7 +325,7 @@
 protected:
 
 	void update() override {
-		while (true) readNext();
+		while (true) processNext();
 	}
 
 public: