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#include "ccf/ds/nonstd.h"
6
7#include <chrono>
8#define FMT_HEADER_ONLY
9#include <fmt/chrono.h>
10#include <fmt/format.h>
11#include <iomanip>
12#include <sstream>
13#include <time.h>
14#include <vector>
15
16namespace ccf::ds
17{
18 static inline std::string to_x509_time_string(const std::tm& time)
19 {
20 // Returns ASN1 time string (YYYYMMDDHHMMSSZ) from time_t, as per
21 // https://www.openssl.org/docs/man1.1.1/man3/ASN1_UTCTIME_set.html
22 return fmt::format("{:%Y%m%d%H%M%SZ}", time);
23 }
24
25 static inline std::string to_x509_time_string(
27 {
28 return to_x509_time_string(
29 fmt::gmtime(ccf::nonstd::SystemClock::to_time_t(time)));
30 }
31
32 static inline std::string to_x509_time_string(
33 const std::chrono::system_clock::time_point& time)
34 {
35 return to_x509_time_string(fmt::gmtime(time));
36 }
37
38 static inline ccf::nonstd::SystemClock::time_point time_point_from_string(
39 const std::string& time)
40 {
41 // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
42 const char* ts = time.c_str();
43
44 auto accepted_formats = {
45 "%y%m%d%H%M%SZ", // ASN.1
46 "%Y%m%d%H%M%SZ", // Generalized ASN.1
47 "%Y-%m-%d %H:%M:%S"};
48
49 for (const auto* afmt : accepted_formats)
50 {
51 // Sadly %y in std::get_time seems to be broken, so strptime it is.
52 struct tm t = {};
53 auto* sres = strptime(ts, afmt, &t);
54 if (sres != nullptr && *sres == '\0')
55 {
56 auto r = ccf::nonstd::SystemClock::from_time_t(timegm(&t));
57 r -= std::chrono::seconds(t.tm_gmtoff);
58 return r;
59 }
60 }
61
62 // Then there are formats that strptime doesn't support...
63 std::vector<std::pair<const char*, int>> more_formats = {
64 // Note: longest format to match first
65 {"%04u-%02u-%02u %02u:%02u:%f %d:%02u", 8},
66 {"%04u-%02u-%02uT%02u:%02u:%f %d:%02u", 8},
67 {"%04u-%02u-%02u %02u:%02u:%f %03d %02u", 8},
68 {"%02u%02u%02u%02u%02u%f%03d%02u", 8},
69 {"%04u%02u%02u%02u%02u%f%03d%02u", 8},
70 {"%04u-%02u-%02uT%02u:%02u:%f", 6},
71 {"%04u-%02u-%02u %02u:%02u:%f", 6}};
72
73 for (auto [fmt, n] : more_formats)
74 {
75 unsigned y = 0;
76 unsigned m = 0;
77 unsigned d = 0;
78 unsigned h = 0;
79 unsigned mn = 0;
80 unsigned om = 0;
81 int oh = 0;
82 float s = 0.0;
83
84 int rs = sscanf(ts, fmt, &y, &m, &d, &h, &mn, &s, &oh, &om);
85 if (rs >= 1 && rs == n)
86 {
87 using namespace std::chrono;
88 if (strncmp(fmt, "%02u", 4) == 0)
89 {
90 // ASN.1 two-digit year range
91 y += y >= 50 ? 1900 : 2000;
92 }
93
94 if (rs >= 3)
95 {
96 auto date = year_month_day(year(y), month(m), day(d));
97
98 if (
99 !date.ok() || (rs >= 6 && (h > 24 || mn > 60 || s < 0.0)) ||
100 (rs >= 8 && (s > 60.0 || oh < -23 || oh > 23 || om > 60)))
101 {
102 continue;
103 }
104
105 // Build a struct tm and use timegm() to convert to time_t
106 // directly, avoiding system_clock::time_point which can
107 // overflow for dates outside ~1677-2262.
108 struct tm t = {};
109 t.tm_year = static_cast<int>(y) - 1900;
110 t.tm_mon = static_cast<int>(m) - 1;
111 t.tm_mday = static_cast<int>(d);
112 if (rs >= 6)
113 {
114 t.tm_hour = static_cast<int>(h);
115 t.tm_min = static_cast<int>(mn);
116 t.tm_sec = static_cast<int>(s);
117 }
118
119 auto tt = timegm(&t);
120
121 if (rs >= 8)
122 {
123 auto offset_secs = oh * 3600 +
124 (oh < 0 ? -static_cast<int>(om) : static_cast<int>(om)) * 60;
125 tt -= offset_secs;
126 }
127
129 }
130 }
131 }
132 // NOLINTEND(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
133
134 throw std::runtime_error(
135 fmt::format("'{}' does not match any accepted time format", time));
136 }
137
138 static inline std::string to_x509_time_string(const std::string& time)
139 {
140 return to_x509_time_string(time_point_from_string(time));
141 }
142}
Definition contiguous_set.h:13
std::chrono::time_point< SystemClock > time_point
Definition nonstd.h:231
static time_point from_time_t(std::time_t t) noexcept
Definition nonstd.h:244
static std::time_t to_time_t(const time_point &t) noexcept
Definition nonstd.h:239