Botan 3.0.0
Crypto and TLS for C&
parsing.cpp
Go to the documentation of this file.
1/*
2* Various string utils and parsing functions
3* (C) 1999-2007,2013,2014,2015,2018 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/internal/parsing.h>
11#include <botan/internal/loadstor.h>
12#include <botan/internal/fmt.h>
13#include <botan/exceptn.h>
14#include <algorithm>
15#include <cctype>
16#include <limits>
17#include <sstream>
18
19namespace Botan {
20
21uint16_t to_uint16(std::string_view str)
22 {
23 const uint32_t x = to_u32bit(str);
24
25 if(x >> 16)
26 throw Invalid_Argument("Integer value exceeds 16 bit range");
27
28 return static_cast<uint16_t>(x);
29 }
30
31uint32_t to_u32bit(std::string_view str_view)
32 {
33 const std::string str(str_view);
34
35 // std::stoul is not strict enough. Ensure that str is digit only [0-9]*
36 for(const char chr : str)
37 {
38 if(chr < '0' || chr > '9')
39 {
40 throw Invalid_Argument("to_u32bit invalid decimal string '" + str + "'");
41 }
42 }
43
44 const unsigned long int x = std::stoul(str);
45
46 if constexpr(sizeof(unsigned long int) > 4)
47 {
48 // x might be uint64
49 if(x > std::numeric_limits<uint32_t>::max())
50 {
51 throw Invalid_Argument("Integer value of " + str + " exceeds 32 bit range");
52 }
53 }
54
55 return static_cast<uint32_t>(x);
56 }
57
58/*
59* Parse a SCAN-style algorithm name
60*/
61std::vector<std::string> parse_algorithm_name(std::string_view namex)
62 {
63 if(namex.find('(') == std::string::npos &&
64 namex.find(')') == std::string::npos)
65 {
66 return {std::string(namex)};
67 }
68
69 std::string name(namex);
70 std::string substring;
71 std::vector<std::string> elems;
72 size_t level = 0;
73
74 elems.push_back(name.substr(0, name.find('(')));
75 name = name.substr(name.find('('));
76
77 for(auto i = name.begin(); i != name.end(); ++i)
78 {
79 char c = *i;
80
81 if(c == '(')
82 ++level;
83 if(c == ')')
84 {
85 if(level == 1 && i == name.end() - 1)
86 {
87 if(elems.size() == 1)
88 elems.push_back(substring.substr(1));
89 else
90 elems.push_back(substring);
91 return elems;
92 }
93
94 if(level == 0 || (level == 1 && i != name.end() - 1))
95 throw Invalid_Algorithm_Name(namex);
96 --level;
97 }
98
99 if(c == ',' && level == 1)
100 {
101 if(elems.size() == 1)
102 elems.push_back(substring.substr(1));
103 else
104 elems.push_back(substring);
105 substring.clear();
106 }
107 else
108 substring += c;
109 }
110
111 if(!substring.empty())
112 throw Invalid_Algorithm_Name(namex);
113
114 return elems;
115 }
116
117std::vector<std::string> split_on(std::string_view str, char delim)
118 {
119 std::vector<std::string> elems;
120 if(str.empty()) return elems;
121
122 std::string substr;
123 for(auto i = str.begin(); i != str.end(); ++i)
124 {
125 if(*i == delim)
126 {
127 if(!substr.empty())
128 elems.push_back(substr);
129 substr.clear();
130 }
131 else
132 substr += *i;
133 }
134
135 if(substr.empty())
136 {
137 throw Invalid_Argument(fmt("Unable to split string '{}", str));
138 }
139 elems.push_back(substr);
140
141 return elems;
142 }
143
144/*
145* Join a string
146*/
147std::string string_join(const std::vector<std::string>& strs, char delim)
148 {
149 std::ostringstream out;
150
151 for(size_t i = 0; i != strs.size(); ++i)
152 {
153 if(i != 0)
154 out << delim;
155 out << strs[i];
156 }
157
158 return out.str();
159 }
160
161/*
162* Convert a decimal-dotted string to binary IP
163*/
164uint32_t string_to_ipv4(std::string_view str)
165 {
166 const auto parts = split_on(str, '.');
167
168 if(parts.size() != 4)
169 {
170 throw Decoding_Error(fmt("Invalid IPv4 string '{}'", str));
171 }
172
173 uint32_t ip = 0;
174
175 for(auto part = parts.begin(); part != parts.end(); ++part)
176 {
177 uint32_t octet = to_u32bit(*part);
178
179 if(octet > 255)
180 {
181 throw Decoding_Error(fmt("Invalid IPv4 string '{}'", str));
182 }
183
184 ip = (ip << 8) | (octet & 0xFF);
185 }
186
187 return ip;
188 }
189
190/*
191* Convert an IP address to decimal-dotted string
192*/
193std::string ipv4_to_string(uint32_t ip)
194 {
195 std::string str;
196 uint8_t bits[4];
197 store_be(ip, bits);
198
199 for(size_t i = 0; i != 4; ++i)
200 {
201 if(i > 0)
202 {
203 str += ".";
204 }
205 str += std::to_string(bits[i]);
206 }
207
208 return str;
209 }
210
211std::string tolower_string(std::string_view in)
212 {
213 std::string s(in);
214 for(size_t i = 0; i != s.size(); ++i)
215 {
216 const int cu = static_cast<unsigned char>(s[i]);
217 if(std::isalpha(cu))
218 s[i] = static_cast<char>(std::tolower(cu));
219 }
220 return s;
221 }
222
223bool host_wildcard_match(std::string_view issued_, std::string_view host_)
224 {
225 const std::string issued = tolower_string(issued_);
226 const std::string host = tolower_string(host_);
227
228 if(host.empty() || issued.empty())
229 return false;
230
231 /*
232 If there are embedded nulls in your issued name
233 Well I feel bad for you son
234 */
235 if(std::count(issued.begin(), issued.end(), char(0)) > 0)
236 return false;
237
238 // If more than one wildcard, then issued name is invalid
239 const size_t stars = std::count(issued.begin(), issued.end(), '*');
240 if(stars > 1)
241 return false;
242
243 // '*' is not a valid character in DNS names so should not appear on the host side
244 if(std::count(host.begin(), host.end(), '*') != 0)
245 return false;
246
247 // Similarly a DNS name can't end in .
248 if(host[host.size() - 1] == '.')
249 return false;
250
251 // And a host can't have an empty name component, so reject that
252 if(host.find("..") != std::string::npos)
253 return false;
254
255 // Exact match: accept
256 if(issued == host)
257 {
258 return true;
259 }
260
261 /*
262 Otherwise it might be a wildcard
263
264 If the issued size is strictly longer than the hostname size it
265 couldn't possibly be a match, even if the issued value is a
266 wildcard. The only exception is when the wildcard ends up empty
267 (eg www.example.com matches www*.example.com)
268 */
269 if(issued.size() > host.size() + 1)
270 {
271 return false;
272 }
273
274 // If no * at all then not a wildcard, and so not a match
275 if(stars != 1)
276 {
277 return false;
278 }
279
280 /*
281 Now walk through the issued string, making sure every character
282 matches. When we come to the (singular) '*', jump forward in the
283 hostname by the corresponding amount. We know exactly how much
284 space the wildcard takes because it must be exactly `len(host) -
285 len(issued) + 1 chars`.
286
287 We also verify that the '*' comes in the leftmost component, and
288 doesn't skip over any '.' in the hostname.
289 */
290 size_t dots_seen = 0;
291 size_t host_idx = 0;
292
293 for(size_t i = 0; i != issued.size(); ++i)
294 {
295 dots_seen += (issued[i] == '.');
296
297 if(issued[i] == '*')
298 {
299 // Fail: wildcard can only come in leftmost component
300 if(dots_seen > 0)
301 {
302 return false;
303 }
304
305 /*
306 Since there is only one * we know the tail of the issued and
307 hostname must be an exact match. In this case advance host_idx
308 to match.
309 */
310 const size_t advance = (host.size() - issued.size() + 1);
311
312 if(host_idx + advance > host.size()) // shouldn't happen
313 return false;
314
315 // Can't be any intervening .s that we would have skipped
316 if(std::count(host.begin() + host_idx,
317 host.begin() + host_idx + advance, '.') != 0)
318 return false;
319
320 host_idx += advance;
321 }
322 else
323 {
324 if(issued[i] != host[host_idx])
325 {
326 return false;
327 }
328
329 host_idx += 1;
330 }
331 }
332
333 // Wildcard issued name must have at least 3 components
334 if(dots_seen < 2)
335 {
336 return false;
337 }
338
339 return true;
340 }
341
342}
std::string name
Definition: alg_id.cpp:12
uint32_t to_u32bit(std::string_view str_view)
Definition: parsing.cpp:31
uint32_t string_to_ipv4(std::string_view str)
Definition: parsing.cpp:164
uint16_t to_uint16(std::string_view str)
Definition: parsing.cpp:21
std::string fmt(std::string_view format, const T &... args)
Definition: fmt.h:60
std::vector< std::string > split_on(std::string_view str, char delim)
Definition: parsing.cpp:117
std::string tolower_string(std::string_view in)
Definition: parsing.cpp:211
std::string string_join(const std::vector< std::string > &strs, char delim)
Definition: parsing.cpp:147
bool host_wildcard_match(std::string_view issued_, std::string_view host_)
Definition: parsing.cpp:223
constexpr void store_be(uint16_t in, uint8_t out[2])
Definition: loadstor.h:449
std::vector< std::string > parse_algorithm_name(std::string_view namex)
Definition: parsing.cpp:61
std::string ipv4_to_string(uint32_t ip)
Definition: parsing.cpp:193