CCF
Loading...
Searching...
No Matches
quote_endorsements_client.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
7
8namespace ccf
9{
11 std::function<void(std::vector<uint8_t>&& endorsements)>;
12
13 // Resilient client to fetch attestation report endorsement certificate.
15 : public std::enable_shared_from_this<QuoteEndorsementsClient>
16 {
17 private:
18 using EndpointInfo =
21
22 // Resend request after this interval if no response was received from
23 // remote server
24 static constexpr size_t server_connection_timeout_s = 3;
25
26 // Maximum number of retries per remote server before giving up and moving
27 // on to the next server.
28 static constexpr size_t max_server_retries_count = 3;
29
30 std::shared_ptr<RPCSessions> rpcsessions;
31
34
35 std::vector<uint8_t> endorsements_pem;
36
37 ccf::pal::Mutex lock;
38
39 // Uniquely identify each received request. We assume that this client sends
40 // requests in series, after receiving the response to each one or after a
41 // long timeout.
42 size_t last_received_request_id = 0;
43 bool has_completed = false;
44 size_t server_retries_count = 0;
45
46 struct QuoteEndorsementsClientMsg
47 {
48 QuoteEndorsementsClientMsg(
49 const std::shared_ptr<QuoteEndorsementsClient>& self_,
50 const Server& server_) :
51 self(self_),
52 server(server_)
53 {}
54
55 std::shared_ptr<QuoteEndorsementsClient> self;
56 Server server;
57 };
58
59 struct QuoteEndorsementsClientTimeoutMsg
60 {
61 QuoteEndorsementsClientTimeoutMsg(
62 const std::shared_ptr<QuoteEndorsementsClient>& self_,
63 const EndpointInfo& endpoint_,
64 size_t request_id_) :
65 self(self_),
66 endpoint(endpoint_),
67 request_id(request_id_)
68 {}
69
70 std::shared_ptr<QuoteEndorsementsClient> self;
71 EndpointInfo endpoint;
72 size_t request_id;
73 };
74
75 std::shared_ptr<ClientSession> create_unauthenticated_client()
76 {
77 // Note: server CA is not checked here as this client is not sending
78 // private data. If the server was malicious and the certificate chain was
79 // bogus, the verification of the endorsement of the quote would fail
80 // anyway.
81 return rpcsessions->create_client(std::make_shared<::tls::Cert>(
82 nullptr, std::nullopt, std::nullopt, std::nullopt, false));
83 }
84
85 std::shared_ptr<ClientSession> create_unencrypted_client()
86 {
87 return rpcsessions->create_unencrypted_client();
88 }
89
90 void send_request(
91 const std::shared_ptr<ClientSession>& client,
92 const EndpointInfo& endpoint)
93 {
94 {
95 ::http::Request r(endpoint.uri, HTTP_GET);
96 for (auto const& [k, v] : endpoint.params)
97 {
98 r.set_query_param(k, v);
99 }
100 for (auto const& [k, v] : endpoint.headers)
101 {
102 r.set_header(k, v);
103 }
104 r.set_header(http::headers::HOST, endpoint.host);
105
107 "Fetching endorsements for attestation report at {}{}{}",
108 endpoint,
109 r.get_path(),
111 client->send_request(std::move(r));
112 }
113
114 // Start watchdog to send request on new server if it is unresponsive
115 auto msg = std::make_unique<
117 [](std::unique_ptr<::threading::Tmsg<QuoteEndorsementsClientTimeoutMsg>>
118 msg) {
119 std::lock_guard<ccf::pal::Mutex> guard(msg->data.self->lock);
120 if (msg->data.self->has_completed)
121 {
122 return;
123 }
124 if (msg->data.request_id >= msg->data.self->last_received_request_id)
125 {
126 auto& servers = msg->data.self->config.servers;
127 msg->data.self->server_retries_count++;
128 if (
129 msg->data.self->server_retries_count >= max_server_retries_count)
130 {
131 if (servers.size() > 1)
132 {
133 // Move on to next server if we have passed max retries count
134 servers.pop_front();
135 }
136 else
137 {
138 auto& server = servers.front();
140 "Giving up retrying fetching attestation endorsements from "
141 "{} after {} attempts",
142 server.front().host,
143 max_server_retries_count);
144 return;
145 }
146 }
147
148 msg->data.self->fetch(servers.front());
149 }
150 },
151 shared_from_this(),
152 endpoint,
153 last_received_request_id);
154
156 std::move(msg),
157 std::chrono::milliseconds(server_connection_timeout_s * 1000));
158 }
159
160 void handle_success_response(
161 std::vector<uint8_t>&& data, const EndpointInfo& response_endpoint)
162 {
163 // We may receive a response to an in-flight request after having
164 // fetched all endorsements
165 auto& server = config.servers.front();
166 if (server.empty())
167 {
168 return;
169 }
170 auto endpoint = server.front();
171 if (has_completed || response_endpoint != endpoint)
172 {
173 return;
174 }
175
176 if (response_endpoint.response_is_der)
177 {
178 auto raw = ccf::crypto::cert_der_to_pem(data).raw();
179 endorsements_pem.insert(endorsements_pem.end(), raw.begin(), raw.end());
180 }
181 else if (response_endpoint.response_is_thim_json)
182 {
183 auto j = nlohmann::json::parse(data);
184 auto vcekCert = j.at("vcekCert").get<std::string>();
185 auto certificateChain = j.at("certificateChain").get<std::string>();
186 endorsements_pem.insert(
187 endorsements_pem.end(), vcekCert.begin(), vcekCert.end());
188 endorsements_pem.insert(
189 endorsements_pem.end(),
190 certificateChain.begin(),
191 certificateChain.end());
192 }
193 else
194 {
195 endorsements_pem.insert(
196 endorsements_pem.end(), data.begin(), data.end());
197 }
198
199 server.pop_front();
200 if (server.empty())
201 {
202 LOG_INFO_FMT("Complete endorsement chain successfully retrieved");
204 "{}", std::string(endorsements_pem.begin(), endorsements_pem.end()));
205 has_completed = true;
206 done_cb(std::move(endorsements_pem));
207 }
208 else
209 {
210 fetch(server);
211 }
212 }
213
214 void fetch(const Server& server)
215 {
216 auto endpoint = server.front();
217
218 auto c = endpoint.tls ? create_unauthenticated_client() :
219 create_unencrypted_client();
220 c->connect(
221 endpoint.host,
222 endpoint.port,
223 [this, server, endpoint](
224 http_status status,
225 http::HeaderMap&& headers,
226 std::vector<uint8_t>&& data) {
227 std::lock_guard<ccf::pal::Mutex> guard(this->lock);
228
229 last_received_request_id++;
230
231 if (status == HTTP_STATUS_OK)
232 {
233 LOG_INFO_FMT(
234 "Successfully retrieved endorsements for attestation report: "
235 "{} bytes",
236 data.size());
237
238 handle_success_response(std::move(data), endpoint);
239 return;
240 }
241
243 "Error fetching endorsements for attestation report: {}", status);
244 if (status == HTTP_STATUS_TOO_MANY_REQUESTS)
245 {
246 constexpr size_t default_retry_after_s = 3;
247 size_t retry_after_s = default_retry_after_s;
248 auto h = headers.find(http::headers::RETRY_AFTER);
249 if (h != headers.end())
250 {
251 const auto& retry_after_value = h->second;
252 // If value is invalid, retry_after_s is unchanged
253 std::from_chars(
254 retry_after_value.data(),
255 retry_after_value.data() + retry_after_value.size(),
256 retry_after_s);
257 }
258
259 auto msg =
261 [](
263 msg) { msg->data.self->fetch(msg->data.server); },
264 shared_from_this(),
265 server);
266
268 "{} endorsements endpoint had too many requests. Retrying "
269 "in {}s",
270 endpoint,
271 retry_after_s);
272
274 std::move(msg), std::chrono::milliseconds(retry_after_s * 1000));
275 }
276 return;
277 },
278 [endpoint](const std::string& error_msg) {
280 "TLS error when connecting to quote endorsements endpoint {}: {}",
281 endpoint,
282 error_msg);
283 });
284 send_request(c, endpoint);
285 }
286
287 public:
289 const std::shared_ptr<RPCSessions>& rpcsessions_,
292 rpcsessions(rpcsessions_),
293 config(config_),
294 done_cb(cb){};
295
297 {
298 std::lock_guard<ccf::pal::Mutex> guard(this->lock);
299 auto const& server = config.servers.front();
300 if (server.empty())
301 {
302 throw std::logic_error("No server specified to fetch endorsements");
303 }
304 fetch(server);
305 }
306 };
307}
Definition quote_endorsements_client.h:16
void fetch_endorsements()
Definition quote_endorsements_client.h:296
QuoteEndorsementsClient(const std::shared_ptr< RPCSessions > &rpcsessions_, const pal::snp::EndorsementEndpointsConfiguration &config_, QuoteEndorsementsFetchedCallback cb)
Definition quote_endorsements_client.h:288
std::vector< uint8_t > raw() const
Definition pem.h:71
void set_header(std::string k, const std::string &v)
Definition http_builder.h:45
Definition http_builder.h:106
std::string get_path() const
Definition http_builder.h:142
std::string get_formatted_query() const
Definition http_builder.h:152
void set_query_param(const std::string &k, const std::string &v)
Definition http_builder.h:147
static ThreadMessaging & instance()
Definition thread_messaging.h:278
TaskQueue::TimerEntry add_task_after(std::unique_ptr< Tmsg< Payload > > msg, std::chrono::milliseconds ms)
Definition thread_messaging.h:320
llhttp_status http_status
Definition http_status.h:7
#define LOG_INFO_FMT
Definition logger.h:395
#define LOG_DEBUG_FMT
Definition logger.h:380
#define LOG_FAIL_FMT
Definition logger.h:396
ccf::crypto::Pem cert_der_to_pem(const std::vector< uint8_t > &der)
Definition verifier.cpp:33
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
std::mutex Mutex
Definition locking.h:17
Definition app_interface.h:15
std::function< void(std::vector< uint8_t > &&endorsements)> QuoteEndorsementsFetchedCallback
Definition quote_endorsements_client.h:11
Definition perf_client.h:26
Definition attestation_sev_snp_endorsements.h:40
std::string host
Definition attestation_sev_snp_endorsements.h:41
std::map< std::string, std::string > params
Definition attestation_sev_snp_endorsements.h:44
std::map< std::string, std::string > headers
Definition attestation_sev_snp_endorsements.h:47
bool response_is_der
Definition attestation_sev_snp_endorsements.h:45
std::string port
Definition attestation_sev_snp_endorsements.h:42
std::string uri
Definition attestation_sev_snp_endorsements.h:43
bool tls
Definition attestation_sev_snp_endorsements.h:48
bool response_is_thim_json
Definition attestation_sev_snp_endorsements.h:46
Definition attestation_sev_snp_endorsements.h:38
std::list< Server > servers
Definition attestation_sev_snp_endorsements.h:56
std::list< EndpointInfo > Server
Definition attestation_sev_snp_endorsements.h:52
Definition thread_messaging.h:27