diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 564439c..7b640d1 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -20,7 +20,7 @@ jobs: - name: Checkout main project uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: - submodules: true + submodules: recursive lfs: true - name: Show releases diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ecc19c5..49b4352 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: - name: Checkout main project uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: - submodules: true + submodules: recursive - name: Expose Docker environmental variables for gha cache # This action takes in the ID tokens etc provided by the permissions, diff --git a/.gitmodules b/.gitmodules index 1f8e978..166f8de 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/protocolbuffers/protobuf.git [submodule "enclave/noise-c"] path = enclave/noise-c - url = https://github.com/rweather/noise-c.git + url = https://github.com/signalapp/noise-c.git [submodule "enclave/SipHash"] path = enclave/SipHash url = https://github.com/veorq/SipHash diff --git a/Makefile b/Makefile index 23308b1..dc0b693 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,8 @@ validate: git: git submodule init || true - git submodule update || true + git submodule update --recursive --init || true + git submodule update --recursive || true ETARGET ?= all diff --git a/enclave/Makefile b/enclave/Makefile index 5a866a3..3021e1a 100644 --- a/enclave/Makefile +++ b/enclave/Makefile @@ -43,33 +43,36 @@ build/noise-c/TEST.a: build/libsodium/TEST.a $(QUIET) echo -e "BUILD\t$@" $(QUIET) mkdir -p $(@D) $(QUIET) (cd noise-c && \ + (git clean -fx ; git submodule foreach --recursive git clean -xf ; true) && \ ./autogen.sh && \ libsodium_CFLAGS=-I$$PWD/../build/libsodium/TEST.a.dir/include/ libsodium_LIBS=$$PWD/../build/libsodium/TEST.a \ CC=$(CC) CFLAGS="$(TEST_CFLAGS) -I$(shell ./find_header.sh $(CC) immintrin.h)" ./configure --with-libsodium && \ $(MAKE) clean && \ - $(MAKE)) $(QUIET_OUT) + $(MAKE) -C src/protocol) $(QUIET_OUT) $(QUIET) cp noise-c/src/protocol/libnoiseprotocol.a $@ $(QUIET) echo -e "BUILT\t$@" build/noise-c/SGX.a: build/libsodium/SGX.a | build/noise-c/TEST.a $(QUIET) echo -e "BUILD\t$@" $(QUIET) mkdir -p $(@D) $(QUIET) (cd noise-c && \ + (git clean -fx ; git submodule foreach --recursive git clean -xf ; true) && \ ./autogen.sh && \ libsodium_CFLAGS=-I$$PWD/../build/libsodium/SGX.a.dir/include/ libsodium_LIBS=$$PWD/../build/libsodium/SGX.a \ CC=$(CC) CFLAGS="$(SGX_CFLAGS) -I$(shell ./find_header.sh $(CC) immintrin.h)" ./configure --with-libsodium && \ $(MAKE) clean && \ - $(MAKE)) $(QUIET_OUT) + $(MAKE) -C src/protocol) $(QUIET_OUT) $(QUIET) cp noise-c/src/protocol/libnoiseprotocol.a $@ $(QUIET) echo -e "BUILT\t$@" build/noise-c/X86.a: build/libsodium/X86.a | build/noise-c/SGX.a $(QUIET) echo -e "BUILD\t$@" $(QUIET) mkdir -p $(@D) $(QUIET) (cd noise-c && \ + (git clean -fx ; git submodule foreach --recursive git clean -xf ; true) && \ ./autogen.sh && \ libsodium_CFLAGS=-I$$PWD/../build/libsodium/X86.a.dir/include/ libsodium_LIBS=$$PWD/../build/libsodium/X86.a \ CC=$(CC) CFLAGS="$(X86_CFLAGS) -I$(shell ./find_header.sh $(CC) immintrin.h)" ./configure --with-libsodium && \ $(MAKE) clean && \ - $(MAKE)) $(QUIET_OUT) + $(MAKE) -C src/protocol) $(QUIET_OUT) $(QUIET) cp noise-c/src/protocol/libnoiseprotocol.a $@ $(QUIET) echo -e "BUILT\t$@" @@ -271,10 +274,11 @@ build/attest.azuresnp: build/initmain/X86.a $(patsubst %,build/%/X86.a,$(AZURESN clean: $(QUIET) echo CLEAN - $(QUIET) (cd protobuf ; make clean ; git clean -fx ; true) $(QUIET_OUT) - $(QUIET) (cd noise-c ; make clean ; git clean -fx ; true) $(QUIET_OUT) - $(QUIET) (cd SipHash ; make clean ; git clean -fx ; true) $(QUIET_OUT) - $(QUIET) (cd boringssl ; make clean ; git clean -fx ; true) $(QUIET_OUT) + $(QUIET) (cd protobuf ; make clean ; true) $(QUIET_OUT) + $(QUIET) (cd noise-c ; make clean ; true) $(QUIET_OUT) + $(QUIET) (cd SipHash ; make clean ; true) $(QUIET_OUT) + $(QUIET) (cd boringssl ; make clean ; true) $(QUIET_OUT) + $(QUIET) (git submodule foreach --recursive git clean -xf ; true) $(QUIET_OUT) $(QUIET) rm -vfr build $(QUIET_OUT) $(QUIET) rm -vf .testdepends $(QUIET_OUT) diff --git a/enclave/client/client.cc b/enclave/client/client.cc index 4322f15..1b04464 100644 --- a/enclave/client/client.cc +++ b/enclave/client/client.cc @@ -23,13 +23,23 @@ const NoiseProtocolId client_protocol = { .hybrid_id = 0, }; +const NoiseProtocolId client_protocol_pq = { + .prefix_id = NOISE_PREFIX_STANDARD, + .pattern_id = NOISE_PATTERN_NK_HFS, + .dh_id = NOISE_DH_CURVE25519, + .cipher_id = NOISE_CIPHER_CHACHAPOLY, + .hash_id = NOISE_HASH_SHA256, + .hybrid_id = NOISE_DH_KYBER1024, +}; + Client::Client( - std::unique_ptr cs) + std::unique_ptr cs, bool pq) : hs_(noise::WrapHandshakeState(nullptr)), tx_(noise::WrapCipherState(nullptr)), rx_(noise::WrapCipherState(nullptr)), id_(id_gen.fetch_add(1)), - cs_(std::move(cs)) { + cs_(std::move(cs)), + pq_(pq) { } Client::~Client() { @@ -38,7 +48,8 @@ Client::~Client() { error::Error Client::Init(const noise::DHState& dhstate, const e2e::Attestation& attestation) { util::unique_lock lock(mu_); NoiseHandshakeState* hs; - if (NOISE_ERROR_NONE != noise_handshakestate_new_by_id(&hs, &client_protocol, NOISE_ROLE_RESPONDER)) { + const NoiseProtocolId* protocol = pq_ ? &client_protocol_pq : &client_protocol; + if (NOISE_ERROR_NONE != noise_handshakestate_new_by_id(&hs, protocol, NOISE_ROLE_RESPONDER)) { return COUNTED_ERROR(Client_HandshakeState); } auto hs_wrap = noise::WrapHandshakeState(hs); @@ -131,7 +142,7 @@ std::pair ClientManager::NewClient( context::Context* ctx, std::unique_ptr cs) { MEASURE_CPU(ctx, cpu_client_hs_start); - std::unique_ptr c(new Client(std::move(cs))); + std::unique_ptr c(new Client(std::move(cs), pq_)); auto [dhstate, attestation] = ClientArgs(ctx); error::Error err = c->Init(dhstate, attestation); if (err != error::OK) { diff --git a/enclave/client/client.h b/enclave/client/client.h index 2ba3908..163ebf2 100644 --- a/enclave/client/client.h +++ b/enclave/client/client.h @@ -44,7 +44,7 @@ class Client { private: ~Client(); - explicit Client(std::unique_ptr cs); + Client(std::unique_ptr cs, bool pq); error::Error Init(const noise::DHState& dhstate, const e2e::Attestation& attestation) EXCLUDES(mu_); friend class ClientManager; friend std::unique_ptr::deleter_type; @@ -56,11 +56,12 @@ class Client { noise::CipherState rx_ GUARDED_BY(mu_); const size_t id_; std::unique_ptr cs_; + const bool pq_; }; class ClientManager { public: - ClientManager(noise::DHState dhstate) : dhstate_(std::move(dhstate)) {} + ClientManager(noise::DHState dhstate, bool pq) : dhstate_(std::move(dhstate)), pq_(pq) {} error::Error RefreshAttestation(context::Context* ctx, const enclaveconfig::RaftGroupConfig& config) EXCLUDES(mu_); error::Error RotateKeyAndRefreshAttestation(context::Context* ctx, const enclaveconfig::RaftGroupConfig& config) EXCLUDES(mu_); static noise::DHState NewDHState(); @@ -82,6 +83,7 @@ class ClientManager { noise::DHState dhstate_ GUARDED_BY(mu_); e2e::Attestation attestation_ GUARDED_BY(mu_); std::unordered_map> clients_ GUARDED_BY(mu_); + const bool pq_; }; } // namespace svr2::client diff --git a/enclave/core/core.cc b/enclave/core/core.cc index 5d52e53..c742dd1 100644 --- a/enclave/core/core.cc +++ b/enclave/core/core.cc @@ -149,7 +149,7 @@ error::Error Core::Init(context::Context* ctx, const enclaveconfig::EnclaveConfi enclave_config_ = config; } peer_manager_ = std::move(peer_manager); - client_manager_ = std::make_unique(std::move(client_dh)); + client_manager_ = std::make_unique(std::move(client_dh), config.client_pq()); clock_.SetLocalTime(initial_timestamp_unix_secs); peer_manager_->SetPeerAttestationTimestamp(ctx, initial_timestamp_unix_secs, raft_config_template_.attestation_timeout()); diff --git a/enclave/metrics/metrics.cc b/enclave/metrics/metrics.cc index bdbf37e..eb4d641 100644 --- a/enclave/metrics/metrics.cc +++ b/enclave/metrics/metrics.cc @@ -81,7 +81,8 @@ void Gauge::Clear() { } namespace internal { -error::Error RecordError(error::Error e) { +error::Error RecordError(error::Error e, const char* file, int line) { + LOG(VERBOSE) << e << " @ " << file << ":" << line; recorded_errors[e].fetch_add(1); return e; } diff --git a/enclave/metrics/metrics.h b/enclave/metrics/metrics.h index 585f92d..baffc06 100644 --- a/enclave/metrics/metrics.h +++ b/enclave/metrics/metrics.h @@ -75,7 +75,7 @@ enum Gauges { }; namespace internal { -error::Error RecordError(error::Error); +error::Error RecordError(error::Error, const char* file, int line); extern Counter counters[COUNTERS_ARRAY_SIZE]; extern Gauge gauges[GAUGES_ARRAY_SIZE]; } // namespace internal @@ -93,6 +93,6 @@ extern Gauge gauges[GAUGES_ARRAY_SIZE]; // COUNTED_ERROR counts an error within metrics, returning that same error. // It's generally used like: // return COUNTED_ERROR(Foo_Bar); -#define COUNTED_ERROR(x) ::svr2::metrics::internal::RecordError(error::x) +#define COUNTED_ERROR(x) ::svr2::metrics::internal::RecordError(error::x, __FILE__, __LINE__) #endif // __SVR2_METRICS_METRICS_H__ diff --git a/enclave/noise-c b/enclave/noise-c index 9ab8de7..68814be 160000 --- a/enclave/noise-c +++ b/enclave/noise-c @@ -1 +1 @@ -Subproject commit 9ab8de7db4082d08aea7d5f7bb614ea3e777b09d +Subproject commit 68814bedb0dbe893796a585ba9aed7c78360f2da diff --git a/enclave/noise/noise.h b/enclave/noise/noise.h index 5689860..452ffbb 100644 --- a/enclave/noise/noise.h +++ b/enclave/noise/noise.h @@ -21,7 +21,7 @@ namespace svr2::noise { -const size_t HANDSHAKE_INIT_SIZE = 64; +const size_t HANDSHAKE_INIT_SIZE = 4096; const size_t HANDSHAKE_HFS_INIT_SIZE = 4096; inline uint8_t* StrU8Ptr(std::string* s) { diff --git a/enclave/peers/peers.cc b/enclave/peers/peers.cc index 30ef751..ae595e5 100644 --- a/enclave/peers/peers.cc +++ b/enclave/peers/peers.cc @@ -55,7 +55,7 @@ static NoiseProtocolId peer_to_peer_protocol = { // have access to hardware-accelerated AES, so we use that. .cipher_id = NOISE_CIPHER_AESGCM, .hash_id = NOISE_HASH_SHA256, - .hybrid_id = NOISE_DH_NEWHOPE, + .hybrid_id = NOISE_DH_KYBER1024, }; Peer::Peer(const peerid::PeerID& id, PeerManager* parent) diff --git a/enclave/util/tests/hex.cc b/enclave/util/tests/hex.cc index 401f50f..5fcc557 100644 --- a/enclave/util/tests/hex.cc +++ b/enclave/util/tests/hex.cc @@ -6,6 +6,8 @@ //TESTDEP metrics //TESTDEP proto //TESTDEP protobuf-lite +//TESTDEP env +//TESTDEP libsodium #include #include "util/hex.h" #include diff --git a/host/Makefile b/host/Makefile index 9eec44d..aab89ca 100644 --- a/host/Makefile +++ b/host/Makefile @@ -15,11 +15,11 @@ enclave_binaries: # -count=1 forces this test to run un-cached, since enclave.test may have changed # even though Go code has not, and tests may depend on it. -test: build enclave_binaries rustclient +test: build enclave_binaries rustclient/target/debug/rustclient go test $(GO_TEST_FLAGS) -count=1 ./... -rustclient: - cd integration/rustclient && cargo build +rustclient/target/debug/rustclient: + cd rustclient && cargo build EDGER8R_FILES=enclave/c/svr2_u.c enclave/c/svr2_u.h enclave/c/svr2_args.h # This $(firstword) trick allows for grouped targets. diff --git a/host/integration/integration_test.go b/host/integration/integration_test.go index 988dc03..723371c 100644 --- a/host/integration/integration_test.go +++ b/host/integration/integration_test.go @@ -49,37 +49,6 @@ const ( enclaveNitro = "nitro" ) -type prefixWriter struct { - written bool - prefix []byte - to io.Writer -} - -func (p *prefixWriter) Write(bs []byte) (int, error) { - lastStart := 0 - for i, b := range bs { - if !p.written { - if n, err := p.to.Write(bs[lastStart:i]); err != nil { - return lastStart + n, err - } - lastStart = i - if _, err := p.to.Write(p.prefix); err != nil { - return i, err - } - p.written = true - } else if b == '\n' { - p.written = false - } - } - if n, err := p.to.Write(bs[lastStart:]); err != nil { - return lastStart + n, err - } - return len(bs), nil -} -func newPrefixWriter(s string, to io.Writer) io.Writer { - return &prefixWriter{to: to, prefix: []byte(s)} -} - func userName(i int) string { return fmt.Sprintf("%032x", i) } @@ -92,18 +61,6 @@ func TestIntegration(t *testing.T) { restore(t, testClient(t, u, userName(9999)), pin) } -func TestRustClient(t *testing.T) { - host := fmt.Sprintf("localhost:%v", port(clientType, 1)) - u := url.URL{Scheme: "ws", Host: host, Path: "v1/enclave"} - cmd := exec.Command("./rustclient/target/debug/rustclient", u.String()) - w := newPrefixWriter("RUSTCLIENT ", os.Stderr) - cmd.Stdout = w - cmd.Stderr = w - if err := cmd.Run(); err != nil { - t.Errorf("rustclient: %v", err) - } -} - func TestConcurrentClients(t *testing.T) { host := fmt.Sprintf("localhost:%v", port(clientType, 1)) u := url.URL{Scheme: "ws", Host: host, Path: "v1/enclave"} @@ -348,7 +305,7 @@ func start(enclaveType string) group { args...) cmd.Env = append(cmd.Env, "AUTH_SECRET="+base64.StdEncoding.EncodeToString([]byte(authSecret))) - w := newPrefixWriter(fmt.Sprintf("[%d] ", i), os.Stderr) + w := servicetest.NewPrefixWriter(fmt.Sprintf("[%d] ", i), os.Stderr) cmd.Stdout = w cmd.Stderr = w diff --git a/host/integration/rustclient/proto b/host/integration/rustclient/proto deleted file mode 120000 index 0b826e6..0000000 --- a/host/integration/rustclient/proto +++ /dev/null @@ -1 +0,0 @@ -../../../shared/proto/ \ No newline at end of file diff --git a/host/integration/rustclient/target b/host/integration/rustclient/target deleted file mode 120000 index e4a4043..0000000 --- a/host/integration/rustclient/target +++ /dev/null @@ -1 +0,0 @@ -../../../.cargotarget/ \ No newline at end of file diff --git a/host/integration/rustclient/Cargo.lock b/host/rustclient/Cargo.lock similarity index 92% rename from host/integration/rustclient/Cargo.lock rename to host/rustclient/Cargo.lock index 9a375d1..0077f94 100644 --- a/host/integration/rustclient/Cargo.lock +++ b/host/rustclient/Cargo.lock @@ -161,15 +161,19 @@ dependencies = [ [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cc" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" +checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" +dependencies = [ + "jobserver", + "libc", +] [[package]] name = "cfg-if" @@ -329,6 +333,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "either" version = "1.13.0" @@ -348,7 +358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -440,7 +450,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check 0.9.5", ] [[package]] @@ -464,6 +474,12 @@ dependencies = [ "polyval", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.14.5" @@ -535,9 +551,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", @@ -570,6 +586,15 @@ dependencies = [ "either", ] +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -750,9 +775,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.6.0", "cfg-if 1.0.0", @@ -782,9 +807,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -865,9 +890,43 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pqcrypto-internals" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d34bec6abe2283e6de7748b68b292d1ffa2203397e3e71380ff8418a49fb46" +dependencies = [ + "cc", + "dunce", + "getrandom", + "libc", +] + +[[package]] +name = "pqcrypto-kyber" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c00293cf898859d0c771455388054fd69ab712263c73fdc7f287a39b1ba000" +dependencies = [ + "cc", + "glob", + "libc", + "pqcrypto-internals", + "pqcrypto-traits", +] + +[[package]] +name = "pqcrypto-traits" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e851c7654eed9e68d7d27164c454961a616cf8c203d500607ef22c737b51bb" [[package]] name = "prettyplease" @@ -894,7 +953,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc" dependencies = [ - "bytes 1.6.1", + "bytes 1.7.1", "prost-derive", ] @@ -904,7 +963,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ - "bytes 1.6.1", + "bytes 1.7.1", "heck", "itertools", "log 0.4.22", @@ -1103,9 +1162,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -1173,7 +1232,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1188,7 +1247,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1298,6 +1357,8 @@ dependencies = [ "blake2", "chacha20poly1305", "curve25519-dalek", + "pqcrypto-kyber", + "pqcrypto-traits", "rand_core 0.6.4", "rustc_version 0.4.0", "sha2", @@ -1312,9 +1373,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -1323,14 +1384,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if 1.0.0", "fastrand", + "once_cell", "rustix", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -1528,9 +1590,9 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1628,6 +1690,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1702,6 +1773,27 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/host/integration/rustclient/Cargo.toml b/host/rustclient/Cargo.toml similarity index 83% rename from host/integration/rustclient/Cargo.toml rename to host/rustclient/Cargo.toml index 2e05f80..72e1a2f 100644 --- a/host/integration/rustclient/Cargo.toml +++ b/host/rustclient/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -snow = "0.9.6" +snow = { version = "0.9.6", features = ["hfs", "pqclean_kyber1024"] } websocket = "0.27.1" prost = "0.13.1" simple-error = "0.3.1" diff --git a/host/integration/rustclient/build.rs b/host/rustclient/build.rs similarity index 74% rename from host/integration/rustclient/build.rs rename to host/rustclient/build.rs index 5566e81..fbaca06 100644 --- a/host/integration/rustclient/build.rs +++ b/host/rustclient/build.rs @@ -1,3 +1,6 @@ +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + fn main() { let protos = [ "proto/msgs.proto", diff --git a/host/rustclient/proto b/host/rustclient/proto new file mode 120000 index 0000000..632b565 --- /dev/null +++ b/host/rustclient/proto @@ -0,0 +1 @@ +../../shared/proto/ \ No newline at end of file diff --git a/host/rustclient/rustclient_test.go b/host/rustclient/rustclient_test.go new file mode 100644 index 0000000..88ed5f3 --- /dev/null +++ b/host/rustclient/rustclient_test.go @@ -0,0 +1,124 @@ +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +package rustclient + +import ( + "context" + "encoding/base64" + "fmt" + "net/url" + "os" + "os/exec" + "path/filepath" + "testing" + "time" + + "github.com/alicebob/miniredis/v2" + "github.com/signalapp/svr2/servicetest" + "google.golang.org/protobuf/encoding/prototext" + + pb "github.com/signalapp/svr2/proto" +) + +var ( + validConfig = pb.InitConfig{ + EnclaveConfig: &pb.EnclaveConfig{ + Raft: &pb.RaftConfig{ + ElectionTicks: 30, + HeartbeatTicks: 15, + ReplicationChunkBytes: 1 << 20, + ReplicaVotingTimeoutTicks: 120, + ReplicaMembershipTimeoutTicks: 240, + LogMaxBytes: 10 << 20, + }, + E2ETxnTimeoutTicks: 30, + ClientPq: true, + }, + GroupConfig: &pb.RaftGroupConfig{ + DbVersion: pb.DatabaseVersion_DATABASE_VERSION_SVR2, + MinVotingReplicas: 1, + MaxVotingReplicas: 1, + AttestationTimeout: 3600, + Simulated: true, + }, + } + hostPath = "../main" + sgxPath = "../../enclave/build/enclave.test" + clientPath = "target/debug/rustclient" + authSecret = "123456" +) + +func hostConfig(redisAddr string) string { + return fmt.Sprintf(` +peerAddr: localhost:9990 +clientListenAddr: localhost:9991 +controlListenAddr: localhost:9992 +raft: + tickDuration: 250ms +redis: + addrs: [%s]`, redisAddr) +} + +func TestRustClient(t *testing.T) { + dir, err := os.MkdirTemp("", "rustclient") + if err != nil { + t.Fatalf("mkdir: %v", err) + } + t.Logf("using dir %v", dir) + defer os.RemoveAll(dir) + + redis, err := miniredis.Run() + if err != nil { + t.Fatalf("miniredis: %v", err) + } + hConfig := hostConfig(redis.Addr()) + + eConfig, err := prototext.Marshal(&validConfig) + if err != nil { + t.Fatalf("proto marshal: %v", err) + } + + if err := os.WriteFile(filepath.Join(dir, "hconfig"), []byte(hConfig), 0600); err != nil { + t.Fatalf("write hconfig: %v", err) + } + if err := os.WriteFile(filepath.Join(dir, "econfig"), eConfig, 0600); err != nil { + t.Fatalf("write econfig: %v", err) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + hostCmd := exec.CommandContext( + ctx, + hostPath, + "-hconfig_path", filepath.Join(dir, "hconfig"), + "-econfig_path", filepath.Join(dir, "econfig"), + "-enclave_type", "sgx", + "-sgx_path", sgxPath) + hostCmd.Env = append(hostCmd.Env, "AUTH_SECRET="+base64.StdEncoding.EncodeToString([]byte(authSecret))) + hostCmd.Stdout = servicetest.NewPrefixWriter("HOST_OUT: ", os.Stderr) + hostCmd.Stderr = servicetest.NewPrefixWriter("HOST_ERR: ", os.Stderr) + + defer time.Sleep(time.Second) // allow logs to make it out. + t.Logf("Starting...") + if err := hostCmd.Start(); err != nil { + t.Fatalf("starting: %v", err) + } + + t.Logf("Waiting for healthy") + if err := servicetest.WaitFor200(time.Minute, "http://localhost:9992/health/ready"); err != nil { + t.Fatalf("wait for 200: %v", err) + } + + t.Logf("Running test") + u := url.URL{Scheme: "ws", Host: "localhost:9991", Path: "v1/enclave"} + clientCmd := exec.CommandContext( + ctx, + clientPath, + u.String()) + clientCmd.Stdout = servicetest.NewPrefixWriter("CLIENT_OUT: ", os.Stderr) + clientCmd.Stderr = servicetest.NewPrefixWriter("CLIENT_ERR: ", os.Stderr) + if err := clientCmd.Run(); err != nil { + t.Fatalf("client error: %v", err) + } +} diff --git a/host/integration/rustclient/src/main.rs b/host/rustclient/src/main.rs similarity index 92% rename from host/integration/rustclient/src/main.rs rename to host/rustclient/src/main.rs index 31270d7..40fde88 100644 --- a/host/integration/rustclient/src/main.rs +++ b/host/rustclient/src/main.rs @@ -1,3 +1,6 @@ +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + use hmac::Mac; use websocket::header::{Authorization, Basic, Headers}; use prost::Message; @@ -25,7 +28,7 @@ pub mod svr2 { type HmacSha256 = hmac::Hmac; -static PATTERN: &'static str = "Noise_NK_25519_ChaChaPoly_SHA256"; +static PATTERN: &'static str = "Noise_NKhfs_25519+Kyber1024_ChaChaPoly_SHA256"; fn main() -> Result<(), Box> { let args: Vec = env::args().collect(); @@ -77,9 +80,10 @@ fn main() -> Result<(), Box> { println!("Send handshake start"); client.send_message(&websocket::Message::binary(&buf[..len]))?; - println!("Recv handshake finish"); + println!("Recv handshake start"); let msg2 = client.recv_message()?; let bin2 = if let websocket::OwnedMessage::Binary(b) = msg2 { + println!("Received!"); b } else { bail!("received message not binary"); diff --git a/host/rustclient/target b/host/rustclient/target new file mode 120000 index 0000000..7d0d746 --- /dev/null +++ b/host/rustclient/target @@ -0,0 +1 @@ +../../.cargotarget/ \ No newline at end of file diff --git a/host/servicetest/servicetest.go b/host/servicetest/servicetest.go index 83eeee1..cb4867b 100644 --- a/host/servicetest/servicetest.go +++ b/host/servicetest/servicetest.go @@ -79,3 +79,34 @@ func WaitFor200(timeout time.Duration, url string) error { }) return err } + +type PrefixWriter struct { + written bool + prefix []byte + to io.Writer +} + +func (p *PrefixWriter) Write(bs []byte) (int, error) { + lastStart := 0 + for i, b := range bs { + if !p.written { + if n, err := p.to.Write(bs[lastStart:i]); err != nil { + return lastStart + n, err + } + lastStart = i + if _, err := p.to.Write(p.prefix); err != nil { + return i, err + } + p.written = true + } else if b == '\n' { + p.written = false + } + } + if n, err := p.to.Write(bs[lastStart:]); err != nil { + return lastStart + n, err + } + return len(bs), nil +} +func NewPrefixWriter(s string, to io.Writer) io.Writer { + return &PrefixWriter{to: to, prefix: []byte(s)} +} diff --git a/shared/proto/enclaveconfig.proto b/shared/proto/enclaveconfig.proto index 4f6e7b2..2a55675 100644 --- a/shared/proto/enclaveconfig.proto +++ b/shared/proto/enclaveconfig.proto @@ -48,6 +48,8 @@ message EnclaveConfig { uint32 e2e_txn_timeout_ticks = 2; // Every N ticks, send our local timestamp to our peers. uint32 send_timestamp_ticks = 5; + // If true, use HFS for client/server PQ protection. + bool client_pq = 6; } // RaftGroupConfig is a configuration shared by members of a Raft group.