CCF
Loading...
Searching...
No Matches
x509_time_fmt.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
5#define FMT_HEADER_ONLY
6#include <chrono>
7#include <fmt/chrono.h>
8#include <fmt/format.h>
9#include <iomanip>
10#include <sstream>
11#include <time.h>
12#include <vector>
13
14namespace ccf::ds
15{
16 static inline std::string to_x509_time_string(const std::tm& time)
17 {
18 // Returns ASN1 time string (YYYYMMDDHHMMSSZ) from time_t, as per
19 // https://www.openssl.org/docs/man1.1.1/man3/ASN1_UTCTIME_set.html
20 return fmt::format("{:%Y%m%d%H%M%SZ}", time);
21 }
22
23 static inline std::string to_x509_time_string(
24 const std::chrono::system_clock::time_point& time)
25 {
26 return to_x509_time_string(fmt::gmtime(time));
27 }
28
29 static inline std::chrono::system_clock::time_point time_point_from_string(
30 const std::string& time)
31 {
32 // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
33 const char* ts = time.c_str();
34
35 auto accepted_formats = {
36 "%y%m%d%H%M%SZ", // ASN.1
37 "%Y%m%d%H%M%SZ", // Generalized ASN.1
38 "%Y-%m-%d %H:%M:%S"};
39
40 for (const auto* afmt : accepted_formats)
41 {
42 // Sadly %y in std::get_time seems to be broken, so strptime it is.
43 struct tm t = {};
44 auto* sres = strptime(ts, afmt, &t);
45 if (sres != nullptr && *sres == '\0')
46 {
47 auto r = std::chrono::system_clock::from_time_t(timegm(&t));
48 r -= std::chrono::seconds(t.tm_gmtoff);
49 return r;
50 }
51 }
52
53 // Then there are formats that strptime doesn't support...
54 std::vector<std::pair<const char*, int>> more_formats = {
55 // Note: longest format to match first
56 {"%04u-%02u-%02u %02u:%02u:%f %d:%02u", 8},
57 {"%04u-%02u-%02uT%02u:%02u:%f %d:%02u", 8},
58 {"%04u-%02u-%02u %02u:%02u:%f %03d %02u", 8},
59 {"%02u%02u%02u%02u%02u%02f%03d%02u", 8},
60 {"%04u%02u%02u%02u%02u%02f%03d%02u", 8},
61 {"%04u-%02u-%02uT%02u:%02u:%f", 6},
62 {"%04u-%02u-%02u %02u:%02u:%f", 6}};
63
64 for (auto [fmt, n] : more_formats)
65 {
66 unsigned y = 0;
67 unsigned m = 0;
68 unsigned d = 0;
69 unsigned h = 0;
70 unsigned mn = 0;
71 unsigned om = 0;
72 int oh = 0;
73 float s = 0.0;
74
75 int rs = sscanf(ts, fmt, &y, &m, &d, &h, &mn, &s, &oh, &om);
76 if (rs >= 1 && rs == n)
77 {
78 using namespace std::chrono;
79
80 if (strncmp(fmt, "%02u", 4) == 0)
81 {
82 // ASN.1 two-digit year range
83 y += y >= 50 ? 1900 : 2000;
84 }
85
86 if (rs >= 3)
87 {
88 auto date = year_month_day(year(y), month(m), day(d));
89
90 if (
91 !date.ok() || (rs >= 6 && (h > 24 || mn > 60 || s < 0.0)) ||
92 (rs >= 8 && (s > 60.0 || oh < -23 || oh > 23 || om > 60)))
93 {
94 continue;
95 }
96
97 system_clock::time_point r = (sys_days)date;
98 if (rs >= 6)
99 {
100 r += hours(h) + minutes(mn) + microseconds((long)(s * 1e6));
101 }
102 if (rs >= 8)
103 {
104 r -= hours(oh) + minutes(om);
105 }
106 return r;
107 }
108 }
109 }
110 // NOLINTEND(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
111
112 throw std::runtime_error(
113 fmt::format("'{}' does not match any accepted time format", time));
114 }
115
116 static inline std::string to_x509_time_string(const std::string& time)
117 {
118 return to_x509_time_string(time_point_from_string(time));
119 }
120}
Definition contiguous_set.h:12