xref: /openbmc/bmcweb/http/utility.hpp (revision d8ef9915)
1 #pragma once
2 
3 #include "nlohmann/json.hpp"
4 
5 #include <openssl/crypto.h>
6 
7 #include <chrono>
8 #include <cstdint>
9 #include <cstring>
10 #include <functional>
11 #include <regex>
12 #include <stdexcept>
13 #include <string>
14 #include <tuple>
15 
16 namespace crow
17 {
18 namespace black_magic
19 {
20 
21 constexpr unsigned findClosingTag(std::string_view s, unsigned p)
22 {
23     return s[p] == '>' ? p : findClosingTag(s, p + 1);
24 }
25 
26 constexpr bool isInt(std::string_view s, unsigned i)
27 {
28     return s.substr(i, 5) == "<int>";
29 }
30 
31 constexpr bool isUint(std::string_view s, unsigned i)
32 {
33     return s.substr(i, 6) == "<uint>";
34 }
35 
36 constexpr bool isFloat(std::string_view s, unsigned i)
37 {
38     return s.substr(i, 7) == "<float>" || s.substr(i, 8) == "<double>";
39 }
40 
41 constexpr bool isStr(std::string_view s, unsigned i)
42 {
43     return s.substr(i, 5) == "<str>" || s.substr(i, 8) == "<string>";
44 }
45 
46 constexpr bool isPath(std::string_view s, unsigned i)
47 {
48     return s.substr(i, 6) == "<path>";
49 }
50 
51 template <typename T>
52 constexpr int getParameterTag()
53 {
54     if constexpr (std::is_same_v<int, T>)
55     {
56         return 1;
57     }
58     if constexpr (std::is_same_v<char, T>)
59     {
60         return 1;
61     }
62     if constexpr (std::is_same_v<short, T>)
63     {
64         return 1;
65     }
66     if constexpr (std::is_same_v<long, T>)
67     {
68         return 1;
69     }
70     if constexpr (std::is_same_v<long long, T>)
71     {
72         return 1;
73     }
74     if constexpr (std::is_same_v<unsigned int, T>)
75     {
76         return 2;
77     }
78     if constexpr (std::is_same_v<unsigned char, T>)
79     {
80         return 2;
81     }
82     if constexpr (std::is_same_v<unsigned short, T>)
83     {
84         return 2;
85     }
86     if constexpr (std::is_same_v<unsigned long, T>)
87     {
88         return 2;
89     }
90     if constexpr (std::is_same_v<unsigned long long, T>)
91     {
92         return 2;
93     }
94     if constexpr (std::is_same_v<double, T>)
95     {
96         return 3;
97     }
98     if constexpr (std::is_same_v<std::string, T>)
99     {
100         return 4;
101     }
102     return 0;
103 }
104 
105 template <typename... Args>
106 struct computeParameterTagFromArgsList;
107 
108 template <>
109 struct computeParameterTagFromArgsList<>
110 {
111     static constexpr int value = 0;
112 };
113 
114 template <typename Arg, typename... Args>
115 struct computeParameterTagFromArgsList<Arg, Args...>
116 {
117     static constexpr int subValue =
118         computeParameterTagFromArgsList<Args...>::value;
119     static constexpr int value =
120         getParameterTag<typename std::decay<Arg>::type>()
121             ? subValue * 6 + getParameterTag<typename std::decay<Arg>::type>()
122             : subValue;
123 };
124 
125 inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
126 {
127 
128     if (a == 0)
129     {
130         return b == 0;
131     }
132     if (b == 0)
133     {
134         return a == 0;
135     }
136     uint64_t sa = a % 6;
137     uint64_t sb = a % 6;
138     if (sa == 5)
139     {
140         sa = 4;
141     }
142     if (sb == 5)
143     {
144         sb = 4;
145     }
146     if (sa != sb)
147     {
148         return false;
149     }
150     return isParameterTagCompatible(a / 6, b / 6);
151 }
152 
153 constexpr uint64_t getParameterTag(std::string_view s, unsigned p = 0)
154 {
155 
156     if (p == s.size())
157     {
158         return 0;
159     }
160 
161     if (s[p] != '<')
162     {
163         return getParameterTag(s, p + 1);
164     }
165 
166     if (isInt(s, p))
167     {
168         return getParameterTag(s, findClosingTag(s, p)) * 6 + 1;
169     }
170 
171     if (isUint(s, p))
172     {
173         return getParameterTag(s, findClosingTag(s, p)) * 6 + 2;
174     }
175 
176     if (isFloat(s, p))
177     {
178         return getParameterTag(s, findClosingTag(s, p)) * 6 + 3;
179     }
180 
181     if (isStr(s, p))
182     {
183         return getParameterTag(s, findClosingTag(s, p)) * 6 + 4;
184     }
185 
186     if (isPath(s, p))
187     {
188         return getParameterTag(s, findClosingTag(s, p)) * 6 + 5;
189     }
190 
191     throw std::runtime_error("invalid parameter type");
192 }
193 
194 template <typename... T>
195 struct S
196 {
197     template <typename U>
198     using push = S<U, T...>;
199     template <typename U>
200     using push_back = S<T..., U>;
201     template <template <typename... Args> class U>
202     using rebind = U<T...>;
203 };
204 
205 template <typename F, typename Set>
206 struct CallHelper;
207 
208 template <typename F, typename... Args>
209 struct CallHelper<F, S<Args...>>
210 {
211     template <typename F1, typename... Args1,
212               typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
213     static char test(int);
214 
215     template <typename...>
216     static int test(...);
217 
218     static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
219 };
220 
221 template <uint64_t N>
222 struct SingleTagToType
223 {};
224 
225 template <>
226 struct SingleTagToType<1>
227 {
228     using type = int64_t;
229 };
230 
231 template <>
232 struct SingleTagToType<2>
233 {
234     using type = uint64_t;
235 };
236 
237 template <>
238 struct SingleTagToType<3>
239 {
240     using type = double;
241 };
242 
243 template <>
244 struct SingleTagToType<4>
245 {
246     using type = std::string;
247 };
248 
249 template <>
250 struct SingleTagToType<5>
251 {
252     using type = std::string;
253 };
254 
255 template <uint64_t Tag>
256 struct Arguments
257 {
258     using subarguments = typename Arguments<Tag / 6>::type;
259     using type = typename subarguments::template push<
260         typename SingleTagToType<Tag % 6>::type>;
261 };
262 
263 template <>
264 struct Arguments<0>
265 {
266     using type = S<>;
267 };
268 
269 template <typename T>
270 struct Promote
271 {
272     using type = T;
273 };
274 
275 template <typename T>
276 using PromoteT = typename Promote<T>::type;
277 
278 template <>
279 struct Promote<char>
280 {
281     using type = int64_t;
282 };
283 template <>
284 struct Promote<short>
285 {
286     using type = int64_t;
287 };
288 template <>
289 struct Promote<int>
290 {
291     using type = int64_t;
292 };
293 template <>
294 struct Promote<long>
295 {
296     using type = int64_t;
297 };
298 template <>
299 struct Promote<long long>
300 {
301     using type = int64_t;
302 };
303 template <>
304 struct Promote<unsigned char>
305 {
306     using type = uint64_t;
307 };
308 template <>
309 struct Promote<unsigned short>
310 {
311     using type = uint64_t;
312 };
313 template <>
314 struct Promote<unsigned int>
315 {
316     using type = uint64_t;
317 };
318 template <>
319 struct Promote<unsigned long>
320 {
321     using type = uint64_t;
322 };
323 template <>
324 struct Promote<unsigned long long>
325 {
326     using type = uint64_t;
327 };
328 
329 } // namespace black_magic
330 
331 namespace detail
332 {
333 
334 template <class T, std::size_t N, class... Args>
335 struct GetIndexOfElementFromTupleByTypeImpl
336 {
337     static constexpr std::size_t value = N;
338 };
339 
340 template <class T, std::size_t N, class... Args>
341 struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...>
342 {
343     static constexpr std::size_t value = N;
344 };
345 
346 template <class T, std::size_t N, class U, class... Args>
347 struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...>
348 {
349     static constexpr std::size_t value =
350         GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value;
351 };
352 
353 } // namespace detail
354 
355 namespace utility
356 {
357 template <class T, class... Args>
358 T& getElementByType(std::tuple<Args...>& t)
359 {
360     return std::get<
361         detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t);
362 }
363 
364 template <typename T>
365 struct function_traits;
366 
367 template <typename T>
368 struct function_traits : public function_traits<decltype(&T::operator())>
369 {
370     using parent_t = function_traits<decltype(&T::operator())>;
371     static const size_t arity = parent_t::arity;
372     using result_type = typename parent_t::result_type;
373     template <size_t i>
374     using arg = typename parent_t::template arg<i>;
375 };
376 
377 template <typename ClassType, typename r, typename... Args>
378 struct function_traits<r (ClassType::*)(Args...) const>
379 {
380     static const size_t arity = sizeof...(Args);
381 
382     using result_type = r;
383 
384     template <size_t i>
385     using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
386 };
387 
388 template <typename ClassType, typename r, typename... Args>
389 struct function_traits<r (ClassType::*)(Args...)>
390 {
391     static const size_t arity = sizeof...(Args);
392 
393     using result_type = r;
394 
395     template <size_t i>
396     using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
397 };
398 
399 template <typename r, typename... Args>
400 struct function_traits<std::function<r(Args...)>>
401 {
402     static const size_t arity = sizeof...(Args);
403 
404     using result_type = r;
405 
406     template <size_t i>
407     using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
408 };
409 
410 inline std::string base64encode(const std::string_view data)
411 {
412     const std::array<char, 64> key = {
413         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
414         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
415         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
416         'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
417         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
418 
419     size_t size = data.size();
420     std::string ret;
421     ret.resize((size + 2) / 3 * 4);
422     auto it = ret.begin();
423 
424     size_t i = 0;
425     while (i < size)
426     {
427         size_t keyIndex;
428 
429         keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
430         *it++ = key[keyIndex];
431 
432         if (i + 1 < size)
433         {
434             keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
435             keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
436             *it++ = key[keyIndex];
437 
438             if (i + 2 < size)
439             {
440                 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
441                 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
442                 *it++ = key[keyIndex];
443 
444                 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
445                 *it++ = key[keyIndex];
446             }
447             else
448             {
449                 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
450                 *it++ = key[keyIndex];
451                 *it++ = '=';
452             }
453         }
454         else
455         {
456             keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
457             *it++ = key[keyIndex];
458             *it++ = '=';
459             *it++ = '=';
460         }
461 
462         i += 3;
463     }
464 
465     return ret;
466 }
467 
468 // TODO this is temporary and should be deleted once base64 is refactored out of
469 // crow
470 inline bool base64Decode(const std::string_view input, std::string& output)
471 {
472     static const char nop = static_cast<char>(-1);
473     // See note on encoding_data[] in above function
474     static const std::array<char, 256> decodingData = {
475         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
476         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
477         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
478         nop, 62,  nop, nop, nop, 63,  52,  53,  54,  55,  56,  57,  58,  59,
479         60,  61,  nop, nop, nop, nop, nop, nop, nop, 0,   1,   2,   3,   4,
480         5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  17,  18,
481         19,  20,  21,  22,  23,  24,  25,  nop, nop, nop, nop, nop, nop, 26,
482         27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
483         41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  nop, nop, nop,
484         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
485         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
486         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
487         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
488         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
489         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
490         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
491         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
492         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
493         nop, nop, nop, nop};
494 
495     size_t inputLength = input.size();
496 
497     // allocate space for output string
498     output.clear();
499     output.reserve(((inputLength + 2) / 3) * 4);
500 
501     auto getCodeValue = [](char c) {
502         auto code = static_cast<unsigned char>(c);
503         // Ensure we cannot index outside the bounds of the decoding array
504         static_assert(std::numeric_limits<decltype(code)>::max() <
505                       decodingData.size());
506         return decodingData[code];
507     };
508 
509     // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
510     // dropping first two bits
511     // and regenerate into 3 8-bits sequences
512 
513     for (size_t i = 0; i < inputLength; i++)
514     {
515         char base64code0;
516         char base64code1;
517         char base64code2 = 0; // initialized to 0 to suppress warnings
518         char base64code3;
519 
520         base64code0 = getCodeValue(input[i]);
521         if (base64code0 == nop)
522         { // non base64 character
523             return false;
524         }
525         if (!(++i < inputLength))
526         { // we need at least two input bytes for first
527           // byte output
528             return false;
529         }
530         base64code1 = getCodeValue(input[i]);
531         if (base64code1 == nop)
532         { // non base64 character
533             return false;
534         }
535         output +=
536             static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
537 
538         if (++i < inputLength)
539         {
540             char c = input[i];
541             if (c == '=')
542             { // padding , end of input
543                 return (base64code1 & 0x0f) == 0;
544             }
545             base64code2 = getCodeValue(input[i]);
546             if (base64code2 == nop)
547             { // non base64 character
548                 return false;
549             }
550             output += static_cast<char>(((base64code1 << 4) & 0xf0) |
551                                         ((base64code2 >> 2) & 0x0f));
552         }
553 
554         if (++i < inputLength)
555         {
556             char c = input[i];
557             if (c == '=')
558             { // padding , end of input
559                 return (base64code2 & 0x03) == 0;
560             }
561             base64code3 = getCodeValue(input[i]);
562             if (base64code3 == nop)
563             { // non base64 character
564                 return false;
565             }
566             output +=
567                 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
568         }
569     }
570 
571     return true;
572 }
573 
574 /**
575  * Method returns Date Time information according to requested format
576  *
577  * @param[in] time time in second since the Epoch
578  *
579  * @return Date Time according to requested format
580  */
581 inline std::string getDateTime(const std::time_t& time)
582 {
583     std::array<char, 128> dateTime;
584     std::string redfishDateTime("0000-00-00T00:00:00Z00:00");
585 
586     if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z",
587                       std::localtime(&time)))
588     {
589         // insert the colon required by the ISO 8601 standard
590         redfishDateTime = std::string(dateTime.data());
591         redfishDateTime.insert(redfishDateTime.end() - 2, ':');
592     }
593 
594     return redfishDateTime;
595 }
596 
597 inline std::string dateTimeNow()
598 {
599     std::time_t time = std::time(nullptr);
600     return getDateTime(time);
601 }
602 
603 inline bool constantTimeStringCompare(const std::string_view a,
604                                       const std::string_view b)
605 {
606     // Important note, this function is ONLY constant time if the two input
607     // sizes are the same
608     if (a.size() != b.size())
609     {
610         return false;
611     }
612     return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
613 }
614 
615 struct ConstantTimeCompare
616 {
617     bool operator()(const std::string_view a, const std::string_view b) const
618     {
619         return constantTimeStringCompare(a, b);
620     }
621 };
622 
623 inline std::time_t getTimestamp(uint64_t millisTimeStamp)
624 {
625     // Retrieve Created property with format:
626     // yyyy-mm-ddThh:mm:ss
627     std::chrono::milliseconds chronoTimeStamp(millisTimeStamp);
628     return std::chrono::duration_cast<std::chrono::duration<int>>(
629                chronoTimeStamp)
630         .count();
631 }
632 
633 } // namespace utility
634 } // namespace crow
635