Skip to content

Commit

Permalink
Merge pull request #9 from itzmeanjan/add-uSaber
Browse files Browse the repository at this point in the history
Implement uSaber KEM Variants
  • Loading branch information
itzmeanjan committed Oct 20, 2023
2 parents b0f5ac2 + 80afe4f commit 2dd5cee
Show file tree
Hide file tree
Showing 24 changed files with 3,829 additions and 488 deletions.
2 changes: 1 addition & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 88
ColumnLimit: 176
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
CXX = g++
CXX_FLAGS = -std=c++20
WARN_FLAGS = -Wall -Wextra -pedantic
OPT_FLAGS = -O3 -march=native -mtune=native
OPT_FLAGS = -O3 -march=native
LINK_FLAGS = -flto
I_FLAGS = -I ./include
DEP_IFLAGS = -I ./sha3/include -I ./subtle/include
Expand Down
402 changes: 290 additions & 112 deletions README.md

Large diffs are not rendered by default.

77 changes: 35 additions & 42 deletions benchmarks/bench_kem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@
#include <cassert>

// Benchmark Saber KEM key generation algorithm for various suggested parameters.
template<size_t L,
size_t EQ,
size_t EP,
size_t MU,
size_t seedBytes,
size_t noiseBytes,
size_t keyBytes>
template<size_t L, size_t EQ, size_t EP, size_t MU, size_t seedBytes, size_t noiseBytes, size_t keyBytes, bool uniform_sampling>
void
keygen(benchmark::State& state)
{
Expand All @@ -37,8 +31,7 @@ keygen(benchmark::State& state)
auto _skey = std::span<uint8_t, sklen>(skey);

for (auto _ : state) {
saber_kem::keygen<L, EQ, EP, MU, seedBytes, noiseBytes, keyBytes>(
_seedA, _seedS, _z, _pkey, _skey);
_saber_kem::keygen<L, EQ, EP, MU, seedBytes, noiseBytes, keyBytes, uniform_sampling>(_seedA, _seedS, _z, _pkey, _skey);

benchmark::DoNotOptimize(_seedA);
benchmark::DoNotOptimize(_seedS);
Expand All @@ -52,14 +45,7 @@ keygen(benchmark::State& state)
}

// Benchmark Saber KEM encapsulation algorithm for various suggested parameters.
template<size_t L,
size_t EQ,
size_t EP,
size_t ET,
size_t MU,
size_t seedBytes,
size_t noiseBytes,
size_t keyBytes>
template<size_t L, size_t EQ, size_t EP, size_t ET, size_t MU, size_t seedBytes, size_t noiseBytes, size_t keyBytes, bool uniform_sampling>
void
encaps(benchmark::State& state)
{
Expand Down Expand Up @@ -92,11 +78,10 @@ encaps(benchmark::State& state)
auto _ctxt = std::span<uint8_t, ctlen>(ctxt);
auto _seskey = std::span<uint8_t, sha3_256::DIGEST_LEN>(seskey);

saber_kem::keygen<L, EQ, EP, MU>(_seedA, _seedS, _z, _pkey, _skey);
_saber_kem::keygen<L, EQ, EP, MU, seedBytes, noiseBytes, keyBytes, uniform_sampling>(_seedA, _seedS, _z, _pkey, _skey);

for (auto _ : state) {
saber_kem::encaps<L, EQ, EP, ET, MU, seedBytes, keyBytes>(
_m, _pkey, _ctxt, _seskey);
_saber_kem::encaps<L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling>(_m, _pkey, _ctxt, _seskey);

benchmark::DoNotOptimize(_m);
benchmark::DoNotOptimize(_pkey);
Expand All @@ -109,14 +94,7 @@ encaps(benchmark::State& state)
}

// Benchmark Saber KEM decapsulation algorithm for various suggested parameters.
template<size_t L,
size_t EQ,
size_t EP,
size_t ET,
size_t MU,
size_t seedBytes,
size_t noiseBytes,
size_t keyBytes>
template<size_t L, size_t EQ, size_t EP, size_t ET, size_t MU, size_t seedBytes, size_t noiseBytes, size_t keyBytes, bool uniform_sampling>
void
decaps(benchmark::State& state)
{
Expand Down Expand Up @@ -151,12 +129,11 @@ decaps(benchmark::State& state)
auto _seskey0 = std::span<uint8_t, sha3_256::DIGEST_LEN>(seskey0);
auto _seskey1 = std::span<uint8_t, sha3_256::DIGEST_LEN>(seskey1);

saber_kem::keygen<L, EQ, EP, MU, seedBytes, noiseBytes, keyBytes>(
_seedA, _seedS, _z, _pkey, _skey);
saber_kem::encaps<L, EQ, EP, ET, MU, seedBytes, keyBytes>(_m, _pkey, _ctxt, _seskey0);
_saber_kem::keygen<L, EQ, EP, MU, seedBytes, noiseBytes, keyBytes, uniform_sampling>(_seedA, _seedS, _z, _pkey, _skey);
_saber_kem::encaps<L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling>(_m, _pkey, _ctxt, _seskey0);

for (auto _ : state) {
saber_kem::decaps<L, EQ, EP, ET, MU, seedBytes, keyBytes>(_ctxt, _skey, _seskey1);
_saber_kem::decaps<L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling>(_ctxt, _skey, _seskey1);

benchmark::DoNotOptimize(_ctxt);
benchmark::DoNotOptimize(_skey);
Expand All @@ -168,15 +145,31 @@ decaps(benchmark::State& state)
state.SetItemsProcessed(state.iterations());
}

// Register for benchmarking LightSaber, Saber and FireSaber KEM routines.
BENCHMARK(keygen<2, 13, 10, 10, 32, 32, 32>)->Name("lightsaber/keygen");
BENCHMARK(encaps<2, 13, 10, 3, 10, 32, 32, 32>)->Name("lightsaber/encaps");
BENCHMARK(decaps<2, 13, 10, 3, 10, 32, 32, 32>)->Name("lightsaber/decaps");
const auto compute_min = [](const std::vector<double>& v) -> double { return *std::min_element(v.begin(), v.end()); };
const auto compute_max = [](const std::vector<double>& v) -> double { return *std::max_element(v.begin(), v.end()); };

BENCHMARK(keygen<3, 13, 10, 8, 32, 32, 32>)->Name("saber/keygen");
BENCHMARK(encaps<3, 13, 10, 4, 8, 32, 32, 32>)->Name("saber/encaps");
BENCHMARK(decaps<3, 13, 10, 4, 8, 32, 32, 32>)->Name("saber/decaps");
// Register for benchmarking LightSaber, Saber, FireSaber, uLightSaber, uSaber and
// uFireSaber KEM routines.
BENCHMARK(keygen<2, 13, 10, 10, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("lightsaber/keygen");
BENCHMARK(encaps<2, 13, 10, 3, 10, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("lightsaber/encaps");
BENCHMARK(decaps<2, 13, 10, 3, 10, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("lightsaber/decaps");

BENCHMARK(keygen<4, 13, 10, 6, 32, 32, 32>)->Name("firesaber/keygen");
BENCHMARK(encaps<4, 13, 10, 6, 6, 32, 32, 32>)->Name("firesaber/encaps");
BENCHMARK(decaps<4, 13, 10, 6, 6, 32, 32, 32>)->Name("firesaber/decaps");
BENCHMARK(keygen<3, 13, 10, 8, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("saber/keygen");
BENCHMARK(encaps<3, 13, 10, 4, 8, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("saber/encaps");
BENCHMARK(decaps<3, 13, 10, 4, 8, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("saber/decaps");

BENCHMARK(keygen<4, 13, 10, 6, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("firesaber/keygen");
BENCHMARK(encaps<4, 13, 10, 6, 6, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("firesaber/encaps");
BENCHMARK(decaps<4, 13, 10, 6, 6, 32, 32, 32, false>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("firesaber/decaps");

BENCHMARK(keygen<2, 12, 10, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("ulightsaber/keygen");
BENCHMARK(encaps<2, 12, 10, 3, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("ulightsaber/encaps");
BENCHMARK(decaps<2, 12, 10, 3, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("ulightsaber/decaps");

BENCHMARK(keygen<3, 12, 10, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("usaber/keygen");
BENCHMARK(encaps<3, 12, 10, 4, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("usaber/encaps");
BENCHMARK(decaps<3, 12, 10, 4, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("usaber/decaps");

BENCHMARK(keygen<4, 12, 10, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("ufiresaber/keygen");
BENCHMARK(encaps<4, 12, 10, 6, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("ufiresaber/encaps");
BENCHMARK(decaps<4, 12, 10, 6, 2, 32, 32, 32, true>)->ComputeStatistics("min", compute_min)->ComputeStatistics("max", compute_max)->Name("ufiresaber/decaps");
16 changes: 6 additions & 10 deletions include/firesaber_kem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ constexpr size_t MU = 6;
constexpr size_t seedBytes = 32;
constexpr size_t noiseBytes = 32;
constexpr size_t keyBytes = 32;
constexpr bool uniform_sampling = false;

// 1312 -bytes FireSaber KEM public key
constexpr size_t PK_LEN = saber_utils::kem_pklen<L, EP, seedBytes>();
Expand All @@ -32,29 +33,24 @@ keygen(std::span<const uint8_t, seedBytes> seedA,
std::span<uint8_t, PK_LEN> pkey,
std::span<uint8_t, SK_LEN> skey)
{
saber_kem::keygen<L, EQ, EP, MU>(seedA, seedS, z, pkey, skey);
_saber_kem::keygen<L, EQ, EP, MU, seedBytes, noiseBytes, keyBytes, uniform_sampling>(seedA, seedS, z, pkey, skey);
}

// Given 32 -bytes random sampled `m` and 1312 -bytes FireSaber KEM public key, this
// routine generates a 1472 -bytes cipher text ( encapsulating fixed width message,
// which will be used for deriving shared secret key ) and 32 -bytes session key.
inline void
encaps(std::span<const uint8_t, keyBytes> m,
std::span<const uint8_t, PK_LEN> pkey,
std::span<uint8_t, CT_LEN> ctxt,
std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
encaps(std::span<const uint8_t, keyBytes> m, std::span<const uint8_t, PK_LEN> pkey, std::span<uint8_t, CT_LEN> ctxt, std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
{
saber_kem::encaps<L, EQ, EP, ET, MU, seedBytes>(m, pkey, ctxt, seskey);
_saber_kem::encaps<L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling>(m, pkey, ctxt, seskey);
}

// Given 1472 -bytes cipher text and 3040 -bytes FireSaber KEM secret key, this routine
// can be used for decapsulating the cipher text, deriving 32 -bytes session key.
inline void
decaps(std::span<const uint8_t, CT_LEN> ctxt,
std::span<const uint8_t, SK_LEN> skey,
std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
decaps(std::span<const uint8_t, CT_LEN> ctxt, std::span<const uint8_t, SK_LEN> skey, std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
{
saber_kem::decaps<L, EQ, EP, ET, MU, seedBytes, keyBytes>(ctxt, skey, seskey);
_saber_kem::decaps<L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling>(ctxt, skey, seskey);
}

}
62 changes: 17 additions & 45 deletions include/kem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,21 @@
#include "utils.hpp"

// Algorithms related to Saber Key Encapsulation Mechanism
namespace saber_kem {
namespace _saber_kem {

// Given seedBytes `seedA` ( used for generating matrix A, in Saber PKE keygen algorithm
// ), noiseBytes `seedS` ( used for generating secret vector s, in Saber PKE keygen
// algorithm ) and keyBytes `z` ( random sampled bytes, used for randomizing Saber KEM
// secret key ), this routine can be used for generating a Saber KEM public/ private
// keypair, following algorithm 20 in section 8.5.1 of Saber spec.
template<size_t L,
size_t EQ,
size_t EP,
size_t MU,
size_t seedBytes,
size_t noiseBytes,
size_t keyBytes>
template<size_t L, size_t EQ, size_t EP, size_t MU, size_t seedBytes, size_t noiseBytes, size_t keyBytes, bool uniform_sampling>
inline void
keygen(
std::span<const uint8_t, seedBytes> seedA,
std::span<const uint8_t, noiseBytes> seedS,
std::span<const uint8_t, keyBytes> z,
std::span<uint8_t, saber_utils::kem_pklen<L, EP, seedBytes>()> pkey,
std::span<uint8_t, saber_utils::kem_sklen<L, EQ, EP, seedBytes, keyBytes>()> skey)
requires(saber_params::validate_kem_keygen_args(L,
EQ,
EP,
MU,
seedBytes,
noiseBytes,
keyBytes))
keygen(std::span<const uint8_t, seedBytes> seedA,
std::span<const uint8_t, noiseBytes> seedS,
std::span<const uint8_t, keyBytes> z,
std::span<uint8_t, saber_utils::kem_pklen<L, EP, seedBytes>()> pkey,
std::span<uint8_t, saber_utils::kem_sklen<L, EQ, EP, seedBytes, keyBytes>()> skey)
requires(saber_params::validate_kem_keygen_args(L, EQ, EP, MU, seedBytes, noiseBytes, keyBytes, uniform_sampling))
{
constexpr size_t pke_pklen = saber_utils::pke_pklen<L, EP, seedBytes>();
constexpr size_t pke_sklen = saber_utils::pke_sklen<L, EQ>();
Expand All @@ -47,7 +34,7 @@ keygen(
auto sk_z = skey.template subspan<off2, keyBytes>();

// step 1
saber_pke::keygen<L, EQ, EP, MU>(seedA, seedS, pkey, sk_sk);
saber_pke::keygen<L, EQ, EP, MU, seedBytes, noiseBytes, uniform_sampling>(seedA, seedS, pkey, sk_sk);
// step 4 ( partial )
std::memcpy(sk_pk.data(), pkey.data(), pkey.size());

Expand All @@ -65,20 +52,13 @@ keygen(
// Given keyBytes input `m` ( random sampled ) and Saber KEM public key, this routine
// can be used for generating a session key ( of 32 -bytes ) and Saber KEM cipher text.
// This is an implementation of algorithm 21 in section 8.5.2 of Saber spec.
template<size_t L,
size_t EQ,
size_t EP,
size_t ET,
size_t MU,
size_t seedBytes,
size_t keyBytes>
template<size_t L, size_t EQ, size_t EP, size_t ET, size_t MU, size_t seedBytes, size_t keyBytes, bool uniform_sampling>
inline void
encaps(std::span<const uint8_t, keyBytes> m, // step 1
std::span<const uint8_t, saber_utils::kem_pklen<L, EP, seedBytes>()> pkey,
std::span<uint8_t, saber_utils::kem_ctlen<L, EP, ET>()> ctxt,
std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
requires(
saber_params::validate_kem_encaps_args(L, EQ, EP, ET, MU, seedBytes, keyBytes))
requires(saber_params::validate_kem_encaps_args(L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling))
{
std::array<uint8_t, sha3_256::DIGEST_LEN> hashed_m;
std::array<uint8_t, sha3_256::DIGEST_LEN> hashed_pk;
Expand Down Expand Up @@ -113,7 +93,7 @@ encaps(std::span<const uint8_t, keyBytes> m, // step 1
// step 7
auto _hm = std::span<const uint8_t, hashed_m.size()>(hashed_m);
auto _r = std::span<const uint8_t, r.size()>(r);
saber_pke::encrypt<L, EQ, EP, ET, MU>(_hm, _r, pkey, ctxt);
saber_pke::encrypt<L, EQ, EP, ET, MU, seedBytes, uniform_sampling>(_hm, _r, pkey, ctxt);

// step 8
h256.absorb(ctxt);
Expand All @@ -132,20 +112,12 @@ encaps(std::span<const uint8_t, keyBytes> m, // step 1
// Given Saber KEM cipher text and Saber KEM secret key, this routine can be used for
// decapsulating the received cipher text, extracting a shared secret key of 32 -bytes.
// This is an implementation of algorithm 22 in section 8.5.3 of Saber spec.
template<size_t L,
size_t EQ,
size_t EP,
size_t ET,
size_t MU,
size_t seedBytes,
size_t keyBytes>
template<size_t L, size_t EQ, size_t EP, size_t ET, size_t MU, size_t seedBytes, size_t keyBytes, bool uniform_sampling>
inline void
decaps(std::span<const uint8_t, saber_utils::kem_ctlen<L, EP, ET>()> ctxt,
std::span<const uint8_t,
saber_utils::kem_sklen<L, EQ, EP, seedBytes, keyBytes>()> skey,
std::span<const uint8_t, saber_utils::kem_sklen<L, EQ, EP, seedBytes, keyBytes>()> skey,
std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
requires(
saber_params::validate_kem_decaps_args(L, EQ, EP, ET, MU, seedBytes, keyBytes))
requires(saber_params::validate_kem_decaps_args(L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling))
{
constexpr size_t pke_pklen = saber_utils::pke_pklen<L, EP, seedBytes>();
constexpr size_t pke_sklen = saber_utils::pke_sklen<L, EQ>();
Expand All @@ -166,7 +138,7 @@ decaps(std::span<const uint8_t, saber_utils::kem_ctlen<L, EP, ET>()> ctxt,
std::array<uint8_t, keyBytes> temp;

// step 2
saber_pke::decrypt<L, EQ, EP, ET, MU>(ctxt, sk, m);
saber_pke::decrypt<L, EQ, EP, ET, MU, uniform_sampling>(ctxt, sk, m);

// step 3, 4
sha3_512::sha3_512_t h512;
Expand All @@ -183,7 +155,7 @@ decaps(std::span<const uint8_t, saber_utils::kem_ctlen<L, EP, ET>()> ctxt,
// step 6
auto _m = std::span<const uint8_t, m.size()>(m);
auto _r = std::span<const uint8_t, r.size()>(r);
saber_pke::encrypt<L, EQ, EP, ET, MU>(_m, _r, pk, ctxt_prm);
saber_pke::encrypt<L, EQ, EP, ET, MU, seedBytes, uniform_sampling>(_m, _r, pk, ctxt_prm);

// step 7
auto c = saber_utils::ct_eq_bytes<ctxt.size()>(ctxt_prm, ctxt);
Expand Down
16 changes: 6 additions & 10 deletions include/lightsaber_kem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ constexpr size_t MU = 10;
constexpr size_t seedBytes = 32;
constexpr size_t noiseBytes = 32;
constexpr size_t keyBytes = 32;
constexpr bool uniform_sampling = false;

// 672 -bytes LightSaber KEM public key
constexpr size_t PK_LEN = saber_utils::kem_pklen<L, EP, seedBytes>();
Expand All @@ -32,29 +33,24 @@ keygen(std::span<const uint8_t, seedBytes> seedA,
std::span<uint8_t, PK_LEN> pkey,
std::span<uint8_t, SK_LEN> skey)
{
saber_kem::keygen<L, EQ, EP, MU>(seedA, seedS, z, pkey, skey);
_saber_kem::keygen<L, EQ, EP, MU, seedBytes, noiseBytes, keyBytes, uniform_sampling>(seedA, seedS, z, pkey, skey);
}

// Given 32 -bytes random sampled `m` and 672 -bytes LightSaber KEM public key, this
// routine generates a 736 -bytes cipher text ( encapsulating fixed width message, which
// will be used for deriving shared secret key ) and 32 -bytes session key.
inline void
encaps(std::span<const uint8_t, keyBytes> m,
std::span<const uint8_t, PK_LEN> pkey,
std::span<uint8_t, CT_LEN> ctxt,
std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
encaps(std::span<const uint8_t, keyBytes> m, std::span<const uint8_t, PK_LEN> pkey, std::span<uint8_t, CT_LEN> ctxt, std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
{
saber_kem::encaps<L, EQ, EP, ET, MU, seedBytes>(m, pkey, ctxt, seskey);
_saber_kem::encaps<L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling>(m, pkey, ctxt, seskey);
}

// Given 736 -bytes cipher text and 1568 -bytes LightSaber KEM secret key, this routine
// can be used for decapsulating the cipher text, deriving 32 -bytes session key.
inline void
decaps(std::span<const uint8_t, CT_LEN> ctxt,
std::span<const uint8_t, SK_LEN> skey,
std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
decaps(std::span<const uint8_t, CT_LEN> ctxt, std::span<const uint8_t, SK_LEN> skey, std::span<uint8_t, sha3_256::DIGEST_LEN> seskey)
{
saber_kem::decaps<L, EQ, EP, ET, MU, seedBytes, keyBytes>(ctxt, skey, seskey);
_saber_kem::decaps<L, EQ, EP, ET, MU, seedBytes, keyBytes, uniform_sampling>(ctxt, skey, seskey);
}

}
Loading

0 comments on commit 2dd5cee

Please sign in to comment.