-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
oak_runtime: add simple roughtime client #428
Conversation
This is the initial implementation of a roughtime client to provide trusted time to server components inside an enclave.
WORKSPACE
Outdated
http_archive( | ||
name = "roughtime", | ||
build_file = "@//third_party/roughtime:roughtime.BUILD", | ||
#sha256 = "47c99712b4b34a6713ea7ecfec6860e6b88ba046df206386346de4c3409fe6dd", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
un-comment this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of a problem. Each time this is downloaded it generates a new archive, so the hash changes. I will look into other options, such as using new_git_repository.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to using new_git_repository with shallow_since to make it deterministic.
WORKSPACE
Outdated
# Roughtime | ||
http_archive( | ||
name = "roughtime", | ||
build_file = "@//third_party/roughtime:roughtime.BUILD", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
build_file = "@//third_party/roughtime:roughtime.BUILD", | |
build_file = "//third_party/roughtime:roughtime.BUILD", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
oak/server/time/roughtime_client.cc
Outdated
// Number of seconds that we will wait for a reply from the server. | ||
constexpr int kTimeoutSeconds = 3; | ||
|
||
// Number of times we will retry connecting to the server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Number of times we will retry connecting to the server. | |
// Number of times we will retry connecting to each server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
oak/server/time/roughtime_client.cc
Outdated
// The minimum number of overlapping intervals we need to trust the time. | ||
constexpr int kMinOverlappingTimeIntervals = 2; | ||
|
||
// Number of seconds that we will wait for a reply from the server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Number of seconds that we will wait for a reply from the server. | |
// Number of seconds that we will wait for a reply from each server. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
oak/server/time/roughtime_client.cc
Outdated
constexpr size_t kReceiveBufferSize = roughtime::kMinRequestSize; | ||
|
||
// Maximum radius accepted for a roughtime response (1 minute in microseconds). | ||
constexpr uint32_t kMaxRadius = 60000000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to something that makes it clear what the unit is (e.g. *Microseconds
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
oak/server/time/roughtime_client.cc
Outdated
×tamp, &radius, &error, reinterpret_cast<const uint8_t*>(public_key.data()), | ||
reinterpret_cast<const uint8_t*>(response.data()), response.size(), nonce.data())) { | ||
return Status(asylo::error::GoogleError::INTERNAL, | ||
absl::StrFormat("Response from %s failed verification: %s", server.name.c_str(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe s/failed verification/could not be parsed/?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
oak/server/time/roughtime_client.cc
Outdated
Nonce<roughtime::kNonceLength> nonce = generator.NextNonce(); | ||
ASYLO_ASSIGN_OR_RETURN(response, SendRequest(server, nonce)); | ||
roughtime::rough_time_t timestamp; | ||
uint32_t radius; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this in seconds? could you clarify in the symbol name or in a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but I don't trust myself that much reviewing C++ code :) Adding @daviddrysdale as another pair of eyes, please wait for his approval too.
@@ -297,3 +297,12 @@ http_archive( | |||
strip_prefix = "clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04", | |||
url = "http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz", | |||
) | |||
|
|||
# Roughtime | |||
new_git_repository( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we need from new_git_repository
that git_repository
does not support? I thought new
was added during a transition period, but now it's fine to use the non-new version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The build_file attribute is not supported on git_repository and I need a custom build file to change the visibility of the roughtime targets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooh, that might be useful for some other things too…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only a partial review, I'm afraid, but I thought it was worth sending out what I've got so far – I'll finish off later today.
@@ -297,3 +297,12 @@ http_archive( | |||
strip_prefix = "clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04", | |||
url = "http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz", | |||
) | |||
|
|||
# Roughtime | |||
new_git_repository( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooh, that might be useful for some other things too…
oak/server/time/BUILD
Outdated
) | ||
|
||
test_suite( | ||
name = "host_tests", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
W00t, tests. Could you update the collection of targets in scripts/run_tests
to hit this too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test requires an internet connection and the Google and Clouflare roughtime servers to be up, so could lead to flakiness. For now I added it only for my own local testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, good point.
namespace oak { | ||
|
||
// Client for getting roughtime (in microseconds since Unix epoch) from multiple servers. | ||
// Based on https://roughtime.googlesource.com/roughtime/+/refs/heads/master/simple_client.cc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be under third_party/
with a pristine upstream commit for provenance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am unsure. This differs very significantly from the original file. The socket code is mostly identical, but apart from that it is more a case of 'inspired by' than 'copied from'. It is also additionally inspired by the go code in a subdirectory.
Should I add a pristine copy of the entire roughtime repository into third_party/ and then after the commit add a header and make simple_client a library rather than a cli?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oak/server/time/roughtime_client.h
Outdated
#ifndef OAK_SERVER_TIME_ROUGHTIME_CLIENT_H_ | ||
#define OAK_SERVER_TIME_ROUGHTIME_CLIENT_H_ | ||
|
||
#include <stdint.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
deps = [ | ||
"//oak/common:nonce_generator", | ||
"@com_google_asylo//asylo/util:status", | ||
"@roughtime//:client", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is missing some direct deps (absl dependencies, Asylo logging).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
namespace oak { | ||
|
||
TEST(RoughtimeClient, TestGetRoughtTime) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extra 't' in RoughtTime
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
|
||
namespace oak { | ||
|
||
// Client for getting roughtime (in microseconds since Unix epoch) from multiple servers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which multiple servers? Are there some hardcoded in the underlying roughtime library?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are currently hardcoded in roughtime_client.cc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a note about that to the comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
// Creates a UDP socket connected to the host and port. | ||
StatusOr<int> CreateSocket(const std::string& host, const std::string& port) { | ||
addrinfo hints = {}; | ||
hints.ai_socktype = SOCK_DGRAM; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: hints.ai_family = AF_UNSPEC;
explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
oak/server/time/roughtime_client.cc
Outdated
} | ||
|
||
auto handle = create_socket_result.ValueOrDie(); | ||
const std::string request = roughtime::CreateRequest(nonce.data()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: move this line down to after the setsockopt
call, nearer to first use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
oak/server/time/roughtime_client.cc
Outdated
do { | ||
send_size = send(handle, request.data(), request.size(), 0 /* flags */); | ||
} while (send_size == -1 && errno == EINTR); | ||
if (send_size < 0 || static_cast<size_t>(send_size) != request.size()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: could this be simpler as something like if (send_size != static_cast<ssize_t>(request.size())) {
(moving the cast so the second arm encompasses the first)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
oak/server/time/roughtime_client.cc
Outdated
"Network error on sending request to " + server.name); | ||
} | ||
|
||
uint8_t receive_buffer[kReceiveBufferSize]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: would making this const char
rather than uint8
allow the reinterpret_cast
below to be removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
return Status(asylo::error::GoogleError::INVALID_ARGUMENT, | ||
absl::StrFormat("Public key for server %s is not a valid base64 encoding.", | ||
server.name.c_str())); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe move l.163-l.168 up to the top of the function, so an invalid argument is detected before a request/response happens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
oak/server/time/roughtime_client.cc
Outdated
ASYLO_ASSIGN_OR_RETURN(response, SendRequest(server, nonce)); | ||
roughtime::rough_time_t timestamp_microseconds; | ||
uint32_t radius_microseconds; | ||
std::string error; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: move these 3 declarations down closer to first use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
oak/server/time/roughtime_client.cc
Outdated
|
||
StatusOr<RoughtimeInterval> FindOverlap(const std::vector<RoughtimeInterval>& intervals, | ||
const int min_overlap) { | ||
for (auto interval : intervals) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: const auto&
might be better (http://go/totw/44), here and below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
auto current = std::chrono::duration_cast<std::chrono::microseconds>( | ||
std::chrono::system_clock::now().time_since_epoch()) | ||
.count(); | ||
auto time_or_status = RoughtimeClient::GetRoughTime(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, is this test "live"? (i.e. would it work on a machine not connected to the internet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test requires an internet connection and both the Google and Cloudflare roughtime servers to be up and working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment and/or change the test name slightly ("..Live") so if someone runs it in isolation and it fails, they get a clue as to why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
(timestamp_microseconds + radius_microseconds)}; | ||
} | ||
|
||
StatusOr<RoughtimeInterval> FindOverlap(const std::vector<RoughtimeInterval>& intervals, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we get unit tests for this at some point (not necessarily now)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am unsure of the best approach without unnecessarily increasing the visibility. Should I make this a private static method and use FRIEND_TEST, or should I move this to a separate library (eg roughtime_util.h) with only package level visibility? Or is there a better option?
oak/server/time/BUILD
Outdated
) | ||
|
||
test_suite( | ||
name = "host_tests", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, good point.
auto current = std::chrono::duration_cast<std::chrono::microseconds>( | ||
std::chrono::system_clock::now().time_since_epoch()) | ||
.count(); | ||
auto time_or_status = RoughtimeClient::GetRoughTime(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment and/or change the test name slightly ("..Live") so if someone runs it in isolation and it fails, they get a clue as to why.
|
||
namespace oak { | ||
|
||
// Client for getting roughtime (in microseconds since Unix epoch) from multiple servers. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a note about that to the comment?
namespace oak { | ||
|
||
// Client for getting roughtime (in microseconds since Unix epoch) from multiple servers. | ||
// Based on https://roughtime.googlesource.com/roughtime/+/refs/heads/master/simple_client.cc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the initial implementation of a roughtime client to provide
trusted time to server components inside an enclave.