/* * Various string utils and parsing functions * (C) 1999-2007,2013,2014,2015 Jack Lloyd * (C) 2015 Simon Warta (Kullo GmbH) * (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ #include #include #include #include #include #include #include namespace Botan { uint32_t to_u32bit(const std::string& str) { try { // std::stoul is not strict enough. Ensure that str is digit only [0-9]* for (const char chr : str) { if (chr < '0' || chr > '9') { auto chrAsString = std::string(1, chr); throw Invalid_Argument("String contains non-digit char: " + chrAsString); } } const auto integerValue = std::stoul(str); // integerValue might be uint64 if (integerValue > std::numeric_limits::max()) { throw Invalid_Argument("Integer value exceeds 32 bit range: " + std::to_string(integerValue)); } return integerValue; } catch(std::exception& e) { auto message = std::string("Could not read '" + str + "' as decimal string"); auto exceptionMessage = std::string(e.what()); if (!exceptionMessage.empty()) message += ": " + exceptionMessage; throw Exception(message); } } /* * Convert a string into a time duration */ uint32_t timespec_to_u32bit(const std::string& timespec) { if(timespec.empty()) return 0; const char suffix = timespec[timespec.size()-1]; std::string value = timespec.substr(0, timespec.size()-1); uint32_t scale = 1; if(Charset::is_digit(suffix)) value += suffix; else if(suffix == 's') scale = 1; else if(suffix == 'm') scale = 60; else if(suffix == 'h') scale = 60 * 60; else if(suffix == 'd') scale = 24 * 60 * 60; else if(suffix == 'y') scale = 365 * 24 * 60 * 60; else throw Decoding_Error("timespec_to_u32bit: Bad input " + timespec); return scale * to_u32bit(value); } /* * Parse a SCAN-style algorithm name */ std::vector parse_algorithm_name(const std::string& namex) { if(namex.find('(') == std::string::npos && namex.find(')') == std::string::npos) return std::vector(1, namex); std::string name = namex, substring; std::vector elems; size_t level = 0; elems.push_back(name.substr(0, name.find('('))); name = name.substr(name.find('(')); for(auto i = name.begin(); i != name.end(); ++i) { char c = *i; if(c == '(') ++level; if(c == ')') { if(level == 1 && i == name.end() - 1) { if(elems.size() == 1) elems.push_back(substring.substr(1)); else elems.push_back(substring); return elems; } if(level == 0 || (level == 1 && i != name.end() - 1)) throw Invalid_Algorithm_Name(namex); --level; } if(c == ',' && level == 1) { if(elems.size() == 1) elems.push_back(substring.substr(1)); else elems.push_back(substring); substring.clear(); } else substring += c; } if(!substring.empty()) throw Invalid_Algorithm_Name(namex); return elems; } std::vector split_on(const std::string& str, char delim) { return split_on_pred(str, [delim](char c) { return c == delim; }); } std::vector split_on_pred(const std::string& str, std::function pred) { std::vector elems; if(str.empty()) return elems; std::string substr; for(auto i = str.begin(); i != str.end(); ++i) { if(pred(*i)) { if(!substr.empty()) elems.push_back(substr); substr.clear(); } else substr += *i; } if(substr.empty()) throw Invalid_Argument("Unable to split string: " + str); elems.push_back(substr); return elems; } /* * Join a string */ std::string string_join(const std::vector& strs, char delim) { std::string out = ""; for(size_t i = 0; i != strs.size(); ++i) { if(i != 0) out += delim; out += strs[i]; } return out; } /* * Parse an ASN.1 OID string */ std::vector parse_asn1_oid(const std::string& oid) { std::string substring; std::vector oid_elems; for(auto i = oid.begin(); i != oid.end(); ++i) { char c = *i; if(c == '.') { if(substring.empty()) throw Invalid_OID(oid); oid_elems.push_back(to_u32bit(substring)); substring.clear(); } else substring += c; } if(substring.empty()) throw Invalid_OID(oid); oid_elems.push_back(to_u32bit(substring)); if(oid_elems.size() < 2) throw Invalid_OID(oid); return oid_elems; } /* * X.500 String Comparison */ bool x500_name_cmp(const std::string& name1, const std::string& name2) { auto p1 = name1.begin(); auto p2 = name2.begin(); while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1; while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2; while(p1 != name1.end() && p2 != name2.end()) { if(Charset::is_space(*p1)) { if(!Charset::is_space(*p2)) return false; while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1; while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2; if(p1 == name1.end() && p2 == name2.end()) return true; if(p1 == name1.end() || p2 == name2.end()) return false; } if(!Charset::caseless_cmp(*p1, *p2)) return false; ++p1; ++p2; } while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1; while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2; if((p1 != name1.end()) || (p2 != name2.end())) return false; return true; } /* * Convert a decimal-dotted string to binary IP */ uint32_t string_to_ipv4(const std::string& str) { std::vector parts = split_on(str, '.'); if(parts.size() != 4) throw Decoding_Error("Invalid IP string " + str); uint32_t ip = 0; for(auto part = parts.begin(); part != parts.end(); ++part) { uint32_t octet = to_u32bit(*part); if(octet > 255) throw Decoding_Error("Invalid IP string " + str); ip = (ip << 8) | (octet & 0xFF); } return ip; } /* * Convert an IP address to decimal-dotted string */ std::string ipv4_to_string(uint32_t ip) { std::string str; for(size_t i = 0; i != sizeof(ip); ++i) { if(i) str += "."; str += std::to_string(get_byte(i, ip)); } return str; } std::string erase_chars(const std::string& str, const std::set& chars) { std::string out; for(auto c: str) if(chars.count(c) == 0) out += c; return out; } std::string replace_chars(const std::string& str, const std::set& chars, char to_char) { std::string out = str; for(size_t i = 0; i != out.size(); ++i) if(chars.count(out[i])) out[i] = to_char; return out; } std::string replace_char(const std::string& str, char from_char, char to_char) { std::string out = str; for(size_t i = 0; i != out.size(); ++i) if(out[i] == from_char) out[i] = to_char; return out; } bool host_wildcard_match(const std::string& issued, const std::string& host) { if(issued == host) { return true; } if(std::count(issued.begin(), issued.end(), '*') > 1) { return false; } // first try to match the base, then the left-most label // which can contain exactly one wildcard at any position if(issued.size() > 2) { size_t host_i = host.find('.'); if(host_i == std::string::npos || host_i == host.size() - 1) { return false; } size_t issued_i = issued.find('.'); if(issued_i == std::string::npos || issued_i == issued.size() - 1) { return false; } const std::string host_base = host.substr(host_i + 1); const std::string issued_base = issued.substr(issued_i + 1); // if anything but the left-most label doesn't equal, // we are already out here if(host_base != issued_base) { return false; } // compare the left-most labels std::string host_prefix = host.substr(0, host_i); if(host_prefix.empty()) { return false; } const std::string issued_prefix = issued.substr(0, issued_i); // if split_on would work on strings with less than 2 items, // the if/else block would not be necessary if(issued_prefix == "*") { return true; } std::vector p; if(issued_prefix[0] == '*') { p = std::vector{"", issued_prefix.substr(1, issued_prefix.size())}; } else if(issued_prefix[issued_prefix.size()-1] == '*') { p = std::vector{issued_prefix.substr(0, issued_prefix.size() - 1), ""}; } else { p = split_on(issued_prefix, '*'); } if(p.size() != 2) { return false; } // match anything before and after the wildcard character const std::string first = p[0]; const std::string last = p[1]; if(host_prefix.substr(0, first.size()) == first) { host_prefix.erase(0, first.size()); } // nothing to match anymore if(last.empty()) { return true; } if(host_prefix.size() >= last.size() && host_prefix.substr(host_prefix.size() - last.size(), last.size()) == last) { return true; } } return false; } }