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