Skip to content
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

Merged
merged 8 commits into from
Jan 10, 2020

Conversation

conradgrobler
Copy link
Collaborator

This is the initial implementation of a roughtime client to provide
trusted time to server components inside an enclave.

This is the initial implementation of a roughtime client to provide
trusted time to server components inside an enclave.
@conradgrobler conradgrobler added the WIP Work in progress label Jan 7, 2020
@conradgrobler conradgrobler marked this pull request as ready for review January 7, 2020 16:34
WORKSPACE Outdated
http_archive(
name = "roughtime",
build_file = "@//third_party/roughtime:roughtime.BUILD",
#sha256 = "47c99712b4b34a6713ea7ecfec6860e6b88ba046df206386346de4c3409fe6dd",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

un-comment this?

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
build_file = "@//third_party/roughtime:roughtime.BUILD",
build_file = "//third_party/roughtime:roughtime.BUILD",

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Number of times we will retry connecting to the server.
// Number of times we will retry connecting to each server.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

constexpr size_t kReceiveBufferSize = roughtime::kMinRequestSize;

// Maximum radius accepted for a roughtime response (1 minute in microseconds).
constexpr uint32_t kMaxRadius = 60000000;
Copy link
Collaborator

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)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

&timestamp, &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(),
Copy link
Collaborator

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/?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Nonce<roughtime::kNonceLength> nonce = generator.NextNonce();
ASYLO_ASSIGN_OR_RETURN(response, SendRequest(server, nonce));
roughtime::rough_time_t timestamp;
uint32_t radius;
Copy link
Collaborator

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@conradgrobler conradgrobler removed the WIP Work in progress label Jan 8, 2020
Copy link
Collaborator

@tiziano88 tiziano88 left a 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(
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

Copy link
Contributor

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…

Copy link
Contributor

@daviddrysdale daviddrysdale left a 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(
Copy link
Contributor

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…

)

test_suite(
name = "host_tests",
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Copy link
Contributor

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
Copy link
Contributor

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?

Copy link
Collaborator Author

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably OK as-is then (but if you were to do the third_party thing I don't think there's any need to copy the whole repo, just the bits that are used as a basis of the code – cf. 93902e6 & b3e20cb)

#ifndef OAK_SERVER_TIME_ROUGHTIME_CLIENT_H_
#define OAK_SERVER_TIME_ROUGHTIME_CLIENT_H_

#include <stdint.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Is this needed?

Copy link
Collaborator Author

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",
Copy link
Contributor

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).

Copy link
Collaborator Author

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) {
Copy link
Contributor

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

Copy link
Collaborator Author

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.
Copy link
Contributor

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?

Copy link
Collaborator Author

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

Copy link
Contributor

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?

Copy link
Collaborator Author

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;
Copy link
Contributor

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

auto handle = create_socket_result.ValueOrDie();
const std::string request = roughtime::CreateRequest(nonce.data());
Copy link
Contributor

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

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()) {
Copy link
Contributor

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)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"Network error on sending request to " + server.name);
}

uint8_t receive_buffer[kReceiveBufferSize];
Copy link
Contributor

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?

Copy link
Collaborator Author

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()));
}
Copy link
Contributor

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

ASYLO_ASSIGN_OR_RETURN(response, SendRequest(server, nonce));
roughtime::rough_time_t timestamp_microseconds;
uint32_t radius_microseconds;
std::string error;
Copy link
Contributor

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


StatusOr<RoughtimeInterval> FindOverlap(const std::vector<RoughtimeInterval>& intervals,
const int min_overlap) {
for (auto interval : intervals) {
Copy link
Contributor

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.

Copy link
Collaborator Author

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();
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Copy link
Contributor

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.

Copy link
Collaborator Author

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,
Copy link
Contributor

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)?

Copy link
Collaborator Author

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?

)

test_suite(
name = "host_tests",
Copy link
Contributor

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();
Copy link
Contributor

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.
Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably OK as-is then (but if you were to do the third_party thing I don't think there's any need to copy the whole repo, just the bits that are used as a basis of the code – cf. 93902e6 & b3e20cb)

@conradgrobler conradgrobler merged commit 1869878 into project-oak:master Jan 10, 2020
@conradgrobler conradgrobler deleted the roughtime branch January 10, 2020 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants