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

Use "RandomShake" as CSPRNG #3

Merged
merged 11 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Execute Tests on ${{matrix.os}}, compiled with ${{matrix.compiler}}
if: ${{matrix.test_type == 'standard'}}
run: |
CXX=${{matrix.compiler}} make -j
CXX=${{matrix.compiler}} make test -j
make clean

- name: Execute Tests with ${{matrix.test_type}}, in ${{matrix.build_type}} mode, on ${{matrix.os}}, compiled with ${{matrix.compiler}}
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "gtest-parallel"]
path = gtest-parallel
url = https://github.com/google/gtest-parallel.git
[submodule "RandomShake"]
path = RandomShake
url = https://github.com/itzmeanjan/RandomShake
26 changes: 20 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
.DEFAULT_GOAL := help

# Collects inspiration from https://github.com/itzmeanjan/sha3/blob/b6ce906994961b711b6f2864fa8ee393c84d23ef/Makefile
.PHONY: help
help:
@for file in $(MAKEFILE_LIST); do \
grep -E '^[a-zA-Z_-]+:.*?## .*$$' $${file} | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}';\
done

CXX ?= clang++
CXX_DEFS +=
CXX_FLAGS := -std=c++20
Expand All @@ -8,7 +17,8 @@ LINK_OPT_FLAGS := -flto

I_FLAGS := -I ./include
SHA3_INC_DIR := ./sha3/include
DEP_IFLAGS := -I $(SHA3_INC_DIR)
RANDOMSHAKE_INC_DIR := ./RandomShake/include
DEP_IFLAGS := -I $(SHA3_INC_DIR) -I $(RANDOMSHAKE_INC_DIR)

SRC_DIR := include
FrodoPIR_SOURCES := $(shell find $(SRC_DIR) -name '*.hpp')
Expand All @@ -18,17 +28,21 @@ all: test

include tests/test.mk
include benches/bench.mk
include examples/example.mk

$(GTEST_PARALLEL):
git submodule update --init gtest-parallel

$(SHA3_INC_DIR): $(GTEST_PARALLEL)
git submodule update --init sha3
$(RANDOMSHAKE_INC_DIR): $(GTEST_PARALLEL)
git submodule update --init --recursive RandomShake

.PHONY: format clean
$(SHA3_INC_DIR): $(RANDOMSHAKE_INC_DIR)
git submodule update --init sha3

clean:
.PHONY: clean
clean: ## Remove build directory
rm -rf $(BUILD_DIR)

format: $(FrodoPIR_SOURCES) $(TEST_SOURCES) $(TEST_HEADERS) $(BENCHMARK_SOURCES) $(BENCHMARK_HEADERS)
.PHONY: format
format: $(FrodoPIR_SOURCES) $(TEST_SOURCES) $(TEST_HEADERS) $(BENCHMARK_SOURCES) $(BENCHMARK_HEADERS) ## Format source code
clang-format -i $^
33 changes: 10 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ g++ (Ubuntu 14-20240412-0ubuntu1) 14.0.1 20240412 (experimental) [master r14-993
For ensuring functional correctness of this implementation of FrodoPIR scheme, issue

```bash
make -j # Run tests without any sort of sanitizers, with default C++ compiler.
CXX=clang++ make -j # Switch to non-default compiler, by setting variable `CXX`.
make test -j # Run tests without any sort of sanitizers, with default C++ compiler.
CXX=clang++ make test -j # Switch to non-default compiler, by setting variable `CXX`.

make debug_asan_test -j # Run tests with AddressSanitizer enabled, with `-O1`.
make release_asan_test -j # Run tests with AddressSanitizer enabled, with `-O3 -march=native`.
Expand All @@ -80,6 +80,9 @@ PASSED TESTS (4/4):
66962 ms: build/test/test.out FrodoPIR.PrivateInformationRetrieval
```

> [!NOTE]
> There is a help menu, which introduces you to all available commands; just run `make` from the root directory of this project.

## Benchmarking

Benchmarking of all 6 algorithms of FrodoPIR scheme can be done, by issuing
Expand All @@ -96,23 +99,13 @@ make perf -j # If you have built google-benchmark library with libPFM supp

### On 12th Gen Intel(R) Core(TM) i7-1260P

Compiled with **gcc version 14.0.1 20240412** on

```bash
$ uname -srm
Linux 6.8.0-45-generic x86_64
```
Compiled with **gcc version 14.0.1 20240412** on `Linux 6.8.0-45-generic x86_64`.

Benchmark result in JSON format @ [bench_result_on_Linux_6.8.0-45-generic_x86_64_with_g++_14.json](./bench_result_on_Linux_6.8.0-45-generic_x86_64_with_g++_14.json).

### On ARM Neoverse-V2 (AWS EC2 Instance `c8g.2xlarge`)

Compiled with **gcc version 13.2.0** on

```bash
$ uname -srm
Linux 6.8.0-1016-aws aarch64
```
Compiled with **gcc version 13.2.0** on `Linux 6.8.0-1016-aws aarch64`.

Benchmark result in JSON format @ [bench_result_on_Linux_6.8.0-1016-aws_aarch64_with_g++_13.json](./bench_result_on_Linux_6.8.0-1016-aws_aarch64_with_g++_13.json).

Expand All @@ -121,7 +114,7 @@ Benchmark result in JSON format @ [bench_result_on_Linux_6.8.0-1016-aws_aarch64_

## Usage

FrodoPIR is a header-only C++20 library implementing all recommended variants (see table 5) in https://ia.cr/2022/98. FrodoPIR header files live `./include` directory, while only additional dependency `sha3` header files live under `sha3/include`.
FrodoPIR is a header-only C++20 library implementing all recommended variants (see table 5) in https://ia.cr/2022/98. FrodoPIR header files live `./include` directory, while additional dependency `sha3` and `RandomShake` header files live under `sha3/include` and `RandomShake/include`, respectively.

- Let's begin by cloning the repository.

Expand All @@ -136,19 +129,13 @@ git clone https://github.com/itzmeanjan/frodoPIR.git --recurse-submodules

```bash
pushd frodoPIR
make -j # Also runs tests
make test -j # Also runs tests
popd
```

- Now that we've all the dependencies to use FrodoPIR header-only library, let's run our [example program](./examples/frodoPIR.cpp).
- Now that we've all the dependencies to use FrodoPIR header-only library, let's run our [example](./examples/frodoPIR.cpp) program, by issuing `make example`.

```bash
FrodoPIR_HEADERS=include
SHA3_HEADERS=sha3/include

g++ -std=c++20 -Wall -Wextra -pedantic -O3 -march=native -I $FrodoPIR_HEADERS -I $SHA3_HEADERS examples/frodoPIR.cpp
./a.out && echo $? # = 0 means success!

Original database row bytes : bf71e04189fff486c062cf1e814bedfc2205422807da319d4ac6f5a956d63e48
PIR decoded database row bytes : bf71e04189fff486c062cf1e814bedfc2205422807da319d4ac6f5a956d63e48
```
1 change: 1 addition & 0 deletions RandomShake
Submodule RandomShake added at a9cd40
4 changes: 2 additions & 2 deletions benches/bench.mk
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ $(BENCHMARK_BUILD_DIR)/%.o: $(BENCHMARK_DIR)/%.cpp $(BENCHMARK_BUILD_DIR) $(SHA3
$(BENCHMARK_BINARY): $(BENCHMARK_OBJECTS)
$(CXX) $(RELEASE_FLAGS) $(LINK_OPT_FLAGS) $^ $(BENCHMARK_LINK_FLAGS) -o $@

benchmark: $(BENCHMARK_BINARY)
benchmark: $(BENCHMARK_BINARY) ## Build and run all benchmarks, without libPFM -based CPU CYCLE counter statistics
# Must *not* build google-benchmark with libPFM
./$< --benchmark_min_warmup_time=.5 --benchmark_enable_random_interleaving=false --benchmark_repetitions=10 --benchmark_min_time=0.1s --benchmark_display_aggregates_only=true --benchmark_report_aggregates_only=true --benchmark_counters_tabular=true --benchmark_out_format=json --benchmark_out=$(BENCHMARK_OUT_FILE)

$(PERF_BINARY): $(BENCHMARK_OBJECTS)
$(CXX) $(RELEASE_FLAGS) $(LINK_OPT_FLAGS) $^ $(PERF_LINK_FLAGS) -o $@

perf: $(PERF_BINARY)
perf: $(PERF_BINARY) ## Build and run all benchmarks, while also collecting libPFM -based CPU CYCLE counter statistics
# Must build google-benchmark with libPFM, follow https://gist.github.com/itzmeanjan/05dc3e946f635d00c5e0b21aae6203a7
./$< --benchmark_min_warmup_time=.5 --benchmark_enable_random_interleaving=false --benchmark_repetitions=10 --benchmark_min_time=0.1s --benchmark_display_aggregates_only=true --benchmark_report_aggregates_only=true --benchmark_counters_tabular=true --benchmark_perf_counters=CYCLES --benchmark_out_format=json --benchmark_out=$(BENCHMARK_OUT_FILE)
12 changes: 6 additions & 6 deletions benches/bench_client_prepare_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ bench_client_prepare_query(benchmark::State& state)
auto response_bytes_span = std::span<uint8_t, response_byte_len>(response_bytes);
auto db_row_bytes_span = std::span<uint8_t, db_entry_byte_len>(db_row_bytes);

prng::prng_t prng{};
csprng::csprng_t csprng{};

prng.read(seed_μ_span);
prng.read(db_bytes_span);
csprng.generate(seed_μ_span);
csprng.generate(db_bytes_span);

auto [server, M] = server_t::setup(seed_μ_span, db_bytes_span);

Expand All @@ -45,7 +45,7 @@ bench_client_prepare_query(benchmark::State& state)
size_t buffer = 0;
auto buffer_span = std::span<uint8_t, sizeof(buffer)>(reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer));

prng.read(buffer_span);
csprng.generate(buffer_span);

return buffer % db_entry_count;
}();
Expand All @@ -55,12 +55,12 @@ bench_client_prepare_query(benchmark::State& state)
bool is_response_decoded = true;

for (auto _ : state) {
is_query_preprocessed &= client.prepare_query(rand_db_row_index, prng);
is_query_preprocessed &= client.prepare_query(rand_db_row_index, csprng);

benchmark::DoNotOptimize(is_query_preprocessed);
benchmark::DoNotOptimize(client);
benchmark::DoNotOptimize(rand_db_row_index);
benchmark::DoNotOptimize(prng);
benchmark::DoNotOptimize(&csprng);
benchmark::ClobberMemory();

state.PauseTiming();
Expand Down
12 changes: 6 additions & 6 deletions benches/bench_client_process_response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ bench_client_process_response(benchmark::State& state)
auto response_bytes_span = std::span<uint8_t, response_byte_len>(response_bytes);
auto db_row_bytes_span = std::span<uint8_t, db_entry_byte_len>(db_row_bytes);

prng::prng_t prng{};
csprng::csprng_t csprng{};

prng.read(seed_μ_span);
prng.read(db_bytes_span);
csprng.generate(seed_μ_span);
csprng.generate(db_bytes_span);

auto [server, M] = server_t::setup(seed_μ_span, db_bytes_span);

Expand All @@ -45,12 +45,12 @@ bench_client_process_response(benchmark::State& state)
size_t buffer = 0;
auto buffer_span = std::span<uint8_t, sizeof(buffer)>(reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer));

prng.read(buffer_span);
csprng.generate(buffer_span);

return buffer % db_entry_count;
}();

auto is_query_preprocessed = client.prepare_query(rand_db_row_index, prng);
auto is_query_preprocessed = client.prepare_query(rand_db_row_index, csprng);
auto is_query_ready = client.query(rand_db_row_index, query_bytes_span);
server.respond(query_bytes_span, response_bytes_span);

Expand All @@ -69,7 +69,7 @@ bench_client_process_response(benchmark::State& state)
rand_db_row_index ^= (rand_db_row_index << 1) ^ 1ul;
rand_db_row_index %= db_entry_count;

is_query_preprocessed &= client.prepare_query(rand_db_row_index, prng);
is_query_preprocessed &= client.prepare_query(rand_db_row_index, csprng);
is_query_ready &= client.query(rand_db_row_index, query_bytes_span);
server.respond(query_bytes_span, response_bytes_span);
state.ResumeTiming();
Expand Down
12 changes: 6 additions & 6 deletions benches/bench_client_query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ bench_client_query(benchmark::State& state)
auto response_bytes_span = std::span<uint8_t, response_byte_len>(response_bytes);
auto db_row_bytes_span = std::span<uint8_t, db_entry_byte_len>(db_row_bytes);

prng::prng_t prng{};
csprng::csprng_t csprng{};

prng.read(seed_μ_span);
prng.read(db_bytes_span);
csprng.generate(seed_μ_span);
csprng.generate(db_bytes_span);

auto [server, M] = server_t::setup(seed_μ_span, db_bytes_span);

Expand All @@ -45,12 +45,12 @@ bench_client_query(benchmark::State& state)
size_t buffer = 0;
auto buffer_span = std::span<uint8_t, sizeof(buffer)>(reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer));

prng.read(buffer_span);
csprng.generate(buffer_span);

return buffer % db_entry_count;
}();

auto is_query_preprocessed = client.prepare_query(rand_db_row_index, prng);
auto is_query_preprocessed = client.prepare_query(rand_db_row_index, csprng);
bool is_query_ready = true;
bool is_response_decoded = true;

Expand All @@ -70,7 +70,7 @@ bench_client_query(benchmark::State& state)
rand_db_row_index ^= (rand_db_row_index << 1) ^ 1ul;
rand_db_row_index %= db_entry_count;

is_query_preprocessed &= client.prepare_query(rand_db_row_index, prng);
is_query_preprocessed &= client.prepare_query(rand_db_row_index, csprng);
state.ResumeTiming();
}

Expand Down
6 changes: 3 additions & 3 deletions benches/bench_client_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ bench_client_setup(benchmark::State& state)
auto db_bytes_span = std::span<uint8_t, db_byte_len>(db_bytes);
auto pub_matM_bytes_span = std::span<uint8_t, pub_matM_byte_len>(pub_matM_bytes);

prng::prng_t prng{};
csprng::csprng_t csprng{};

prng.read(seed_μ_span);
prng.read(db_bytes_span);
csprng.generate(seed_μ_span);
csprng.generate(db_bytes_span);

auto [server, M] = server_t::setup(seed_μ_span, db_bytes_span);

Expand Down
10 changes: 5 additions & 5 deletions benches/bench_server_respond.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ bench_server_respond(benchmark::State& state)
auto query_bytes_span = std::span<uint8_t, query_byte_len>(query_bytes);
auto response_bytes_span = std::span<uint8_t, response_byte_len>(response_bytes);

prng::prng_t prng{};
csprng::csprng_t csprng{};

prng.read(seed_μ_span);
prng.read(db_bytes_span);
csprng.generate(seed_μ_span);
csprng.generate(db_bytes_span);

auto [server, M] = server_t::setup(seed_μ_span, db_bytes_span);

Expand All @@ -43,12 +43,12 @@ bench_server_respond(benchmark::State& state)
size_t buffer = 0;
auto buffer_span = std::span<uint8_t, sizeof(buffer)>(reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer));

prng.read(buffer_span);
csprng.generate(buffer_span);

return buffer % db_entry_count;
}();

const auto is_query_preprocessed = client.prepare_query(rand_db_row_index, prng);
const auto is_query_preprocessed = client.prepare_query(rand_db_row_index, csprng);
assert(is_query_preprocessed);

const auto is_query_ready = client.query(rand_db_row_index, query_bytes_span);
Expand Down
6 changes: 3 additions & 3 deletions benches/bench_server_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ bench_server_setup(benchmark::State& state)
auto seed_μ_span = std::span(seed_μ);
auto db_bytes_span = std::span<uint8_t, db_byte_len>(db_bytes);

prng::prng_t prng{};
csprng::csprng_t csprng{};

prng.read(seed_μ_span);
prng.read(db_bytes_span);
csprng.generate(seed_μ_span);
csprng.generate(db_bytes_span);

for (auto _ : state) {
auto [server, M] = server_t::setup(seed_μ_span, db_bytes_span);
Expand Down
15 changes: 15 additions & 0 deletions examples/example.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
EXAMPLE_BUILD_DIR := $(BUILD_DIR)/example

EXAMPLE_DIR := examples
EXAMPLE_SOURCES := $(wildcard $(EXAMPLE_DIR)/*.cpp)
EXAMPLE_HEADERS := $(wildcard $(EXAMPLE_DIR)/*.hpp)
EXAMPLE_EXECS := $(addprefix $(EXAMPLE_BUILD_DIR)/, $(notdir $(EXAMPLE_SOURCES:.cpp=.exe)))

$(EXAMPLE_BUILD_DIR):
mkdir -p $@

$(EXAMPLE_BUILD_DIR)/%.exe: $(EXAMPLE_DIR)/%.cpp $(EXAMPLE_BUILD_DIR)
$(CXX) $(CXX_DEFS) $(CXX_FLAGS) $(WARN_FLAGS) $(RELEASE_FLAGS) $(I_FLAGS) $(DEP_IFLAGS) $< -o $@

example: $(EXAMPLE_EXECS) ## Build and run example program, demonstrating usage of FrodoPIR API
$(foreach exec,$^,./$(exec))
11 changes: 6 additions & 5 deletions examples/frodoPIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "frodoPIR/server.hpp"
#include <algorithm>
#include <cassert>
#include <iomanip>
#include <iostream>
#include <sstream>

Expand All @@ -20,7 +21,7 @@ to_hex(std::span<const uint8_t> bytes)
}

// Compile with
// g++ -std=c++20 -Wall -Wextra -pedantic -O3 -march=native -I include -I sha3/include examples/frodoPIR.cpp
// g++ -std=c++20 -Wall -Wextra -pedantic -O3 -march=native -I include -I sha3/include -I RandomShake/include examples/frodoPIR.cpp
int
main()
{
Expand Down Expand Up @@ -52,12 +53,12 @@ main()
auto response_bytes_span = std::span<uint8_t, response_byte_len>(response_bytes);
auto obtained_db_row_bytes_span = std::span<uint8_t, db_entry_byte_len>(obtained_db_row_bytes);

prng::prng_t prng{};
csprng::csprng_t csprng{};

// Sample pseudo random seed
prng.read(seed_μ);
csprng.generate(seed_μ);
// Fill pseudo random database content
prng.read(db_bytes);
csprng.generate(db_bytes);

// Setup the FrodoPIR server
auto [server, M] = frodoPIR_server::server_t<λ, db_entry_count, db_entry_byte_len, mat_element_bitlen, lwe_dimension>::setup(seed_μ, db_bytes_span);
Expand All @@ -70,7 +71,7 @@ main()
constexpr size_t to_be_queried_db_row_index = 31;

// Client preprocesses a query, keeps cached for now; to be used when enquiring content of specified row of the database
const auto is_query_preprocessed = client.prepare_query(to_be_queried_db_row_index, prng);
const auto is_query_preprocessed = client.prepare_query(to_be_queried_db_row_index, csprng);
assert(is_query_preprocessed);

// Client wants to query content of specific database row, for which we've already a query partially prepared
Expand Down
Loading