Botan  2.4.0
Crypto and TLS for C++11
parsing.cpp
Go to the documentation of this file.
1 /*
2 * Various string utils and parsing functions
3 * (C) 1999-2007,2013,2014,2015 Jack Lloyd
4 * (C) 2015 Simon Warta (Kullo GmbH)
5 * (C) 2017 RenĂ© Korthaus, Rohde & Schwarz Cybersecurity
6 *
7 * Botan is released under the Simplified BSD License (see license.txt)
8 */
9 
10 #include <botan/parsing.h>
11 #include <botan/exceptn.h>
12 #include <botan/charset.h>
13 #include <botan/loadstor.h>
14 #include <limits>
15 #include <set>
16 
17 namespace Botan {
18 
19 uint16_t to_uint16(const std::string& str)
20  {
21  const uint32_t x = to_u32bit(str);
22 
23  if(x >> 16)
24  throw Invalid_Argument("Integer value exceeds 16 bit range");
25 
26  return static_cast<uint16_t>(x);
27  }
28 
29 uint32_t to_u32bit(const std::string& str)
30  {
31  // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
32  for(const char chr : str)
33  {
34  if(chr < '0' || chr > '9')
35  {
36  std::string chrAsString(1, chr);
37  throw Invalid_Argument("String contains non-digit char: " + chrAsString);
38  }
39  }
40 
41  const unsigned long int x = std::stoul(str);
42 
43  if(sizeof(unsigned long int) > 4)
44  {
45  // x might be uint64
46  if (x > std::numeric_limits<uint32_t>::max())
47  {
48  throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range");
49  }
50  }
51 
52  return static_cast<uint32_t>(x);
53  }
54 
55 /*
56 * Convert a string into a time duration
57 */
58 uint32_t timespec_to_u32bit(const std::string& timespec)
59  {
60  if(timespec.empty())
61  return 0;
62 
63  const char suffix = timespec[timespec.size()-1];
64  std::string value = timespec.substr(0, timespec.size()-1);
65 
66  uint32_t scale = 1;
67 
68  if(Charset::is_digit(suffix))
69  value += suffix;
70  else if(suffix == 's')
71  scale = 1;
72  else if(suffix == 'm')
73  scale = 60;
74  else if(suffix == 'h')
75  scale = 60 * 60;
76  else if(suffix == 'd')
77  scale = 24 * 60 * 60;
78  else if(suffix == 'y')
79  scale = 365 * 24 * 60 * 60;
80  else
81  throw Decoding_Error("timespec_to_u32bit: Bad input " + timespec);
82 
83  return scale * to_u32bit(value);
84  }
85 
86 /*
87 * Parse a SCAN-style algorithm name
88 */
89 std::vector<std::string> parse_algorithm_name(const std::string& namex)
90  {
91  if(namex.find('(') == std::string::npos &&
92  namex.find(')') == std::string::npos)
93  return std::vector<std::string>(1, namex);
94 
95  std::string name = namex, substring;
96  std::vector<std::string> elems;
97  size_t level = 0;
98 
99  elems.push_back(name.substr(0, name.find('(')));
100  name = name.substr(name.find('('));
101 
102  for(auto i = name.begin(); i != name.end(); ++i)
103  {
104  char c = *i;
105 
106  if(c == '(')
107  ++level;
108  if(c == ')')
109  {
110  if(level == 1 && i == name.end() - 1)
111  {
112  if(elems.size() == 1)
113  elems.push_back(substring.substr(1));
114  else
115  elems.push_back(substring);
116  return elems;
117  }
118 
119  if(level == 0 || (level == 1 && i != name.end() - 1))
120  throw Invalid_Algorithm_Name(namex);
121  --level;
122  }
123 
124  if(c == ',' && level == 1)
125  {
126  if(elems.size() == 1)
127  elems.push_back(substring.substr(1));
128  else
129  elems.push_back(substring);
130  substring.clear();
131  }
132  else
133  substring += c;
134  }
135 
136  if(!substring.empty())
137  throw Invalid_Algorithm_Name(namex);
138 
139  return elems;
140  }
141 
142 std::vector<std::string> split_on(const std::string& str, char delim)
143  {
144  return split_on_pred(str, [delim](char c) { return c == delim; });
145  }
146 
147 std::vector<std::string> split_on_pred(const std::string& str,
148  std::function<bool (char)> pred)
149  {
150  std::vector<std::string> elems;
151  if(str.empty()) return elems;
152 
153  std::string substr;
154  for(auto i = str.begin(); i != str.end(); ++i)
155  {
156  if(pred(*i))
157  {
158  if(!substr.empty())
159  elems.push_back(substr);
160  substr.clear();
161  }
162  else
163  substr += *i;
164  }
165 
166  if(substr.empty())
167  throw Invalid_Argument("Unable to split string: " + str);
168  elems.push_back(substr);
169 
170  return elems;
171  }
172 
173 /*
174 * Join a string
175 */
176 std::string string_join(const std::vector<std::string>& strs, char delim)
177  {
178  std::string out = "";
179 
180  for(size_t i = 0; i != strs.size(); ++i)
181  {
182  if(i != 0)
183  out += delim;
184  out += strs[i];
185  }
186 
187  return out;
188  }
189 
190 /*
191 * Parse an ASN.1 OID string
192 */
193 std::vector<uint32_t> parse_asn1_oid(const std::string& oid)
194  {
195  std::string substring;
196  std::vector<uint32_t> oid_elems;
197 
198  for(auto i = oid.begin(); i != oid.end(); ++i)
199  {
200  char c = *i;
201 
202  if(c == '.')
203  {
204  if(substring.empty())
205  throw Invalid_OID(oid);
206  oid_elems.push_back(to_u32bit(substring));
207  substring.clear();
208  }
209  else
210  substring += c;
211  }
212 
213  if(substring.empty())
214  throw Invalid_OID(oid);
215  oid_elems.push_back(to_u32bit(substring));
216 
217  if(oid_elems.size() < 2)
218  throw Invalid_OID(oid);
219 
220  return oid_elems;
221  }
222 
223 /*
224 * X.500 String Comparison
225 */
226 bool x500_name_cmp(const std::string& name1, const std::string& name2)
227  {
228  auto p1 = name1.begin();
229  auto p2 = name2.begin();
230 
231  while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
232  while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
233 
234  while(p1 != name1.end() && p2 != name2.end())
235  {
236  if(Charset::is_space(*p1))
237  {
238  if(!Charset::is_space(*p2))
239  return false;
240 
241  while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
242  while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
243 
244  if(p1 == name1.end() && p2 == name2.end())
245  return true;
246  if(p1 == name1.end() || p2 == name2.end())
247  return false;
248  }
249 
250  if(!Charset::caseless_cmp(*p1, *p2))
251  return false;
252  ++p1;
253  ++p2;
254  }
255 
256  while((p1 != name1.end()) && Charset::is_space(*p1)) ++p1;
257  while((p2 != name2.end()) && Charset::is_space(*p2)) ++p2;
258 
259  if((p1 != name1.end()) || (p2 != name2.end()))
260  return false;
261  return true;
262  }
263 
264 /*
265 * Convert a decimal-dotted string to binary IP
266 */
267 uint32_t string_to_ipv4(const std::string& str)
268  {
269  std::vector<std::string> parts = split_on(str, '.');
270 
271  if(parts.size() != 4)
272  throw Decoding_Error("Invalid IP string " + str);
273 
274  uint32_t ip = 0;
275 
276  for(auto part = parts.begin(); part != parts.end(); ++part)
277  {
278  uint32_t octet = to_u32bit(*part);
279 
280  if(octet > 255)
281  throw Decoding_Error("Invalid IP string " + str);
282 
283  ip = (ip << 8) | (octet & 0xFF);
284  }
285 
286  return ip;
287  }
288 
289 /*
290 * Convert an IP address to decimal-dotted string
291 */
292 std::string ipv4_to_string(uint32_t ip)
293  {
294  std::string str;
295 
296  for(size_t i = 0; i != sizeof(ip); ++i)
297  {
298  if(i)
299  str += ".";
300  str += std::to_string(get_byte(i, ip));
301  }
302 
303  return str;
304  }
305 
306 std::string erase_chars(const std::string& str, const std::set<char>& chars)
307  {
308  std::string out;
309 
310  for(auto c: str)
311  if(chars.count(c) == 0)
312  out += c;
313 
314  return out;
315  }
316 
317 std::string replace_chars(const std::string& str,
318  const std::set<char>& chars,
319  char to_char)
320  {
321  std::string out = str;
322 
323  for(size_t i = 0; i != out.size(); ++i)
324  if(chars.count(out[i]))
325  out[i] = to_char;
326 
327  return out;
328  }
329 
330 std::string replace_char(const std::string& str, char from_char, char to_char)
331  {
332  std::string out = str;
333 
334  for(size_t i = 0; i != out.size(); ++i)
335  if(out[i] == from_char)
336  out[i] = to_char;
337 
338  return out;
339  }
340 
341 bool host_wildcard_match(const std::string& issued, const std::string& host)
342  {
343  if(issued == host)
344  {
345  return true;
346  }
347 
348  size_t stars = 0;
349  for(char c : issued)
350  {
351  if(c == '*')
352  stars += 1;
353  }
354 
355  if(stars > 1)
356  {
357  return false;
358  }
359 
360  // first try to match the base, then the left-most label
361  // which can contain exactly one wildcard at any position
362  if(issued.size() > 2)
363  {
364  size_t host_i = host.find('.');
365  if(host_i == std::string::npos || host_i == host.size() - 1)
366  {
367  return false;
368  }
369 
370  size_t issued_i = issued.find('.');
371  if(issued_i == std::string::npos || issued_i == issued.size() - 1)
372  {
373  return false;
374  }
375 
376  const std::string host_base = host.substr(host_i + 1);
377  const std::string issued_base = issued.substr(issued_i + 1);
378 
379  // if anything but the left-most label doesn't equal,
380  // we are already out here
381  if(host_base != issued_base)
382  {
383  return false;
384  }
385 
386  // compare the left-most labels
387  std::string host_prefix = host.substr(0, host_i);
388 
389  if(host_prefix.empty())
390  {
391  return false;
392  }
393 
394  const std::string issued_prefix = issued.substr(0, issued_i);
395 
396  // if split_on would work on strings with less than 2 items,
397  // the if/else block would not be necessary
398  if(issued_prefix == "*")
399  {
400  return true;
401  }
402 
403  std::vector<std::string> p;
404 
405  if(issued_prefix[0] == '*')
406  {
407  p = std::vector<std::string>{"", issued_prefix.substr(1, issued_prefix.size())};
408  }
409  else if(issued_prefix[issued_prefix.size()-1] == '*')
410  {
411  p = std::vector<std::string>{issued_prefix.substr(0, issued_prefix.size() - 1), ""};
412  }
413  else
414  {
415  p = split_on(issued_prefix, '*');
416  }
417 
418  if(p.size() != 2)
419  {
420  return false;
421  }
422 
423  // match anything before and after the wildcard character
424  const std::string first = p[0];
425  const std::string last = p[1];
426 
427  if(host_prefix.substr(0, first.size()) == first)
428  {
429  host_prefix.erase(0, first.size());
430  }
431 
432  // nothing to match anymore
433  if(last.empty())
434  {
435  return true;
436  }
437 
438  if(host_prefix.size() >= last.size() &&
439  host_prefix.substr(host_prefix.size() - last.size(), last.size()) == last)
440  {
441  return true;
442  }
443  }
444 
445  return false;
446  }
447 }
uint16_t to_uint16(const std::string &str)
Definition: parsing.cpp:19
std::string erase_chars(const std::string &str, const std::set< char > &chars)
Definition: parsing.cpp:306
std::vector< std::string > split_on(const std::string &str, char delim)
Definition: parsing.cpp:142
std::vector< std::string > split_on_pred(const std::string &str, std::function< bool(char)> pred)
Definition: parsing.cpp:147
bool host_wildcard_match(const std::string &issued, const std::string &host)
Definition: parsing.cpp:341
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:108
uint32_t to_u32bit(const std::string &str)
Definition: parsing.cpp:29
uint32_t timespec_to_u32bit(const std::string &timespec)
Definition: parsing.cpp:58
bool caseless_cmp(char a, char b)
Definition: charset.cpp:275
std::string ipv4_to_string(uint32_t ip)
Definition: parsing.cpp:292
std::string replace_char(const std::string &str, char from_char, char to_char)
Definition: parsing.cpp:330
bool x500_name_cmp(const std::string &name1, const std::string &name2)
Definition: parsing.cpp:226
bool is_space(char c)
Definition: charset.cpp:221
std::vector< std::string > parse_algorithm_name(const std::string &namex)
Definition: parsing.cpp:89
Definition: alg_id.cpp:13
uint32_t string_to_ipv4(const std::string &str)
Definition: parsing.cpp:267
std::string replace_chars(const std::string &str, const std::set< char > &chars, char to_char)
Definition: parsing.cpp:317
bool is_digit(char c)
Definition: charset.cpp:210
uint8_t get_byte(size_t byte_num, T input)
Definition: loadstor.h:39
std::vector< uint32_t > parse_asn1_oid(const std::string &oid)
Definition: parsing.cpp:193
std::string string_join(const std::vector< std::string > &strs, char delim)
Definition: parsing.cpp:176